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"
54 /* We have to has-a GSimpleProxyResolver rather than is-a one,
55 * because a dynamic type cannot reimplement an interface that
56 * its parent also implements... for some reason.
59 struct _GProxyResolverGnome {
60 GObject parent_instance;
62 GProxyResolver *base_resolver;
64 GSettings *proxy_settings;
65 GSettings *http_settings;
66 GSettings *https_settings;
67 GSettings *ftp_settings;
68 GSettings *socks_settings;
71 GDesktopProxyMode mode;
72 gchar *autoconfig_url;
73 gboolean use_same_proxy;
75 GDBusProxy *pacrunner;
80 static GProxyResolverInterface *g_proxy_resolver_gnome_parent_iface;
82 static void g_proxy_resolver_gnome_iface_init (GProxyResolverInterface *iface);
84 G_DEFINE_DYNAMIC_TYPE_EXTENDED (GProxyResolverGnome,
85 g_proxy_resolver_gnome,
87 G_IMPLEMENT_INTERFACE_DYNAMIC (G_TYPE_PROXY_RESOLVER,
88 g_proxy_resolver_gnome_iface_init))
91 g_proxy_resolver_gnome_class_finalize (GProxyResolverGnomeClass *klass)
96 gsettings_changed (GSettings *settings,
100 GProxyResolverGnome *resolver = user_data;
102 g_mutex_lock (&resolver->lock);
103 resolver->need_update = TRUE;
104 g_mutex_unlock (&resolver->lock);
108 g_proxy_resolver_gnome_finalize (GObject *object)
110 GProxyResolverGnome *resolver = G_PROXY_RESOLVER_GNOME (object);
112 if (resolver->proxy_settings)
114 g_signal_handlers_disconnect_by_func (resolver->proxy_settings,
115 (gpointer)gsettings_changed,
117 g_object_unref (resolver->proxy_settings);
119 g_signal_handlers_disconnect_by_func (resolver->http_settings,
120 (gpointer)gsettings_changed,
122 g_object_unref (resolver->http_settings);
124 g_signal_handlers_disconnect_by_func (resolver->https_settings,
125 (gpointer)gsettings_changed,
127 g_object_unref (resolver->https_settings);
129 g_signal_handlers_disconnect_by_func (resolver->ftp_settings,
130 (gpointer)gsettings_changed,
132 g_object_unref (resolver->ftp_settings);
134 g_signal_handlers_disconnect_by_func (resolver->socks_settings,
135 (gpointer)gsettings_changed,
137 g_object_unref (resolver->socks_settings);
140 g_clear_object (&resolver->base_resolver);
141 g_clear_object (&resolver->pacrunner);
143 g_free (resolver->autoconfig_url);
145 g_mutex_clear (&resolver->lock);
147 G_OBJECT_CLASS (g_proxy_resolver_gnome_parent_class)->finalize (object);
151 g_proxy_resolver_gnome_init (GProxyResolverGnome *resolver)
153 g_mutex_init (&resolver->lock);
155 resolver->base_resolver = g_simple_proxy_resolver_new (NULL, NULL);
157 resolver->proxy_settings = g_settings_new (GNOME_PROXY_SETTINGS_SCHEMA);
158 g_signal_connect (resolver->proxy_settings, "changed",
159 G_CALLBACK (gsettings_changed), resolver);
160 resolver->http_settings = g_settings_get_child (resolver->proxy_settings,
161 GNOME_PROXY_HTTP_CHILD_SCHEMA);
162 g_signal_connect (resolver->http_settings, "changed",
163 G_CALLBACK (gsettings_changed), resolver);
164 resolver->https_settings = g_settings_get_child (resolver->proxy_settings,
165 GNOME_PROXY_HTTPS_CHILD_SCHEMA);
166 g_signal_connect (resolver->https_settings, "changed",
167 G_CALLBACK (gsettings_changed), resolver);
168 resolver->ftp_settings = g_settings_get_child (resolver->proxy_settings,
169 GNOME_PROXY_FTP_CHILD_SCHEMA);
170 g_signal_connect (resolver->ftp_settings, "changed",
171 G_CALLBACK (gsettings_changed), resolver);
172 resolver->socks_settings = g_settings_get_child (resolver->proxy_settings,
173 GNOME_PROXY_SOCKS_CHILD_SCHEMA);
174 g_signal_connect (resolver->socks_settings, "changed",
175 G_CALLBACK (gsettings_changed), resolver);
177 resolver->need_update = TRUE;
180 /* called with lock held */
182 update_settings (GProxyResolverGnome *resolver)
184 GSimpleProxyResolver *simple = G_SIMPLE_PROXY_RESOLVER (resolver->base_resolver);
185 gchar **ignore_hosts;
186 gchar *host, *http_proxy, *proxy;
189 resolver->need_update = FALSE;
191 g_free (resolver->autoconfig_url);
192 g_simple_proxy_resolver_set_default_proxy (simple, NULL);
193 g_simple_proxy_resolver_set_ignore_hosts (simple, NULL);
194 g_simple_proxy_resolver_set_uri_proxy (simple, "http", NULL);
195 g_simple_proxy_resolver_set_uri_proxy (simple, "https", NULL);
196 g_simple_proxy_resolver_set_uri_proxy (simple, "ftp", NULL);
199 g_settings_get_enum (resolver->proxy_settings, GNOME_PROXY_MODE_KEY);
200 resolver->autoconfig_url =
201 g_settings_get_string (resolver->proxy_settings, GNOME_PROXY_AUTOCONFIG_URL_KEY);
203 if (resolver->mode == G_DESKTOP_PROXY_MODE_AUTO && !resolver->pacrunner)
205 GError *error = NULL;
206 resolver->pacrunner =
207 g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
208 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
209 G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
211 "org.gtk.GLib.PACRunner",
212 "/org/gtk/GLib/PACRunner",
213 "org.gtk.GLib.PACRunner",
217 g_warning ("Could not start proxy autoconfiguration helper:"
218 "\n %s\nProxy autoconfiguration will not work",
222 else if (resolver->mode != G_DESKTOP_PROXY_MODE_AUTO && resolver->pacrunner)
224 g_object_unref (resolver->pacrunner);
225 resolver->pacrunner = NULL;
229 g_settings_get_strv (resolver->proxy_settings, GNOME_PROXY_IGNORE_HOSTS_KEY);
230 g_simple_proxy_resolver_set_ignore_hosts (simple, ignore_hosts);
231 g_strfreev (ignore_hosts);
233 if (resolver->mode == G_DESKTOP_PROXY_MODE_AUTO)
235 /* We use the base_resolver to handle ignore_hosts in the AUTO case,
236 * so we have to set a non-"direct://" default proxy so we can distinguish
239 g_simple_proxy_resolver_set_default_proxy (simple, "use-proxy:");
242 if (resolver->mode != G_DESKTOP_PROXY_MODE_MANUAL)
245 host = g_settings_get_string (resolver->http_settings, GNOME_PROXY_HTTP_HOST_KEY);
246 port = g_settings_get_int (resolver->http_settings, GNOME_PROXY_HTTP_PORT_KEY);
249 if (g_settings_get_boolean (resolver->http_settings, GNOME_PROXY_HTTP_USE_AUTH_KEY))
251 gchar *user, *password;
252 gchar *enc_user, *enc_password;
254 user = g_settings_get_string (resolver->http_settings, GNOME_PROXY_HTTP_USER_KEY);
255 enc_user = g_uri_escape_string (user, NULL, TRUE);
257 password = g_settings_get_string (resolver->http_settings, GNOME_PROXY_HTTP_PASSWORD_KEY);
258 enc_password = g_uri_escape_string (password, NULL, TRUE);
261 http_proxy = g_strdup_printf ("http://%s:%s@%s:%u",
262 enc_user, enc_password,
265 g_free (enc_password);
268 http_proxy = g_strdup_printf ("http://%s:%u", host, port);
270 g_simple_proxy_resolver_set_uri_proxy (simple, "http", http_proxy);
271 if (g_settings_get_boolean (resolver->proxy_settings, GNOME_PROXY_USE_SAME_PROXY_KEY))
272 g_simple_proxy_resolver_set_default_proxy (simple, http_proxy);
278 host = g_settings_get_string (resolver->https_settings, GNOME_PROXY_HTTPS_HOST_KEY);
279 port = g_settings_get_int (resolver->https_settings, GNOME_PROXY_HTTPS_PORT_KEY);
282 proxy = g_strdup_printf ("http://%s:%u", host, port);
283 g_simple_proxy_resolver_set_uri_proxy (simple, "https", proxy);
287 g_simple_proxy_resolver_set_uri_proxy (simple, "https", http_proxy);
290 host = g_settings_get_string (resolver->socks_settings, GNOME_PROXY_SOCKS_HOST_KEY);
291 port = g_settings_get_int (resolver->socks_settings, GNOME_PROXY_SOCKS_PORT_KEY);
294 proxy = g_strdup_printf ("socks://%s:%u", host, port);
295 g_simple_proxy_resolver_set_default_proxy (simple, proxy);
302 host = g_settings_get_string (resolver->ftp_settings, GNOME_PROXY_FTP_HOST_KEY);
303 port = g_settings_get_int (resolver->ftp_settings, GNOME_PROXY_FTP_PORT_KEY);
306 proxy = g_strdup_printf ("ftp://%s:%u", host, port);
307 g_simple_proxy_resolver_set_uri_proxy (simple, "ftp", proxy);
314 g_proxy_resolver_gnome_is_supported (GProxyResolver *object)
318 if (g_getenv ("GNOME_DESKTOP_SESSION_ID"))
321 session = g_getenv ("DESKTOP_SESSION");
325 return g_str_has_prefix (session, "gnome") ||
326 strcmp (session, "ubuntu") == 0;
329 static inline gchar **
330 make_proxies (const gchar *proxy)
334 proxies = g_new (gchar *, 2);
335 proxies[0] = g_strdup (proxy);
341 /* Threadsafely determines what to do with @uri; returns %FALSE if an
342 * error occurs, %TRUE and an array of proxies if the mode is NONE or
343 * MANUAL, or if @uri is covered by ignore-hosts, or %TRUE and a
344 * (transfer-full) pacrunner and autoconfig url if the mode is AUTOMATIC.
347 g_proxy_resolver_gnome_lookup_internal (GProxyResolverGnome *resolver,
349 gchar ***out_proxies,
350 GDBusProxy **out_pacrunner,
351 gchar **out_autoconfig_url,
352 GCancellable *cancellable,
355 gchar **proxies = NULL;
358 *out_pacrunner = NULL;
359 *out_autoconfig_url = NULL;
361 g_mutex_lock (&resolver->lock);
362 if (resolver->need_update)
363 update_settings (resolver);
365 proxies = g_proxy_resolver_lookup (resolver->base_resolver,
366 uri, cancellable, error);
370 /* Parent class does ignore-host handling */
371 if (!strcmp (proxies[0], "direct://") && !proxies[1])
374 if (resolver->pacrunner)
376 g_clear_pointer (&proxies, g_strfreev);
377 *out_pacrunner = g_object_ref (resolver->pacrunner);
378 *out_autoconfig_url = g_strdup (resolver->autoconfig_url);
383 g_mutex_unlock (&resolver->lock);
387 *out_proxies = proxies;
390 else if (*out_pacrunner)
397 g_proxy_resolver_gnome_lookup (GProxyResolver *proxy_resolver,
399 GCancellable *cancellable,
402 GProxyResolverGnome *resolver = G_PROXY_RESOLVER_GNOME (proxy_resolver);
403 GDBusProxy *pacrunner;
404 gchar **proxies, *autoconfig_url;
406 if (!g_proxy_resolver_gnome_lookup_internal (resolver, uri,
407 &proxies, &pacrunner, &autoconfig_url,
415 vproxies = g_dbus_proxy_call_sync (pacrunner,
417 g_variant_new ("(ss)",
420 G_DBUS_CALL_FLAGS_NONE,
425 g_variant_get (vproxies, "(^as)", &proxies);
426 g_variant_unref (vproxies);
431 g_object_unref (pacrunner);
432 g_free (autoconfig_url);
439 got_autoconfig_proxies (GObject *source,
440 GAsyncResult *result,
443 GTask *task = user_data;
446 GError *error = NULL;
448 vproxies = g_dbus_proxy_call_finish (G_DBUS_PROXY (source),
452 g_variant_get (vproxies, "(^as)", &proxies);
453 g_task_return_pointer (task, proxies, (GDestroyNotify)g_strfreev);
454 g_variant_unref (vproxies);
457 g_task_return_error (task, error);
458 g_object_unref (task);
462 g_proxy_resolver_gnome_lookup_async (GProxyResolver *proxy_resolver,
464 GCancellable *cancellable,
465 GAsyncReadyCallback callback,
468 GProxyResolverGnome *resolver = G_PROXY_RESOLVER_GNOME (proxy_resolver);
470 char **proxies, *autoconfig_url;
471 GDBusProxy *pacrunner;
472 GError *error = NULL;
474 task = g_task_new (resolver, cancellable, callback, user_data);
475 g_task_set_source_tag (task, g_proxy_resolver_gnome_lookup_async);
477 if (!g_proxy_resolver_gnome_lookup_internal (resolver, uri,
478 &proxies, &pacrunner, &autoconfig_url,
479 cancellable, &error))
481 g_task_return_error (task, error);
482 g_object_unref (task);
487 g_task_return_pointer (task, proxies, (GDestroyNotify)g_strfreev);
488 g_object_unref (task);
492 g_dbus_proxy_call (pacrunner,
494 g_variant_new ("(ss)",
497 G_DBUS_CALL_FLAGS_NONE,
500 got_autoconfig_proxies,
502 g_object_unref (pacrunner);
503 g_free (autoconfig_url);
507 g_proxy_resolver_gnome_lookup_finish (GProxyResolver *resolver,
508 GAsyncResult *result,
511 g_return_val_if_fail (g_task_is_valid (result, resolver), NULL);
513 return g_task_propagate_pointer (G_TASK (result), error);
517 g_proxy_resolver_gnome_class_init (GProxyResolverGnomeClass *resolver_class)
519 GObjectClass *object_class;
521 object_class = G_OBJECT_CLASS (resolver_class);
522 object_class->finalize = g_proxy_resolver_gnome_finalize;
526 g_proxy_resolver_gnome_iface_init (GProxyResolverInterface *iface)
528 g_proxy_resolver_gnome_parent_iface = g_type_interface_peek_parent (iface);
530 iface->is_supported = g_proxy_resolver_gnome_is_supported;
531 iface->lookup = g_proxy_resolver_gnome_lookup;
532 iface->lookup_async = g_proxy_resolver_gnome_lookup_async;
533 iface->lookup_finish = g_proxy_resolver_gnome_lookup_finish;
537 g_proxy_resolver_gnome_register (GIOModule *module)
539 g_proxy_resolver_gnome_register_type (G_TYPE_MODULE (module));
540 g_io_extension_point_implement (G_PROXY_RESOLVER_EXTENSION_POINT_NAME,
541 g_proxy_resolver_gnome_get_type(),