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");
321 if (desktops == NULL)
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 static inline gchar **
332 make_proxies (const gchar *proxy)
336 proxies = g_new (gchar *, 2);
337 proxies[0] = g_strdup (proxy);
343 /* Threadsafely determines what to do with @uri; returns %FALSE if an
344 * error occurs, %TRUE and an array of proxies if the mode is NONE or
345 * MANUAL, or if @uri is covered by ignore-hosts, or %TRUE and a
346 * (transfer-full) pacrunner and autoconfig url if the mode is AUTOMATIC.
349 g_proxy_resolver_gnome_lookup_internal (GProxyResolverGnome *resolver,
351 gchar ***out_proxies,
352 GDBusProxy **out_pacrunner,
353 gchar **out_autoconfig_url,
354 GCancellable *cancellable,
357 gchar **proxies = NULL;
360 *out_pacrunner = NULL;
361 *out_autoconfig_url = NULL;
363 g_mutex_lock (&resolver->lock);
364 if (resolver->need_update)
365 update_settings (resolver);
367 proxies = g_proxy_resolver_lookup (resolver->base_resolver,
368 uri, cancellable, error);
372 /* Parent class does ignore-host handling */
373 if (!strcmp (proxies[0], "direct://") && !proxies[1])
376 if (resolver->pacrunner)
378 g_clear_pointer (&proxies, g_strfreev);
379 *out_pacrunner = g_object_ref (resolver->pacrunner);
380 *out_autoconfig_url = g_strdup (resolver->autoconfig_url);
385 g_mutex_unlock (&resolver->lock);
389 *out_proxies = proxies;
392 else if (*out_pacrunner)
399 g_proxy_resolver_gnome_lookup (GProxyResolver *proxy_resolver,
401 GCancellable *cancellable,
404 GProxyResolverGnome *resolver = G_PROXY_RESOLVER_GNOME (proxy_resolver);
405 GDBusProxy *pacrunner;
406 gchar **proxies, *autoconfig_url;
408 if (!g_proxy_resolver_gnome_lookup_internal (resolver, uri,
409 &proxies, &pacrunner, &autoconfig_url,
417 vproxies = g_dbus_proxy_call_sync (pacrunner,
419 g_variant_new ("(ss)",
422 G_DBUS_CALL_FLAGS_NONE,
427 g_variant_get (vproxies, "(^as)", &proxies);
428 g_variant_unref (vproxies);
433 g_object_unref (pacrunner);
434 g_free (autoconfig_url);
441 got_autoconfig_proxies (GObject *source,
442 GAsyncResult *result,
445 GTask *task = user_data;
448 GError *error = NULL;
450 vproxies = g_dbus_proxy_call_finish (G_DBUS_PROXY (source),
454 g_variant_get (vproxies, "(^as)", &proxies);
455 g_task_return_pointer (task, proxies, (GDestroyNotify)g_strfreev);
456 g_variant_unref (vproxies);
459 g_task_return_error (task, error);
460 g_object_unref (task);
464 g_proxy_resolver_gnome_lookup_async (GProxyResolver *proxy_resolver,
466 GCancellable *cancellable,
467 GAsyncReadyCallback callback,
470 GProxyResolverGnome *resolver = G_PROXY_RESOLVER_GNOME (proxy_resolver);
472 char **proxies, *autoconfig_url;
473 GDBusProxy *pacrunner;
474 GError *error = NULL;
476 task = g_task_new (resolver, cancellable, callback, user_data);
477 g_task_set_source_tag (task, g_proxy_resolver_gnome_lookup_async);
479 if (!g_proxy_resolver_gnome_lookup_internal (resolver, uri,
480 &proxies, &pacrunner, &autoconfig_url,
481 cancellable, &error))
483 g_task_return_error (task, error);
484 g_object_unref (task);
489 g_task_return_pointer (task, proxies, (GDestroyNotify)g_strfreev);
490 g_object_unref (task);
494 g_dbus_proxy_call (pacrunner,
496 g_variant_new ("(ss)",
499 G_DBUS_CALL_FLAGS_NONE,
502 got_autoconfig_proxies,
504 g_object_unref (pacrunner);
505 g_free (autoconfig_url);
509 g_proxy_resolver_gnome_lookup_finish (GProxyResolver *resolver,
510 GAsyncResult *result,
513 g_return_val_if_fail (g_task_is_valid (result, resolver), NULL);
515 return g_task_propagate_pointer (G_TASK (result), error);
519 g_proxy_resolver_gnome_class_init (GProxyResolverGnomeClass *resolver_class)
521 GObjectClass *object_class;
523 object_class = G_OBJECT_CLASS (resolver_class);
524 object_class->finalize = g_proxy_resolver_gnome_finalize;
528 g_proxy_resolver_gnome_iface_init (GProxyResolverInterface *iface)
530 g_proxy_resolver_gnome_parent_iface = g_type_interface_peek_parent (iface);
532 iface->is_supported = g_proxy_resolver_gnome_is_supported;
533 iface->lookup = g_proxy_resolver_gnome_lookup;
534 iface->lookup_async = g_proxy_resolver_gnome_lookup_async;
535 iface->lookup_finish = g_proxy_resolver_gnome_lookup_finish;
539 g_proxy_resolver_gnome_register (GIOModule *module)
541 g_proxy_resolver_gnome_register_type (G_TYPE_MODULE (module));
543 g_io_extension_point_register (G_PROXY_RESOLVER_EXTENSION_POINT_NAME);
544 g_io_extension_point_implement (G_PROXY_RESOLVER_EXTENSION_POINT_NAME,
545 g_proxy_resolver_gnome_get_type(),