1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
3 * GIO - GLib Input, Output and Streaming Library
5 * Copyright 2010 Red Hat, Inc.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General
18 * Public License along with this library; if not, see
19 * <http://www.gnu.org/licenses/>.
26 #include "gproxyresolvergnome.h"
28 #include <glib/gi18n-lib.h>
29 #include <gdesktop-enums.h>
31 #define GNOME_PROXY_SETTINGS_SCHEMA "org.gnome.system.proxy"
32 #define GNOME_PROXY_MODE_KEY "mode"
33 #define GNOME_PROXY_AUTOCONFIG_URL_KEY "autoconfig-url"
34 #define GNOME_PROXY_IGNORE_HOSTS_KEY "ignore-hosts"
35 #define GNOME_PROXY_USE_SAME_PROXY_KEY "use-same-proxy"
37 #define GNOME_PROXY_HTTP_CHILD_SCHEMA "http"
38 #define GNOME_PROXY_HTTP_HOST_KEY "host"
39 #define GNOME_PROXY_HTTP_PORT_KEY "port"
40 #define GNOME_PROXY_HTTP_USE_AUTH_KEY "use-authentication"
41 #define GNOME_PROXY_HTTP_USER_KEY "authentication-user"
42 #define GNOME_PROXY_HTTP_PASSWORD_KEY "authentication-password"
44 #define GNOME_PROXY_HTTPS_CHILD_SCHEMA "https"
45 #define GNOME_PROXY_HTTPS_HOST_KEY "host"
46 #define GNOME_PROXY_HTTPS_PORT_KEY "port"
48 #define GNOME_PROXY_FTP_CHILD_SCHEMA "ftp"
49 #define GNOME_PROXY_FTP_HOST_KEY "host"
50 #define GNOME_PROXY_FTP_PORT_KEY "port"
52 #define GNOME_PROXY_SOCKS_CHILD_SCHEMA "socks"
53 #define GNOME_PROXY_SOCKS_HOST_KEY "host"
54 #define GNOME_PROXY_SOCKS_PORT_KEY "port"
56 /* We have to has-a GSimpleProxyResolver rather than is-a one,
57 * because a dynamic type cannot reimplement an interface that
58 * its parent also implements... for some reason.
61 struct _GProxyResolverGnome {
62 GObject parent_instance;
64 GProxyResolver *base_resolver;
66 GSettings *proxy_settings;
67 GSettings *http_settings;
68 GSettings *https_settings;
69 GSettings *ftp_settings;
70 GSettings *socks_settings;
73 GDesktopProxyMode mode;
74 gchar *autoconfig_url;
75 gboolean use_same_proxy;
77 GDBusProxy *pacrunner;
82 static GProxyResolverInterface *g_proxy_resolver_gnome_parent_iface;
84 static void g_proxy_resolver_gnome_iface_init (GProxyResolverInterface *iface);
86 G_DEFINE_DYNAMIC_TYPE_EXTENDED (GProxyResolverGnome,
87 g_proxy_resolver_gnome,
89 G_IMPLEMENT_INTERFACE_DYNAMIC (G_TYPE_PROXY_RESOLVER,
90 g_proxy_resolver_gnome_iface_init))
93 g_proxy_resolver_gnome_class_finalize (GProxyResolverGnomeClass *klass)
98 gsettings_changed (GSettings *settings,
102 GProxyResolverGnome *resolver = user_data;
104 g_mutex_lock (&resolver->lock);
105 resolver->need_update = TRUE;
106 g_mutex_unlock (&resolver->lock);
110 g_proxy_resolver_gnome_finalize (GObject *object)
112 GProxyResolverGnome *resolver = G_PROXY_RESOLVER_GNOME (object);
114 if (resolver->proxy_settings)
116 g_signal_handlers_disconnect_by_func (resolver->proxy_settings,
117 (gpointer)gsettings_changed,
119 g_object_unref (resolver->proxy_settings);
121 g_signal_handlers_disconnect_by_func (resolver->http_settings,
122 (gpointer)gsettings_changed,
124 g_object_unref (resolver->http_settings);
126 g_signal_handlers_disconnect_by_func (resolver->https_settings,
127 (gpointer)gsettings_changed,
129 g_object_unref (resolver->https_settings);
131 g_signal_handlers_disconnect_by_func (resolver->ftp_settings,
132 (gpointer)gsettings_changed,
134 g_object_unref (resolver->ftp_settings);
136 g_signal_handlers_disconnect_by_func (resolver->socks_settings,
137 (gpointer)gsettings_changed,
139 g_object_unref (resolver->socks_settings);
142 g_clear_object (&resolver->base_resolver);
143 g_clear_object (&resolver->pacrunner);
145 g_free (resolver->autoconfig_url);
147 g_mutex_clear (&resolver->lock);
149 G_OBJECT_CLASS (g_proxy_resolver_gnome_parent_class)->finalize (object);
153 g_proxy_resolver_gnome_init (GProxyResolverGnome *resolver)
155 g_mutex_init (&resolver->lock);
157 resolver->base_resolver = g_simple_proxy_resolver_new (NULL, NULL);
159 resolver->proxy_settings = g_settings_new (GNOME_PROXY_SETTINGS_SCHEMA);
160 g_signal_connect (resolver->proxy_settings, "changed",
161 G_CALLBACK (gsettings_changed), resolver);
162 resolver->http_settings = g_settings_get_child (resolver->proxy_settings,
163 GNOME_PROXY_HTTP_CHILD_SCHEMA);
164 g_signal_connect (resolver->http_settings, "changed",
165 G_CALLBACK (gsettings_changed), resolver);
166 resolver->https_settings = g_settings_get_child (resolver->proxy_settings,
167 GNOME_PROXY_HTTPS_CHILD_SCHEMA);
168 g_signal_connect (resolver->https_settings, "changed",
169 G_CALLBACK (gsettings_changed), resolver);
170 resolver->ftp_settings = g_settings_get_child (resolver->proxy_settings,
171 GNOME_PROXY_FTP_CHILD_SCHEMA);
172 g_signal_connect (resolver->ftp_settings, "changed",
173 G_CALLBACK (gsettings_changed), resolver);
174 resolver->socks_settings = g_settings_get_child (resolver->proxy_settings,
175 GNOME_PROXY_SOCKS_CHILD_SCHEMA);
176 g_signal_connect (resolver->socks_settings, "changed",
177 G_CALLBACK (gsettings_changed), resolver);
179 resolver->need_update = TRUE;
182 /* called with lock held */
184 update_settings (GProxyResolverGnome *resolver)
186 GSimpleProxyResolver *simple = G_SIMPLE_PROXY_RESOLVER (resolver->base_resolver);
187 gchar **ignore_hosts;
188 gchar *host, *http_proxy, *proxy;
191 resolver->need_update = FALSE;
193 g_free (resolver->autoconfig_url);
194 g_simple_proxy_resolver_set_default_proxy (simple, NULL);
195 g_simple_proxy_resolver_set_ignore_hosts (simple, NULL);
196 g_simple_proxy_resolver_set_uri_proxy (simple, "http", NULL);
197 g_simple_proxy_resolver_set_uri_proxy (simple, "https", NULL);
198 g_simple_proxy_resolver_set_uri_proxy (simple, "ftp", NULL);
201 g_settings_get_enum (resolver->proxy_settings, GNOME_PROXY_MODE_KEY);
202 resolver->autoconfig_url =
203 g_settings_get_string (resolver->proxy_settings, GNOME_PROXY_AUTOCONFIG_URL_KEY);
205 if (resolver->mode == G_DESKTOP_PROXY_MODE_AUTO && !resolver->pacrunner)
207 GError *error = NULL;
208 resolver->pacrunner =
209 g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
210 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
211 G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
213 "org.gtk.GLib.PACRunner",
214 "/org/gtk/GLib/PACRunner",
215 "org.gtk.GLib.PACRunner",
219 g_warning ("Could not start proxy autoconfiguration helper:"
220 "\n %s\nProxy autoconfiguration will not work",
224 else if (resolver->mode != G_DESKTOP_PROXY_MODE_AUTO && resolver->pacrunner)
226 g_object_unref (resolver->pacrunner);
227 resolver->pacrunner = NULL;
231 g_settings_get_strv (resolver->proxy_settings, GNOME_PROXY_IGNORE_HOSTS_KEY);
232 g_simple_proxy_resolver_set_ignore_hosts (simple, ignore_hosts);
233 g_strfreev (ignore_hosts);
235 if (resolver->mode == G_DESKTOP_PROXY_MODE_AUTO)
237 /* We use the base_resolver to handle ignore_hosts in the AUTO case,
238 * so we have to set a non-"direct://" default proxy so we can distinguish
241 g_simple_proxy_resolver_set_default_proxy (simple, "use-proxy:");
244 if (resolver->mode != G_DESKTOP_PROXY_MODE_MANUAL)
247 host = g_settings_get_string (resolver->http_settings, GNOME_PROXY_HTTP_HOST_KEY);
248 port = g_settings_get_int (resolver->http_settings, GNOME_PROXY_HTTP_PORT_KEY);
251 if (g_settings_get_boolean (resolver->http_settings, GNOME_PROXY_HTTP_USE_AUTH_KEY))
253 gchar *user, *password;
254 gchar *enc_user, *enc_password;
256 user = g_settings_get_string (resolver->http_settings, GNOME_PROXY_HTTP_USER_KEY);
257 enc_user = g_uri_escape_string (user, NULL, TRUE);
259 password = g_settings_get_string (resolver->http_settings, GNOME_PROXY_HTTP_PASSWORD_KEY);
260 enc_password = g_uri_escape_string (password, NULL, TRUE);
263 http_proxy = g_strdup_printf ("http://%s:%s@%s:%u",
264 enc_user, enc_password,
267 g_free (enc_password);
270 http_proxy = g_strdup_printf ("http://%s:%u", host, port);
272 g_simple_proxy_resolver_set_uri_proxy (simple, "http", http_proxy);
273 if (g_settings_get_boolean (resolver->proxy_settings, GNOME_PROXY_USE_SAME_PROXY_KEY))
274 g_simple_proxy_resolver_set_default_proxy (simple, http_proxy);
280 host = g_settings_get_string (resolver->https_settings, GNOME_PROXY_HTTPS_HOST_KEY);
281 port = g_settings_get_int (resolver->https_settings, GNOME_PROXY_HTTPS_PORT_KEY);
284 proxy = g_strdup_printf ("http://%s:%u", host, port);
285 g_simple_proxy_resolver_set_uri_proxy (simple, "https", proxy);
289 g_simple_proxy_resolver_set_uri_proxy (simple, "https", http_proxy);
292 host = g_settings_get_string (resolver->socks_settings, GNOME_PROXY_SOCKS_HOST_KEY);
293 port = g_settings_get_int (resolver->socks_settings, GNOME_PROXY_SOCKS_PORT_KEY);
296 proxy = g_strdup_printf ("socks://%s:%u", host, port);
297 g_simple_proxy_resolver_set_default_proxy (simple, proxy);
304 host = g_settings_get_string (resolver->ftp_settings, GNOME_PROXY_FTP_HOST_KEY);
305 port = g_settings_get_int (resolver->ftp_settings, GNOME_PROXY_FTP_PORT_KEY);
308 proxy = g_strdup_printf ("ftp://%s:%u", host, port);
309 g_simple_proxy_resolver_set_uri_proxy (simple, "ftp", proxy);
316 g_proxy_resolver_gnome_is_supported (GProxyResolver *object)
318 const char *desktops;
320 desktops = g_getenv ("XDG_CURRENT_DESKTOP");
324 /* Remember that XDG_CURRENT_DESKTOP is a list of strings. Desktops that
325 * pretend to be GNOME and want to use our proxy settings will list
326 * themselves alongside GNOME. That's fine; they'll get our proxy settings.
328 return strstr (desktops, "GNOME") != NULL;
331 /* Threadsafely determines what to do with @uri; returns %FALSE if an
332 * error occurs, %TRUE and an array of proxies if the mode is NONE or
333 * MANUAL, or if @uri is covered by ignore-hosts, or %TRUE and a
334 * (transfer-full) pacrunner and autoconfig url if the mode is AUTOMATIC.
337 g_proxy_resolver_gnome_lookup_internal (GProxyResolverGnome *resolver,
339 gchar ***out_proxies,
340 GDBusProxy **out_pacrunner,
341 gchar **out_autoconfig_url,
342 GCancellable *cancellable,
345 gchar **proxies = NULL;
348 *out_pacrunner = NULL;
349 *out_autoconfig_url = NULL;
351 g_mutex_lock (&resolver->lock);
352 if (resolver->need_update)
353 update_settings (resolver);
355 proxies = g_proxy_resolver_lookup (resolver->base_resolver,
356 uri, cancellable, error);
360 /* Parent class does ignore-host handling */
361 if (!strcmp (proxies[0], "direct://") && !proxies[1])
364 if (resolver->pacrunner)
366 g_clear_pointer (&proxies, g_strfreev);
367 *out_pacrunner = g_object_ref (resolver->pacrunner);
368 *out_autoconfig_url = g_strdup (resolver->autoconfig_url);
373 g_mutex_unlock (&resolver->lock);
377 *out_proxies = proxies;
380 else if (*out_pacrunner)
387 g_proxy_resolver_gnome_lookup (GProxyResolver *proxy_resolver,
389 GCancellable *cancellable,
392 GProxyResolverGnome *resolver = G_PROXY_RESOLVER_GNOME (proxy_resolver);
393 GDBusProxy *pacrunner;
394 gchar **proxies, *autoconfig_url;
396 if (!g_proxy_resolver_gnome_lookup_internal (resolver, uri,
397 &proxies, &pacrunner, &autoconfig_url,
405 vproxies = g_dbus_proxy_call_sync (pacrunner,
407 g_variant_new ("(ss)",
410 G_DBUS_CALL_FLAGS_NONE,
415 g_variant_get (vproxies, "(^as)", &proxies);
416 g_variant_unref (vproxies);
421 g_object_unref (pacrunner);
422 g_free (autoconfig_url);
429 got_autoconfig_proxies (GObject *source,
430 GAsyncResult *result,
433 GTask *task = user_data;
436 GError *error = NULL;
438 vproxies = g_dbus_proxy_call_finish (G_DBUS_PROXY (source),
442 g_variant_get (vproxies, "(^as)", &proxies);
443 g_task_return_pointer (task, proxies, (GDestroyNotify)g_strfreev);
444 g_variant_unref (vproxies);
447 g_task_return_error (task, error);
448 g_object_unref (task);
452 g_proxy_resolver_gnome_lookup_async (GProxyResolver *proxy_resolver,
454 GCancellable *cancellable,
455 GAsyncReadyCallback callback,
458 GProxyResolverGnome *resolver = G_PROXY_RESOLVER_GNOME (proxy_resolver);
460 char **proxies, *autoconfig_url;
461 GDBusProxy *pacrunner;
462 GError *error = NULL;
464 task = g_task_new (resolver, cancellable, callback, user_data);
465 g_task_set_source_tag (task, g_proxy_resolver_gnome_lookup_async);
466 g_task_set_name (task, "[glib-networking] g_proxy_resolver_gnome_lookup_async");
468 if (!g_proxy_resolver_gnome_lookup_internal (resolver, uri,
469 &proxies, &pacrunner, &autoconfig_url,
470 cancellable, &error))
472 g_task_return_error (task, error);
473 g_object_unref (task);
478 g_task_return_pointer (task, proxies, (GDestroyNotify)g_strfreev);
479 g_object_unref (task);
483 g_dbus_proxy_call (pacrunner,
485 g_variant_new ("(ss)",
488 G_DBUS_CALL_FLAGS_NONE,
491 got_autoconfig_proxies,
493 g_object_unref (pacrunner);
494 g_free (autoconfig_url);
498 g_proxy_resolver_gnome_lookup_finish (GProxyResolver *resolver,
499 GAsyncResult *result,
502 g_return_val_if_fail (g_task_is_valid (result, resolver), NULL);
503 g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == g_proxy_resolver_gnome_lookup_async, NULL);
505 return g_task_propagate_pointer (G_TASK (result), error);
509 g_proxy_resolver_gnome_class_init (GProxyResolverGnomeClass *resolver_class)
511 GObjectClass *object_class;
513 object_class = G_OBJECT_CLASS (resolver_class);
514 object_class->finalize = g_proxy_resolver_gnome_finalize;
518 g_proxy_resolver_gnome_iface_init (GProxyResolverInterface *iface)
520 g_proxy_resolver_gnome_parent_iface = g_type_interface_peek_parent (iface);
522 iface->is_supported = g_proxy_resolver_gnome_is_supported;
523 iface->lookup = g_proxy_resolver_gnome_lookup;
524 iface->lookup_async = g_proxy_resolver_gnome_lookup_async;
525 iface->lookup_finish = g_proxy_resolver_gnome_lookup_finish;
529 g_proxy_resolver_gnome_register (GIOModule *module)
531 g_proxy_resolver_gnome_register_type (G_TYPE_MODULE (module));
533 g_io_extension_point_register (G_PROXY_RESOLVER_EXTENSION_POINT_NAME);
534 g_io_extension_point_implement (G_PROXY_RESOLVER_EXTENSION_POINT_NAME,
535 g_proxy_resolver_gnome_get_type(),