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