Migrate to openssl 3
[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)
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 /* 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.
335  */
336 static gboolean
337 g_proxy_resolver_gnome_lookup_internal (GProxyResolverGnome   *resolver,
338                                         const gchar           *uri,
339                                         gchar               ***out_proxies,
340                                         GDBusProxy           **out_pacrunner,
341                                         gchar                **out_autoconfig_url,
342                                         GCancellable          *cancellable,
343                                         GError               **error)
344 {
345   gchar **proxies = NULL;
346
347   *out_proxies = NULL;
348   *out_pacrunner = NULL;
349   *out_autoconfig_url = NULL;
350
351   g_mutex_lock (&resolver->lock);
352   if (resolver->need_update)
353     update_settings (resolver);
354
355   proxies = g_proxy_resolver_lookup (resolver->base_resolver,
356                                      uri, cancellable, error);
357   if (!proxies)
358     goto done;
359
360   /* Parent class does ignore-host handling */
361   if (!strcmp (proxies[0], "direct://") && !proxies[1])
362     goto done;
363
364   if (resolver->pacrunner)
365     {
366       g_clear_pointer (&proxies, g_strfreev);
367       *out_pacrunner = g_object_ref (resolver->pacrunner);
368       *out_autoconfig_url = g_strdup (resolver->autoconfig_url);
369       goto done;
370     }
371
372  done:
373   g_mutex_unlock (&resolver->lock);
374
375   if (proxies)
376     {
377       *out_proxies = proxies;
378       return TRUE;
379     }
380   else if (*out_pacrunner)
381     return TRUE;
382   else
383     return FALSE;
384 }
385
386 static gchar **
387 g_proxy_resolver_gnome_lookup (GProxyResolver  *proxy_resolver,
388                                const gchar     *uri,
389                                GCancellable    *cancellable,
390                                GError         **error)
391 {
392   GProxyResolverGnome *resolver = G_PROXY_RESOLVER_GNOME (proxy_resolver);
393   GDBusProxy *pacrunner;
394   gchar **proxies, *autoconfig_url;
395
396   if (!g_proxy_resolver_gnome_lookup_internal (resolver, uri,
397                                                &proxies, &pacrunner, &autoconfig_url,
398                                                cancellable, error))
399     return NULL;
400
401   if (pacrunner)
402     {
403       GVariant *vproxies;
404
405       vproxies = g_dbus_proxy_call_sync (pacrunner,
406                                          "Lookup",
407                                          g_variant_new ("(ss)",
408                                                         autoconfig_url,
409                                                         uri),
410                                          G_DBUS_CALL_FLAGS_NONE,
411                                          -1,
412                                          cancellable, error);
413       if (vproxies)
414         {
415           g_variant_get (vproxies, "(^as)", &proxies);
416           g_variant_unref (vproxies);
417         }
418       else
419         proxies = NULL;
420
421       g_object_unref (pacrunner);
422       g_free (autoconfig_url);
423     }
424
425   return proxies;
426 }
427
428 static void
429 got_autoconfig_proxies (GObject      *source,
430                         GAsyncResult *result,
431                         gpointer      user_data)
432 {
433   GTask *task = user_data;
434   GVariant *vproxies;
435   char **proxies;
436   GError *error = NULL;
437
438   vproxies = g_dbus_proxy_call_finish (G_DBUS_PROXY (source),
439                                        result, &error);
440   if (vproxies)
441     {
442       g_variant_get (vproxies, "(^as)", &proxies);
443       g_task_return_pointer (task, proxies, (GDestroyNotify)g_strfreev);
444       g_variant_unref (vproxies);
445     }
446   else
447     g_task_return_error (task, error);
448   g_object_unref (task);
449 }
450
451 static void
452 g_proxy_resolver_gnome_lookup_async (GProxyResolver      *proxy_resolver,
453                                      const gchar         *uri,
454                                      GCancellable        *cancellable,
455                                      GAsyncReadyCallback  callback,
456                                      gpointer             user_data)
457 {
458   GProxyResolverGnome *resolver = G_PROXY_RESOLVER_GNOME (proxy_resolver);
459   GTask *task;
460   char **proxies, *autoconfig_url;
461   GDBusProxy *pacrunner;
462   GError *error = NULL;
463
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");
467
468    if (!g_proxy_resolver_gnome_lookup_internal (resolver, uri,
469                                                 &proxies, &pacrunner, &autoconfig_url,
470                                                 cancellable, &error))
471      {
472        g_task_return_error (task, error);
473        g_object_unref (task);
474        return;
475      }
476    else if (proxies)
477      {
478        g_task_return_pointer (task, proxies, (GDestroyNotify)g_strfreev);
479        g_object_unref (task);
480        return;
481      }
482
483    g_dbus_proxy_call (pacrunner,
484                       "Lookup",
485                       g_variant_new ("(ss)",
486                                      autoconfig_url,
487                                      uri),
488                       G_DBUS_CALL_FLAGS_NONE,
489                       -1,
490                       cancellable,
491                       got_autoconfig_proxies,
492                       task);
493    g_object_unref (pacrunner);
494    g_free (autoconfig_url);
495 }
496
497 static gchar **
498 g_proxy_resolver_gnome_lookup_finish (GProxyResolver  *resolver,
499                                       GAsyncResult    *result,
500                                       GError         **error)
501 {
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);
504
505   return g_task_propagate_pointer (G_TASK (result), error);
506 }
507
508 static void
509 g_proxy_resolver_gnome_class_init (GProxyResolverGnomeClass *resolver_class)
510 {
511   GObjectClass *object_class;
512   
513   object_class = G_OBJECT_CLASS (resolver_class);
514   object_class->finalize = g_proxy_resolver_gnome_finalize;
515 }
516
517 static void
518 g_proxy_resolver_gnome_iface_init (GProxyResolverInterface *iface)
519 {
520   g_proxy_resolver_gnome_parent_iface = g_type_interface_peek_parent (iface);
521
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;
526 }
527
528 void
529 g_proxy_resolver_gnome_register (GIOModule *module)
530 {
531   g_proxy_resolver_gnome_register_type (G_TYPE_MODULE (module));
532   if (!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(),
536                                   "gnome",
537                                   80);
538 }