50b63cd7416c2f247b079c3635d9c57833fb4b48
[platform/upstream/glib-networking.git] / proxy / gnome / gproxyresolvergnome.c
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /*
3  * GIO - GLib Input, Output and Streaming Library
4  *
5  * Copyright 2010 Red Hat, Inc.
6  *
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.
11  *
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.
16  *
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/>.
20  */
21
22 #include "config.h"
23
24 #include <stdlib.h>
25
26 #include "gproxyresolvergnome.h"
27
28 #include <glib/gi18n-lib.h>
29 #include <gdesktop-enums.h>
30
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"
36
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"
43
44 #define GNOME_PROXY_HTTPS_CHILD_SCHEMA    "https"
45 #define GNOME_PROXY_HTTPS_HOST_KEY        "host"
46 #define GNOME_PROXY_HTTPS_PORT_KEY        "port"
47
48 #define GNOME_PROXY_FTP_CHILD_SCHEMA      "ftp"
49 #define GNOME_PROXY_FTP_HOST_KEY          "host"
50 #define GNOME_PROXY_FTP_PORT_KEY          "port"
51
52 #define GNOME_PROXY_SOCKS_CHILD_SCHEMA    "socks"
53 #define GNOME_PROXY_SOCKS_HOST_KEY        "host"
54 #define GNOME_PROXY_SOCKS_PORT_KEY        "port"
55
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.
59  */
60
61 struct _GProxyResolverGnome {
62   GObject parent_instance;
63
64   GProxyResolver *base_resolver;
65
66   GSettings *proxy_settings;
67   GSettings *http_settings;
68   GSettings *https_settings;
69   GSettings *ftp_settings;
70   GSettings *socks_settings;
71   gboolean need_update;
72
73   GDesktopProxyMode mode;
74   gchar *autoconfig_url;
75   gboolean use_same_proxy;
76
77   GDBusProxy *pacrunner;
78
79   GMutex lock;
80 };
81
82 static GProxyResolverInterface *g_proxy_resolver_gnome_parent_iface;
83
84 static void g_proxy_resolver_gnome_iface_init (GProxyResolverInterface *iface);
85
86 G_DEFINE_DYNAMIC_TYPE_EXTENDED (GProxyResolverGnome,
87                                 g_proxy_resolver_gnome,
88                                 G_TYPE_OBJECT, 0,
89                                 G_IMPLEMENT_INTERFACE_DYNAMIC (G_TYPE_PROXY_RESOLVER,
90                                                                g_proxy_resolver_gnome_iface_init))
91
92 static void
93 g_proxy_resolver_gnome_class_finalize (GProxyResolverGnomeClass *klass)
94 {
95 }
96
97 static void
98 gsettings_changed (GSettings   *settings,
99                    const gchar *key,
100                    gpointer     user_data)
101 {
102   GProxyResolverGnome *resolver = user_data;
103
104   g_mutex_lock (&resolver->lock);
105   resolver->need_update = TRUE;
106   g_mutex_unlock (&resolver->lock);
107 }
108
109 static void
110 g_proxy_resolver_gnome_finalize (GObject *object)
111 {
112   GProxyResolverGnome *resolver = G_PROXY_RESOLVER_GNOME (object);
113
114   if (resolver->proxy_settings)
115     {
116       g_signal_handlers_disconnect_by_func (resolver->proxy_settings,
117                                             (gpointer)gsettings_changed,
118                                             resolver);
119       g_object_unref (resolver->proxy_settings);
120
121       g_signal_handlers_disconnect_by_func (resolver->http_settings,
122                                             (gpointer)gsettings_changed,
123                                             resolver);
124       g_object_unref (resolver->http_settings);
125
126       g_signal_handlers_disconnect_by_func (resolver->https_settings,
127                                             (gpointer)gsettings_changed,
128                                             resolver);
129       g_object_unref (resolver->https_settings);
130
131       g_signal_handlers_disconnect_by_func (resolver->ftp_settings,
132                                             (gpointer)gsettings_changed,
133                                             resolver);
134       g_object_unref (resolver->ftp_settings);
135
136       g_signal_handlers_disconnect_by_func (resolver->socks_settings,
137                                             (gpointer)gsettings_changed,
138                                             resolver);
139       g_object_unref (resolver->socks_settings);
140     }
141
142   g_clear_object (&resolver->base_resolver);
143   g_clear_object (&resolver->pacrunner);
144
145   g_free (resolver->autoconfig_url);
146
147   g_mutex_clear (&resolver->lock);
148
149   G_OBJECT_CLASS (g_proxy_resolver_gnome_parent_class)->finalize (object);
150 }
151
152 static void
153 g_proxy_resolver_gnome_init (GProxyResolverGnome *resolver)
154 {
155   g_mutex_init (&resolver->lock);
156
157   resolver->base_resolver = g_simple_proxy_resolver_new (NULL, NULL);
158
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);
178
179   resolver->need_update = TRUE;
180 }
181
182 /* called with lock held */
183 static void
184 update_settings (GProxyResolverGnome *resolver)
185 {
186   GSimpleProxyResolver *simple = G_SIMPLE_PROXY_RESOLVER (resolver->base_resolver);
187   gchar **ignore_hosts;
188   gchar *host, *http_proxy, *proxy;
189   guint port;
190
191   resolver->need_update = FALSE;
192
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);
199
200   resolver->mode =
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);
204
205   if (resolver->mode == G_DESKTOP_PROXY_MODE_AUTO && !resolver->pacrunner)
206     {
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,
212                                        NULL,
213                                        "org.gtk.GLib.PACRunner",
214                                        "/org/gtk/GLib/PACRunner",
215                                        "org.gtk.GLib.PACRunner",
216                                        NULL, &error);
217       if (error)
218         {
219           g_warning ("Could not start proxy autoconfiguration helper:"
220                      "\n    %s\nProxy autoconfiguration will not work",
221                      error->message);
222         }
223     }
224   else if (resolver->mode != G_DESKTOP_PROXY_MODE_AUTO && resolver->pacrunner)
225     {
226       g_object_unref (resolver->pacrunner);
227       resolver->pacrunner = NULL;
228     }
229
230   ignore_hosts =
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);
234
235   if (resolver->mode == G_DESKTOP_PROXY_MODE_AUTO)
236     {
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
239        * the two cases.
240        */
241        g_simple_proxy_resolver_set_default_proxy (simple, "use-proxy:");
242     }
243
244   if (resolver->mode != G_DESKTOP_PROXY_MODE_MANUAL)
245     return;
246
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);
249   if (host && *host)
250     {
251       if (g_settings_get_boolean (resolver->http_settings, GNOME_PROXY_HTTP_USE_AUTH_KEY))
252         {
253           gchar *user, *password;
254           gchar *enc_user, *enc_password;
255
256           user = g_settings_get_string (resolver->http_settings, GNOME_PROXY_HTTP_USER_KEY);
257           enc_user = g_uri_escape_string (user, NULL, TRUE);
258           g_free (user);
259           password = g_settings_get_string (resolver->http_settings, GNOME_PROXY_HTTP_PASSWORD_KEY);
260           enc_password = g_uri_escape_string (password, NULL, TRUE);
261           g_free (password);
262
263           http_proxy = g_strdup_printf ("http://%s:%s@%s:%u",
264                                         enc_user, enc_password,
265                                         host, port);
266           g_free (enc_user);
267           g_free (enc_password);
268         }
269       else
270         http_proxy = g_strdup_printf ("http://%s:%u", host, port);
271
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);
275     }
276   else
277     http_proxy = NULL;
278   g_free (host);
279
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);
282   if (host && *host)
283     {
284       proxy = g_strdup_printf ("http://%s:%u", host, port);
285       g_simple_proxy_resolver_set_uri_proxy (simple, "https", proxy);
286       g_free (proxy);
287     }
288   else if (http_proxy)
289     g_simple_proxy_resolver_set_uri_proxy (simple, "https", http_proxy);
290   g_free (host);
291
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);
294   if (host && *host)
295     {
296       proxy = g_strdup_printf ("socks://%s:%u", host, port);
297       g_simple_proxy_resolver_set_default_proxy (simple, proxy);
298       g_free (proxy);
299     }
300   g_free (host);
301
302   g_free (http_proxy);
303
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);
306   if (host && *host)
307     {
308       proxy = g_strdup_printf ("ftp://%s:%u", host, port);
309       g_simple_proxy_resolver_set_uri_proxy (simple, "ftp", proxy);
310       g_free (proxy);
311     }
312   g_free (host);
313 }
314
315 static gboolean
316 g_proxy_resolver_gnome_is_supported (GProxyResolver *object)
317 {
318   const char *desktops;
319
320   desktops = g_getenv ("XDG_CURRENT_DESKTOP");
321   if (desktops == NULL)
322     return FALSE;
323
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.
327    */
328   return strstr (desktops, "GNOME") != NULL;
329 }
330
331 static inline gchar **
332 make_proxies (const gchar *proxy)
333 {
334   gchar **proxies;
335
336   proxies = g_new (gchar *, 2);
337   proxies[0] = g_strdup (proxy);
338   proxies[1] = NULL;
339
340   return proxies;
341 }
342
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.
347  */
348 static gboolean
349 g_proxy_resolver_gnome_lookup_internal (GProxyResolverGnome   *resolver,
350                                         const gchar           *uri,
351                                         gchar               ***out_proxies,
352                                         GDBusProxy           **out_pacrunner,
353                                         gchar                **out_autoconfig_url,
354                                         GCancellable          *cancellable,
355                                         GError               **error)
356 {
357   gchar **proxies = NULL;
358
359   *out_proxies = NULL;
360   *out_pacrunner = NULL;
361   *out_autoconfig_url = NULL;
362
363   g_mutex_lock (&resolver->lock);
364   if (resolver->need_update)
365     update_settings (resolver);
366
367   proxies = g_proxy_resolver_lookup (resolver->base_resolver,
368                                      uri, cancellable, error);
369   if (!proxies)
370     goto done;
371
372   /* Parent class does ignore-host handling */
373   if (!strcmp (proxies[0], "direct://") && !proxies[1])
374     goto done;
375
376   if (resolver->pacrunner)
377     {
378       g_clear_pointer (&proxies, g_strfreev);
379       *out_pacrunner = g_object_ref (resolver->pacrunner);
380       *out_autoconfig_url = g_strdup (resolver->autoconfig_url);
381       goto done;
382     }
383
384  done:
385   g_mutex_unlock (&resolver->lock);
386
387   if (proxies)
388     {
389       *out_proxies = proxies;
390       return TRUE;
391     }
392   else if (*out_pacrunner)
393     return TRUE;
394   else
395     return FALSE;
396 }
397
398 static gchar **
399 g_proxy_resolver_gnome_lookup (GProxyResolver  *proxy_resolver,
400                                const gchar     *uri,
401                                GCancellable    *cancellable,
402                                GError         **error)
403 {
404   GProxyResolverGnome *resolver = G_PROXY_RESOLVER_GNOME (proxy_resolver);
405   GDBusProxy *pacrunner;
406   gchar **proxies, *autoconfig_url;
407
408   if (!g_proxy_resolver_gnome_lookup_internal (resolver, uri,
409                                                &proxies, &pacrunner, &autoconfig_url,
410                                                cancellable, error))
411     return NULL;
412
413   if (pacrunner)
414     {
415       GVariant *vproxies;
416
417       vproxies = g_dbus_proxy_call_sync (pacrunner,
418                                          "Lookup",
419                                          g_variant_new ("(ss)",
420                                                         autoconfig_url,
421                                                         uri),
422                                          G_DBUS_CALL_FLAGS_NONE,
423                                          -1,
424                                          cancellable, error);
425       if (vproxies)
426         {
427           g_variant_get (vproxies, "(^as)", &proxies);
428           g_variant_unref (vproxies);
429         }
430       else
431         proxies = NULL;
432
433       g_object_unref (pacrunner);
434       g_free (autoconfig_url);
435     }
436
437   return proxies;
438 }
439
440 static void
441 got_autoconfig_proxies (GObject      *source,
442                         GAsyncResult *result,
443                         gpointer      user_data)
444 {
445   GTask *task = user_data;
446   GVariant *vproxies;
447   char **proxies;
448   GError *error = NULL;
449
450   vproxies = g_dbus_proxy_call_finish (G_DBUS_PROXY (source),
451                                        result, &error);
452   if (vproxies)
453     {
454       g_variant_get (vproxies, "(^as)", &proxies);
455       g_task_return_pointer (task, proxies, (GDestroyNotify)g_strfreev);
456       g_variant_unref (vproxies);
457     }
458   else
459     g_task_return_error (task, error);
460   g_object_unref (task);
461 }
462
463 static void
464 g_proxy_resolver_gnome_lookup_async (GProxyResolver      *proxy_resolver,
465                                      const gchar         *uri,
466                                      GCancellable        *cancellable,
467                                      GAsyncReadyCallback  callback,
468                                      gpointer             user_data)
469 {
470   GProxyResolverGnome *resolver = G_PROXY_RESOLVER_GNOME (proxy_resolver);
471   GTask *task;
472   char **proxies, *autoconfig_url;
473   GDBusProxy *pacrunner;
474   GError *error = NULL;
475
476   task = g_task_new (resolver, cancellable, callback, user_data);
477   g_task_set_source_tag (task, g_proxy_resolver_gnome_lookup_async);
478
479    if (!g_proxy_resolver_gnome_lookup_internal (resolver, uri,
480                                                 &proxies, &pacrunner, &autoconfig_url,
481                                                 cancellable, &error))
482      {
483        g_task_return_error (task, error);
484        g_object_unref (task);
485        return;
486      }
487    else if (proxies)
488      {
489        g_task_return_pointer (task, proxies, (GDestroyNotify)g_strfreev);
490        g_object_unref (task);
491        return;
492      }
493
494    g_dbus_proxy_call (pacrunner,
495                       "Lookup",
496                       g_variant_new ("(ss)",
497                                      autoconfig_url,
498                                      uri),
499                       G_DBUS_CALL_FLAGS_NONE,
500                       -1,
501                       cancellable,
502                       got_autoconfig_proxies,
503                       task);
504    g_object_unref (pacrunner);
505    g_free (autoconfig_url);
506 }
507
508 static gchar **
509 g_proxy_resolver_gnome_lookup_finish (GProxyResolver  *resolver,
510                                       GAsyncResult    *result,
511                                       GError         **error)
512 {
513   g_return_val_if_fail (g_task_is_valid (result, resolver), NULL);
514
515   return g_task_propagate_pointer (G_TASK (result), error);
516 }
517
518 static void
519 g_proxy_resolver_gnome_class_init (GProxyResolverGnomeClass *resolver_class)
520 {
521   GObjectClass *object_class;
522   
523   object_class = G_OBJECT_CLASS (resolver_class);
524   object_class->finalize = g_proxy_resolver_gnome_finalize;
525 }
526
527 static void
528 g_proxy_resolver_gnome_iface_init (GProxyResolverInterface *iface)
529 {
530   g_proxy_resolver_gnome_parent_iface = g_type_interface_peek_parent (iface);
531
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;
536 }
537
538 void
539 g_proxy_resolver_gnome_register (GIOModule *module)
540 {
541   g_proxy_resolver_gnome_register_type (G_TYPE_MODULE (module));
542   if (module == NULL)
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(),
546                                   "gnome",
547                                   80);
548 }