[Spice-commits] 18 commits - configure.ac doc/reference gtk/bio-gio.c gtk/bio-gio.h gtk/bio-gsocket.c gtk/bio-gsocket.h gtk/channel-base.c gtk/channel-port.c gtk/glib-compat.c gtk/glib-compat.h gtk/Makefile.am gtk/map-file gtk/spice-channel.c gtk/spice-channel.h gtk/spice-channel-priv.h gtk/spice-client.h gtk/spice-glib-sym-file gtk/spice-proxy.c gtk/spice-proxy.h gtk/spice-session.c gtk/spice-session.h gtk/spice-session-priv.h gtk/spice-uri.c gtk/spice-uri.h gtk/spice-uri-priv.h gtk/spicy.c gtk/vmcstream.c gtk/vmcstream.h gtk/wocky-http-proxy.c gtk/wocky-http-proxy.h spice-common

Marc-André Lureau elmarco at kemper.freedesktop.org
Fri Feb 21 03:50:27 PST 2014


 configure.ac                         |    2 
 doc/reference/Makefile.am            |    1 
 doc/reference/spice-gtk-docs.xml     |    1 
 doc/reference/spice-gtk-sections.txt |   28 +
 gtk/Makefile.am                      |   11 
 gtk/bio-gio.c                        |  135 ++++++++
 gtk/bio-gio.h                        |   34 ++
 gtk/bio-gsocket.c                    |  111 -------
 gtk/bio-gsocket.h                    |   30 -
 gtk/channel-base.c                   |   48 +++
 gtk/channel-port.c                   |   33 --
 gtk/glib-compat.c                    |   35 ++
 gtk/glib-compat.h                    |    9 
 gtk/map-file                         |   14 
 gtk/spice-channel-priv.h             |   11 
 gtk/spice-channel.c                  |  113 +++++--
 gtk/spice-channel.h                  |    2 
 gtk/spice-client.h                   |    1 
 gtk/spice-glib-sym-file              |   14 
 gtk/spice-proxy.c                    |  270 -----------------
 gtk/spice-proxy.h                    |   60 ---
 gtk/spice-session-priv.h             |    5 
 gtk/spice-session.c                  |   50 ++-
 gtk/spice-session.h                  |    2 
 gtk/spice-uri-priv.h                 |   30 +
 gtk/spice-uri.c                      |  460 ++++++++++++++++++++++++++++++
 gtk/spice-uri.h                      |   52 +++
 gtk/spicy.c                          |    6 
 gtk/vmcstream.c                      |  532 +++++++++++++++++++++++++++++++++++
 gtk/vmcstream.h                      |   81 +++++
 gtk/wocky-http-proxy.c               |  177 +++++++++--
 gtk/wocky-http-proxy.h               |   14 
 spice-common                         |    2 
 33 files changed, 1783 insertions(+), 591 deletions(-)

New commits:
commit 8cbdc3d2d6ca6d8f9b8e75c0c11f0e89f4427eb2
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Mon Feb 3 15:56:31 2014 +0100

    session: add spice_session_get_proxy_uri()
    
    Learn to return the currently configured proxy, to allow
    client to tweak parameters, such as username and password.

diff --git a/doc/reference/spice-gtk-sections.txt b/doc/reference/spice-gtk-sections.txt
index 9f0cf67..08b1b4e 100644
--- a/doc/reference/spice-gtk-sections.txt
+++ b/doc/reference/spice-gtk-sections.txt
@@ -28,6 +28,7 @@ spice_session_disconnect
 spice_session_get_channels
 spice_session_get_read_only
 spice_session_has_channel_type
+spice_session_get_proxy_uri
 <SUBSECTION>
 SpiceSessionMigration
 SpiceSessionVerify
diff --git a/gtk/map-file b/gtk/map-file
index d9e596b..f98680c 100644
--- a/gtk/map-file
+++ b/gtk/map-file
@@ -90,6 +90,7 @@ spice_session_migration_get_type;
 spice_session_new;
 spice_session_open_fd;
 spice_session_verify_get_type;
+spice_session_get_proxy_uri;
 spice_set_session_option;
 spice_smartcard_channel_get_type;
 spice_smartcard_manager_get;
diff --git a/gtk/spice-glib-sym-file b/gtk/spice-glib-sym-file
index b90736b..2aa17cb 100644
--- a/gtk/spice-glib-sym-file
+++ b/gtk/spice-glib-sym-file
@@ -105,3 +105,4 @@ spice_uri_set_port
 spice_uri_set_scheme
 spice_uri_set_user
 spice_uri_to_string
+spice_session_get_proxy_uri
diff --git a/gtk/spice-session.c b/gtk/spice-session.c
index 859f3cd..09556dc 100644
--- a/gtk/spice-session.c
+++ b/gtk/spice-session.c
@@ -2189,3 +2189,17 @@ guint32 spice_session_get_playback_latency(SpiceSession *session)
         return 0;
     }
 }
+
+/**
+ * spice_session_get_proxy_uri:
+ * @session: a #SpiceSession
+ *
+ * Returns: (transfer none): the session proxy #SpiceURI or %NULL.
+ * Since: 0.24
+ **/
+SpiceURI *spice_session_get_proxy_uri(SpiceSession *session)
+{
+    SpiceSessionPrivate *s = SPICE_SESSION_GET_PRIVATE(session);
+
+    return s->proxy;
+}
diff --git a/gtk/spice-session.h b/gtk/spice-session.h
index 4ea645e..665c609 100644
--- a/gtk/spice-session.h
+++ b/gtk/spice-session.h
@@ -93,6 +93,7 @@ void spice_session_disconnect(SpiceSession *session);
 GList *spice_session_get_channels(SpiceSession *session);
 gboolean spice_session_has_channel_type(SpiceSession *session, gint type);
 gboolean spice_session_get_read_only(SpiceSession *session);
+SpiceURI *spice_session_get_proxy_uri(SpiceSession *session);
 
 G_END_DECLS
 
commit 3edcc04f20b8e2360a05e16c337b26404f636081
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Mon Feb 3 15:47:11 2014 +0100

    channel: add spice_channel_get_error()
    
    Add a function to retrieve the last GError from a channel, this may be
    useful to provide additional error details to the client.

diff --git a/doc/reference/spice-gtk-sections.txt b/doc/reference/spice-gtk-sections.txt
index 411ca0e..9f0cf67 100644
--- a/doc/reference/spice-gtk-sections.txt
+++ b/doc/reference/spice-gtk-sections.txt
@@ -102,6 +102,7 @@ spice_channel_string_to_type
 spice_channel_set_capability
 spice_channel_flush_async
 spice_channel_flush_finish
+spice_channel_get_error
 <SUBSECTION Standard>
 SPICE_TYPE_CHANNEL_EVENT
 spice_channel_event_get_type
diff --git a/gtk/map-file b/gtk/map-file
index cf65db0..d9e596b 100644
--- a/gtk/map-file
+++ b/gtk/map-file
@@ -7,6 +7,7 @@ spice_channel_connect;
 spice_channel_destroy;
 spice_channel_disconnect;
 spice_channel_event_get_type;
+spice_channel_get_error;
 spice_channel_get_type;
 spice_channel_new;
 spice_channel_open_fd;
diff --git a/gtk/spice-channel-priv.h b/gtk/spice-channel-priv.h
index f38156e..607c0d4 100644
--- a/gtk/spice-channel-priv.h
+++ b/gtk/spice-channel-priv.h
@@ -136,6 +136,7 @@ struct _SpiceChannelPrivate {
     GSList                      *flushing;
 
     gboolean                    disable_channel_msg;
+    GError                      *error;
 };
 
 SpiceMsgIn *spice_msg_in_new(SpiceChannel *channel);
diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c
index aff53ea..632a286 100644
--- a/gtk/spice-channel.c
+++ b/gtk/spice-channel.c
@@ -154,6 +154,8 @@ static void spice_channel_dispose(GObject *gobject)
          c->session = NULL;
     }
 
+    g_clear_error(&c->error);
+
     /* Chain up to the parent class */
     if (G_OBJECT_CLASS(spice_channel_parent_class)->dispose)
         G_OBJECT_CLASS(spice_channel_parent_class)->dispose(gobject);
@@ -317,7 +319,9 @@ static void spice_channel_class_init(SpiceChannelClass *klass)
      * @event: a #SpiceChannelEvent
      *
      * The #SpiceChannel::channel-event signal is emitted when the
-     * state of the connection change.
+     * state of the connection is changed. In case of errors,
+     * spice_channel_get_error() may provide additional informations
+     * on the source of the error.
      **/
     signals[SPICE_CHANNEL_EVENT] =
         g_signal_new("channel-event",
@@ -2214,6 +2218,25 @@ static int spice_channel_load_ca(SpiceChannel *channel)
     return count;
 }
 
+/**
+ * spice_channel_get_error:
+ * @channel:
+ *
+ * Retrieves the #GError currently set on channel, if the #SpiceChannel
+ * is in error state and can provide additional error details.
+ *
+ * Returns: the pointer to the error, or %NULL
+ * Since: 0.24
+ **/
+const GError* spice_channel_get_error(SpiceChannel *self)
+{
+    SpiceChannelPrivate *c;
+
+    g_return_val_if_fail(SPICE_IS_CHANNEL(self), NULL);
+    c = self->priv;
+
+    return c->error;
+}
 
 /* coroutine context */
 static void *spice_channel_coroutine(void *data)
@@ -2251,15 +2274,16 @@ static void *spice_channel_coroutine(void *data)
 
 
 reconnect:
-    c->conn = spice_session_channel_open_host(c->session, channel, &c->tls);
+    c->conn = spice_session_channel_open_host(c->session, channel, &c->tls, &c->error);
     if (c->conn == NULL) {
-        if (!c->tls) {
+        if (!c->error && !c->tls) {
             CHANNEL_DEBUG(channel, "trying with TLS port");
             c->tls = true; /* FIXME: does that really work with provided fd */
             goto reconnect;
         } else {
             CHANNEL_DEBUG(channel, "Connect error");
             emit_main_context(channel, SPICE_CHANNEL_EVENT, SPICE_CHANNEL_ERROR_CONNECT);
+            g_clear_error(&c->error);
             goto cleanup;
         }
     }
diff --git a/gtk/spice-channel.h b/gtk/spice-channel.h
index 705dddf..1c303b4 100644
--- a/gtk/spice-channel.h
+++ b/gtk/spice-channel.h
@@ -123,6 +123,8 @@ void spice_channel_set_capability(SpiceChannel *channel, guint32 cap);
 const gchar* spice_channel_type_to_string(gint type);
 gint spice_channel_string_to_type(const gchar *str);
 
+const GError* spice_channel_get_error(SpiceChannel *channel);
+
 G_END_DECLS
 
 #endif /* __SPICE_CLIENT_CHANNEL_H__ */
diff --git a/gtk/spice-glib-sym-file b/gtk/spice-glib-sym-file
index ac7e871..b90736b 100644
--- a/gtk/spice-glib-sym-file
+++ b/gtk/spice-glib-sym-file
@@ -7,6 +7,7 @@ spice_channel_disconnect
 spice_channel_event_get_type
 spice_channel_flush_async
 spice_channel_flush_finish
+spice_channel_get_error
 spice_channel_get_type
 spice_channel_new
 spice_channel_open_fd
diff --git a/gtk/spice-session-priv.h b/gtk/spice-session-priv.h
index 0dc2bfb..1aae342 100644
--- a/gtk/spice-session-priv.h
+++ b/gtk/spice-session-priv.h
@@ -116,7 +116,7 @@ int spice_session_get_connection_id(SpiceSession *session);
 gboolean spice_session_get_client_provided_socket(SpiceSession *session);
 
 GSocketConnection* spice_session_channel_open_host(SpiceSession *session, SpiceChannel *channel,
-                                                   gboolean *use_tls);
+                                                   gboolean *use_tls, GError **error);
 void spice_session_channel_new(SpiceSession *session, SpiceChannel *channel);
 void spice_session_channel_destroy(SpiceSession *session, SpiceChannel *channel);
 void spice_session_channel_migrate(SpiceSession *session, SpiceChannel *channel);
diff --git a/gtk/spice-session.c b/gtk/spice-session.c
index 707fabd..859f3cd 100644
--- a/gtk/spice-session.c
+++ b/gtk/spice-session.c
@@ -1795,7 +1795,7 @@ static gboolean connect_timeout(gpointer data)
 /* coroutine context */
 G_GNUC_INTERNAL
 GSocketConnection* spice_session_channel_open_host(SpiceSession *session, SpiceChannel *channel,
-                                                   gboolean *use_tls)
+                                                   gboolean *use_tls, GError **error)
 {
     SpiceSessionPrivate *s = SPICE_SESSION_GET_PRIVATE(session);
     SpiceChannelPrivate *c = channel->priv;
@@ -1844,8 +1844,8 @@ GSocketConnection* spice_session_channel_open_host(SpiceSession *session, SpiceC
 #endif
 
     if (open_host.error != NULL) {
-        g_warning("open host: %s", open_host.error->message);
-        g_clear_error(&open_host.error);
+        SPICE_DEBUG("open host: %s", open_host.error->message);
+        g_propagate_error(error, open_host.error);
     } else if (open_host.connection != NULL) {
         GSocket *socket;
         socket = g_socket_connection_get_socket(open_host.connection);
diff --git a/gtk/spicy.c b/gtk/spicy.c
index 3e280a0..038d622 100644
--- a/gtk/spicy.c
+++ b/gtk/spicy.c
@@ -1208,6 +1208,7 @@ static void recent_add(SpiceSession *session)
 static void main_channel_event(SpiceChannel *channel, SpiceChannelEvent event,
                                gpointer data)
 {
+    const GError *error = NULL;
     spice_connection *conn = data;
     char password[64];
     int rc;
@@ -1231,7 +1232,12 @@ static void main_channel_event(SpiceChannel *channel, SpiceChannelEvent event,
     case SPICE_CHANNEL_ERROR_TLS:
     case SPICE_CHANNEL_ERROR_LINK:
     case SPICE_CHANNEL_ERROR_CONNECT:
+        error = spice_channel_get_error(channel);
         g_message("main channel: failed to connect");
+        if (error) {
+            g_message("channel error: %s", error->message);
+        }
+
         rc = connect_dialog(conn->session);
         if (rc == 0) {
             connection_connect(conn);
commit 7c909790ca93d67347f830599f8611d387def743
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Mon Feb 3 15:38:52 2014 +0100

    Make SpiceURI a public API
    
    Generalize a little bit SpiceProxy to allow easy URI manipulation by
    clients.

diff --git a/doc/reference/Makefile.am b/doc/reference/Makefile.am
index a0a856c..76c7d34 100644
--- a/doc/reference/Makefile.am
+++ b/doc/reference/Makefile.am
@@ -44,6 +44,7 @@ IGNORE_HFILES=					\
 	spice-marshal.h				\
 	spice-pulse.h				\
 	spice-session-priv.h			\
+	spice-uri-priv.h			\
 	spice-util-priv.h			\
 	spice-widget-priv.h			\
 	usb-acl-helper.h			\
diff --git a/doc/reference/spice-gtk-docs.xml b/doc/reference/spice-gtk-docs.xml
index 4a9a3cf..d2c1a2b 100644
--- a/doc/reference/spice-gtk-docs.xml
+++ b/doc/reference/spice-gtk-docs.xml
@@ -52,6 +52,7 @@
       <xi:include href="xml/smartcard-manager.xml"/>
       <xi:include href="xml/usb-device-manager.xml"/>
       <xi:include href="xml/spice-util.xml"/>
+      <xi:include href="xml/spice-uri.xml"/>
     </chapter>
 
   </part>
diff --git a/doc/reference/spice-gtk-sections.txt b/doc/reference/spice-gtk-sections.txt
index 8d61aa9..411ca0e 100644
--- a/doc/reference/spice-gtk-sections.txt
+++ b/doc/reference/spice-gtk-sections.txt
@@ -429,3 +429,29 @@ SPICE_PORT_CHANNEL_GET_CLASS
 SpicePortChannelPrivate
 </SECTION>
 
+<SECTION>
+<FILE>spice-uri</FILE>
+spice_uri_get_scheme
+spice_uri_set_scheme
+spice_uri_get_hostname
+spice_uri_set_hostname
+spice_uri_get_port
+spice_uri_set_port
+spice_uri_get_user
+spice_uri_set_user
+spice_uri_get_password
+spice_uri_set_password
+spice_uri_to_string
+SpiceURIClass
+SpiceURI
+<SUBSECTION Standard>
+SPICE_IS_URI
+SPICE_IS_URI_CLASS
+SPICE_TYPE_URI
+SPICE_URI
+SPICE_URI_CLASS
+SPICE_URI_GET_CLASS
+spice_uri_get_type
+<SUBSECTION Private>
+SpiceURIPrivate
+</SECTION>
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 0c1ad9a..61ed88e 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -248,8 +248,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					\
+	spice-uri.c					\
+	spice-uri-priv.h				\
 	usb-device-manager.c				\
 	usb-device-manager-priv.h			\
 	usbutil.c					\
@@ -284,6 +284,7 @@ libspice_client_glibincludedir = $(includedir)/spice-client-glib-2.0
 libspice_client_glibinclude_HEADERS =	\
 	spice-audio.h			\
 	spice-client.h			\
+	spice-uri.h			\
 	spice-types.h			\
 	spice-session.h			\
 	spice-channel.h			\
diff --git a/gtk/map-file b/gtk/map-file
index 368b44f..cf65db0 100644
--- a/gtk/map-file
+++ b/gtk/map-file
@@ -118,6 +118,18 @@ spice_util_get_debug;
 spice_util_get_version_string;
 spice_util_set_debug;
 spice_uuid_to_string;
+spice_uri_get_hostname;
+spice_uri_get_password;
+spice_uri_get_port;
+spice_uri_get_scheme;
+spice_uri_get_type;
+spice_uri_get_user;
+spice_uri_set_hostname;
+spice_uri_set_password;
+spice_uri_set_port;
+spice_uri_set_scheme;
+spice_uri_set_user;
+spice_uri_to_string;
 local:
 *;
 };
diff --git a/gtk/spice-client.h b/gtk/spice-client.h
index 730d11a..975259a 100644
--- a/gtk/spice-client.h
+++ b/gtk/spice-client.h
@@ -31,6 +31,7 @@
 #include "spice-session.h"
 #include "spice-channel.h"
 #include "spice-option.h"
+#include "spice-uri.h"
 
 #include "channel-main.h"
 #include "channel-display.h"
diff --git a/gtk/spice-glib-sym-file b/gtk/spice-glib-sym-file
index 4fc8643..ac7e871 100644
--- a/gtk/spice-glib-sym-file
+++ b/gtk/spice-glib-sym-file
@@ -92,3 +92,15 @@ spice_util_get_debug
 spice_util_get_version_string
 spice_util_set_debug
 spice_uuid_to_string
+spice_uri_get_hostname
+spice_uri_get_password
+spice_uri_get_port
+spice_uri_get_scheme
+spice_uri_get_type
+spice_uri_get_user
+spice_uri_set_hostname
+spice_uri_set_password
+spice_uri_set_port
+spice_uri_set_scheme
+spice_uri_set_user
+spice_uri_to_string
diff --git a/gtk/spice-proxy.c b/gtk/spice-proxy.c
deleted file mode 100644
index cf4b6ad..0000000
--- a/gtk/spice-proxy.c
+++ /dev/null
@@ -1,369 +0,0 @@
-/* -*- 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 "glib-compat.h"
-#include "spice-client.h"
-#include "spice-proxy.h"
-
-struct _SpiceProxyPrivate {
-    gchar *protocol;
-    gchar *hostname;
-    guint port;
-    gchar *user;
-    gchar *password;
-};
-
-#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_USER,
-    SPICE_PROXY_PASSWORD,
-    SPICE_PROXY_HOSTNAME,
-    SPICE_PROXY_PORT
-};
-
-G_GNUC_INTERNAL
-SpiceProxy* spice_proxy_new(void)
-{
-    SpiceProxy * self = NULL;
-    self = (SpiceProxy*)g_object_new(SPICE_TYPE_PROXY, NULL);
-    return self;
-}
-
-G_GNUC_INTERNAL
-gboolean spice_proxy_parse(SpiceProxy *self, const gchar *proxyuri, GError **error)
-{
-    gchar *dup, *uri;
-    gboolean success = FALSE;
-    size_t len;
-
-    g_return_val_if_fail(self != NULL, FALSE);
-    g_return_val_if_fail(proxyuri != NULL, FALSE);
-
-    uri = dup = g_strdup(proxyuri);
-    /* 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);
-    } else if (g_ascii_strncasecmp("https://", uri, 8) == 0) {
-        uri += 8;
-        spice_proxy_set_protocol(self, "https");
-        spice_proxy_set_port(self, 3129);
-    } else {
-        return FALSE;
-    }
-    /* remove trailing slash */
-    len = strlen(uri);
-    for (; len > 0; len--)
-        if (uri[len-1] == '/')
-            uri[len-1] = '\0';
-        else
-            break;
-
-
-    /* yes, that parser is bad, we need GUri... */
-    if (strstr(uri, "@")) {
-        gchar *saveptr, *saveptr2;
-        gchar *next = strstr(uri, "@") + 1;
-        gchar *auth = strtok_r(uri, "@", &saveptr);
-        const gchar *user = strtok_r(auth, ":", &saveptr2);
-        const gchar *pass = strtok_r(NULL, ":", &saveptr2);
-        spice_proxy_set_user(self, user);
-        spice_proxy_set_password(self, pass);
-        uri = next;
-    }
-
-    /* max 2 parts, host:port */
-    gchar **proxyv = g_strsplit(uri, ":", 2);
-    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_free(dup);
-    g_strfreev(proxyv);
-    return success;
-}
-
-G_GNUC_INTERNAL
-const gchar* spice_proxy_get_protocol(SpiceProxy *self)
-{
-    g_return_val_if_fail(SPICE_IS_PROXY(self), NULL);
-    return self->priv->protocol;
-}
-
-G_GNUC_INTERNAL
-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");
-}
-
-G_GNUC_INTERNAL
-const gchar* spice_proxy_get_hostname(SpiceProxy *self)
-{
-    g_return_val_if_fail(SPICE_IS_PROXY(self), NULL);
-    return self->priv->hostname;
-}
-
-
-G_GNUC_INTERNAL
-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");
-}
-
-G_GNUC_INTERNAL
-guint spice_proxy_get_port(SpiceProxy *self)
-{
-    g_return_val_if_fail(SPICE_IS_PROXY(self), 0);
-    return self->priv->port;
-}
-
-G_GNUC_INTERNAL
-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;
-    case SPICE_PROXY_USER:
-        g_value_set_string(value, spice_proxy_get_user(self));
-        break;
-    case SPICE_PROXY_PASSWORD:
-        g_value_set_string(value, spice_proxy_get_password(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_USER:
-        spice_proxy_set_user(self, g_value_get_string(value));
-        break;
-    case SPICE_PROXY_PASSWORD:
-        spice_proxy_set_password(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_free(self->priv->user);
-    g_free(self->priv->password);
-
-    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));
-
-    g_object_class_install_property(G_OBJECT_CLASS (klass),
-                                    SPICE_PROXY_USER,
-                                    g_param_spec_string ("user",
-                                                         "user",
-                                                         "user",
-                                                         NULL,
-                                                         G_PARAM_STATIC_STRINGS |
-                                                         G_PARAM_READWRITE));
-
-    g_object_class_install_property(G_OBJECT_CLASS (klass),
-                                    SPICE_PROXY_PASSWORD,
-                                    g_param_spec_string ("password",
-                                                         "password",
-                                                         "password",
-                                                         NULL,
-                                                         G_PARAM_STATIC_STRINGS |
-                                                         G_PARAM_READWRITE));
-}
-
-G_GNUC_INTERNAL
-gchar* spice_proxy_to_string(SpiceProxy* self)
-{
-    SpiceProxyPrivate *p;
-
-    g_return_val_if_fail(SPICE_IS_PROXY(self), NULL);
-    p = self->priv;
-
-    if (p->protocol == NULL || p->hostname == NULL)
-        return NULL;
-
-    if (p->user || p->password)
-        return g_strdup_printf("%s://%s:%s@%s:%u",
-                               p->protocol,
-                               p->user, p->password,
-                               p->hostname, p->port);
-    else
-        return g_strdup_printf("%s://%s:%u",
-                               p->protocol, p->hostname, p->port);
-}
-
-G_GNUC_INTERNAL
-const gchar* spice_proxy_get_user(SpiceProxy *self)
-{
-    g_return_val_if_fail(SPICE_IS_PROXY(self), NULL);
-    return self->priv->user;
-}
-
-
-G_GNUC_INTERNAL
-void spice_proxy_set_user(SpiceProxy *self, const gchar *value)
-{
-    g_return_if_fail(SPICE_IS_PROXY(self));
-
-    g_free(self->priv->user);
-    self->priv->user = g_strdup(value);
-    g_object_notify((GObject *)self, "user");
-}
-
-G_GNUC_INTERNAL
-const gchar* spice_proxy_get_password(SpiceProxy *self)
-{
-    g_return_val_if_fail(SPICE_IS_PROXY(self), NULL);
-    return self->priv->password;
-}
-
-
-G_GNUC_INTERNAL
-void spice_proxy_set_password(SpiceProxy *self, const gchar *value)
-{
-    g_return_if_fail(SPICE_IS_PROXY(self));
-
-    g_free(self->priv->password);
-    self->priv->password = g_strdup(value);
-    g_object_notify((GObject *)self, "password");
-}
diff --git a/gtk/spice-proxy.h b/gtk/spice-proxy.h
deleted file mode 100644
index e74053b..0000000
--- a/gtk/spice-proxy.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/* -*- 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);
-gchar *spice_proxy_to_string(SpiceProxy* self);
-const gchar* spice_proxy_get_user(SpiceProxy* self);
-void spice_proxy_set_user(SpiceProxy* self, const gchar* value);
-const gchar* spice_proxy_get_password(SpiceProxy* self);
-void spice_proxy_set_password(SpiceProxy* self, const gchar* value);
-
-G_END_DECLS
-
-#endif /* __SPICE_PROXY_H__ */
diff --git a/gtk/spice-session-priv.h b/gtk/spice-session-priv.h
index 55fee47..0dc2bfb 100644
--- a/gtk/spice-session-priv.h
+++ b/gtk/spice-session-priv.h
@@ -22,7 +22,6 @@
 #include <gio/gio.h>
 #include "desktop-integration.h"
 #include "spice-session.h"
-#include "spice-proxy.h"
 #include "spice-gtk-session.h"
 #include "spice-channel-cache.h"
 #include "decode.h"
@@ -45,7 +44,7 @@ struct _SpiceSessionPrivate {
     char              *cert_subject;
     guint             verify;
     gboolean          read_only;
-    SpiceProxy        *proxy;
+    SpiceURI          *proxy;
 
     /* whether to enable audio */
     gboolean          audio;
diff --git a/gtk/spice-session.c b/gtk/spice-session.c
index 833992e..707fabd 100644
--- a/gtk/spice-session.c
+++ b/gtk/spice-session.c
@@ -28,7 +28,7 @@
 #include "gio-coroutine.h"
 #include "glib-compat.h"
 #include "wocky-http-proxy.h"
-#include "spice-proxy.h"
+#include "spice-uri-priv.h"
 #include "channel-playback-priv.h"
 
 struct channel {
@@ -139,7 +139,7 @@ static void do_emit_main_context(GObject *object, int signum, gpointer params)
 static void update_proxy(SpiceSession *self, const gchar *str)
 {
     SpiceSessionPrivate *s = self->priv;
-    SpiceProxy *proxy = NULL;
+    SpiceURI *proxy = NULL;
     GError *error = NULL;
 
     if (str == NULL)
@@ -149,8 +149,8 @@ static void update_proxy(SpiceSession *self, const gchar *str)
         return;
     }
 
-    proxy = spice_proxy_new();
-    if (!spice_proxy_parse(proxy, str, &error))
+    proxy = spice_uri_new();
+    if (!spice_uri_parse(proxy, str, &error))
         g_clear_object(&proxy);
     if (error) {
         g_warning("%s", error->message);
@@ -269,7 +269,7 @@ static int spice_uri_create(SpiceSession *session, char *dest, int len)
     return pos;
 }
 
-static int spice_uri_parse(SpiceSession *session, const char *original_uri)
+static int spice_parse_uri(SpiceSession *session, const char *original_uri)
 {
     SpiceSessionPrivate *s = SPICE_SESSION_GET_PRIVATE(session);
     gchar *host = NULL, *port = NULL, *tls_port = NULL, *uri = NULL, *password = NULL;
@@ -499,7 +499,7 @@ static void spice_session_get_property(GObject    *gobject,
         g_value_set_pointer(value, s->uuid);
 	break;
     case PROP_PROXY:
-        g_value_take_string(value, spice_proxy_to_string(s->proxy));
+        g_value_take_string(value, spice_uri_to_string(s->proxy));
 	break;
     default:
 	G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, pspec);
@@ -547,7 +547,7 @@ static void spice_session_set_property(GObject      *gobject,
     case PROP_URI:
         str = g_value_get_string(value);
         if (str != NULL)
-            spice_uri_parse(session, str);
+            spice_parse_uri(session, str);
         break;
     case PROP_CLIENT_SOCKETS:
         s->client_provided_sockets = g_value_get_boolean(value);
@@ -1664,7 +1664,7 @@ struct spice_open_host {
     struct coroutine *from;
     SpiceSession *session;
     SpiceChannel *channel;
-    SpiceProxy *proxy;
+    SpiceURI *proxy;
     int port;
     GCancellable *cancellable;
     GError *error;
@@ -1723,11 +1723,11 @@ static void proxy_lookup_ready(GObject *source_object, GAsyncResult *result,
 
     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),
-                                      spice_proxy_get_protocol(open_host->proxy),
+                                      spice_uri_get_port(open_host->proxy),
+                                      spice_uri_get_scheme(open_host->proxy),
                                       s->host, open_host->port,
-                                      spice_proxy_get_user(open_host->proxy),
-                                      spice_proxy_get_password(open_host->proxy));
+                                      spice_uri_get_user(open_host->proxy),
+                                      spice_uri_get_password(open_host->proxy));
         if (address != NULL)
             break;
     }
@@ -1755,7 +1755,7 @@ static gboolean open_host_idle_cb(gpointer data)
 
     if (open_host->proxy)
         g_resolver_lookup_by_name_async(g_resolver_get_default(),
-                                        spice_proxy_get_hostname(open_host->proxy),
+                                        spice_uri_get_hostname(open_host->proxy),
                                         open_host->cancellable,
                                         proxy_lookup_ready, open_host);
     else
@@ -1770,7 +1770,7 @@ static gboolean open_host_idle_cb(gpointer data)
 
     SPICE_DEBUG("open host %s:%d", s->host, open_host->port);
     if (open_host->proxy != NULL) {
-        gchar *str = spice_proxy_to_string(open_host->proxy);
+        gchar *str = spice_uri_to_string(open_host->proxy);
         SPICE_DEBUG("(with proxy %s)", str);
         g_free(str);
     }
diff --git a/gtk/spice-session.h b/gtk/spice-session.h
index b07f525..4ea645e 100644
--- a/gtk/spice-session.h
+++ b/gtk/spice-session.h
@@ -20,6 +20,7 @@
 
 #include <glib-object.h>
 #include "spice-types.h"
+#include "spice-uri.h"
 #include "spice-glib-enums.h"
 #include "spice-util.h"
 
diff --git a/gtk/spice-uri-priv.h b/gtk/spice-uri-priv.h
new file mode 100644
index 0000000..54351de
--- /dev/null
+++ b/gtk/spice-uri-priv.h
@@ -0,0 +1,30 @@
+/* -*- 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_URI_PRIV_H__
+#define __SPICE_URI_PRIV_H__
+
+#include "spice-uri.h"
+
+G_BEGIN_DECLS
+
+SpiceURI* spice_uri_new(void);
+gboolean spice_uri_parse(SpiceURI* self, const gchar* uri, GError** error);
+
+G_END_DECLS
+
+#endif /* __SPICE_URI_PRIV_H__ */
diff --git a/gtk/spice-uri.c b/gtk/spice-uri.c
new file mode 100644
index 0000000..03b8c22
--- /dev/null
+++ b/gtk/spice-uri.c
@@ -0,0 +1,460 @@
+/* -*- 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 "glib-compat.h"
+#include "spice-client.h"
+#include "spice-uri.h"
+
+/**
+ * SECTION:spice-uri
+ * @short_description: URIs handling
+ * @title: SpiceURI
+ * @section_id:
+ * @stability: Stable
+ * @include: spice-uri.h
+ *
+ * A SpiceURI represents a (parsed) URI.
+ * Since: 0.24
+ */
+
+struct _SpiceURI {
+    GObject parent_instance;
+    gchar *scheme;
+    gchar *hostname;
+    guint port;
+    gchar *user;
+    gchar *password;
+};
+
+struct _SpiceURIClass {
+    GObjectClass parent_class;
+};
+
+G_DEFINE_TYPE(SpiceURI, spice_uri, G_TYPE_OBJECT);
+
+enum  {
+    SPICE_URI_DUMMY_PROPERTY,
+    SPICE_URI_SCHEME,
+    SPICE_URI_USER,
+    SPICE_URI_PASSWORD,
+    SPICE_URI_HOSTNAME,
+    SPICE_URI_PORT
+};
+
+G_GNUC_INTERNAL
+SpiceURI* spice_uri_new(void)
+{
+    SpiceURI * self = NULL;
+    self = (SpiceURI*)g_object_new(SPICE_TYPE_URI, NULL);
+    return self;
+}
+
+G_GNUC_INTERNAL
+gboolean spice_uri_parse(SpiceURI *self, const gchar *_uri, GError **error)
+{
+    gchar *dup, *uri;
+    gboolean success = FALSE;
+    size_t len;
+
+    g_return_val_if_fail(self != NULL, FALSE);
+    g_return_val_if_fail(_uri != NULL, FALSE);
+
+    uri = dup = g_strdup(_uri);
+    /* 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_uri_set_scheme(self, "http");
+        spice_uri_set_port(self, 3128);
+    } else if (g_ascii_strncasecmp("https://", uri, 8) == 0) {
+        uri += 8;
+        spice_uri_set_scheme(self, "https");
+        spice_uri_set_port(self, 3129);
+    } else {
+        return FALSE;
+    }
+    /* remove trailing slash */
+    len = strlen(uri);
+    for (; len > 0; len--)
+        if (uri[len-1] == '/')
+            uri[len-1] = '\0';
+        else
+            break;
+
+
+    /* yes, that parser is bad, we need GUri... */
+    if (strstr(uri, "@")) {
+        gchar *saveptr, *saveptr2;
+        gchar *next = strstr(uri, "@") + 1;
+        gchar *auth = strtok_r(uri, "@", &saveptr);
+        const gchar *user = strtok_r(auth, ":", &saveptr2);
+        const gchar *pass = strtok_r(NULL, ":", &saveptr2);
+        spice_uri_set_user(self, user);
+        spice_uri_set_password(self, pass);
+        uri = next;
+    }
+
+    /* max 2 parts, host:port */
+    gchar **uriv = g_strsplit(uri, ":", 2);
+    const gchar *uri_port = NULL;
+
+    if (uriv[0] == NULL || strlen(uriv[0]) == 0) {
+        g_set_error(error, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
+                    "Invalid hostname in uri address");
+        goto end;
+    }
+
+    spice_uri_set_hostname(self, uriv[0]);
+    if (uriv[0] != NULL)
+        uri_port = uriv[1];
+
+    if (uri_port != NULL) {
+        char *endptr;
+        guint port = strtoul(uri_port, &endptr, 10);
+        if (*endptr != '\0') {
+            g_set_error(error, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
+                        "Invalid uri port: %s", uri_port);
+            goto end;
+        }
+        spice_uri_set_port(self, port);
+    }
+
+    success = TRUE;
+
+end:
+    g_free(dup);
+    g_strfreev(uriv);
+    return success;
+}
+
+/**
+ * spice_uri_get_scheme:
+ * @uri: a #SpiceURI
+ *
+ * Gets @uri's scheme.
+ *
+ * Returns: @uri's scheme.
+ * Since: 0.24
+ **/
+const gchar* spice_uri_get_scheme(SpiceURI *self)
+{
+    g_return_val_if_fail(SPICE_IS_URI(self), NULL);
+    return self->scheme;
+}
+
+/**
+ * spice_uri_set_scheme:
+ * @uri: a #SpiceURI
+ * @scheme: the scheme
+ *
+ * Sets @uri's scheme to @scheme.
+ * Since: 0.24
+ **/
+void spice_uri_set_scheme(SpiceURI *self, const gchar *scheme)
+{
+    g_return_if_fail(SPICE_IS_URI(self));
+
+    g_free(self->scheme);
+    self->scheme = g_strdup(scheme);
+    g_object_notify((GObject *)self, "scheme");
+}
+
+/**
+ * spice_uri_get_hostname:
+ * @uri: a #SpiceURI
+ *
+ * Gets @uri's hostname.
+ *
+ * Returns: @uri's hostname.
+ * Since: 0.24
+ **/
+const gchar* spice_uri_get_hostname(SpiceURI *self)
+{
+    g_return_val_if_fail(SPICE_IS_URI(self), NULL);
+    return self->hostname;
+}
+
+
+/**
+ * spice_uri_set_hostname:
+ * @uri: a #SpiceURI
+ * @hostname: the hostname
+ *
+ * Sets @uri's hostname to @hostname.
+ * Since: 0.24
+ **/
+void spice_uri_set_hostname(SpiceURI *self, const gchar *hostname)
+{
+    g_return_if_fail(SPICE_IS_URI(self));
+
+    g_free(self->hostname);
+    self->hostname = g_strdup(hostname);
+    g_object_notify((GObject *)self, "hostname");
+}
+
+/**
+ * spice_uri_get_port:
+ * @uri: a #SpiceURI
+ *
+ * Gets @uri's port.
+ *
+ * Returns: @uri's port.
+ * Since: 0.24
+ **/
+guint spice_uri_get_port(SpiceURI *self)
+{
+    g_return_val_if_fail(SPICE_IS_URI(self), 0);
+    return self->port;
+}
+
+/**
+ * spice_uri_set_port:
+ * @uri: a #SpiceURI
+ * @port: the port
+ *
+ * Sets @uri's port to @port.
+ * Since: 0.24
+ **/
+void spice_uri_set_port(SpiceURI *self, guint port)
+{
+    g_return_if_fail(SPICE_IS_URI(self));
+    self->port = port;
+    g_object_notify((GObject *)self, "port");
+}
+
+static void spice_uri_get_property(GObject *object, guint property_id,
+                                     GValue *value, GParamSpec *pspec)
+{
+    SpiceURI *self;
+    self = G_TYPE_CHECK_INSTANCE_CAST(object, SPICE_TYPE_URI, SpiceURI);
+
+    switch (property_id) {
+    case SPICE_URI_SCHEME:
+        g_value_set_string(value, spice_uri_get_scheme(self));
+        break;
+    case SPICE_URI_HOSTNAME:
+        g_value_set_string(value, spice_uri_get_hostname(self));
+        break;
+    case SPICE_URI_PORT:
+        g_value_set_uint(value, spice_uri_get_port(self));
+        break;
+    case SPICE_URI_USER:
+        g_value_set_string(value, spice_uri_get_user(self));
+        break;
+    case SPICE_URI_PASSWORD:
+        g_value_set_string(value, spice_uri_get_password(self));
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+        break;
+    }
+}
+
+
+static void spice_uri_set_property(GObject *object, guint property_id,
+                                     const GValue *value, GParamSpec *pspec)
+{
+    SpiceURI * self;
+    self = G_TYPE_CHECK_INSTANCE_CAST(object, SPICE_TYPE_URI, SpiceURI);
+
+    switch (property_id) {
+    case SPICE_URI_SCHEME:
+        spice_uri_set_scheme(self, g_value_get_string(value));
+        break;
+    case SPICE_URI_HOSTNAME:
+        spice_uri_set_hostname(self, g_value_get_string(value));
+        break;
+    case SPICE_URI_USER:
+        spice_uri_set_user(self, g_value_get_string(value));
+        break;
+    case SPICE_URI_PASSWORD:
+        spice_uri_set_password(self, g_value_get_string(value));
+        break;
+    case SPICE_URI_PORT:
+        spice_uri_set_port(self, g_value_get_uint(value));
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+        break;
+    }
+}
+
+static void spice_uri_finalize(GObject* obj)
+{
+    SpiceURI *self;
+
+    self = G_TYPE_CHECK_INSTANCE_CAST(obj, SPICE_TYPE_URI, SpiceURI);
+    g_free(self->scheme);
+    g_free(self->hostname);
+    g_free(self->user);
+    g_free(self->password);
+
+    G_OBJECT_CLASS (spice_uri_parent_class)->finalize (obj);
+}
+
+static void spice_uri_init (SpiceURI *self)
+{
+}
+
+
+static void spice_uri_class_init(SpiceURIClass *klass)
+{
+    spice_uri_parent_class = g_type_class_peek_parent (klass);
+
+    G_OBJECT_CLASS (klass)->get_property = spice_uri_get_property;
+    G_OBJECT_CLASS (klass)->set_property = spice_uri_set_property;
+    G_OBJECT_CLASS (klass)->finalize = spice_uri_finalize;
+
+    g_object_class_install_property(G_OBJECT_CLASS (klass),
+                                    SPICE_URI_SCHEME,
+                                    g_param_spec_string ("scheme",
+                                                         "scheme",
+                                                         "scheme",
+                                                         NULL,
+                                                         G_PARAM_STATIC_STRINGS |
+                                                         G_PARAM_READWRITE));
+
+    g_object_class_install_property(G_OBJECT_CLASS (klass),
+                                    SPICE_URI_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_URI_PORT,
+                                    g_param_spec_uint ("port",
+                                                       "port",
+                                                       "port",
+                                                       0, G_MAXUINT, 0,
+                                                       G_PARAM_STATIC_STRINGS |
+                                                       G_PARAM_READWRITE));
+
+    g_object_class_install_property(G_OBJECT_CLASS (klass),
+                                    SPICE_URI_USER,
+                                    g_param_spec_string ("user",
+                                                         "user",
+                                                         "user",
+                                                         NULL,
+                                                         G_PARAM_STATIC_STRINGS |
+                                                         G_PARAM_READWRITE));
+
+    g_object_class_install_property(G_OBJECT_CLASS (klass),
+                                    SPICE_URI_PASSWORD,
+                                    g_param_spec_string ("password",
+                                                         "password",
+                                                         "password",
+                                                         NULL,
+                                                         G_PARAM_STATIC_STRINGS |
+                                                         G_PARAM_READWRITE));
+}
+
+/**
+ * spice_uri_to_string:
+ * @uri: a #SpiceURI
+ *
+ * Returns a string representing @uri.
+ *
+ * Returns: a string representing @uri, which the caller must free.
+ * Since: 0.24
+ **/
+gchar* spice_uri_to_string(SpiceURI* self)
+{
+    g_return_val_if_fail(SPICE_IS_URI(self), NULL);
+
+    if (self->scheme == NULL || self->hostname == NULL)
+        return NULL;
+
+    if (self->user || self->password)
+        return g_strdup_printf("%s://%s:%s@%s:%u",
+                               self->scheme,
+                               self->user, self->password,
+                               self->hostname, self->port);
+    else
+        return g_strdup_printf("%s://%s:%u",
+                               self->scheme, self->hostname, self->port);
+}
+
+/**
+ * spice_uri_get_user:
+ * @uri: a #SpiceURI
+ *
+ * Gets @uri's user.
+ *
+ * Returns: @uri's user.
+ * Since: 0.24
+ **/
+const gchar* spice_uri_get_user(SpiceURI *self)
+{
+    g_return_val_if_fail(SPICE_IS_URI(self), NULL);
+    return self->user;
+}
+
+/**
+ * spice_uri_set_user:
+ * @uri: a #SpiceURI
+ * @user: the user, or %NULL.
+ *
+ * Sets @uri's user to @user.
+ * Since: 0.24
+ **/
+void spice_uri_set_user(SpiceURI *self, const gchar *user)
+{
+    g_return_if_fail(SPICE_IS_URI(self));
+
+    g_free(self->user);
+    self->user = g_strdup(user);
+    g_object_notify((GObject *)self, "user");
+}
+
+/**
+ * spice_uri_get_password:
+ * @uri: a #SpiceURI
+ *
+ * Gets @uri's password.
+ *
+ * Returns: @uri's password.
+ * Since: 0.24
+ **/
+const gchar* spice_uri_get_password(SpiceURI *self)
+{
+    g_return_val_if_fail(SPICE_IS_URI(self), NULL);
+    return self->password;
+}
+
+/**
+ * spice_uri_set_password:
+ * @uri: a #SpiceURI
+ * @password: the password, or %NULL.
+ *
+ * Sets @uri's password to @password.
+ * Since: 0.24
+ **/
+void spice_uri_set_password(SpiceURI *self, const gchar *password)
+{
+    g_return_if_fail(SPICE_IS_URI(self));
+
+    g_free(self->password);
+    self->password = g_strdup(password);
+    g_object_notify((GObject *)self, "password");
+}
diff --git a/gtk/spice-uri.h b/gtk/spice-uri.h
new file mode 100644
index 0000000..9e8d590
--- /dev/null
+++ b/gtk/spice-uri.h
@@ -0,0 +1,52 @@
+/* -*- 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_URI_H__
+#define __SPICE_URI_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define SPICE_TYPE_URI (spice_uri_get_type ())
+#define SPICE_URI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SPICE_TYPE_URI, SpiceURI))
+#define SPICE_URI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SPICE_TYPE_URI, SpiceURIClass))
+#define SPICE_IS_URI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SPICE_TYPE_URI))
+#define SPICE_IS_URI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SPICE_TYPE_URI))
+#define SPICE_URI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SPICE_TYPE_URI, SpiceURIClass))
+
+typedef struct _SpiceURI SpiceURI;
+typedef struct _SpiceURIClass SpiceURIClass;
+typedef struct _SpiceURIPrivate SpiceURIPrivate;
+
+GType spice_uri_get_type(void) G_GNUC_CONST;
+
+const gchar* spice_uri_get_scheme(SpiceURI* uri);
+void spice_uri_set_scheme(SpiceURI* uri, const gchar* scheme);
+const gchar* spice_uri_get_hostname(SpiceURI* uri);
+void spice_uri_set_hostname(SpiceURI* uri, const gchar* hostname);
+guint spice_uri_get_port(SpiceURI* uri);
+void spice_uri_set_port(SpiceURI* uri, guint port);
+gchar *spice_uri_to_string(SpiceURI* uri);
+const gchar* spice_uri_get_user(SpiceURI* uri);
+void spice_uri_set_user(SpiceURI* uri, const gchar* user);
+const gchar* spice_uri_get_password(SpiceURI* uri);
+void spice_uri_set_password(SpiceURI* uri, const gchar* password);
+
+G_END_DECLS
+
+#endif /* __SPICE_URI_H__ */
commit 68370795f08af3f834d5814ca8d71b82ca4a1396
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Thu Jan 30 18:20:00 2014 +0100

    channel: talk to giostream instead of gsocket

diff --git a/gtk/spice-channel-priv.h b/gtk/spice-channel-priv.h
index 35704ea..f38156e 100644
--- a/gtk/spice-channel-priv.h
+++ b/gtk/spice-channel-priv.h
@@ -82,6 +82,8 @@ struct _SpiceChannelPrivate {
     SpiceOpenSSLVerify          *sslverify;
     GSocket                     *sock;
     GSocketConnection           *conn;
+    GInputStream                *in;
+    GOutputStream               *out;
 
 #if HAVE_SASL
     sasl_conn_t                 *sasl_conn;
diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c
index e729ffc..aff53ea 100644
--- a/gtk/spice-channel.c
+++ b/gtk/spice-channel.c
@@ -763,7 +763,8 @@ static void spice_channel_flush_wire(SpiceChannel *channel,
     GIOCondition cond;
 
     while (offset < datalen) {
-        int ret;
+        gssize ret;
+        GError *error = NULL;
 
         if (c->has_error) return;
 
@@ -779,9 +780,12 @@ static void spice_channel_flush_wire(SpiceChannel *channel,
                 ret = -1;
             }
         } else {
-            GError *error = NULL;
-            ret = g_socket_send(c->sock, ptr+offset, datalen-offset,
-                                NULL, &error);
+#if GLIB_CHECK_VERSION(2, 28, 0)
+            ret = g_pollable_output_stream_write_nonblocking(G_POLLABLE_OUTPUT_STREAM(c->out),
+                                                             ptr+offset, datalen-offset, NULL, &error);
+#else
+            ret = g_socket_send(c->sock, ptr+offset, datalen-offset, NULL, &error);
+#endif
             if (ret < 0) {
                 if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
                     cond = G_IO_OUT;
@@ -794,6 +798,7 @@ static void spice_channel_flush_wire(SpiceChannel *channel,
         }
         if (ret == -1) {
             if (cond != 0) {
+                // TODO: should use g_pollable_input/output_stream_create_source() in 2.28 ?
                 g_coroutine_socket_wait(&c->coroutine, c->sock, cond);
                 continue;
             } else {
@@ -888,7 +893,7 @@ static void spice_channel_write_msg(SpiceChannel *channel, SpiceMsgOut *out)
 static int spice_channel_read_wire(SpiceChannel *channel, void *data, size_t len)
 {
     SpiceChannelPrivate *c = channel->priv;
-    int ret;
+    gssize ret;
     GIOCondition cond;
 
 reread:
@@ -908,7 +913,13 @@ reread:
         }
     } else {
         GError *error = NULL;
-        ret = g_socket_receive(c->sock, data, len, NULL, &error);
+#if GLIB_CHECK_VERSION(2, 28, 0)
+        ret = g_pollable_input_stream_read_nonblocking(G_POLLABLE_INPUT_STREAM(c->in),
+                                                       data, len, NULL, &error);
+#else
+        ret = g_socket_receive(c->sock,
+                               data, len, NULL, &error);
+#endif
         if (ret < 0) {
             if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
                 cond = G_IO_IN;
@@ -922,6 +933,7 @@ reread:
 
     if (ret == -1) {
         if (cond != 0) {
+            // TODO: should use g_pollable_input/output_stream_create_source() ?
             g_coroutine_socket_wait(&c->coroutine, c->sock, cond);
             goto reread;
         } else {
@@ -2061,9 +2073,12 @@ static void spice_channel_iterate_read(SpiceChannel *channel)
     /* treat all incoming data (block on message completion) */
     while (!c->has_error &&
            c->state != SPICE_CHANNEL_STATE_MIGRATING &&
-           g_socket_condition_check(c->sock, G_IO_IN) & G_IO_IN) {
-
-        do
+#if GLIB_CHECK_VERSION(2, 28, 0)
+           g_pollable_input_stream_is_readable(G_POLLABLE_INPUT_STREAM(c->in))
+#else
+           g_socket_condition_check(c->sock, G_IO_IN) & G_IO_IN
+#endif
+    ) { do
             spice_channel_recv_msg(channel,
                                    (handler_msg_in)SPICE_CHANNEL_GET_CLASS(channel)->handle_msg, NULL);
 #if HAVE_SASL
@@ -2230,9 +2245,11 @@ static void *spice_channel_coroutine(void *data)
 
         g_socket_set_blocking(c->sock, FALSE);
         g_socket_set_keepalive(c->sock, TRUE);
+        c->conn = g_socket_connection_factory_create_connection(c->sock);
         goto connected;
     }
 
+
 reconnect:
     c->conn = spice_session_channel_open_host(c->session, channel, &c->tls);
     if (c->conn == NULL) {
@@ -2293,7 +2310,11 @@ reconnect:
         }
 
 
+#if GLIB_CHECK_VERSION(2, 28, 0)
+        BIO *bio = bio_new_giostream(G_IO_STREAM(c->conn));
+#else
         BIO *bio = bio_new_gsocket(c->sock);
+#endif
         SSL_set_bio(c->ssl, bio, bio);
 
         {
@@ -2324,6 +2345,9 @@ ssl_reconnect:
     }
 
 connected:
+    c->in = g_io_stream_get_input_stream(G_IO_STREAM(c->conn));
+    c->out = g_io_stream_get_output_stream(G_IO_STREAM(c->conn));
+
     rc = setsockopt(g_socket_get_fd(c->sock), IPPROTO_TCP, TCP_NODELAY,
                     (const char*)&delay_val, sizeof(delay_val));
     if ((rc != 0)
@@ -2497,10 +2521,9 @@ static void channel_reset(SpiceChannel *channel, gboolean migrating)
         g_object_unref(c->conn);
         c->conn = NULL;
     }
-    if (c->sock) {
-        g_object_unref(c->sock);
-        c->sock = NULL;
-    }
+
+    g_clear_object(&c->sock);
+
     c->fd = -1;
 
     free(c->peer_msg);
@@ -2726,8 +2749,10 @@ void spice_channel_swap(SpiceChannel *channel, SpiceChannel *swap, gboolean swap
 
     /* TODO: split channel in 2 objects: a controller and a swappable
        state object */
-    SWAP(conn);
     SWAP(sock);
+    SWAP(conn);
+    SWAP(in);
+    SWAP(out);
     SWAP(ctx);
     SWAP(ssl);
     SWAP(sslverify);
commit 9cf9ca4348a58c99dc1bd849933687724be18748
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Thu Jan 30 18:21:31 2014 +0100

    channel: simplify has error code
    
    Get rid of a superflous g_socket_condition_check().

diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c
index 9e97f28..e729ffc 100644
--- a/gtk/spice-channel.c
+++ b/gtk/spice-channel.c
@@ -2093,29 +2093,23 @@ static gboolean wait_migration(gpointer data)
 static gboolean spice_channel_iterate(SpiceChannel *channel)
 {
     SpiceChannelPrivate *c = channel->priv;
-    GIOCondition ret;
 
     if (c->state == SPICE_CHANNEL_STATE_MIGRATING &&
         !g_coroutine_condition_wait(&c->coroutine, wait_migration, channel))
         CHANNEL_DEBUG(channel, "migration wait cancelled");
 
-    if (c->has_error) {
-        CHANNEL_DEBUG(channel, "channel has error, breaking loop");
-        return FALSE;
-    }
-
     /* flush any pending write and read */
-    SPICE_CHANNEL_GET_CLASS(channel)->iterate_write(channel);
-    SPICE_CHANNEL_GET_CLASS(channel)->iterate_read(channel);
-
-    ret = g_socket_condition_check(c->sock, G_IO_IN | G_IO_ERR | G_IO_HUP);
-    if (c->state > SPICE_CHANNEL_STATE_CONNECTING &&
-        ret & (G_IO_ERR|G_IO_HUP)) {
-        SPICE_DEBUG("got socket error: %d", ret);
-        emit_main_context(channel, SPICE_CHANNEL_EVENT,
-                          c->state == SPICE_CHANNEL_STATE_READY ?
-                          SPICE_CHANNEL_ERROR_IO : SPICE_CHANNEL_ERROR_LINK);
-        c->has_error = TRUE;
+    if (!c->has_error)
+        SPICE_CHANNEL_GET_CLASS(channel)->iterate_write(channel);
+    if (!c->has_error)
+        SPICE_CHANNEL_GET_CLASS(channel)->iterate_read(channel);
+
+    if (c->has_error) {
+        CHANNEL_DEBUG(channel, "channel got error");
+        if (c->state > SPICE_CHANNEL_STATE_CONNECTING)
+            emit_main_context(channel, SPICE_CHANNEL_EVENT,
+                              c->state == SPICE_CHANNEL_STATE_READY ?
+                              SPICE_CHANNEL_ERROR_IO : SPICE_CHANNEL_ERROR_LINK);
         return FALSE;
     }
 
commit 35f06b8fda1115bee6dfd54e6e041e63f62c10fe
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Thu Jan 30 18:06:12 2014 +0100

    openssl: learn to handle a new kind of BIO based on GIOStream
    
    Although reusing BIO_new_socket() once again is a hack, it seems
    to be the easiest way... The proper solution is certainly to start
    using GTls instead, but that will require a glib 2.28 dep bump.

diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 7ceb22f..0c1ad9a 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -210,8 +210,8 @@ USB_ACL_HELPER_SRCS =
 endif
 
 libspice_client_glib_2_0_la_SOURCES =			\
-	bio-gsocket.c					\
-	bio-gsocket.h					\
+	bio-gio.c					\
+	bio-gio.h					\
 	glib-compat.c					\
 	glib-compat.h					\
 	spice-audio.c					\
diff --git a/gtk/bio-gio.c b/gtk/bio-gio.c
new file mode 100644
index 0000000..22d58b6
--- /dev/null
+++ b/gtk/bio-gio.c
@@ -0,0 +1,135 @@
+/* -*- 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 <string.h>
+#include <glib.h>
+
+#include "spice-util.h"
+#include "bio-gio.h"
+
+typedef struct bio_gsocket_method {
+    BIO_METHOD method;
+#if GLIB_CHECK_VERSION(2, 28, 0)
+    GIOStream *stream;
+#else
+    GSocket *gsocket;
+#endif
+} bio_gsocket_method;
+
+#define BIO_GET_GSOCKET(bio)  (((bio_gsocket_method*)bio->method)->gsocket)
+#define BIO_GET_ISTREAM(bio)  (g_io_stream_get_input_stream(((bio_gsocket_method*)bio->method)->stream))
+#define BIO_GET_OSTREAM(bio)  (g_io_stream_get_output_stream(((bio_gsocket_method*)bio->method)->stream))
+
+static int bio_gio_write(BIO *bio, const char *in, int inl)
+{
+    gssize ret;
+    GError *error = NULL;
+
+#if GLIB_CHECK_VERSION(2, 28, 0)
+    ret = g_pollable_output_stream_write_nonblocking(G_POLLABLE_OUTPUT_STREAM(BIO_GET_OSTREAM(bio)),
+#else
+    ret = g_socket_send(BIO_GET_GSOCKET(bio),
+#endif
+                        in, inl, NULL, &error);
+    BIO_clear_retry_flags(bio);
+
+    if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+        BIO_set_retry_write(bio);
+    if (error != NULL) {
+        g_warning("%s", error->message);
+        g_clear_error(&error);
+    }
+
+    return ret;
+}
+
+static int bio_gio_read(BIO *bio, char *out, int outl)
+{
+    gssize ret;
+    GError *error = NULL;
+
+#if GLIB_CHECK_VERSION(2, 28, 0)
+    ret = g_pollable_input_stream_read_nonblocking(G_POLLABLE_INPUT_STREAM(BIO_GET_ISTREAM(bio)),
+#else
+    ret = g_socket_receive(BIO_GET_GSOCKET(bio),
+#endif
+                           out, outl, NULL, &error);
+    BIO_clear_retry_flags(bio);
+
+    if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+        BIO_set_retry_read(bio);
+    else if (error != NULL)
+        g_warning("%s", error->message);
+
+    g_clear_error(&error);
+
+    return ret;
+}
+
+static int bio_gio_destroy(BIO *bio)
+{
+    if (bio == NULL || bio->method == NULL)
+        return 0;
+
+    SPICE_DEBUG("bio gsocket destroy");
+    g_free(bio->method);
+    bio->method = NULL;;
+
+    return 1;
+}
+
+static int bio_gio_puts(BIO *bio, const char *str)
+{
+    int n, ret;
+
+    n = strlen(str);
+    ret = bio_gio_write(bio, str, n);
+
+    return ret;
+}
+
+G_GNUC_INTERNAL
+#if GLIB_CHECK_VERSION(2, 28, 0)
+BIO* bio_new_giostream(GIOStream *stream)
+{
+    // TODO: make an actual new BIO type, or just switch to GTls already...
+    BIO *bio = BIO_new_socket(-1, BIO_NOCLOSE);
+#else
+BIO* bio_new_gsocket(GSocket *gsocket)
+{
+    BIO *bio = BIO_new_socket(g_socket_get_fd(gsocket), BIO_NOCLOSE);
+#endif
+
+    bio_gsocket_method *bio_method = g_new(bio_gsocket_method, 1);
+    bio_method->method = *bio->method;
+#if GLIB_CHECK_VERSION(2, 28, 0)
+    bio_method->stream = stream;
+#else
+    bio_method->gsocket = gsocket;
+#endif
+
+    bio->method->destroy(bio);
+    bio->method = (BIO_METHOD*)bio_method;
+
+    bio->method->bwrite = bio_gio_write;
+    bio->method->bread = bio_gio_read;
+    bio->method->bputs = bio_gio_puts;
+    bio->method->destroy = bio_gio_destroy;
+
+    return bio;
+}
diff --git a/gtk/bio-gio.h b/gtk/bio-gio.h
new file mode 100644
index 0000000..9bd3566
--- /dev/null
+++ b/gtk/bio-gio.h
@@ -0,0 +1,34 @@
+/* -*- 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 BIO_GIO_H_
+# define BIO_GIO_H_
+
+#include <openssl/bio.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#if GLIB_CHECK_VERSION(2, 28, 0)
+BIO* bio_new_giostream(GIOStream *stream);
+#else
+BIO* bio_new_gsocket(GSocket *gsocket);
+#endif
+
+G_END_DECLS
+
+#endif /* !BIO_GIO_H_ */
diff --git a/gtk/bio-gsocket.c b/gtk/bio-gsocket.c
deleted file mode 100644
index dbf17a2..0000000
--- a/gtk/bio-gsocket.c
+++ /dev/null
@@ -1,111 +0,0 @@
-/* -*- 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 <string.h>
-#include <glib.h>
-
-#include "spice-util.h"
-#include "bio-gsocket.h"
-
-typedef struct bio_gsocket_method {
-    BIO_METHOD method;
-    GSocket *gsocket;
-} bio_gsocket_method;
-
-#define BIO_GET_GSOCKET(bio)  (((bio_gsocket_method*)bio->method)->gsocket)
-
-static int bio_gsocket_bwrite(BIO *bio, const char *in, int inl)
-{
-    int ret;
-    GError *error = NULL;
-
-    ret = g_socket_send(BIO_GET_GSOCKET(bio),
-                        in, inl, NULL, &error);
-    BIO_clear_retry_flags(bio);
-
-    if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
-        BIO_set_retry_write(bio);
-    if (error != NULL) {
-        g_warning("%s", error->message);
-        g_clear_error(&error);
-    }
-
-    return ret;
-}
-
-static int bio_gsocket_bread(BIO *bio, char *out, int outl)
-{
-    int ret;
-    GError *error = NULL;
-
-    ret = g_socket_receive(BIO_GET_GSOCKET(bio),
-                           out, outl, NULL, &error);
-    BIO_clear_retry_flags(bio);
-
-    if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
-        BIO_set_retry_read(bio);
-    else if (error != NULL)
-        g_warning("%s", error->message);
-
-    g_clear_error(&error);
-
-    return ret;
-}
-
-static int bio_gsocket_destroy(BIO *bio)
-{
-    if (bio == NULL || bio->method == NULL)
-        return 0;
-
-    SPICE_DEBUG("bio gsocket destroy");
-    g_free(bio->method);
-    bio->method = NULL;;
-
-    return 1;
-}
-
-static int bio_gsocket_bputs(BIO *bio, const char *str)
-{
-    int n, ret;
-
-    n = strlen(str);
-    ret = bio_gsocket_bwrite(bio, str, n);
-
-    return ret;
-}
-
-G_GNUC_INTERNAL
-BIO* bio_new_gsocket(GSocket *gsocket)
-{
-    BIO *bio = BIO_new_socket(g_socket_get_fd(gsocket), BIO_NOCLOSE);
-
-    bio_gsocket_method *bio_method = g_new(bio_gsocket_method, 1);
-    bio_method->method = *bio->method;
-    bio_method->gsocket = gsocket;
-
-    bio->method->destroy(bio);
-    bio->method = (BIO_METHOD*)bio_method;
-
-    bio->method->bwrite = bio_gsocket_bwrite;
-    bio->method->bread = bio_gsocket_bread;
-    bio->method->bputs = bio_gsocket_bputs;
-    bio->method->destroy = bio_gsocket_destroy;
-
-    return bio;
-}
-
diff --git a/gtk/bio-gsocket.h b/gtk/bio-gsocket.h
deleted file mode 100644
index 7ee5e64..0000000
--- a/gtk/bio-gsocket.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/* -*- 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 BIO_GSOCKET_H_
-# define BIO_GSOCKET_H_
-
-#include <openssl/bio.h>
-#include <gio/gio.h>
-
-G_BEGIN_DECLS
-
-BIO* bio_new_gsocket(GSocket *gsocket);
-
-G_END_DECLS
-
-#endif /* !BIO_GSOCKET_H_ */
diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c
index f101c3a..9e97f28 100644
--- a/gtk/spice-channel.c
+++ b/gtk/spice-channel.c
@@ -22,7 +22,7 @@
 #include "spice-channel-priv.h"
 #include "spice-session-priv.h"
 #include "spice-marshal.h"
-#include "bio-gsocket.h"
+#include "bio-gio.h"
 
 #include <openssl/rsa.h>
 #include <openssl/evp.h>
commit 263e3014b949f7d3d4e3c8372997c01bee2bb2b5
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Fri Jan 24 21:08:23 2014 +0100

    Fill g_proxy_address_new() with protocol, user and password
    
    This way, the call might eventually support more proxy and
    authentication.

diff --git a/gtk/spice-session.c b/gtk/spice-session.c
index 7fe85e8..833992e 100644
--- a/gtk/spice-session.c
+++ b/gtk/spice-session.c
@@ -1723,8 +1723,11 @@ static void proxy_lookup_ready(GObject *source_object, GAsyncResult *result,
 
     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);
+                                      spice_proxy_get_port(open_host->proxy),
+                                      spice_proxy_get_protocol(open_host->proxy),
+                                      s->host, open_host->port,
+                                      spice_proxy_get_user(open_host->proxy),
+                                      spice_proxy_get_password(open_host->proxy));
         if (address != NULL)
             break;
     }
commit 8fa0eb30ff791a159fb6b259c2be9604f0aaf556
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Fri Jan 24 21:07:15 2014 +0100

    http-proxy: add https proxy
    
    This will require glib 2.28 for GTls support, atm

diff --git a/gtk/spice-session.c b/gtk/spice-session.c
index e6f66c5..7fe85e8 100644
--- a/gtk/spice-session.c
+++ b/gtk/spice-session.c
@@ -636,6 +636,9 @@ static void spice_session_class_init(SpiceSessionClass *klass)
 #if GLIB_CHECK_VERSION(2, 26, 0)
     _wocky_http_proxy_get_type();
 #endif
+#if GLIB_CHECK_VERSION(2, 28, 0)
+    _wocky_https_proxy_get_type();
+#endif
 
     gobject_class->dispose      = spice_session_dispose;
     gobject_class->finalize     = spice_session_finalize;
diff --git a/gtk/wocky-http-proxy.c b/gtk/wocky-http-proxy.c
index 11f557e..4226b10 100644
--- a/gtk/wocky-http-proxy.c
+++ b/gtk/wocky-http-proxy.c
@@ -1,7 +1,9 @@
  /* wocky-http-proxy.c: Source for WockyHttpProxy
  *
  * Copyright (C) 2010 Collabora, Ltd.
+ * Copyright (C) 2014 Red Hat, Inc.
  * @author Nicolas Dufresne <nicolas.dufresne at collabora.co.uk>
+ * @author Marc-André Lureau <marcandre.lureau at redhat.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -180,9 +182,32 @@ wocky_http_proxy_connect (GProxy *proxy,
 {
   GInputStream *in;
   GOutputStream *out;
-  GDataInputStream *data_in;
-  gchar *buffer;
+  GDataInputStream *data_in = NULL;
+  gchar *buffer = NULL;
   gboolean has_cred;
+  GIOStream *tlsconn = NULL;
+
+#if GLIB_CHECK_VERSION(2, 28, 0)
+  if (WOCKY_IS_HTTPS_PROXY (proxy))
+    {
+      tlsconn = g_tls_client_connection_new (io_stream,
+                                             G_SOCKET_CONNECTABLE(proxy_address),
+                                             error);
+      if (!tlsconn)
+          goto error;
+
+      GTlsCertificateFlags tls_validation_flags = G_TLS_CERTIFICATE_VALIDATE_ALL;
+#ifdef DEBUG
+      tls_validation_flags &= ~(G_TLS_CERTIFICATE_UNKNOWN_CA | G_TLS_CERTIFICATE_BAD_IDENTITY);
+#endif
+      g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (tlsconn),
+                                                    tls_validation_flags);
+      if (!g_tls_connection_handshake (G_TLS_CONNECTION (tlsconn), cancellable, error))
+          goto error;
+
+      io_stream = tlsconn;
+    }
+#endif
 
   in = g_io_stream_get_input_stream (io_stream);
   out = g_io_stream_get_output_stream (io_stream);
@@ -215,12 +240,14 @@ wocky_http_proxy_connect (GProxy *proxy,
 
   g_free (buffer);
 
-  return g_object_ref (io_stream);
+  g_object_ref (io_stream);
+  g_clear_object (&tlsconn);
 
-error:
-  if (data_in != NULL)
-    g_object_unref (data_in);
+  return io_stream;
 
+error:
+  g_clear_object (&tlsconn);
+  g_clear_object (&data_in);
   g_free (buffer);
   return NULL;
 }
@@ -291,6 +318,41 @@ do_write (GAsyncReadyCallback callback, ConnectAsyncData *data)
 }
 
 static void
+stream_connected (ConnectAsyncData *data,
+                  GIOStream *io_stream)
+{
+  GInputStream *in;
+
+  data->io_stream = g_object_ref (io_stream);
+  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);
+
+  do_write (request_write_cb, data);
+}
+
+#if GLIB_CHECK_VERSION(2, 28, 0)
+static void
+handshake_completed (GObject *source_object,
+                     GAsyncResult *res,
+                     gpointer user_data)
+{
+  GTlsConnection *conn = G_TLS_CONNECTION (source_object);
+  ConnectAsyncData *data = user_data;
+  GError *error = NULL;
+
+  if (!g_tls_connection_handshake_finish (conn, res, &error))
+    {
+      complete_async_from_error (data, error);
+      return;
+    }
+
+  stream_connected (data, G_IO_STREAM (conn));
+}
+#endif
+
+static void
 wocky_http_proxy_connect_async (GProxy *proxy,
     GIOStream *io_stream,
     GProxyAddress *proxy_address,
@@ -300,34 +362,55 @@ wocky_http_proxy_connect_async (GProxy *proxy,
 {
   GSimpleAsyncResult *simple;
   ConnectAsyncData *data;
-  GInputStream *in;
 
   simple = g_simple_async_result_new (G_OBJECT (proxy),
-      callback, user_data,
-      wocky_http_proxy_connect_async);
+                                      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->simple = simple;
 
   data->buffer = create_request (proxy_address, &data->has_cred);
   data->length = strlen (data->buffer);
   data->offset = 0;
 
-  do_write (request_write_cb, data);
+  g_simple_async_result_set_op_res_gpointer (simple, data,
+                                             (GDestroyNotify) free_connect_data);
+
+#if GLIB_CHECK_VERSION(2, 28, 0)
+  if (WOCKY_IS_HTTPS_PROXY (proxy))
+    {
+      GError *error = NULL;
+      GIOStream *tlsconn;
+
+      tlsconn = g_tls_client_connection_new (io_stream,
+                                             G_SOCKET_CONNECTABLE(proxy_address),
+                                             &error);
+      if (!tlsconn)
+        {
+          complete_async_from_error (data, error);
+          return;
+        }
+
+      g_return_if_fail (tlsconn != NULL);
+
+      GTlsCertificateFlags tls_validation_flags = G_TLS_CERTIFICATE_VALIDATE_ALL;
+#ifdef DEBUG
+      tls_validation_flags &= ~(G_TLS_CERTIFICATE_UNKNOWN_CA | G_TLS_CERTIFICATE_BAD_IDENTITY);
+#endif
+      g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (tlsconn),
+                                                    tls_validation_flags);
+      g_tls_connection_handshake_async (G_TLS_CONNECTION (tlsconn),
+                                        G_PRIORITY_DEFAULT, cancellable,
+                                        handshake_completed, data);
+    }
+  else
+#endif
+    {
+      stream_connected (data, io_stream);
+    }
 }
 
 static void
@@ -427,3 +510,35 @@ wocky_http_proxy_iface_init (GProxyInterface *proxy_iface)
   proxy_iface->connect_finish = wocky_http_proxy_connect_finish;
   proxy_iface->supports_hostname = wocky_http_proxy_supports_hostname;
 }
+
+#if GLIB_CHECK_VERSION(2, 28, 0)
+struct _WockyHttpsProxy
+{
+  WockyHttpProxy parent;
+};
+
+struct _WockyHttpsProxyClass
+{
+  WockyHttpProxyClass parent_class;
+};
+
+#define wocky_https_proxy_get_type _wocky_https_proxy_get_type
+G_DEFINE_TYPE_WITH_CODE (WockyHttpsProxy, wocky_https_proxy, WOCKY_TYPE_HTTP_PROXY,
+  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, "https", 0))
+
+static void
+wocky_https_proxy_init (WockyHttpsProxy *proxy)
+{
+}
+
+static void
+wocky_https_proxy_class_init (WockyHttpsProxyClass *class)
+{
+}
+#endif
diff --git a/gtk/wocky-http-proxy.h b/gtk/wocky-http-proxy.h
index 3b91e63..9484b51 100644
--- a/gtk/wocky-http-proxy.h
+++ b/gtk/wocky-http-proxy.h
@@ -37,6 +37,20 @@ typedef struct _WockyHttpProxyClass   WockyHttpProxyClass;
 
 GType _wocky_http_proxy_get_type (void);
 
+#if GLIB_CHECK_VERSION(2, 28, 0)
+#define WOCKY_TYPE_HTTPS_PROXY         (_wocky_https_proxy_get_type ())
+#define WOCKY_HTTPS_PROXY(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), WOCKY_TYPE_HTTPS_PROXY, WockyHttpsProxy))
+#define WOCKY_HTTPS_PROXY_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), WOCKY_TYPE_HTTPS_PROXY, WockyHttpsProxyClass))
+#define WOCKY_IS_HTTPS_PROXY(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), WOCKY_TYPE_HTTPS_PROXY))
+#define WOCKY_IS_HTTPS_PROXY_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), WOCKY_TYPE_HTTPS_PROXY))
+#define WOCKY_HTTPS_PROXY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), WOCKY_TYPE_HTTPS_PROXY, WockyHttpsProxyClass))
+
+typedef struct _WockyHttpsProxy        WockyHttpsProxy;
+typedef struct _WockyHttpsProxyClass   WockyHttpsProxyClass;
+
+GType _wocky_https_proxy_get_type (void);
+#endif
+
 G_END_DECLS
 
 #endif /* _WOCKY_HTTP_PROXY_H_ */
commit 5dcab09ac32d0578cdd74540abdaea8eea3858d5
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Fri Jan 24 21:05:56 2014 +0100

    spice-proxy: parse https protocol

diff --git a/gtk/spice-proxy.c b/gtk/spice-proxy.c
index 8613469..cf4b6ad 100644
--- a/gtk/spice-proxy.c
+++ b/gtk/spice-proxy.c
@@ -65,9 +65,17 @@ gboolean spice_proxy_parse(SpiceProxy *self, const gchar *proxyuri, GError **err
     uri = dup = g_strdup(proxyuri);
     /* 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)
+    if (g_ascii_strncasecmp("http://", uri, 7) == 0) {
         uri += 7;
-
+        spice_proxy_set_protocol(self, "http");
+        spice_proxy_set_port(self, 3128);
+    } else if (g_ascii_strncasecmp("https://", uri, 8) == 0) {
+        uri += 8;
+        spice_proxy_set_protocol(self, "https");
+        spice_proxy_set_port(self, 3129);
+    } else {
+        return FALSE;
+    }
     /* remove trailing slash */
     len = strlen(uri);
     for (; len > 0; len--)
@@ -76,8 +84,6 @@ gboolean spice_proxy_parse(SpiceProxy *self, const gchar *proxyuri, GError **err
         else
             break;
 
-    spice_proxy_set_protocol(self, "http");
-    spice_proxy_set_port(self, 3128);
 
     /* yes, that parser is bad, we need GUri... */
     if (strstr(uri, "@")) {
commit 3437c442663cdf761e59a5b8353e35efeb841c43
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Fri Jan 24 21:04:09 2014 +0100

    http-proxy: specify Basic scheme
    
    Or Squid will fail with:
    WARNING: Unsupported or unconfigured/inactive proxy-auth scheme

diff --git a/gtk/wocky-http-proxy.c b/gtk/wocky-http-proxy.c
index d73990b..11f557e 100644
--- a/gtk/wocky-http-proxy.c
+++ b/gtk/wocky-http-proxy.c
@@ -98,7 +98,7 @@ create_request (GProxyAddress *proxy_address, gboolean *has_cred)
       base64_cred = g_base64_encode ((guchar *) cred, strlen (cred));
       g_free (cred);
       g_string_append_printf (request,
-          "Proxy-Authorization: %s\r\n",
+          "Proxy-Authorization: Basic %s\r\n",
           base64_cred);
       g_free (base64_cred);
     }
commit 471ce3991b2220fb5d542d788e8191111ca0d81f
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Wed Feb 12 11:04:23 2014 +0100

    proxy: parse user and pass from uri

diff --git a/gtk/spice-proxy.c b/gtk/spice-proxy.c
index 26e9dff..8613469 100644
--- a/gtk/spice-proxy.c
+++ b/gtk/spice-proxy.c
@@ -19,6 +19,7 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "glib-compat.h"
 #include "spice-client.h"
 #include "spice-proxy.h"
 
@@ -78,6 +79,18 @@ gboolean spice_proxy_parse(SpiceProxy *self, const gchar *proxyuri, GError **err
     spice_proxy_set_protocol(self, "http");
     spice_proxy_set_port(self, 3128);
 
+    /* yes, that parser is bad, we need GUri... */
+    if (strstr(uri, "@")) {
+        gchar *saveptr, *saveptr2;
+        gchar *next = strstr(uri, "@") + 1;
+        gchar *auth = strtok_r(uri, "@", &saveptr);
+        const gchar *user = strtok_r(auth, ":", &saveptr2);
+        const gchar *pass = strtok_r(NULL, ":", &saveptr2);
+        spice_proxy_set_user(self, user);
+        spice_proxy_set_password(self, pass);
+        uri = next;
+    }
+
     /* max 2 parts, host:port */
     gchar **proxyv = g_strsplit(uri, ":", 2);
     const gchar *proxy_port = NULL;
commit 809cf4c7b8143691009b633d338794fadc851531
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Wed Feb 12 11:04:05 2014 +0100

    proxy: add user and pass properties

diff --git a/gtk/spice-proxy.c b/gtk/spice-proxy.c
index f338213..26e9dff 100644
--- a/gtk/spice-proxy.c
+++ b/gtk/spice-proxy.c
@@ -26,6 +26,8 @@ struct _SpiceProxyPrivate {
     gchar *protocol;
     gchar *hostname;
     guint port;
+    gchar *user;
+    gchar *password;
 };
 
 #define SPICE_PROXY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), SPICE_TYPE_PROXY, SpiceProxyPrivate))
@@ -35,6 +37,8 @@ G_DEFINE_TYPE(SpiceProxy, spice_proxy, G_TYPE_OBJECT);
 enum  {
     SPICE_PROXY_DUMMY_PROPERTY,
     SPICE_PROXY_PROTOCOL,
+    SPICE_PROXY_USER,
+    SPICE_PROXY_PASSWORD,
     SPICE_PROXY_HOSTNAME,
     SPICE_PROXY_PORT
 };
@@ -173,6 +177,12 @@ static void spice_proxy_get_property(GObject *object, guint property_id,
     case SPICE_PROXY_PORT:
         g_value_set_uint(value, spice_proxy_get_port(self));
         break;
+    case SPICE_PROXY_USER:
+        g_value_set_string(value, spice_proxy_get_user(self));
+        break;
+    case SPICE_PROXY_PASSWORD:
+        g_value_set_string(value, spice_proxy_get_password(self));
+        break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
         break;
@@ -193,6 +203,12 @@ static void spice_proxy_set_property(GObject *object, guint property_id,
     case SPICE_PROXY_HOSTNAME:
         spice_proxy_set_hostname(self, g_value_get_string(value));
         break;
+    case SPICE_PROXY_USER:
+        spice_proxy_set_user(self, g_value_get_string(value));
+        break;
+    case SPICE_PROXY_PASSWORD:
+        spice_proxy_set_password(self, g_value_get_string(value));
+        break;
     case SPICE_PROXY_PORT:
         spice_proxy_set_port(self, g_value_get_uint(value));
         break;
@@ -209,6 +225,8 @@ static void spice_proxy_finalize(GObject* obj)
     self = G_TYPE_CHECK_INSTANCE_CAST(obj, SPICE_TYPE_PROXY, SpiceProxy);
     g_free(self->priv->protocol);
     g_free(self->priv->hostname);
+    g_free(self->priv->user);
+    g_free(self->priv->password);
 
     G_OBJECT_CLASS (spice_proxy_parent_class)->finalize (obj);
 }
@@ -254,6 +272,24 @@ static void spice_proxy_class_init(SpiceProxyClass *klass)
                                                        0, G_MAXUINT, 0,
                                                        G_PARAM_STATIC_STRINGS |
                                                        G_PARAM_READWRITE));
+
+    g_object_class_install_property(G_OBJECT_CLASS (klass),
+                                    SPICE_PROXY_USER,
+                                    g_param_spec_string ("user",
+                                                         "user",
+                                                         "user",
+                                                         NULL,
+                                                         G_PARAM_STATIC_STRINGS |
+                                                         G_PARAM_READWRITE));
+
+    g_object_class_install_property(G_OBJECT_CLASS (klass),
+                                    SPICE_PROXY_PASSWORD,
+                                    g_param_spec_string ("password",
+                                                         "password",
+                                                         "password",
+                                                         NULL,
+                                                         G_PARAM_STATIC_STRINGS |
+                                                         G_PARAM_READWRITE));
 }
 
 G_GNUC_INTERNAL
@@ -267,5 +303,48 @@ gchar* spice_proxy_to_string(SpiceProxy* self)
     if (p->protocol == NULL || p->hostname == NULL)
         return NULL;
 
-    return g_strdup_printf("%s://%s:%u", p->protocol, p->hostname, p->port);
+    if (p->user || p->password)
+        return g_strdup_printf("%s://%s:%s@%s:%u",
+                               p->protocol,
+                               p->user, p->password,
+                               p->hostname, p->port);
+    else
+        return g_strdup_printf("%s://%s:%u",
+                               p->protocol, p->hostname, p->port);
+}
+
+G_GNUC_INTERNAL
+const gchar* spice_proxy_get_user(SpiceProxy *self)
+{
+    g_return_val_if_fail(SPICE_IS_PROXY(self), NULL);
+    return self->priv->user;
+}
+
+
+G_GNUC_INTERNAL
+void spice_proxy_set_user(SpiceProxy *self, const gchar *value)
+{
+    g_return_if_fail(SPICE_IS_PROXY(self));
+
+    g_free(self->priv->user);
+    self->priv->user = g_strdup(value);
+    g_object_notify((GObject *)self, "user");
+}
+
+G_GNUC_INTERNAL
+const gchar* spice_proxy_get_password(SpiceProxy *self)
+{
+    g_return_val_if_fail(SPICE_IS_PROXY(self), NULL);
+    return self->priv->password;
+}
+
+
+G_GNUC_INTERNAL
+void spice_proxy_set_password(SpiceProxy *self, const gchar *value)
+{
+    g_return_if_fail(SPICE_IS_PROXY(self));
+
+    g_free(self->priv->password);
+    self->priv->password = g_strdup(value);
+    g_object_notify((GObject *)self, "password");
 }
diff --git a/gtk/spice-proxy.h b/gtk/spice-proxy.h
index 1e7b6d7..e74053b 100644
--- a/gtk/spice-proxy.h
+++ b/gtk/spice-proxy.h
@@ -54,6 +54,10 @@ 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);
 gchar *spice_proxy_to_string(SpiceProxy* self);
+const gchar* spice_proxy_get_user(SpiceProxy* self);
+void spice_proxy_set_user(SpiceProxy* self, const gchar* value);
+const gchar* spice_proxy_get_password(SpiceProxy* self);
+void spice_proxy_set_password(SpiceProxy* self, const gchar* value);
 
 G_END_DECLS
 
commit 614821078ff8d115fe63f3a593fba8e7fd36c313
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Wed Feb 12 11:02:58 2014 +0100

    proxy: split uri with : in only 2 parts
    
    We want just host:port here.

diff --git a/gtk/spice-proxy.c b/gtk/spice-proxy.c
index bc4037e..f338213 100644
--- a/gtk/spice-proxy.c
+++ b/gtk/spice-proxy.c
@@ -74,7 +74,8 @@ gboolean spice_proxy_parse(SpiceProxy *self, const gchar *proxyuri, GError **err
     spice_proxy_set_protocol(self, "http");
     spice_proxy_set_port(self, 3128);
 
-    gchar **proxyv = g_strsplit(uri, ":", 0);
+    /* max 2 parts, host:port */
+    gchar **proxyv = g_strsplit(uri, ":", 2);
     const gchar *proxy_port = NULL;
 
     if (proxyv[0] == NULL || strlen(proxyv[0]) == 0) {
commit 2e720c207640b01a373c3e3e438723190c30a719
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Thu Jan 30 18:48:10 2014 +0100

    compat: add strtok_r fallback
    
    The following Spice proxy URI parsing code makes use of it, but it is
    not available on Windows
    
    Origin:
    http://git.videolan.org/gitweb.cgi/vlc.git/?p=vlc.git;a=blob;f=compat/strtok_r.c

diff --git a/configure.ac b/configure.ac
index e2dd742..6e4b29a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -233,7 +233,7 @@ else
         EXTERNAL_PNP_IDS="$with_pnp_ids_path"
 fi
 
-AC_CHECK_FUNCS(clearenv)
+AC_CHECK_FUNCS(clearenv strtok_r)
 
 PKG_CHECK_MODULES(GLIB2, glib-2.0 >= 2.22)
 AC_SUBST(GLIB2_CFLAGS)
diff --git a/gtk/glib-compat.c b/gtk/glib-compat.c
index 9ffadbb..20cbc8b 100644
--- a/gtk/glib-compat.c
+++ b/gtk/glib-compat.c
@@ -1,5 +1,8 @@
 /* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
+   Copyright (C) 2012-2014 Red Hat, Inc.
+   Copyright © 1998-2009 VLC authors and VideoLAN
+
    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
@@ -14,6 +17,8 @@
    License along with this library; if not, see <http://www.gnu.org/licenses/>.
 */
 
+#include <string.h>
+
 #include "glib-compat.h"
 
 #if !GLIB_CHECK_VERSION(2,26,0)
@@ -105,3 +110,33 @@ guint64 g_get_monotonic_time(void)
     return (((gint64) tv.tv_sec) * 1000000) + tv.tv_usec;
 }
 #endif
+
+#ifndef HAVE_STRTOK_R
+G_GNUC_INTERNAL
+char *strtok_r(char *s, const char *delim, char **save_ptr)
+{
+    char *token;
+
+    if (s == NULL)
+        s = *save_ptr;
+
+    /* Scan leading delimiters. */
+    s += strspn (s, delim);
+    if (*s == '\0')
+        return NULL;
+
+    /* Find the end of the token. */
+    token = s;
+    s = strpbrk (token, delim);
+    if (s == NULL)
+        /* This token finishes the string. */
+        *save_ptr = strchr (token, '\0');
+    else
+    {
+        /* Terminate the token and make *SAVE_PTR point past it. */
+        *s = '\0';
+        *save_ptr = s + 1;
+    }
+    return token;
+}
+#endif
diff --git a/gtk/glib-compat.h b/gtk/glib-compat.h
index c30a735..4acf401 100644
--- a/gtk/glib-compat.h
+++ b/gtk/glib-compat.h
@@ -1,5 +1,8 @@
 /* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
+   Copyright (C) 2012-2014 Red Hat, Inc.
+   Copyright © 1998-2009 VLC authors and VideoLAN
+
    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
@@ -16,6 +19,8 @@
 #ifndef GLIB_COMPAT_H
 #define GLIB_COMPAT_H
 
+#include "config.h"
+
 #include <glib-object.h>
 #include <gio/gio.h>
 
@@ -133,4 +138,8 @@ GType spice_main_context_get_type (void) G_GNUC_CONST;
 guint64 g_get_monotonic_time(void);
 #endif
 
+#ifndef HAVE_STRTOK_R
+char* strtok_r(char *s, const char *delim, char **save_ptr);
+#endif
+
 #endif /* GLIB_COMPAT_H */
diff --git a/spice-common b/spice-common
index 96ca358..1cb93fa 160000
--- a/spice-common
+++ b/spice-common
@@ -1 +1 @@
-Subproject commit 96ca358669cd32d17ce51f30de3cdbf0a1c0518c
+Subproject commit 1cb93fab09164e4e285dbf972c2ca34e89e768b7
commit 55b7979c37fc9c8af7634456894f830678f013fe
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Tue Feb 11 19:19:04 2014 +0100

    misc: indent wocky proxy with 2-spaces

diff --git a/gtk/wocky-http-proxy.c b/gtk/wocky-http-proxy.c
index 639cfb0..d73990b 100644
--- a/gtk/wocky-http-proxy.c
+++ b/gtk/wocky-http-proxy.c
@@ -40,13 +40,13 @@ 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))
+  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)
commit 0a2be0ac8abbcc2248a1783ffe5789c3f9143877
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Tue Feb 11 19:15:13 2014 +0100

    misc: make warning more explicit

diff --git a/gtk/spice-session.c b/gtk/spice-session.c
index 9e6c154..e6f66c5 100644
--- a/gtk/spice-session.c
+++ b/gtk/spice-session.c
@@ -1838,7 +1838,7 @@ GSocketConnection* spice_session_channel_open_host(SpiceSession *session, SpiceC
 #endif
 
     if (open_host.error != NULL) {
-        g_warning("%s", open_host.error->message);
+        g_warning("open host: %s", open_host.error->message);
         g_clear_error(&open_host.error);
     } else if (open_host.connection != NULL) {
         GSocket *socket;
commit bc913ce8f8fd7196a1be01eea4795bac45bdf788
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Wed Jun 5 03:18:08 2013 +0200

    Add SpiceVMC GIOStream
    
    This allows to use conveniently GIOStream APIs without caring about
    coroutine and Spice messages details.

diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 62afd36..7ceb22f 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -255,6 +255,8 @@ libspice_client_glib_2_0_la_SOURCES =			\
 	usbutil.c					\
 	usbutil.h					\
 	$(USB_ACL_HELPER_SRCS)				\
+	vmcstream.c					\
+	vmcstream.h					\
 							\
 	decode.h					\
 	decode-glz.c					\
diff --git a/gtk/vmcstream.c b/gtk/vmcstream.c
new file mode 100644
index 0000000..61990e7
--- /dev/null
+++ b/gtk/vmcstream.c
@@ -0,0 +1,532 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+  Copyright (C) 2013 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 <string.h>
+
+#include "vmcstream.h"
+#include "spice-channel-priv.h"
+#include "gio-coroutine.h"
+
+struct _SpiceVmcInputStream
+{
+    GInputStream parent_instance;
+    GSimpleAsyncResult *result;
+    struct coroutine *coroutine;
+
+    SpiceChannel *channel;
+    gboolean all;
+    guint8 *buffer;
+    gsize count;
+    gsize pos;
+
+    GCancellable *cancellable;
+    gulong cancel_id;
+};
+
+struct _SpiceVmcInputStreamClass
+{
+    GInputStreamClass parent_class;
+};
+
+static gssize   spice_vmc_input_stream_read        (GInputStream        *stream,
+                                                    void                *buffer,
+                                                    gsize                count,
+                                                    GCancellable        *cancellable,
+                                                    GError             **error);
+static void     spice_vmc_input_stream_read_async  (GInputStream        *stream,
+                                                    void                *buffer,
+                                                    gsize                count,
+                                                    int                  io_priority,
+                                                    GCancellable        *cancellable,
+                                                    GAsyncReadyCallback  callback,
+                                                    gpointer             user_data);
+static gssize   spice_vmc_input_stream_read_finish (GInputStream        *stream,
+                                                    GAsyncResult        *result,
+                                                    GError             **error);
+static gssize   spice_vmc_input_stream_skip        (GInputStream        *stream,
+                                                    gsize                count,
+                                                    GCancellable        *cancellable,
+                                                    GError             **error);
+static gboolean spice_vmc_input_stream_close       (GInputStream        *stream,
+                                                    GCancellable        *cancellable,
+                                                    GError             **error);
+
+G_DEFINE_TYPE(SpiceVmcInputStream, spice_vmc_input_stream, G_TYPE_INPUT_STREAM)
+
+
+static void
+spice_vmc_input_stream_class_init(SpiceVmcInputStreamClass *klass)
+{
+    GInputStreamClass *istream_class;
+
+    istream_class = G_INPUT_STREAM_CLASS(klass);
+    istream_class->read_fn = spice_vmc_input_stream_read;
+    istream_class->read_async = spice_vmc_input_stream_read_async;
+    istream_class->read_finish = spice_vmc_input_stream_read_finish;
+    istream_class->skip = spice_vmc_input_stream_skip;
+    istream_class->close_fn = spice_vmc_input_stream_close;
+}
+
+static void
+spice_vmc_input_stream_init(SpiceVmcInputStream *self)
+{
+}
+
+static SpiceVmcInputStream *
+spice_vmc_input_stream_new(void)
+{
+    SpiceVmcInputStream *self;
+
+    self = g_object_new(SPICE_TYPE_VMC_INPUT_STREAM, NULL);
+
+    return self;
+}
+
+/* coroutine */
+/**
+ * Feed a SpiceVmc stream with new data from a coroutine
+ *
+ * The other end will be waiting on read_async() until data is fed
+ * here.
+ */
+G_GNUC_INTERNAL void
+spice_vmc_input_stream_co_data(SpiceVmcInputStream *self,
+                               const gpointer d, gsize size)
+{
+    guint8 *data = d;
+
+    g_return_if_fail(SPICE_IS_VMC_INPUT_STREAM(self));
+    g_return_if_fail(self->coroutine == NULL);
+
+    self->coroutine = coroutine_self();
+
+    while (size > 0) {
+        SPICE_DEBUG("spicevmc co_data %p", self->result);
+        if (!self->result)
+            coroutine_yield(NULL);
+
+        g_return_if_fail(self->result != NULL);
+
+        gsize min = MIN(self->count, size);
+        memcpy(self->buffer, data, min);
+
+        size -= min;
+        data += min;
+
+        SPICE_DEBUG("spicevmc co_data complete: %" G_GSIZE_FORMAT
+                    "/%" G_GSIZE_FORMAT, min, self->count);
+
+        self->pos += min;
+        self->buffer += min;
+
+        if (self->all && min > 0 && self->pos != self->count)
+            continue;
+
+        g_simple_async_result_set_op_res_gssize(self->result, self->pos);
+
+        g_simple_async_result_complete_in_idle(self->result);
+        g_clear_object(&self->result);
+        if (self->cancellable) {
+            g_cancellable_disconnect(self->cancellable, self->cancel_id);
+            g_clear_object(&self->cancellable);
+        }
+    }
+
+    self->coroutine = NULL;
+}
+
+static void
+read_cancelled(GCancellable *cancellable,
+               gpointer user_data)
+{
+    SpiceVmcInputStream *self = SPICE_VMC_INPUT_STREAM(user_data);
+
+    SPICE_DEBUG("read cancelled, %p", self->result);
+    g_simple_async_result_set_error(self->result,
+                                    G_IO_ERROR, G_IO_ERROR_CANCELLED,
+                                    "read cancelled");
+    g_simple_async_result_complete_in_idle(self->result);
+
+    g_clear_object(&self->result);
+
+    /* See FIXME */
+    /* if (self->cancellable) { */
+    /*     g_cancellable_disconnect(self->cancellable, self->cancel_id); */
+    /*     g_clear_object(&self->cancellable); */
+    /* } */
+}
+
+G_GNUC_INTERNAL void
+spice_vmc_input_stream_read_all_async(GInputStream        *stream,
+                                      void                *buffer,
+                                      gsize                count,
+                                      int                  io_priority,
+                                      GCancellable        *cancellable,
+                                      GAsyncReadyCallback  callback,
+                                      gpointer             user_data)
+{
+    SpiceVmcInputStream *self = SPICE_VMC_INPUT_STREAM(stream);
+    GSimpleAsyncResult *result;
+
+    /* no concurrent read permitted by ginputstream */
+    g_return_if_fail(self->result == NULL);
+    g_return_if_fail(self->cancellable == NULL);
+    self->all = TRUE;
+    self->buffer = buffer;
+    self->count = count;
+    self->pos = 0;
+    result = g_simple_async_result_new(G_OBJECT(self),
+                                       callback,
+                                       user_data,
+                                       spice_vmc_input_stream_read_async);
+    self->result = result;
+    self->cancellable = g_object_ref(cancellable);
+    if (cancellable)
+        self->cancel_id =
+            g_cancellable_connect(cancellable, G_CALLBACK(read_cancelled), self, NULL);
+
+    if (self->coroutine)
+        coroutine_yieldto(self->coroutine, NULL);
+}
+
+G_GNUC_INTERNAL gssize
+spice_vmc_input_stream_read_all_finish(GInputStream *stream,
+                                       GAsyncResult *result,
+                                       GError **error)
+{
+    GSimpleAsyncResult *simple;
+    SpiceVmcInputStream *self = SPICE_VMC_INPUT_STREAM(stream);
+
+    g_return_val_if_fail(g_simple_async_result_is_valid(result,
+                                                        G_OBJECT(self),
+                                                        spice_vmc_input_stream_read_async),
+                         -1);
+
+    simple = (GSimpleAsyncResult *)result;
+
+    /* FIXME: calling _finish() is required. Disconnecting in
+       read_cancelled() causes a deadlock. #705395 */
+    if (self->cancellable) {
+        g_cancellable_disconnect(self->cancellable, self->cancel_id);
+        g_clear_object(&self->cancellable);
+    }
+
+    if (g_simple_async_result_propagate_error(simple, error))
+        return -1;
+
+    return g_simple_async_result_get_op_res_gssize(simple);
+}
+
+static void
+spice_vmc_input_stream_read_async(GInputStream        *stream,
+                                  void                *buffer,
+                                  gsize                count,
+                                  int                  io_priority,
+                                  GCancellable        *cancellable,
+                                  GAsyncReadyCallback  callback,
+                                  gpointer             user_data)
+{
+    SpiceVmcInputStream *self = SPICE_VMC_INPUT_STREAM(stream);
+    GSimpleAsyncResult *result;
+
+    /* no concurrent read permitted by ginputstream */
+    g_return_if_fail(self->result == NULL);
+    g_return_if_fail(self->cancellable == NULL);
+    self->all = FALSE;
+    self->buffer = buffer;
+    self->count = count;
+    self->pos = 0;
+    result = g_simple_async_result_new(G_OBJECT(self),
+                                       callback,
+                                       user_data,
+                                       spice_vmc_input_stream_read_async);
+    self->result = result;
+    self->cancellable = g_object_ref(cancellable);
+    if (cancellable)
+        self->cancel_id =
+            g_cancellable_connect(cancellable, G_CALLBACK(read_cancelled), self, NULL);
+
+    if (self->coroutine)
+        coroutine_yieldto(self->coroutine, NULL);
+}
+
+static gssize
+spice_vmc_input_stream_read_finish(GInputStream *stream,
+                                   GAsyncResult *result,
+                                   GError **error)
+{
+    GSimpleAsyncResult *simple;
+    SpiceVmcInputStream *self = SPICE_VMC_INPUT_STREAM(stream);
+
+    g_return_val_if_fail(g_simple_async_result_is_valid(result,
+                                                        G_OBJECT(self),
+                                                        spice_vmc_input_stream_read_async),
+                         -1);
+
+    simple = (GSimpleAsyncResult *)result;
+
+    /* FIXME: calling _finish() is required. Disconnecting in
+       read_cancelled() causes a deadlock. #705395 */
+    if (self->cancellable) {
+        g_cancellable_disconnect(self->cancellable, self->cancel_id);
+        g_clear_object(&self->cancellable);
+    }
+
+    if (g_simple_async_result_propagate_error(simple, error))
+        return -1;
+
+    return g_simple_async_result_get_op_res_gssize(simple);
+}
+
+static gssize
+spice_vmc_input_stream_read(GInputStream  *stream,
+                            void          *buffer,
+                            gsize          count,
+                            GCancellable  *cancellable,
+                            GError       **error)
+{
+    g_return_val_if_reached(-1);
+}
+
+static gssize
+spice_vmc_input_stream_skip(GInputStream  *stream,
+                            gsize          count,
+                            GCancellable  *cancellable,
+                            GError       **error)
+{
+    g_return_val_if_reached(-1);
+}
+
+static gboolean
+spice_vmc_input_stream_close(GInputStream  *stream,
+                             GCancellable  *cancellable,
+                             GError       **error)
+{
+    SPICE_DEBUG("fake close");
+    return TRUE;
+}
+
+/* OUTPUT */
+
+struct _SpiceVmcOutputStream
+{
+    GOutputStream parent_instance;
+
+    SpiceChannel *channel; /* weak */
+};
+
+struct _SpiceVmcOutputStreamClass
+{
+    GOutputStreamClass parent_class;
+};
+
+static gssize   spice_vmc_output_stream_write_fn     (GOutputStream   *stream,
+                                                      const void      *buffer,
+                                                      gsize            count,
+                                                      GCancellable    *cancellable,
+                                                      GError         **error);
+static gssize   spice_vmc_output_stream_write_finish (GOutputStream   *stream,
+                                                      GAsyncResult    *result,
+                                                      GError         **error);
+static void     spice_vmc_output_stream_write_async  (GOutputStream   *stream,
+                                                      const void      *buffer,
+                                                      gsize            count,
+                                                      int              io_priority,
+                                                      GCancellable    *cancellable,
+                                                      GAsyncReadyCallback callback,
+                                                      gpointer         user_data);
+
+G_DEFINE_TYPE(SpiceVmcOutputStream, spice_vmc_output_stream, G_TYPE_OUTPUT_STREAM)
+
+
+static void
+spice_vmc_output_stream_class_init(SpiceVmcOutputStreamClass *klass)
+{
+    GOutputStreamClass *ostream_class;
+
+    ostream_class = G_OUTPUT_STREAM_CLASS(klass);
+    ostream_class->write_fn = spice_vmc_output_stream_write_fn;
+    ostream_class->write_async = spice_vmc_output_stream_write_async;
+    ostream_class->write_finish = spice_vmc_output_stream_write_finish;
+}
+
+static void
+spice_vmc_output_stream_init(SpiceVmcOutputStream *self)
+{
+}
+
+static SpiceVmcOutputStream *
+spice_vmc_output_stream_new(SpiceChannel *channel)
+{
+    SpiceVmcOutputStream *self;
+
+    self = g_object_new(SPICE_TYPE_VMC_OUTPUT_STREAM, NULL);
+    self->channel = channel;
+
+    return self;
+}
+
+static gssize
+spice_vmc_output_stream_write_fn(GOutputStream   *stream,
+                                 const void      *buffer,
+                                 gsize            count,
+                                 GCancellable    *cancellable,
+                                 GError         **error)
+{
+    SpiceVmcOutputStream *self = SPICE_VMC_OUTPUT_STREAM(stream);
+    SpiceMsgOut *msg_out;
+
+    msg_out = spice_msg_out_new(SPICE_CHANNEL(self->channel),
+                                SPICE_MSGC_SPICEVMC_DATA);
+    spice_marshaller_add(msg_out->marshaller, buffer, count);
+    spice_msg_out_send(msg_out);
+
+    return count;
+}
+
+static gssize
+spice_vmc_output_stream_write_finish(GOutputStream *stream,
+                                     GAsyncResult *simple,
+                                     GError **error)
+{
+    SpiceVmcOutputStream *self = SPICE_VMC_OUTPUT_STREAM(stream);
+    GSimpleAsyncResult *res =
+        g_simple_async_result_get_op_res_gpointer(G_SIMPLE_ASYNC_RESULT(simple));
+
+    SPICE_DEBUG("spicevmc write finish");
+    return spice_vmc_write_finish(self->channel, G_ASYNC_RESULT(res), error);
+}
+
+static void
+write_cb(GObject *source_object,
+         GAsyncResult *res,
+         gpointer user_data)
+{
+    GSimpleAsyncResult *simple = user_data;
+
+    g_simple_async_result_set_op_res_gpointer(simple, res, NULL);
+
+    g_simple_async_result_complete(simple);
+    g_object_unref(simple);
+}
+
+static void
+spice_vmc_output_stream_write_async(GOutputStream *stream,
+                                    const void *buffer,
+                                    gsize count,
+                                    int io_priority,
+                                    GCancellable *cancellable,
+                                    GAsyncReadyCallback callback,
+                                    gpointer user_data)
+{
+    SpiceVmcOutputStream *self = SPICE_VMC_OUTPUT_STREAM(stream);
+    GSimpleAsyncResult *simple;
+
+    SPICE_DEBUG("spicevmc write async");
+    /* an AsyncResult to forward async op to channel */
+    simple = g_simple_async_result_new(G_OBJECT(self), callback, user_data,
+                                       spice_vmc_output_stream_write_async);
+
+    spice_vmc_write_async(self->channel, buffer, count,
+                          cancellable, write_cb,
+                          simple);
+}
+
+/* STREAM */
+
+struct _SpiceVmcStream
+{
+    GIOStream parent_instance;
+
+    SpiceChannel *channel; /* weak */
+    SpiceVmcInputStream *in;
+    SpiceVmcOutputStream *out;
+};
+
+struct _SpiceVmcStreamClass
+{
+    GIOStreamClass parent_class;
+};
+
+static void            spice_vmc_stream_finalize          (GObject   *object);
+static GInputStream *  spice_vmc_stream_get_input_stream  (GIOStream *stream);
+static GOutputStream * spice_vmc_stream_get_output_stream (GIOStream *stream);
+
+G_DEFINE_TYPE(SpiceVmcStream, spice_vmc_stream, G_TYPE_IO_STREAM)
+
+static void
+spice_vmc_stream_class_init(SpiceVmcStreamClass *klass)
+{
+    GObjectClass *object_class;
+    GIOStreamClass *iostream_class;
+
+    object_class = G_OBJECT_CLASS(klass);
+    object_class->finalize = spice_vmc_stream_finalize;
+
+    iostream_class = G_IO_STREAM_CLASS(klass);
+    iostream_class->get_input_stream = spice_vmc_stream_get_input_stream;
+    iostream_class->get_output_stream = spice_vmc_stream_get_output_stream;
+}
+
+static void
+spice_vmc_stream_finalize(GObject *object)
+{
+    SpiceVmcStream *self = SPICE_VMC_STREAM(object);
+
+    g_clear_object(&self->in);
+    g_clear_object(&self->out);
+
+    G_OBJECT_CLASS(spice_vmc_stream_parent_class)->finalize(object);
+}
+
+static void
+spice_vmc_stream_init(SpiceVmcStream *self)
+{
+}
+
+G_GNUC_INTERNAL SpiceVmcStream *
+spice_vmc_stream_new(SpiceChannel *channel)
+{
+    SpiceVmcStream *self;
+
+    self = g_object_new(SPICE_TYPE_VMC_STREAM, NULL);
+    self->channel = channel;
+
+    return self;
+}
+
+static GInputStream *
+spice_vmc_stream_get_input_stream(GIOStream *stream)
+{
+    SpiceVmcStream *self = SPICE_VMC_STREAM(stream);
+
+    if (!self->in)
+        self->in = spice_vmc_input_stream_new();
+
+    return G_INPUT_STREAM(self->in);
+}
+
+static GOutputStream *
+spice_vmc_stream_get_output_stream(GIOStream *stream)
+{
+    SpiceVmcStream *self = SPICE_VMC_STREAM(stream);
+
+    if (!self->out)
+        self->out = spice_vmc_output_stream_new(self->channel);
+
+    return G_OUTPUT_STREAM(self->out);
+}
diff --git a/gtk/vmcstream.h b/gtk/vmcstream.h
new file mode 100644
index 0000000..1316b77
--- /dev/null
+++ b/gtk/vmcstream.h
@@ -0,0 +1,81 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2013 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_VMC_STREAM_H__
+#define __SPICE_VMC_STREAM_H__
+
+#include <gio/gio.h>
+
+#include "spice-types.h"
+
+G_BEGIN_DECLS
+
+#define SPICE_TYPE_VMC_INPUT_STREAM         (spice_vmc_input_stream_get_type ())
+#define SPICE_VMC_INPUT_STREAM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), SPICE_TYPE_VMC_INPUT_STREAM, SpiceVmcInputStream))
+#define SPICE_VMC_INPUT_STREAM_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), SPICE_TYPE_VMC_INPUT_STREAM, SpiceVmcInputStreamClass))
+#define SPICE_IS_VMC_INPUT_STREAM(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), SPICE_TYPE_VMC_INPUT_STREAM))
+#define SPICE_IS_VMC_INPUT_STREAM_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), SPICE_TYPE_VMC_INPUT_STREAM))
+#define SPICE_VMC_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), SPICE_TYPE_VMC_INPUT_STREAM, SpiceVmcInputStreamClass))
+
+typedef struct _SpiceVmcInputStreamClass     SpiceVmcInputStreamClass;
+typedef struct _SpiceVmcInputStream          SpiceVmcInputStream;
+
+GType          spice_vmc_input_stream_get_type   (void) G_GNUC_CONST;
+void           spice_vmc_input_stream_co_data    (SpiceVmcInputStream *input,
+                                                  const gpointer data,
+                                                  gsize size);
+
+void           spice_vmc_input_stream_read_all_async(GInputStream        *stream,
+                                                     void                *buffer,
+                                                     gsize                count,
+                                                     int                  io_priority,
+                                                     GCancellable        *cancellable,
+                                                     GAsyncReadyCallback  callback,
+                                                     gpointer             user_data);
+gssize         spice_vmc_input_stream_read_all_finish(GInputStream       *stream,
+                                                      GAsyncResult       *result,
+                                                      GError            **error);
+
+
+#define SPICE_TYPE_VMC_OUTPUT_STREAM         (spice_vmc_output_stream_get_type ())
+#define SPICE_VMC_OUTPUT_STREAM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), SPICE_TYPE_VMC_OUTPUT_STREAM, SpiceVmcOutputStream))
+#define SPICE_VMC_OUTPUT_STREAM_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), SPICE_TYPE_VMC_OUTPUT_STREAM, SpiceVmcOutputStreamClass))
+#define SPICE_IS_VMC_OUTPUT_STREAM(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), SPICE_TYPE_VMC_OUTPUT_STREAM))
+#define SPICE_IS_VMC_OUTPUT_STREAM_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), SPICE_TYPE_VMC_OUTPUT_STREAM))
+#define SPICE_VMC_OUTPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), SPICE_TYPE_VMC_OUTPUT_STREAM, SpiceVmcOutputStreamClass))
+
+typedef struct _SpiceVmcOutputStreamClass     SpiceVmcOutputStreamClass;
+typedef struct _SpiceVmcOutputStream          SpiceVmcOutputStream;
+
+GType           spice_vmc_output_stream_get_type (void) G_GNUC_CONST;
+
+#define SPICE_TYPE_VMC_STREAM                (spice_vmc_stream_get_type ())
+#define SPICE_VMC_STREAM(o)                  (G_TYPE_CHECK_INSTANCE_CAST ((o), SPICE_TYPE_VMC_STREAM, SpiceVmcStream))
+#define SPICE_VMC_STREAM_CLASS(k)            (G_TYPE_CHECK_CLASS_CAST((k), SPICE_TYPE_VMC_STREAM, SpiceVmcStreamClass))
+#define SPICE_IS_VMC_STREAM(o)               (G_TYPE_CHECK_INSTANCE_TYPE ((o), SPICE_TYPE_VMC_STREAM))
+#define SPICE_IS_VMC_STREAM_CLASS(k)         (G_TYPE_CHECK_CLASS_TYPE ((k), SPICE_TYPE_VMC_STREAM))
+#define SPICE_VMC_STREAM_GET_CLASS(o)        (G_TYPE_INSTANCE_GET_CLASS ((o), SPICE_TYPE_VMC_STREAM, SpiceVmcStreamClass))
+
+typedef struct _SpiceVmcStreamClass           SpiceVmcStreamClass;
+typedef struct _SpiceVmcStream                SpiceVmcStream;
+
+GType           spice_vmc_stream_get_type        (void) G_GNUC_CONST;
+SpiceVmcStream* spice_vmc_stream_new             (SpiceChannel *channel);
+
+G_END_DECLS
+
+#endif /* __SPICE_VMC_STREAM_H__ */
commit d4ff79d140311b5de4d788ba249209c77013d8e5
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Thu Feb 6 17:52:20 2014 +0100

    channel: add spice_vmc_write_async()
    
    Refactor port code to create a private GIO async function that can send
    SPICE_MSGC_SPICEVMC_DATA message over any channel.

diff --git a/gtk/channel-base.c b/gtk/channel-base.c
index 646042d..363dda5 100644
--- a/gtk/channel-base.c
+++ b/gtk/channel-base.c
@@ -232,3 +232,51 @@ void spice_channel_set_handlers(SpiceChannelClass *klass,
     spice_channel_add_base_handlers(klass);
     set_handlers(klass, handlers, n);
 }
+
+static void
+vmc_write_free_cb(uint8_t *data, void *user_data)
+{
+    GSimpleAsyncResult *result = user_data;
+
+    g_simple_async_result_complete_in_idle(result);
+    g_object_unref(result);
+}
+
+G_GNUC_INTERNAL
+void spice_vmc_write_async(SpiceChannel *self,
+                           const void *buffer, gsize count,
+                           GCancellable *cancellable,
+                           GAsyncReadyCallback callback,
+                           gpointer user_data)
+{
+    SpiceMsgOut *msg;
+    GSimpleAsyncResult *simple;
+
+    simple = g_simple_async_result_new(G_OBJECT(self), callback, user_data,
+                                       spice_port_write_async);
+    g_simple_async_result_set_op_res_gssize(simple, count);
+
+    msg = spice_msg_out_new(SPICE_CHANNEL(self), SPICE_MSGC_SPICEVMC_DATA);
+    spice_marshaller_add_ref_full(msg->marshaller, (uint8_t*)buffer, count,
+                                  vmc_write_free_cb, simple);
+    spice_msg_out_send(msg);
+}
+
+G_GNUC_INTERNAL
+gssize spice_vmc_write_finish(SpiceChannel *self,
+                              GAsyncResult *result, GError **error)
+{
+    GSimpleAsyncResult *simple;
+
+    g_return_val_if_fail(result != NULL, -1);
+
+    simple = (GSimpleAsyncResult *)result;
+
+    if (g_simple_async_result_propagate_error(simple, error))
+        return -1;
+
+    g_return_val_if_fail(g_simple_async_result_is_valid(result, G_OBJECT(self),
+                                                        spice_port_write_async), -1);
+
+    return g_simple_async_result_get_op_res_gssize(simple);
+}
diff --git a/gtk/channel-port.c b/gtk/channel-port.c
index 0a8b37f..5512713 100644
--- a/gtk/channel-port.c
+++ b/gtk/channel-port.c
@@ -289,14 +289,6 @@ static void port_handle_msg(SpiceChannel *channel, SpiceMsgIn *in)
     emit_main_context(channel, SPICE_PORT_DATA, buf, size);
 }
 
-static void port_write_free_cb(uint8_t *data, void *user_data)
-{
-    GSimpleAsyncResult *result = user_data;
-
-    g_simple_async_result_complete(result);
-    g_object_unref(result);
-}
-
 /**
  * spice_port_write_async:
  * @port: A #SpicePortChannel
@@ -320,9 +312,7 @@ void spice_port_write_async(SpicePortChannel *self,
                             GAsyncReadyCallback callback,
                             gpointer user_data)
 {
-    GSimpleAsyncResult *simple;
     SpicePortChannelPrivate *c;
-    SpiceMsgOut *msg;
 
     g_return_if_fail(SPICE_IS_PORT_CHANNEL(self));
     g_return_if_fail(buffer != NULL);
@@ -335,14 +325,8 @@ void spice_port_write_async(SpicePortChannel *self,
         return;
     }
 
-    simple = g_simple_async_result_new(G_OBJECT(self), callback, user_data,
-                                       spice_port_write_async);
-    g_simple_async_result_set_op_res_gssize(simple, count);
-
-    msg = spice_msg_out_new(SPICE_CHANNEL(self), SPICE_MSGC_SPICEVMC_DATA);
-    spice_marshaller_add_ref_full(msg->marshaller, (uint8_t*)buffer, count,
-                                  port_write_free_cb, simple);
-    spice_msg_out_send(msg);
+    spice_vmc_write_async(SPICE_CHANNEL(self), buffer, count,
+                          cancellable, callback, user_data);
 }
 
 /**
@@ -360,20 +344,9 @@ void spice_port_write_async(SpicePortChannel *self,
 gssize spice_port_write_finish(SpicePortChannel *self,
                                GAsyncResult *result, GError **error)
 {
-    GSimpleAsyncResult *simple;
-
     g_return_val_if_fail(SPICE_IS_PORT_CHANNEL(self), -1);
-    g_return_val_if_fail(result != NULL, -1);
-
-    simple = (GSimpleAsyncResult *)result;
-
-    if (g_simple_async_result_propagate_error(simple, error))
-        return -1;
-
-    g_return_val_if_fail(g_simple_async_result_is_valid(result, G_OBJECT(self),
-                                                        spice_port_write_async), -1);
 
-    return g_simple_async_result_get_op_res_gssize(simple);
+    return spice_vmc_write_finish(SPICE_CHANNEL(self), result, error);
 }
 
 /**
diff --git a/gtk/spice-channel-priv.h b/gtk/spice-channel-priv.h
index 0816061..35704ea 100644
--- a/gtk/spice-channel-priv.h
+++ b/gtk/spice-channel-priv.h
@@ -196,6 +196,14 @@ void spice_caps_set(GArray *caps, guint32 cap, const gchar *desc);
 
 gchar *spice_channel_supported_string(void);
 
+void spice_vmc_write_async(SpiceChannel *self,
+                           const void *buffer, gsize count,
+                           GCancellable *cancellable,
+                           GAsyncReadyCallback callback,
+                           gpointer user_data);
+gssize spice_vmc_write_finish(SpiceChannel *self,
+                              GAsyncResult *result, GError **error);
+
 G_END_DECLS
 
 #endif /* __SPICE_CLIENT_CHANNEL_PRIV_H__ */


More information about the Spice-commits mailing list