Implement a GNOME proxy backend using GSettings
authorDan Winship <danw@gnome.org>
Sat, 11 Dec 2010 14:23:51 +0000 (15:23 +0100)
committerDan Winship <danw@gnome.org>
Wed, 16 Mar 2011 14:09:49 +0000 (10:09 -0400)
Use the network proxy settings from gsettings-desktop-schemas and
return proxies based on that.

For Automatic proxy configuration, we use the D-Bus service provided
by the libproxy plugin.

https://bugzilla.gnome.org/show_bug.cgi?id=644211

Makefile.am
configure.ac
proxy/gnome/Makefile.am [new file with mode: 0644]
proxy/gnome/gnome-proxy-module.c [new file with mode: 0644]
proxy/gnome/gproxyresolvergnome.c [new file with mode: 0644]
proxy/gnome/gproxyresolvergnome.h [new file with mode: 0644]

index 6e030c1..2c34280 100644 (file)
@@ -9,6 +9,10 @@ if HAVE_LIBPROXY
 SUBDIRS += proxy/libproxy
 endif
 
+if HAVE_GNOME_PROXY
+SUBDIRS += proxy/gnome
+endif
+
 if HAVE_GNUTLS
 SUBDIRS += tls/gnutls
 endif
index 0218196..dac8721 100644 (file)
@@ -43,9 +43,6 @@ AC_SUBST(GIO_MODULE_DIR)
 AC_PATH_PROG(GIO_QUERYMODULES, gio-querymodules)
 AC_SUBST(GIO_QUERYMODULES)
 
-proxy_support=no
-tls_support=no
-
 dnl *****************************
 dnl *** Checks for LibProxy   ***
 dnl *****************************
@@ -64,6 +61,22 @@ AM_CONDITIONAL(HAVE_LIBPROXY, [test "x$with_libproxy" = "xyes"])
 AC_SUBST(LIBPROXY_CFLAGS)
 AC_SUBST(LIBPROXY_LIBS)
 
+dnl **************************************
+dnl *** Checks for GNOME proxy backend ***
+dnl **************************************
+AC_ARG_WITH(gnome-proxy,
+    [AC_HELP_STRING([--with-gnome-proxy],
+                    [support for GNOME proxy configuration @<:@default=check@:>@])]
+    [],
+    [with_gnome_proxy=check])
+AS_IF([test "x$with_gnome_proxy" != "xno"],
+    [PKG_CHECK_MODULES(GSETTINGS_DESKTOP_SCHEMAS, [gsettings-desktop-schemas],
+        [with_gnome_proxy=yes; proxy_support="gnome $proxy_support"],
+       [AS_IF([test "x$with_gnome_proxy" = "xyes"],
+               [AC_MSG_FAILURE("$GSETTINGS_DESKTOP_SCHEMAS_PKG_ERRORS")])])])
+AM_CONDITIONAL(HAVE_GNOME_PROXY, [test "x$with_gnome_proxy" = "xyes"])
+AC_SUBST(GSETTINGS_DESKTOP_SCHEMAS_CFLAGS)
+
 dnl *****************************
 dnl *** Checks for GNUTLS     ***
 dnl *****************************
@@ -143,13 +156,14 @@ dnl *****************************
 AC_CONFIG_FILES([Makefile
                  po/Makefile.in po/Makefile
                  proxy/libproxy/Makefile
+                 proxy/gnome/Makefile
                 tls/gnutls/Makefile
                ])
 AC_OUTPUT
 
 echo ""
-echo "  Proxy support: $proxy_support"
-echo "  TLS support:   $tls_support"
+echo "  Proxy support: ${proxy_support:-no}"
+echo "  TLS support:   ${tls_support:-no}"
 if test "$tls_support" != "no"; then
     echo "  TLS CA file:   ${with_ca_certificates:-(none)}"
     if test -n "$with_ca_certificates"; then
diff --git a/proxy/gnome/Makefile.am b/proxy/gnome/Makefile.am
new file mode 100644 (file)
index 0000000..a59b436
--- /dev/null
@@ -0,0 +1,26 @@
+include $(top_srcdir)/Makefile.decl
+
+NULL =
+
+module_flags = -export_dynamic -avoid-version -module -no-undefined -export-symbols-regex '^g_io_module_(load|unload|query)'
+
+giomodule_LTLIBRARIES = libgiognomeproxy.la
+giomoduledir = $(GIO_MODULE_DIR)
+
+libgiognomeproxy_la_SOURCES =          \
+       gproxyresolvergnome.c           \
+       gproxyresolvergnome.h           \
+       gnome-proxy-module.c            \
+       $(NULL)
+
+libgiognomeproxy_la_CFLAGS =                   \
+       -DG_LOG_DOMAIN=\"GLib-Net\"             \
+       $(GLIB_CFLAGS)                          \
+       $(GSETTINGS_DESKTOP_SCHEMAS_CFLAGS)     \
+       -DGIO_MODULE_DIR=\"$(GIO_MODULE_DIR)\"  \
+       -DG_DISABLE_DEPRECATED
+
+libgiognomeproxy_la_LDFLAGS = $(module_flags)
+libgiognomeproxy_la_LIBADD = \
+               $(GLIB_LIBS) \
+               $(NULL)
diff --git a/proxy/gnome/gnome-proxy-module.c b/proxy/gnome/gnome-proxy-module.c
new file mode 100644 (file)
index 0000000..03495ca
--- /dev/null
@@ -0,0 +1,45 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gproxyresolvergnome.h"
+
+
+void
+g_io_module_load (GIOModule *module)
+{
+  g_proxy_resolver_gnome_register (module);
+}
+
+void
+g_io_module_unload (GIOModule *module)
+{
+}
+
+gchar **
+g_io_module_query (void)
+{
+  gchar *eps[] = {
+    G_PROXY_RESOLVER_EXTENSION_POINT_NAME,
+    NULL
+  };
+  return g_strdupv (eps);
+}
diff --git a/proxy/gnome/gproxyresolvergnome.c b/proxy/gnome/gproxyresolvergnome.c
new file mode 100644 (file)
index 0000000..80fc806
--- /dev/null
@@ -0,0 +1,641 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include "gproxyresolvergnome.h"
+
+#include <glib/gi18n-lib.h>
+#include <gdesktop-enums.h>
+
+#define GNOME_PROXY_SETTINGS_SCHEMA       "org.gnome.system.proxy"
+#define GNOME_PROXY_MODE_KEY              "mode"
+#define GNOME_PROXY_AUTOCONFIG_URL_KEY    "autoconfig-url"
+#define GNOME_PROXY_IGNORE_HOSTS_KEY      "ignore-hosts"
+#define GNOME_PROXY_USE_SAME_PROXY_KEY    "use-same-proxy"
+
+#define GNOME_PROXY_HTTP_SETTINGS_SCHEMA  "org.gnome.system.proxy.http"
+#define GNOME_PROXY_HTTP_ENABLED_KEY      "enabled"
+#define GNOME_PROXY_HTTP_HOST_KEY         "host"
+#define GNOME_PROXY_HTTP_PORT_KEY         "port"
+#define GNOME_PROXY_HTTP_USE_AUTH_KEY     "use-authentication"
+#define GNOME_PROXY_HTTP_USER_KEY         "authentication-user"
+#define GNOME_PROXY_HTTP_PASSWORD_KEY     "authentication-password"
+
+#define GNOME_PROXY_HTTPS_SETTINGS_SCHEMA "org.gnome.system.proxy.https"
+#define GNOME_PROXY_HTTPS_HOST_KEY        "host"
+#define GNOME_PROXY_HTTPS_PORT_KEY        "port"
+
+#define GNOME_PROXY_FTP_SETTINGS_SCHEMA   "org.gnome.system.proxy.ftp"
+#define GNOME_PROXY_FTP_HOST_KEY          "host"
+#define GNOME_PROXY_FTP_PORT_KEY          "port"
+
+#define GNOME_PROXY_SOCKS_SETTINGS_SCHEMA "org.gnome.system.proxy.socks"
+#define GNOME_PROXY_SOCKS_HOST_KEY        "host"
+#define GNOME_PROXY_SOCKS_PORT_KEY        "port"
+
+typedef struct {
+  GSocketFamily family;
+  guint8        mask[16];
+  gint          length;
+} GProxyResolverGnomeIPMask;
+
+struct _GProxyResolverGnome {
+  GObject parent_instance;
+
+  GSettings *proxy_settings;
+  GSettings *http_settings;
+  GSettings *https_settings;
+  GSettings *ftp_settings;
+  GSettings *socks_settings;
+  gboolean need_update;
+
+  GDesktopProxyMode mode;
+  gchar *autoconfig_url;
+  gboolean use_same_proxy;
+  gchar **ignore_hosts;
+  GProxyResolverGnomeIPMask *ignore_ips;
+
+  gchar *http_proxy, *https_proxy;
+  gchar *ftp_proxy, *socks_authority;
+
+  GDBusProxy *pacrunner;
+};
+
+static void g_proxy_resolver_gnome_iface_init (GProxyResolverInterface *iface);
+
+G_DEFINE_DYNAMIC_TYPE_EXTENDED (GProxyResolverGnome,
+                               g_proxy_resolver_gnome,
+                               G_TYPE_OBJECT, 0,
+                               G_IMPLEMENT_INTERFACE_DYNAMIC (G_TYPE_PROXY_RESOLVER,
+                                                              g_proxy_resolver_gnome_iface_init))
+
+static void
+g_proxy_resolver_gnome_class_finalize (GProxyResolverGnomeClass *klass)
+{
+}
+
+static void
+free_settings (GProxyResolverGnome *resolver)
+{
+  g_free (resolver->autoconfig_url);
+  g_strfreev (resolver->ignore_hosts);
+  g_free (resolver->ignore_ips);
+
+  g_free (resolver->http_proxy);
+  g_free (resolver->https_proxy);
+  g_free (resolver->ftp_proxy);
+  g_free (resolver->socks_authority);
+}
+
+static void
+gsettings_changed (GSettings   *settings,
+                  const gchar *key,
+                  gpointer     user_data)
+{
+  GProxyResolverGnome *resolver = user_data;
+
+  resolver->need_update = TRUE;
+}
+
+static void
+g_proxy_resolver_gnome_finalize (GObject *object)
+{
+  GProxyResolverGnome *resolver = G_PROXY_RESOLVER_GNOME (object);
+
+  if (resolver->proxy_settings)
+    {
+      g_signal_handlers_disconnect_by_func (resolver->proxy_settings,
+                                           (gpointer)gsettings_changed,
+                                           resolver);
+      g_object_unref (resolver->proxy_settings);
+
+      g_signal_handlers_disconnect_by_func (resolver->http_settings,
+                                           (gpointer)gsettings_changed,
+                                           resolver);
+      g_object_unref (resolver->http_settings);
+
+      g_signal_handlers_disconnect_by_func (resolver->https_settings,
+                                           (gpointer)gsettings_changed,
+                                           resolver);
+      g_object_unref (resolver->https_settings);
+
+      g_signal_handlers_disconnect_by_func (resolver->ftp_settings,
+                                           (gpointer)gsettings_changed,
+                                           resolver);
+      g_object_unref (resolver->ftp_settings);
+
+      g_signal_handlers_disconnect_by_func (resolver->socks_settings,
+                                           (gpointer)gsettings_changed,
+                                           resolver);
+      g_object_unref (resolver->socks_settings);
+
+      free_settings (resolver);
+    }
+
+  if (resolver->pacrunner)
+    g_object_unref (resolver->pacrunner);
+
+  G_OBJECT_CLASS (g_proxy_resolver_gnome_parent_class)->finalize (object);
+}
+
+static void
+g_proxy_resolver_gnome_init (GProxyResolverGnome *resolver)
+{
+  resolver->proxy_settings = g_settings_new (GNOME_PROXY_SETTINGS_SCHEMA);
+  g_signal_connect (resolver->proxy_settings, "changed",
+                   G_CALLBACK (gsettings_changed), resolver);
+  resolver->http_settings = g_settings_new (GNOME_PROXY_HTTP_SETTINGS_SCHEMA);
+  g_signal_connect (resolver->http_settings, "changed",
+                   G_CALLBACK (gsettings_changed), resolver);
+  resolver->https_settings = g_settings_new (GNOME_PROXY_HTTPS_SETTINGS_SCHEMA);
+  g_signal_connect (resolver->https_settings, "changed",
+                   G_CALLBACK (gsettings_changed), resolver);
+  resolver->ftp_settings = g_settings_new (GNOME_PROXY_FTP_SETTINGS_SCHEMA);
+  g_signal_connect (resolver->ftp_settings, "changed",
+                   G_CALLBACK (gsettings_changed), resolver);
+  resolver->socks_settings = g_settings_new (GNOME_PROXY_SOCKS_SETTINGS_SCHEMA);
+  g_signal_connect (resolver->socks_settings, "changed",
+                   G_CALLBACK (gsettings_changed), resolver);
+
+  resolver->need_update = TRUE;
+}
+
+static void
+update_settings (GProxyResolverGnome *resolver)
+{
+  gchar *host;
+  guint port;
+  int i;
+
+  resolver->need_update = FALSE;
+
+  free_settings (resolver);
+
+  resolver->mode =
+    g_settings_get_enum (resolver->proxy_settings, GNOME_PROXY_MODE_KEY);
+  resolver->autoconfig_url =
+    g_settings_get_string (resolver->proxy_settings, GNOME_PROXY_AUTOCONFIG_URL_KEY);
+  resolver->use_same_proxy =
+    g_settings_get_boolean (resolver->proxy_settings, GNOME_PROXY_USE_SAME_PROXY_KEY);
+
+  resolver->ignore_hosts =
+    g_settings_get_strv (resolver->proxy_settings, GNOME_PROXY_IGNORE_HOSTS_KEY);
+
+  if (resolver->ignore_hosts && resolver->ignore_hosts[0])
+    {
+      GArray *ignore_ips;
+      gchar *slash;
+      GInetAddress *iaddr;
+      GProxyResolverGnomeIPMask mask;
+
+      ignore_ips = g_array_new (TRUE, FALSE, sizeof (GProxyResolverGnomeIPMask));
+      for (i = 0; resolver->ignore_hosts[i]; i++)
+       {
+         host = resolver->ignore_hosts[i];
+         slash = strchr (host, '/');
+         if (slash)
+           host = g_strndup (host, slash - host);
+         iaddr = g_inet_address_new_from_string (host);
+         if (iaddr)
+           {
+             int addrlen = g_inet_address_get_native_size (iaddr);
+
+             memset (&mask, 0, sizeof (mask));
+             mask.family = g_inet_address_get_family (iaddr);
+             memcpy (mask.mask, g_inet_address_to_bytes (iaddr), addrlen);
+             if (slash)
+               {
+                 mask.length = atoi (slash + 1);
+                 if (mask.length > addrlen * 8)
+                   {
+                     g_warning("ignore_host '%s' has invalid mask length",
+                               resolver->ignore_hosts[i]);
+                     mask.length = addrlen;
+                   }
+               }
+             else
+               mask.length = 0;
+
+             g_array_append_val (ignore_ips, mask);
+
+             g_object_unref (iaddr);
+           }
+         if (slash)
+           g_free (host);
+       }
+
+      if (ignore_ips->len)
+       resolver->ignore_ips = (GProxyResolverGnomeIPMask *)g_array_free (ignore_ips, FALSE);
+      else
+       {
+         g_array_free (ignore_ips, TRUE);
+         resolver->ignore_ips = NULL;
+       }
+    }
+  else
+    resolver->ignore_ips = NULL;
+
+  host = g_settings_get_string (resolver->http_settings, GNOME_PROXY_HTTP_HOST_KEY);
+  port = g_settings_get_int (resolver->http_settings, GNOME_PROXY_HTTP_PORT_KEY);
+
+  if (g_settings_get_boolean (resolver->http_settings, GNOME_PROXY_HTTP_USE_AUTH_KEY))
+    {
+      gchar *user, *password;
+      gchar *enc_user, *enc_password;
+
+      user = g_settings_get_string (resolver->http_settings, GNOME_PROXY_HTTP_USER_KEY);
+      enc_user = g_uri_escape_string (user, NULL, TRUE);
+      g_free (user);
+      password = g_settings_get_string (resolver->http_settings, GNOME_PROXY_HTTP_PASSWORD_KEY);
+      enc_password = g_uri_escape_string (password, NULL, TRUE);
+      g_free (password);
+
+      resolver->http_proxy = g_strdup_printf ("http://%s:%s@%s:%u",
+                                             enc_user, enc_password,
+                                             host, port);
+      g_free (enc_user);
+      g_free (enc_password);
+    }
+  else
+    resolver->http_proxy = g_strdup_printf ("http://%s:%u", host, port);
+  g_free (host);
+
+  host = g_settings_get_string (resolver->https_settings, GNOME_PROXY_HTTPS_HOST_KEY);
+  port = g_settings_get_int (resolver->https_settings, GNOME_PROXY_HTTPS_PORT_KEY);
+  if (host && *host)
+    resolver->https_proxy = g_strdup_printf ("http://%s:%u", host, port);
+  g_free (host);
+
+  host = g_settings_get_string (resolver->ftp_settings, GNOME_PROXY_FTP_HOST_KEY);
+  port = g_settings_get_int (resolver->ftp_settings, GNOME_PROXY_FTP_PORT_KEY);
+  if (host && *host)
+    resolver->ftp_proxy = g_strdup_printf ("ftp://%s:%u", host, port);
+  g_free (host);
+
+  host = g_settings_get_string (resolver->socks_settings, GNOME_PROXY_SOCKS_HOST_KEY);
+  port = g_settings_get_int (resolver->socks_settings, GNOME_PROXY_SOCKS_PORT_KEY);
+  if (host && *host)
+    resolver->socks_authority = g_strdup_printf ("%s:%u", host, port);
+  g_free (host);
+
+  if (resolver->mode == G_DESKTOP_PROXY_MODE_AUTO && !resolver->pacrunner)
+    {
+      GError *error = NULL;
+      resolver->pacrunner =
+       g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
+                                      G_DBUS_PROXY_FLAGS_NONE,
+                                      NULL,
+                                      "org.gtk.GLib.PACRunner",
+                                      "/org/gtk/GLib/PACRunner",
+                                      "org.gtk.GLib.PACRunner",
+                                      NULL, &error);
+      if (error)
+       {
+         g_warning ("Could not start proxy autoconfiguration helper:"
+                    "\n    %s\nProxy autoconfiguration will not work",
+                    error->message);
+       }
+    }
+  else if (resolver->mode != G_DESKTOP_PROXY_MODE_AUTO && resolver->pacrunner)
+    {
+      g_object_unref (resolver->pacrunner);
+      resolver->pacrunner = NULL;
+    }
+}
+
+static gboolean
+g_proxy_resolver_gnome_is_supported (GProxyResolver *object)
+{
+  GProxyResolverGnome *resolver = G_PROXY_RESOLVER_GNOME (object);
+
+  return resolver->proxy_settings != NULL;
+}
+
+static gboolean
+parse_uri (const gchar  *uri,
+          gchar       **scheme,
+          gchar       **host)
+{
+  const gchar *authority, *hoststart, *hostend, *at, *colon, *slash;
+
+  colon = strchr (uri, ':');
+  if (!colon || strncmp (colon, "://", 3) != 0)
+    return FALSE;
+
+  *scheme = g_strndup (uri, colon - uri);
+
+  authority = colon + 3;
+  colon = strchr (authority, ':');
+  slash = strchr (authority, '/');
+  if (colon && (!slash || colon < slash))
+    hostend = colon;
+  else if (slash)
+    hostend = slash;
+  else
+    hostend = authority + strlen (authority);
+
+  at = strchr (authority, '@');
+  if (at && at < hostend)
+    hoststart = at + 1;
+  else
+    hoststart = authority;
+  *host = g_strndup (hoststart, hostend - hoststart);
+
+  return TRUE;
+}
+
+static gboolean
+masked_compare (const guint8 *mask,
+               const guint8 *addr,
+               int           maskbits)
+{
+  int bytes, bits;
+
+  if (maskbits == 0)
+    return TRUE;
+
+  bytes = maskbits / 8;
+  if (bytes != 0 && memcmp (mask, addr, bytes) != 0)
+    return FALSE;
+
+  bits = maskbits % 8;
+  return mask[bytes] == (addr[bytes] & (0xFF << (8 - bits)));
+}
+
+static gboolean
+ignore_host (GProxyResolverGnome *resolver,
+            const gchar         *host)
+{
+  gboolean ignore = FALSE;
+  gint i;
+
+  if (resolver->ignore_ips)
+    {
+      GInetAddress *iaddr;
+
+      iaddr = g_inet_address_new_from_string (host);
+      if (iaddr)
+       {
+         GSocketFamily family = g_inet_address_get_family (iaddr);
+         const guint8 *addr = g_inet_address_to_bytes (iaddr);
+
+         for (i = 0; resolver->ignore_ips[i].length; i++)
+           {
+             if (resolver->ignore_ips[i].family == family &&
+                 masked_compare (resolver->ignore_ips[i].mask, addr,
+                                 resolver->ignore_ips[i].length))
+               {
+                 ignore = TRUE;
+                 break;
+               }
+           }
+
+         g_object_unref (iaddr);
+         return ignore;
+       }
+    }
+
+  if (resolver->ignore_hosts && resolver->ignore_hosts[0])
+    {
+      gchar *ascii_host = NULL;
+
+      if (g_hostname_is_non_ascii (host))
+       host = ascii_host = g_hostname_to_ascii (host);
+
+      for (i = 0; resolver->ignore_hosts[i]; i++)
+       {
+         if (!g_ascii_strcasecmp (host, resolver->ignore_hosts[i]))
+           {
+             ignore = TRUE;
+             break;
+           }
+       }
+
+      g_free (ascii_host);
+    }
+
+  return ignore;
+}
+
+static gchar **
+g_proxy_resolver_gnome_lookup (GProxyResolver  *proxy_resolver,
+                              const gchar     *uri,
+                              GCancellable    *cancellable,
+                              GError         **error)
+{
+  GProxyResolverGnome *resolver = G_PROXY_RESOLVER_GNOME (proxy_resolver);
+  gchar *scheme = NULL, *host = NULL;
+  const gchar *proxy = "direct://";
+  gchar **proxies = NULL;
+
+  if (resolver->need_update)
+    update_settings (resolver);
+
+  if (resolver->mode == G_DESKTOP_PROXY_MODE_NONE)
+    goto done;
+
+  /* FIXME: use guri when it lands... */
+  if (!parse_uri (uri, &scheme, &host))
+    goto done;
+  if (ignore_host (resolver, host))
+    goto done;
+
+  if (resolver->pacrunner)
+    {
+      GVariant *vproxies;
+
+      vproxies = g_dbus_proxy_call_sync (resolver->pacrunner,
+                                        "Lookup",
+                                        g_variant_new ("(ss)",
+                                                       resolver->autoconfig_url,
+                                                       uri),
+                                        G_DBUS_CALL_FLAGS_NONE,
+                                        -1,
+                                        cancellable, error);
+      if (vproxies)
+       {
+         g_variant_get (vproxies, "(^as)", &proxies);
+         g_variant_unref (vproxies);
+       }
+    }
+  else if (resolver->ftp_proxy &&
+          (!strcmp (scheme, "ftp") || !strcmp (scheme, "ftps")))
+    {
+      proxy = resolver->ftp_proxy;
+    }
+  else if (resolver->https_proxy && !strcmp (scheme, "https"))
+    {
+      proxy = resolver->https_proxy;
+    }
+  else if (resolver->http_proxy &&
+      (!strcmp (scheme, "http") || !strcmp (scheme, "https")))
+    {
+      proxy = resolver->http_proxy;
+    }
+  else if (resolver->socks_authority)
+    {
+      proxies = g_new0 (gchar *, 4);
+      proxies[0] = g_strdup_printf ("socks5://%s", resolver->socks_authority);
+      proxies[1] = g_strdup_printf ("socks4a://%s", resolver->socks_authority);
+      proxies[2] = g_strdup_printf ("socks4://%s", resolver->socks_authority);
+    }
+  else if (resolver->use_same_proxy && resolver->http_proxy)
+    {
+      proxy = resolver->http_proxy;
+    }
+
+done:
+  g_free (scheme);
+  g_free (host);
+
+  if (!proxies)
+    {
+      proxies = g_new0 (gchar *, 2);
+      proxies[0] = g_strdup (proxy);
+    }
+  return proxies;
+}
+
+static void
+got_autoconfig_proxies (GObject      *source,
+                       GAsyncResult *result,
+                       gpointer      user_data)
+{
+  GSimpleAsyncResult *simple = user_data;
+  GVariant *vproxies;
+  char **proxies;
+  GError *error = NULL;
+
+  vproxies = g_dbus_proxy_call_finish (G_DBUS_PROXY (source),
+                                      result, &error);
+  if (vproxies)
+    {
+      g_variant_get (vproxies, "(^as)", &proxies);
+      g_simple_async_result_set_op_res_gpointer (simple, proxies,
+                                                (GDestroyNotify)g_strfreev);
+      g_variant_unref (vproxies);
+    }
+  else
+    {
+      g_simple_async_result_set_from_error (simple, error);
+      g_error_free (error);
+    }
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+}
+
+static void
+g_proxy_resolver_gnome_lookup_async (GProxyResolver      *proxy_resolver,
+                                    const gchar         *uri,
+                                    GCancellable        *cancellable,
+                                    GAsyncReadyCallback  callback,
+                                    gpointer             user_data)
+{
+  GProxyResolverGnome *resolver = G_PROXY_RESOLVER_GNOME (proxy_resolver);
+  GSimpleAsyncResult *simple;
+
+  simple = g_simple_async_result_new (G_OBJECT (resolver),
+                                     callback, user_data,
+                                     g_proxy_resolver_gnome_lookup_async);
+
+  if (resolver->pacrunner)
+    {
+      g_dbus_proxy_call (resolver->pacrunner,
+                        "Lookup",
+                        g_variant_new ("(ss)",
+                                       resolver->autoconfig_url,
+                                       uri),
+                        G_DBUS_CALL_FLAGS_NONE,
+                        -1,
+                        cancellable,
+                        got_autoconfig_proxies,
+                        simple);
+    }
+  else
+    {
+      GError *error = NULL;
+      char **proxies;
+
+      proxies = g_proxy_resolver_gnome_lookup (proxy_resolver, uri,
+                                              cancellable, &error);
+      if (proxies)
+       {
+         g_simple_async_result_set_op_res_gpointer (simple, proxies,
+                                                    (GDestroyNotify)g_strfreev);
+       }
+      else
+       {
+         g_simple_async_result_set_from_error (simple, error);
+         g_error_free (error);
+       }
+      g_simple_async_result_complete_in_idle (simple);
+      g_object_unref (simple);
+    }
+}
+
+static gchar **
+g_proxy_resolver_gnome_lookup_finish (GProxyResolver  *resolver,
+                                     GAsyncResult    *result,
+                                     GError         **error)
+{
+  GSimpleAsyncResult *simple;
+  gchar **proxies;
+
+  g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (resolver), g_proxy_resolver_gnome_lookup_async), NULL);
+
+  simple = G_SIMPLE_ASYNC_RESULT (result);
+
+  if (g_simple_async_result_propagate_error (simple, error))
+    return NULL;
+
+  proxies = g_simple_async_result_get_op_res_gpointer (simple);
+  return g_strdupv (proxies);
+}
+
+static void
+g_proxy_resolver_gnome_class_init (GProxyResolverGnomeClass *resolver_class)
+{
+  GObjectClass *object_class;
+  
+  object_class = G_OBJECT_CLASS (resolver_class);
+  object_class->finalize = g_proxy_resolver_gnome_finalize;
+}
+
+static void
+g_proxy_resolver_gnome_iface_init (GProxyResolverInterface *iface)
+{
+  iface->is_supported = g_proxy_resolver_gnome_is_supported;
+  iface->lookup = g_proxy_resolver_gnome_lookup;
+  iface->lookup_async = g_proxy_resolver_gnome_lookup_async;
+  iface->lookup_finish = g_proxy_resolver_gnome_lookup_finish;
+}
+
+void
+g_proxy_resolver_gnome_register (GIOModule *module)
+{
+  g_proxy_resolver_gnome_register_type (G_TYPE_MODULE (module));
+  g_io_extension_point_implement (G_PROXY_RESOLVER_EXTENSION_POINT_NAME,
+                                 g_proxy_resolver_gnome_get_type(),
+                                 "gnome",
+                                 80);
+}
diff --git a/proxy/gnome/gproxyresolvergnome.h b/proxy/gnome/gproxyresolvergnome.h
new file mode 100644 (file)
index 0000000..be173a4
--- /dev/null
@@ -0,0 +1,48 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __G_PROXY_RESOLVER_GNOME_H__
+#define __G_PROXY_RESOLVER_GNOME_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_PROXY_RESOLVER_GNOME         (g_proxy_resolver_gnome_get_type ())
+#define G_PROXY_RESOLVER_GNOME(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_PROXY_RESOLVER_GNOME, GProxyResolverGnome))
+#define G_PROXY_RESOLVER_GNOME_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_PROXY_RESOLVER_GNOME, GProxyResolverGnomeClass))
+#define G_IS_PROXY_RESOLVER_GNOME(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_PROXY_RESOLVER_GNOME))
+#define G_IS_PROXY_RESOLVER_GNOME_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_PROXY_RESOLVER_GNOME))
+#define G_PROXY_RESOLVER_GNOME_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_PROXY_RESOLVER_GNOME, GProxyResolverGnomeClass))
+
+typedef struct _GProxyResolverGnome       GProxyResolverGnome;
+typedef struct _GProxyResolverGnomeClass  GProxyResolverGnomeClass;
+
+struct _GProxyResolverGnomeClass {
+  GObjectClass parent_class;
+};
+
+GType g_proxy_resolver_gnome_get_type (void);
+void  g_proxy_resolver_gnome_register (GIOModule *module);
+
+G_END_DECLS
+
+#endif /* __G_PROXY_RESOLVER_GNOME_H__ */