[Spice-commits] 5 commits - configure.ac gtk/Makefile.am gtk/spice-channel.c gtk/spice-proxy.c gtk/spice-proxy.h gtk/spice-session.c gtk/wocky-http-proxy.c gtk/wocky-http-proxy.h

Marc-André Lureau elmarco at kemper.freedesktop.org
Fri Dec 14 09:32:20 PST 2012


 configure.ac           |    3 
 gtk/Makefile.am        |    9 +
 gtk/spice-channel.c    |    6 
 gtk/spice-proxy.c      |  236 ++++++++++++++++++++++++++
 gtk/spice-proxy.h      |   59 ++++++
 gtk/spice-session.c    |  193 +++++++++++++++-------
 gtk/wocky-http-proxy.c |  429 +++++++++++++++++++++++++++++++++++++++++++++++++
 gtk/wocky-http-proxy.h |   42 ++++
 8 files changed, 919 insertions(+), 58 deletions(-)

New commits:
commit 0f9a432c547d16529c0ca8b83048a065f620aaf1
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Thu Aug 23 21:47:33 2012 +0200

    session: allow to connect via HTTP CONNECT proxy
    
    Allow to connect to a Spice server via a HTTP proxy with CONNECT
    method. spice-gtk will use the SPICE_PROXY environment variable, which
    can currently only have the following syntax: [http://]hostname[:port]
    
    This is paving the way to more proxies support (socks4/socks5).
    
    This code is now entirely sync (it was not even completely async), the
    following patch will make it all async again.
    
    Tested with Squid, locally only.

diff --git a/gtk/spice-session.c b/gtk/spice-session.c
index e44ee08..06c1629 100644
--- a/gtk/spice-session.c
+++ b/gtk/spice-session.c
@@ -28,6 +28,7 @@
 #include "gio-coroutine.h"
 #include "glib-compat.h"
 #include "wocky-http-proxy.h"
+#include "spice-proxy.h"
 
 struct channel {
     SpiceChannel      *channel;
@@ -1573,43 +1574,128 @@ gboolean spice_session_has_channel_type(SpiceSession *session, gint type)
 /* ------------------------------------------------------------------ */
 /* private functions                                                  */
 
-static GSocket *channel_connect_socket(SpiceChannel *channel,
-                                       GSocketAddress *sockaddr,
-                                       GError **error)
+typedef struct spice_open_host spice_open_host;
+
+struct spice_open_host {
+    struct coroutine *from;
+    SpiceSession *session;
+    SpiceChannel *channel;
+    SpiceProxy *proxy;
+    int port;
+    GCancellable *cancellable;
+    GError *error;
+    GSocket *socket;
+};
+
+static void socket_client_connect_ready(GObject *source_object, GAsyncResult *result,
+                                        gpointer data)
 {
-    SpiceChannelPrivate *c = channel->priv;
-    GSocket *sock = g_socket_new(g_socket_address_get_family(sockaddr),
-                                 G_SOCKET_TYPE_STREAM,
-                                 G_SOCKET_PROTOCOL_DEFAULT,
-                                 error);
+    GSocketClient *client = G_SOCKET_CLIENT(source_object);
+    spice_open_host *open_host = data;
+    GSocketConnection *connection = NULL;
 
-    if (!sock)
-        return NULL;
+    SPICE_DEBUG("connect ready");
+    connection = g_socket_client_connect_finish(client, result, &open_host->error);
+    if (connection == NULL)
+        goto end;
 
-    g_socket_set_blocking(sock, FALSE);
-    g_socket_set_keepalive(sock, TRUE);
+    open_host->socket = g_socket_connection_get_socket(connection);
+    g_object_ref(open_host->socket);
 
-    if (!g_socket_connect(sock, sockaddr, NULL, error)) {
-        if (*error && (*error)->code == G_IO_ERROR_PENDING) {
-            g_clear_error(error);
-            CHANNEL_DEBUG(channel, "Socket pending");
-            g_coroutine_socket_wait(&c->coroutine, sock, G_IO_OUT | G_IO_ERR | G_IO_HUP);
+end:
+    g_object_unref(connection);
+    g_object_unref(client);
 
-            if (!g_socket_check_connect_result(sock, error)) {
-                CHANNEL_DEBUG(channel, "Failed to connect %s", (*error)->message);
-                g_object_unref(sock);
-                return NULL;
-            }
-        } else {
-            CHANNEL_DEBUG(channel, "Socket error: %s", *error ? (*error)->message : "unknown");
-            g_object_unref(sock);
-            return NULL;
-        }
+    coroutine_yieldto(open_host->from, NULL);
+}
+
+/* main context */
+static void open_host_connectable_connect(spice_open_host *open_host, GSocketConnectable *connectable)
+{
+    GSocketClient *client;
+
+    SPICE_DEBUG("connecting %p...", open_host);
+    client = g_socket_client_new();
+    g_socket_client_connect_async(client, connectable, open_host->cancellable,
+                                  socket_client_connect_ready, open_host);
+}
+
+#if GLIB_CHECK_VERSION(2,26,0)
+/* main context */
+static void proxy_lookup_ready(GObject *source_object, GAsyncResult *result,
+                               gpointer data)
+{
+    spice_open_host *open_host = data;
+    SpiceSessionPrivate *s = SPICE_SESSION_GET_PRIVATE(open_host->session);
+    GList *addresses = NULL, *it;
+    GSocketAddress *address;
+
+    SPICE_DEBUG("proxy lookup ready");
+    addresses = g_resolver_lookup_by_name_finish(G_RESOLVER(source_object),
+                                                 result, &open_host->error);
+    if (addresses == NULL || open_host->error) {
+        coroutine_yieldto(open_host->from, NULL);
+        return;
+    }
+
+    for (it = addresses; it != NULL; it = it->next) {
+        address = g_proxy_address_new(G_INET_ADDRESS(it->data),
+                                      spice_proxy_get_port(open_host->proxy), "http",
+                                      s->host, open_host->port, NULL, NULL);
+        if (address != NULL)
+            break;
     }
 
-    CHANNEL_DEBUG(channel, "Finally connected");
+    open_host_connectable_connect(open_host, G_SOCKET_CONNECTABLE(address));
+    g_resolver_free_addresses(addresses);
+}
+
+static SpiceProxy* get_proxy(GError **error)
+{
+    SpiceProxy *proxy;
+
+    const gchar *proxy_env = g_getenv("SPICE_PROXY");
+    if (proxy_env == NULL || strlen(proxy_env) == 0)
+        return NULL;
+
+    proxy = spice_proxy_new();
+    if (!spice_proxy_parse(proxy, proxy_env, error))
+        g_clear_object(&proxy);
 
-    return sock;
+    return proxy;
+}
+#endif
+
+/* main context */
+static gboolean open_host_idle_cb(gpointer data)
+{
+    spice_open_host *open_host = data;
+    SpiceSessionPrivate *s = SPICE_SESSION_GET_PRIVATE(open_host->session);
+
+    g_return_val_if_fail(open_host != NULL, FALSE);
+    g_return_val_if_fail(open_host->socket == NULL, FALSE);
+
+#if GLIB_CHECK_VERSION(2,26,0)
+    open_host->proxy = get_proxy(&open_host->error);
+    if (open_host->proxy) {
+        g_resolver_lookup_by_name_async(g_resolver_get_default(),
+                                        spice_proxy_get_hostname(open_host->proxy),
+                                        open_host->cancellable,
+                                        proxy_lookup_ready, open_host);
+    } else
+#endif
+    if (open_host->error != NULL) {
+        coroutine_yieldto(open_host->from, NULL);
+        return FALSE;
+    } else
+        open_host_connectable_connect(open_host,
+                                      g_network_address_new(s->host, open_host->port));
+
+    SPICE_DEBUG("open host %s:%d", s->host, open_host->port);
+    if (open_host->proxy != NULL)
+        SPICE_DEBUG("(with proxy %p)", open_host->proxy);
+
+    return FALSE;
 }
 
 /* coroutine context */
@@ -1618,41 +1704,33 @@ GSocket* spice_session_channel_open_host(SpiceSession *session, SpiceChannel *ch
                                          gboolean use_tls)
 {
     SpiceSessionPrivate *s = SPICE_SESSION_GET_PRIVATE(session);
-    GSocketConnectable *addr;
-    GSocketAddressEnumerator *enumerator;
-    GSocketAddress *sockaddr;
-    GError *conn_error = NULL;
-    GSocket *sock = NULL;
-    int port;
+    spice_open_host open_host = { 0, };
 
     if ((use_tls && !s->tls_port) || (!use_tls && !s->port))
         return NULL;
 
-    port = atoi(use_tls ? s->tls_port : s->port);
-
-    SPICE_DEBUG("Resolving host %s %d", s->host, port);
+    open_host.from = coroutine_self();
+    open_host.session = session;
+    open_host.channel = channel;
+    open_host.port = atoi(use_tls ? s->tls_port : s->port);
+    g_idle_add(open_host_idle_cb, &open_host);
 
-    addr = g_network_address_new(s->host, port);
+    /* switch to main loop and wait for connection */
+    coroutine_yield(NULL);
+    if (open_host.error != NULL) {
+        g_return_val_if_fail(open_host.socket == NULL, NULL);
 
-    enumerator = g_socket_connectable_enumerate (addr);
-    g_object_unref (addr);
+        g_warning("%s", open_host.error->message);
+        g_clear_error(&open_host.error);
+    } else {
+        g_return_val_if_fail(open_host.socket != NULL, NULL);
 
-    /* Try each sockaddr until we succeed. Record the first
-     * connection error, but not any further ones (since they'll probably
-     * be basically the same as the first).
-     */
-    while (!sock &&
-           (sockaddr = g_socket_address_enumerator_next(enumerator, NULL, &conn_error))) {
-        SPICE_DEBUG("Trying one socket");
-        g_clear_error(&conn_error);
-        sock = channel_connect_socket(channel, sockaddr, &conn_error);
-        if (conn_error != NULL)
-            SPICE_DEBUG("%s", conn_error->message);
-        g_object_unref(sockaddr);
+        g_socket_set_blocking(open_host.socket, FALSE);
+        g_socket_set_keepalive(open_host.socket, TRUE);
     }
-    g_object_unref(enumerator);
-    g_clear_error(&conn_error);
-    return sock;
+
+    g_clear_object(&open_host.proxy);
+    return open_host.socket;
 }
 
 
commit 8e96f6e44f3111b0c22938b27d232f0e5fbaac31
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Fri Aug 24 15:02:00 2012 +0200

    Add SpiceProxy object
    
    Add a simple object to handle the SPICE_PROXY values.
    
    It's not clear to me whether each GIO user needs to handle the proxy
    configuration, or if there is a more global mechanism (via
    g_network_address_parse_uri())
    
    Also, the parsing is currently very limited and only support basic
    HTTP proxy URI. In the future, we really want to rely on GUri or
    similar instead...

diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index dd3744d..9f3fb84 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -243,6 +243,8 @@ libspice_client_glib_2_0_la_SOURCES =			\
 	channel-usbredir-priv.h				\
 	smartcard-manager.c				\
 	smartcard-manager-priv.h			\
+	spice-proxy.c					\
+	spice-proxy.h					\
 	usb-device-manager.c				\
 	usb-device-manager-priv.h			\
 	usbutil.c					\
diff --git a/gtk/spice-proxy.c b/gtk/spice-proxy.c
new file mode 100644
index 0000000..97c3a6b
--- /dev/null
+++ b/gtk/spice-proxy.c
@@ -0,0 +1,236 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2012 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.1 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "spice-client.h"
+#include "spice-proxy.h"
+
+struct _SpiceProxyPrivate {
+    gchar *protocol;
+    gchar *hostname;
+    guint port;
+};
+
+#define SPICE_PROXY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), SPICE_TYPE_PROXY, SpiceProxyPrivate))
+
+G_DEFINE_TYPE(SpiceProxy, spice_proxy, G_TYPE_OBJECT);
+
+enum  {
+    SPICE_PROXY_DUMMY_PROPERTY,
+    SPICE_PROXY_PROTOCOL,
+    SPICE_PROXY_HOSTNAME,
+    SPICE_PROXY_PORT
+};
+
+SpiceProxy* spice_proxy_new(void)
+{
+    SpiceProxy * self = NULL;
+    self = (SpiceProxy*)g_object_new(SPICE_TYPE_PROXY, NULL);
+    return self;
+}
+
+gboolean spice_proxy_parse(SpiceProxy *self, const gchar *uri, GError **error)
+{
+    gboolean success = FALSE;
+
+    g_return_val_if_fail(self != NULL, FALSE);
+    g_return_val_if_fail(uri != NULL, FALSE);
+
+    /* FIXME: use GUri when it is ready... only support http atm */
+    /* the code is voluntarily not parsing thoroughly the uri */
+    if (g_ascii_strncasecmp("http://", uri, 7) == 0)
+        uri += 7;
+
+    spice_proxy_set_protocol(self, "http");
+    spice_proxy_set_port(self, 3128);
+
+    gchar **proxyv = g_strsplit(uri, ":", 0);
+    const gchar *proxy_port = NULL;
+
+    if (proxyv[0] == NULL || strlen(proxyv[0]) == 0) {
+        g_set_error(error, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
+                    "Invalid hostname in proxy address");
+        goto end;
+    }
+
+    spice_proxy_set_hostname(self, proxyv[0]);
+    if (proxyv[0] != NULL)
+        proxy_port = proxyv[1];
+
+    if (proxy_port != NULL) {
+        char *endptr;
+        guint port = strtoul(proxy_port, &endptr, 10);
+        if (*endptr != '\0') {
+            g_set_error(error, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
+                        "Invalid proxy port: %s", proxy_port);
+            goto end;
+        }
+        spice_proxy_set_port(self, port);
+    }
+
+    success = TRUE;
+
+end:
+    g_strfreev(proxyv);
+    return success;
+}
+
+const gchar* spice_proxy_get_protocol(SpiceProxy *self)
+{
+    g_return_val_if_fail(SPICE_IS_PROXY(self), NULL);
+    return self->priv->protocol;
+}
+
+void spice_proxy_set_protocol(SpiceProxy *self, const gchar *value)
+{
+    g_return_if_fail(SPICE_IS_PROXY(self));
+
+    g_free(self->priv->protocol);
+    self->priv->protocol = g_strdup(value);
+    g_object_notify((GObject *)self, "protocol");
+}
+
+const gchar* spice_proxy_get_hostname(SpiceProxy *self)
+{
+    g_return_val_if_fail(SPICE_IS_PROXY(self), NULL);
+    return self->priv->hostname;
+}
+
+
+void spice_proxy_set_hostname(SpiceProxy *self, const gchar *value)
+{
+    g_return_if_fail(SPICE_IS_PROXY(self));
+
+    g_free(self->priv->hostname);
+    self->priv->hostname = g_strdup(value);
+    g_object_notify((GObject *)self, "hostname");
+}
+
+guint spice_proxy_get_port(SpiceProxy *self)
+{
+    g_return_val_if_fail(SPICE_IS_PROXY(self), 0);
+    return self->priv->port;
+}
+
+void spice_proxy_set_port(SpiceProxy *self, guint port)
+{
+    g_return_if_fail(SPICE_IS_PROXY(self));
+    self->priv->port = port;
+    g_object_notify((GObject *)self, "port");
+}
+
+static void spice_proxy_get_property(GObject *object, guint property_id,
+                                     GValue *value, GParamSpec *pspec)
+{
+    SpiceProxy *self;
+    self = G_TYPE_CHECK_INSTANCE_CAST(object, SPICE_TYPE_PROXY, SpiceProxy);
+
+    switch (property_id) {
+    case SPICE_PROXY_PROTOCOL:
+        g_value_set_string(value, spice_proxy_get_protocol(self));
+        break;
+    case SPICE_PROXY_HOSTNAME:
+        g_value_set_string(value, spice_proxy_get_hostname(self));
+        break;
+    case SPICE_PROXY_PORT:
+        g_value_set_uint(value, spice_proxy_get_port(self));
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+        break;
+    }
+}
+
+
+static void spice_proxy_set_property(GObject *object, guint property_id,
+                                     const GValue *value, GParamSpec *pspec)
+{
+    SpiceProxy * self;
+    self = G_TYPE_CHECK_INSTANCE_CAST(object, SPICE_TYPE_PROXY, SpiceProxy);
+
+    switch (property_id) {
+    case SPICE_PROXY_PROTOCOL:
+        spice_proxy_set_protocol(self, g_value_get_string(value));
+        break;
+    case SPICE_PROXY_HOSTNAME:
+        spice_proxy_set_hostname(self, g_value_get_string(value));
+        break;
+    case SPICE_PROXY_PORT:
+        spice_proxy_set_port(self, g_value_get_uint(value));
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+        break;
+    }
+}
+
+static void spice_proxy_finalize(GObject* obj)
+{
+    SpiceProxy *self;
+
+    self = G_TYPE_CHECK_INSTANCE_CAST(obj, SPICE_TYPE_PROXY, SpiceProxy);
+    g_free(self->priv->protocol);
+    g_free(self->priv->hostname);
+
+    G_OBJECT_CLASS (spice_proxy_parent_class)->finalize (obj);
+}
+
+static void spice_proxy_init (SpiceProxy *self)
+{
+    self->priv = SPICE_PROXY_GET_PRIVATE(self);
+}
+
+
+static void spice_proxy_class_init(SpiceProxyClass *klass)
+{
+    spice_proxy_parent_class = g_type_class_peek_parent (klass);
+    g_type_class_add_private(klass, sizeof(SpiceProxyPrivate));
+
+    G_OBJECT_CLASS (klass)->get_property = spice_proxy_get_property;
+    G_OBJECT_CLASS (klass)->set_property = spice_proxy_set_property;
+    G_OBJECT_CLASS (klass)->finalize = spice_proxy_finalize;
+
+    g_object_class_install_property(G_OBJECT_CLASS (klass),
+                                    SPICE_PROXY_PROTOCOL,
+                                    g_param_spec_string ("protocol",
+                                                         "protocol",
+                                                         "protocol",
+                                                         NULL,
+                                                         G_PARAM_STATIC_STRINGS |
+                                                         G_PARAM_READWRITE));
+
+    g_object_class_install_property(G_OBJECT_CLASS (klass),
+                                    SPICE_PROXY_HOSTNAME,
+                                    g_param_spec_string ("hostname",
+                                                         "hostname",
+                                                         "hostname",
+                                                         NULL,
+                                                         G_PARAM_STATIC_STRINGS |
+                                                         G_PARAM_READWRITE));
+
+    g_object_class_install_property(G_OBJECT_CLASS (klass),
+                                    SPICE_PROXY_PORT,
+                                    g_param_spec_uint ("port",
+                                                       "port",
+                                                       "port",
+                                                       0, G_MAXUINT, 0,
+                                                       G_PARAM_STATIC_STRINGS |
+                                                       G_PARAM_READWRITE));
+}
diff --git a/gtk/spice-proxy.h b/gtk/spice-proxy.h
new file mode 100644
index 0000000..c780931
--- /dev/null
+++ b/gtk/spice-proxy.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2012 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.1 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, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef __SPICE_PROXY_H__
+#define __SPICE_PROXY_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define SPICE_TYPE_PROXY (spice_proxy_get_type ())
+#define SPICE_PROXY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SPICE_TYPE_PROXY, SpiceProxy))
+#define SPICE_PROXY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SPICE_TYPE_PROXY, SpiceProxyClass))
+#define SPICE_IS_PROXY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SPICE_TYPE_PROXY))
+#define SPICE_IS_PROXY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SPICE_TYPE_PROXY))
+#define SPICE_PROXY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SPICE_TYPE_PROXY, SpiceProxyClass))
+
+typedef struct _SpiceProxy SpiceProxy;
+typedef struct _SpiceProxyClass SpiceProxyClass;
+typedef struct _SpiceProxyPrivate SpiceProxyPrivate;
+
+struct _SpiceProxy {
+    GObject parent_instance;
+    SpiceProxyPrivate * priv;
+};
+
+struct _SpiceProxyClass {
+    GObjectClass parent_class;
+};
+
+
+GType spice_proxy_get_type(void) G_GNUC_CONST;
+
+SpiceProxy* spice_proxy_new(void);
+gboolean spice_proxy_parse(SpiceProxy* self, const gchar* uri, GError** error);
+const gchar* spice_proxy_get_protocol(SpiceProxy* self);
+void spice_proxy_set_protocol(SpiceProxy* self, const gchar* value);
+const gchar* spice_proxy_get_hostname(SpiceProxy* self);
+void spice_proxy_set_hostname(SpiceProxy* self, const gchar* value);
+guint spice_proxy_get_port(SpiceProxy* self);
+void spice_proxy_set_port(SpiceProxy* self, guint port);
+
+G_END_DECLS
+
+#endif /* __SPICE_PROXY_H__ */
commit cb8c5a49cc2014b7df5971eb1e1263deaa7962c9
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Thu Aug 23 16:04:16 2012 +0200

    Add wocky HTTP proxy
    
    Courtesy of Nicolas Dufresne <nicolas.dufresne at collabora.co.uk>
    
    It might make sense to include this proxy in glib/gio, but it is still
    missing some features according to its author, namely SSL and perhaps
    better CRLF.

diff --git a/configure.ac b/configure.ac
index 629fd5a..f31cdc1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -259,6 +259,9 @@ PKG_CHECK_MODULES(GIO, gio-2.0 >= 2.10.0 $gio_os)
 AC_SUBST(GIO_CFLAGS)
 AC_SUBST(GIO_LIBS)
 
+PKG_CHECK_EXISTS([gio-2.0 >= 2.26], [have_gproxy=yes])
+AM_CONDITIONAL([WITH_GPROXY], [test "x$have_gproxy" = "xyes"])
+
 PKG_CHECK_MODULES(CAIRO, cairo >= 1.2.0)
 AC_SUBST(CAIRO_CFLAGS)
 AC_SUBST(CAIRO_LIBS)
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index d38ba7a..dd3744d 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -258,6 +258,13 @@ libspice_client_glib_2_0_la_SOURCES =			\
 	$(top_srcdir)/spice-common/common/sw_canvas.h	\
 	$(NULL)
 
+if WITH_GPROXY
+libspice_client_glib_2_0_la_SOURCES +=			\
+	wocky-http-proxy.c				\
+	wocky-http-proxy.h				\
+	$(NULL)
+endif
+
 nodist_libspice_client_glib_2_0_la_SOURCES =	\
 	spice-glib-enums.c			\
 	spice-marshal.c				\
diff --git a/gtk/spice-session.c b/gtk/spice-session.c
index 85cc14d..e44ee08 100644
--- a/gtk/spice-session.c
+++ b/gtk/spice-session.c
@@ -27,6 +27,7 @@
 #include "spice-session-priv.h"
 #include "gio-coroutine.h"
 #include "glib-compat.h"
+#include "wocky-http-proxy.h"
 
 struct channel {
     SpiceChannel      *channel;
@@ -614,6 +615,8 @@ static void spice_session_class_init(SpiceSessionClass *klass)
 {
     GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
 
+    _wocky_http_proxy_get_type();
+
     gobject_class->dispose      = spice_session_dispose;
     gobject_class->finalize     = spice_session_finalize;
     gobject_class->get_property = spice_session_get_property;
diff --git a/gtk/wocky-http-proxy.c b/gtk/wocky-http-proxy.c
new file mode 100644
index 0000000..639cfb0
--- /dev/null
+++ b/gtk/wocky-http-proxy.c
@@ -0,0 +1,429 @@
+ /* wocky-http-proxy.c: Source for WockyHttpProxy
+ *
+ * Copyright (C) 2010 Collabora, Ltd.
+ * @author Nicolas Dufresne <nicolas.dufresne at collabora.co.uk>
+ *
+ * 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.1 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "config.h"
+
+#include "wocky-http-proxy.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+
+struct _WockyHttpProxy
+{
+  GObject parent;
+};
+
+struct _WockyHttpProxyClass
+{
+  GObjectClass parent_class;
+};
+
+static void wocky_http_proxy_iface_init (GProxyInterface *proxy_iface);
+
+#define wocky_http_proxy_get_type _wocky_http_proxy_get_type
+G_DEFINE_TYPE_WITH_CODE (WockyHttpProxy, wocky_http_proxy, G_TYPE_OBJECT,
+    G_IMPLEMENT_INTERFACE (G_TYPE_PROXY,
+      wocky_http_proxy_iface_init)
+    g_io_extension_point_set_required_type (
+      g_io_extension_point_register (G_PROXY_EXTENSION_POINT_NAME),
+      G_TYPE_PROXY);
+    g_io_extension_point_implement (G_PROXY_EXTENSION_POINT_NAME,
+      g_define_type_id, "http", 0))
+
+static void
+wocky_http_proxy_init (WockyHttpProxy *proxy)
+{
+}
+
+#define HTTP_END_MARKER "\r\n\r\n"
+
+static gchar *
+create_request (GProxyAddress *proxy_address, gboolean *has_cred)
+{
+  const gchar *hostname;
+  gint port;
+  const gchar *username;
+  const gchar *password;
+  GString *request;
+  gchar *ascii_hostname;
+
+  if (has_cred)
+    *has_cred = FALSE;
+
+  hostname = g_proxy_address_get_destination_hostname (proxy_address);
+  port = g_proxy_address_get_destination_port (proxy_address);
+  username = g_proxy_address_get_username (proxy_address);
+  password = g_proxy_address_get_password (proxy_address);
+
+  request = g_string_new (NULL);
+
+  ascii_hostname = g_hostname_to_ascii (hostname);
+  g_string_append_printf (request,
+      "CONNECT %s:%i HTTP/1.0\r\n"
+        "Host: %s:%i\r\n"
+        "Proxy-Connection: keep-alive\r\n"
+        "User-Agent: GLib/%i.%i\r\n",
+      ascii_hostname, port,
+      ascii_hostname, port,
+      GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION);
+  g_free (ascii_hostname);
+
+  if (username != NULL && password != NULL)
+    {
+      gchar *cred;
+      gchar *base64_cred;
+
+      if (has_cred)
+        *has_cred = TRUE;
+
+      cred = g_strdup_printf ("%s:%s", username, password);
+      base64_cred = g_base64_encode ((guchar *) cred, strlen (cred));
+      g_free (cred);
+      g_string_append_printf (request,
+          "Proxy-Authorization: %s\r\n",
+          base64_cred);
+      g_free (base64_cred);
+    }
+
+  g_string_append (request, "\r\n");
+
+  return g_string_free (request, FALSE);
+}
+
+static gboolean
+check_reply (const gchar *buffer, gboolean has_cred, GError **error)
+{
+  gint err_code;
+  const gchar *ptr = buffer + 7;
+
+  if (strncmp (buffer, "HTTP/1.", 7) != 0
+      || (*ptr != '0' && *ptr != '1'))
+    {
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
+          "Bad HTTP proxy reply");
+      return FALSE;
+    }
+
+  ptr++;
+  while (*ptr == ' ') ptr++;
+
+  err_code = atoi (ptr);
+
+  if (err_code < 200 || err_code >= 300)
+    {
+      const gchar *msg_start;
+      gchar *msg;
+
+      while (g_ascii_isdigit (*ptr))
+        ptr++;
+
+      while (*ptr == ' ')
+        ptr++;
+
+      msg_start = ptr;
+
+      ptr = strchr (msg_start, '\r');
+
+      if (ptr == NULL)
+        ptr = strchr (msg_start, '\0');
+
+      msg = g_strndup (msg_start, ptr - msg_start);
+
+      if (err_code == 407)
+        {
+          if (has_cred)
+            g_set_error (error, G_IO_ERROR, G_IO_ERROR_PROXY_AUTH_FAILED,
+                "HTTP proxy authentication failed");
+          else
+            g_set_error (error, G_IO_ERROR, G_IO_ERROR_PROXY_NEED_AUTH,
+                "HTTP proxy authentication required");
+        }
+      else if (msg[0] == '\0')
+        g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
+            "Connection failed due to broken HTTP reply");
+      else
+        g_set_error (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
+            "HTTP proxy connection failed: %i %s",
+            err_code, msg);
+
+      g_free (msg);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static GIOStream *
+wocky_http_proxy_connect (GProxy *proxy,
+    GIOStream *io_stream,
+    GProxyAddress *proxy_address,
+    GCancellable *cancellable,
+    GError **error)
+{
+  GInputStream *in;
+  GOutputStream *out;
+  GDataInputStream *data_in;
+  gchar *buffer;
+  gboolean has_cred;
+
+  in = g_io_stream_get_input_stream (io_stream);
+  out = g_io_stream_get_output_stream (io_stream);
+
+  data_in = g_data_input_stream_new (in);
+  g_filter_input_stream_set_close_base_stream (G_FILTER_INPUT_STREAM (data_in),
+      FALSE);
+
+  buffer = create_request (proxy_address, &has_cred);
+  if (!g_output_stream_write_all (out, buffer, strlen (buffer), NULL,
+        cancellable, error))
+      goto error;
+
+  g_free (buffer);
+  buffer = g_data_input_stream_read_until (data_in, HTTP_END_MARKER, NULL,
+      cancellable, error);
+  g_object_unref (data_in);
+  data_in = NULL;
+
+  if (buffer == NULL)
+    {
+      if (error && (*error == NULL))
+        g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
+            "HTTP proxy server closed connection unexpectedly.");
+      goto error;
+    }
+
+  if (!check_reply (buffer, has_cred, error))
+    goto error;
+
+  g_free (buffer);
+
+  return g_object_ref (io_stream);
+
+error:
+  if (data_in != NULL)
+    g_object_unref (data_in);
+
+  g_free (buffer);
+  return NULL;
+}
+
+
+typedef struct
+{
+  GSimpleAsyncResult *simple;
+  GIOStream *io_stream;
+  gchar *buffer;
+  gssize length;
+  gssize offset;
+  GDataInputStream *data_in;
+  gboolean has_cred;
+  GCancellable *cancellable;
+} ConnectAsyncData;
+
+static void request_write_cb (GObject *source,
+    GAsyncResult *res,
+    gpointer user_data);
+static void reply_read_cb (GObject *source,
+    GAsyncResult *res,
+    gpointer user_data);
+
+static void
+free_connect_data (ConnectAsyncData *data)
+{
+  if (data->io_stream != NULL)
+    g_object_unref (data->io_stream);
+
+  g_free (data->buffer);
+
+  if (data->data_in != NULL)
+    g_object_unref (data->data_in);
+
+  if (data->cancellable != NULL)
+    g_object_unref (data->cancellable);
+
+  g_slice_free (ConnectAsyncData, data);
+}
+
+static void
+complete_async_from_error (ConnectAsyncData *data, GError *error)
+{
+  GSimpleAsyncResult *simple = data->simple;
+
+  if (error == NULL)
+    g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
+        "HTTP proxy server closed connection unexpectedly.");
+
+  g_simple_async_result_set_from_error (data->simple, error);
+  g_error_free (error);
+  g_simple_async_result_set_op_res_gpointer (simple, NULL, NULL);
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+}
+
+static void
+do_write (GAsyncReadyCallback callback, ConnectAsyncData *data)
+{
+  GOutputStream *out;
+  out = g_io_stream_get_output_stream (data->io_stream);
+  g_output_stream_write_async (out,
+      data->buffer + data->offset,
+      data->length - data->offset,
+      G_PRIORITY_DEFAULT, data->cancellable,
+      callback, data);
+}
+
+static void
+wocky_http_proxy_connect_async (GProxy *proxy,
+    GIOStream *io_stream,
+    GProxyAddress *proxy_address,
+    GCancellable *cancellable,
+    GAsyncReadyCallback callback,
+    gpointer user_data)
+{
+  GSimpleAsyncResult *simple;
+  ConnectAsyncData *data;
+  GInputStream *in;
+
+  simple = g_simple_async_result_new (G_OBJECT (proxy),
+      callback, user_data,
+      wocky_http_proxy_connect_async);
+
+  data = g_slice_new0 (ConnectAsyncData);
+
+  data->simple = simple;
+  data->io_stream = g_object_ref (io_stream);
+
+  if (cancellable != NULL)
+    data->cancellable = g_object_ref (cancellable);
+
+  in = g_io_stream_get_input_stream (io_stream);
+
+  data->data_in = g_data_input_stream_new (in);
+  g_filter_input_stream_set_close_base_stream (G_FILTER_INPUT_STREAM (data->data_in),
+      FALSE);
+
+  g_simple_async_result_set_op_res_gpointer (simple, data,
+      (GDestroyNotify) free_connect_data);
+
+  data->buffer = create_request (proxy_address, &data->has_cred);
+  data->length = strlen (data->buffer);
+  data->offset = 0;
+
+  do_write (request_write_cb, data);
+}
+
+static void
+request_write_cb (GObject *source,
+    GAsyncResult *res,
+    gpointer user_data)
+{
+  GError *error = NULL;
+  ConnectAsyncData *data = user_data;
+  gssize written;
+
+  written = g_output_stream_write_finish (G_OUTPUT_STREAM (source),
+      res, &error);
+  if (written < 0)
+    {
+      complete_async_from_error (data, error);
+      return;
+    }
+
+  data->offset += written;
+
+   if (data->offset == data->length)
+    {
+      g_free (data->buffer);
+      data->buffer = NULL;
+
+      g_data_input_stream_read_until_async (data->data_in,
+          HTTP_END_MARKER,
+          G_PRIORITY_DEFAULT,
+          data->cancellable,
+          reply_read_cb, data);
+
+    }
+  else
+    {
+      do_write (request_write_cb, data);
+    }
+}
+
+static void
+reply_read_cb (GObject *source,
+    GAsyncResult *res,
+    gpointer user_data)
+{
+  GError *error = NULL;
+  ConnectAsyncData *data = user_data;
+
+  data->buffer = g_data_input_stream_read_until_finish (data->data_in,
+      res, NULL, &error);
+
+  if (data->buffer == NULL)
+    {
+      complete_async_from_error (data, error);
+      return;
+    }
+
+  if (!check_reply (data->buffer, data->has_cred, &error))
+    {
+      complete_async_from_error (data, error);
+      return;
+    }
+
+  g_simple_async_result_complete (data->simple);
+  g_object_unref (data->simple);
+}
+
+static GIOStream *
+wocky_http_proxy_connect_finish (GProxy *proxy,
+    GAsyncResult *result,
+    GError **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+  ConnectAsyncData *data = g_simple_async_result_get_op_res_gpointer (simple);
+
+  if (g_simple_async_result_propagate_error (simple, error))
+    return NULL;
+
+  return g_object_ref (data->io_stream);
+}
+
+static gboolean
+wocky_http_proxy_supports_hostname (GProxy *proxy)
+{
+  return TRUE;
+}
+
+static void
+wocky_http_proxy_class_init (WockyHttpProxyClass *class)
+{
+}
+
+static void
+wocky_http_proxy_iface_init (GProxyInterface *proxy_iface)
+{
+  proxy_iface->connect  = wocky_http_proxy_connect;
+  proxy_iface->connect_async = wocky_http_proxy_connect_async;
+  proxy_iface->connect_finish = wocky_http_proxy_connect_finish;
+  proxy_iface->supports_hostname = wocky_http_proxy_supports_hostname;
+}
diff --git a/gtk/wocky-http-proxy.h b/gtk/wocky-http-proxy.h
new file mode 100644
index 0000000..3b91e63
--- /dev/null
+++ b/gtk/wocky-http-proxy.h
@@ -0,0 +1,42 @@
+ /* wocky-http-proxy.h: Header for WockyHttpProxy
+ *
+ * Copyright (C) 2010 Collabora, Ltd.
+ * @author Nicolas Dufresne <nicolas.dufresne at collabora.co.uk>
+ *
+ *
+ * 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.1 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+#ifndef _WOCKY_HTTP_PROXY_H_
+#define _WOCKY_HTTP_PROXY_H_
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define WOCKY_TYPE_HTTP_PROXY         (_wocky_http_proxy_get_type ())
+#define WOCKY_HTTP_PROXY(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), WOCKY_TYPE_HTTP_PROXY, WockyHttpProxy))
+#define WOCKY_HTTP_PROXY_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), WOCKY_TYPE_HTTP_PROXY, WockyHttpProxyClass))
+#define WOCKY_IS_HTTP_PROXY(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), WOCKY_TYPE_HTTP_PROXY))
+#define WOCKY_IS_HTTP_PROXY_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), WOCKY_TYPE_HTTP_PROXY))
+#define WOCKY_HTTP_PROXY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), WOCKY_TYPE_HTTP_PROXY, WockyHttpProxyClass))
+
+typedef struct _WockyHttpProxy        WockyHttpProxy;
+typedef struct _WockyHttpProxyClass   WockyHttpProxyClass;
+
+GType _wocky_http_proxy_get_type (void);
+
+G_END_DECLS
+
+#endif /* _WOCKY_HTTP_PROXY_H_ */
commit f8a47870c9aad875555b566422208367f829d108
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Thu Aug 23 21:29:18 2012 +0200

    spice-channel: remove unnecessary g_socket_close()

diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c
index f0945d4..6336485 100644
--- a/gtk/spice-channel.c
+++ b/gtk/spice-channel.c
@@ -2442,7 +2442,6 @@ static void channel_reset(SpiceChannel *channel, gboolean migrating)
     }
 
     if (c->sock) {
-        g_socket_close(c->sock, NULL);
         g_object_unref(c->sock);
         c->sock = NULL;
     }
commit 69388562be847cee5fe008a5522f0360affb5b21
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Thu Aug 23 21:28:54 2012 +0200

    spice-channel: plug a small memory leak

diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c
index 264d1f2..f0945d4 100644
--- a/gtk/spice-channel.c
+++ b/gtk/spice-channel.c
@@ -1326,7 +1326,7 @@ static gboolean spice_channel_perform_auth_sasl(SpiceChannel *channel)
     char *mechlist;
     const char *mechname;
     gboolean ret = FALSE;
-    GSocketAddress *addr;
+    GSocketAddress *addr = NULL;
     guint8 complete;
 
     g_return_val_if_fail(channel != NULL, FALSE);
@@ -1353,6 +1353,7 @@ static gboolean spice_channel_perform_auth_sasl(SpiceChannel *channel)
          g_socket_address_get_family(addr) == G_SOCKET_FAMILY_IPV6) &&
         (localAddr = addr_to_string(addr)) == NULL)
         goto error;
+    g_clear_object(&addr);
 
     /* Get remote address in form  IPADDR:PORT */
     addr = g_socket_get_remote_address(c->sock, NULL);
@@ -1364,6 +1365,7 @@ static gboolean spice_channel_perform_auth_sasl(SpiceChannel *channel)
          g_socket_address_get_family(addr) == G_SOCKET_FAMILY_IPV6) &&
         (remoteAddr = addr_to_string(addr)) == NULL)
         goto error;
+    g_clear_object(&addr);
 
     CHANNEL_DEBUG(channel, "Client SASL new host:'%s' local:'%s' remote:'%s'",
                   spice_session_get_host(c->session), localAddr, remoteAddr);
@@ -1630,6 +1632,7 @@ complete:
     return ret;
 
 error:
+    g_clear_object(&addr);
     if (saslconn)
         sasl_dispose(&saslconn);
     emit_main_context(channel, SPICE_CHANNEL_EVENT, SPICE_CHANNEL_ERROR_AUTH);


More information about the Spice-commits mailing list