[spice-gtk] Fix authentication error handling regression

Marc-André Lureau elmarco at fedoraproject.org
Tue Dec 16 17:49:52 UTC 2014


commit 029fd8e852977ec7e8ce81a80f5cf7a07636d0ed
Author: Marc-André Lureau <marcandre.lureau at gmail.com>
Date:   Tue Dec 16 18:49:11 2014 +0100

    Fix authentication error handling regression

 0001-session-keep-main-channel-on-reconnect.patch  |  115 +++++++++++++++++
 0002-channel-factorize-failed-authentication.patch |  126 +++++++++++++++++++
 ...-not-enter-channel-iterate-on-early-error.patch |  129 ++++++++++++++++++++
 ...ntroduce-SPICE_CHANNEL_STATE_RECONNECTING.patch |  107 ++++++++++++++++
 ...nnel-throw-auth-error-when-coroutine-ends.patch |   54 ++++++++
 ...nnel-clear-channel-error-after-auth-error.patch |   37 ++++++
 spice-gtk.spec                                     |   18 +++-
 7 files changed, 585 insertions(+), 1 deletions(-)
---
diff --git a/0001-session-keep-main-channel-on-reconnect.patch b/0001-session-keep-main-channel-on-reconnect.patch
new file mode 100644
index 0000000..7e5d5cd
--- /dev/null
+++ b/0001-session-keep-main-channel-on-reconnect.patch
@@ -0,0 +1,115 @@
+From 6b475802d78f2a4f2110738cb360b496dc85336c Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= <marcandre.lureau at redhat.com>
+Date: Tue, 16 Dec 2014 15:11:19 +0100
+Subject: [PATCH spice-gtk 1/6] session: keep main channel on reconnect
+
+For legacy reasons, spice-gtk should keep at least one channel in the
+session when reconnecting (clients may decide that the session is
+disconnected when all channels are gone). The most obvious is to
+keep and reuse the main channel.
+---
+ gtk/spice-session.c | 30 +++++++++++++++++-------------
+ 1 file changed, 17 insertions(+), 13 deletions(-)
+
+diff --git a/gtk/spice-session.c b/gtk/spice-session.c
+index c80c8dc..7971f3c 100644
+--- a/gtk/spice-session.c
++++ b/gtk/spice-session.c
+@@ -258,19 +258,23 @@ static void spice_session_init(SpiceSession *session)
+ }
+ 
+ static void
+-session_disconnect(SpiceSession *self)
++session_disconnect(SpiceSession *self, gboolean keep_main)
+ {
+     SpiceSessionPrivate *s;
+     struct channel *item;
+     RingItem *ring, *next;
+ 
+     s = self->priv;
+-    s->cmain = NULL;
+ 
+     for (ring = ring_get_head(&s->channels); ring != NULL; ring = next) {
+         next = ring_next(&s->channels, ring);
+         item = SPICE_CONTAINEROF(ring, struct channel, link);
+-        spice_session_channel_destroy(self, item->channel);
++
++        if (keep_main && item->channel == s->cmain) {
++            spice_channel_disconnect(item->channel, SPICE_CHANNEL_NONE);
++        } else {
++            spice_session_channel_destroy(self, item->channel);
++        }
+     }
+ 
+     s->connection_id = 0;
+@@ -290,7 +294,7 @@ spice_session_dispose(GObject *gobject)
+ 
+     SPICE_DEBUG("session dispose");
+ 
+-    session_disconnect(session);
++    session_disconnect(session, FALSE);
+ 
+     g_warn_if_fail(s->migration == NULL);
+     g_warn_if_fail(s->migration_left == NULL);
+@@ -1410,12 +1414,12 @@ gboolean spice_session_connect(SpiceSession *session)
+     s = session->priv;
+     g_return_val_if_fail(!s->disconnecting, FALSE);
+ 
+-    session_disconnect(session);
++    session_disconnect(session, TRUE);
+ 
+     s->client_provided_sockets = FALSE;
+ 
+-    g_warn_if_fail(s->cmain == NULL);
+-    s->cmain = spice_channel_new(session, SPICE_CHANNEL_MAIN, 0);
++    if (s->cmain == NULL)
++        s->cmain = spice_channel_new(session, SPICE_CHANNEL_MAIN, 0);
+ 
+     glz_decoder_window_clear(s->glz_window);
+     return spice_channel_connect(s->cmain);
+@@ -1445,12 +1449,12 @@ gboolean spice_session_open_fd(SpiceSession *session, int fd)
+     s = session->priv;
+     g_return_val_if_fail(!s->disconnecting, FALSE);
+ 
+-    session_disconnect(session);
++    session_disconnect(session, TRUE);
+ 
+     s->client_provided_sockets = TRUE;
+ 
+-    g_warn_if_fail(s->cmain == NULL);
+-    s->cmain = spice_channel_new(session, SPICE_CHANNEL_MAIN, 0);
++    if (s->cmain == NULL)
++        s->cmain = spice_channel_new(session, SPICE_CHANNEL_MAIN, 0);
+ 
+     glz_decoder_window_clear(s->glz_window);
+     return spice_channel_open_fd(s->cmain, fd);
+@@ -1602,7 +1606,7 @@ void spice_session_abort_migration(SpiceSession *session)
+ end:
+     g_list_free(s->migration_left);
+     s->migration_left = NULL;
+-    session_disconnect(s->migration);
++    session_disconnect(s->migration, FALSE);
+     g_object_unref(s->migration);
+     s->migration = NULL;
+ 
+@@ -1642,7 +1646,7 @@ void spice_session_channel_migrate(SpiceSession *session, SpiceChannel *channel)
+ 
+     if (g_list_length(s->migration_left) == 0) {
+         CHANNEL_DEBUG(channel, "migration: all channel migrated, success");
+-        session_disconnect(s->migration);
++        session_disconnect(s->migration, FALSE);
+         g_object_unref(s->migration);
+         s->migration = NULL;
+         spice_session_set_migration_state(session, SPICE_SESSION_MIGRATION_NONE);
+@@ -1749,7 +1753,7 @@ static gboolean session_disconnect_idle(SpiceSession *self)
+ {
+     SpiceSessionPrivate *s = self->priv;
+ 
+-    session_disconnect(self);
++    session_disconnect(self, FALSE);
+     s->disconnecting = 0;
+ 
+     g_object_unref(self);
+-- 
+2.1.0
+
diff --git a/0002-channel-factorize-failed-authentication.patch b/0002-channel-factorize-failed-authentication.patch
new file mode 100644
index 0000000..27e318c
--- /dev/null
+++ b/0002-channel-factorize-failed-authentication.patch
@@ -0,0 +1,126 @@
+From 21acda1c51241ff2cce013509900e0a8373d409d Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= <marcandre.lureau at redhat.com>
+Date: Tue, 16 Dec 2014 14:24:24 +0100
+Subject: [PATCH spice-gtk 2/6] channel: factorize failed authentication
+
+There are a few things that should be common to all wrong authentication
+cases. Let's put them all in the same function.
+---
+ gtk/spice-channel-priv.h |  1 +
+ gtk/spice-channel.c      | 62 ++++++++++++++++++++++++++----------------------
+ 2 files changed, 34 insertions(+), 29 deletions(-)
+
+diff --git a/gtk/spice-channel-priv.h b/gtk/spice-channel-priv.h
+index 07012db..671e9fe 100644
+--- a/gtk/spice-channel-priv.h
++++ b/gtk/spice-channel-priv.h
+@@ -66,6 +66,7 @@ struct _SpiceMsgIn {
+ 
+ enum spice_channel_state {
+     SPICE_CHANNEL_STATE_UNCONNECTED = 0,
++    SPICE_CHANNEL_STATE_FAILED_AUTHENTICATION,
+     SPICE_CHANNEL_STATE_CONNECTING,
+     SPICE_CHANNEL_STATE_READY,
+     SPICE_CHANNEL_STATE_SWITCHING,
+diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c
+index ea0ed34..d0e6df8 100644
+--- a/gtk/spice-channel.c
++++ b/gtk/spice-channel.c
+@@ -1052,6 +1052,29 @@ static void spice_channel_send_spice_ticket(SpiceChannel *channel)
+ }
+ 
+ /* coroutine context */
++static void spice_channel_failed_authentication(SpiceChannel *channel)
++{
++    SpiceChannelPrivate *c = channel->priv;
++
++    if (c->auth_needs_username_and_password)
++        g_set_error_literal(&c->error,
++                            SPICE_CLIENT_ERROR,
++                            SPICE_CLIENT_ERROR_AUTH_NEEDS_PASSWORD_AND_USERNAME,
++                            _("Authentication failed: password and username are required"));
++    else
++        g_set_error_literal(&c->error,
++                            SPICE_CLIENT_ERROR,
++                            SPICE_CLIENT_ERROR_AUTH_NEEDS_PASSWORD,
++                            _("Authentication failed: password is required"));
++
++    c->state = SPICE_CHANNEL_STATE_FAILED_AUTHENTICATION;
++
++    g_coroutine_signal_emit(channel, signals[SPICE_CHANNEL_EVENT], 0, SPICE_CHANNEL_ERROR_AUTH);
++
++    c->has_error = TRUE; /* force disconnect */
++}
++
++/* coroutine context */
+ static void spice_channel_recv_auth(SpiceChannel *channel)
+ {
+     SpiceChannelPrivate *c = channel->priv;
+@@ -1068,7 +1091,7 @@ static void spice_channel_recv_auth(SpiceChannel *channel)
+ 
+     if (link_res != SPICE_LINK_ERR_OK) {
+         CHANNEL_DEBUG(channel, "link result: reply %d", link_res);
+-        g_coroutine_signal_emit(channel, signals[SPICE_CHANNEL_EVENT], 0, SPICE_CHANNEL_ERROR_AUTH);
++        spice_channel_failed_authentication(channel);
+         return;
+     }
+ 
+@@ -1310,22 +1333,6 @@ spice_channel_gather_sasl_credentials(SpiceChannel *channel,
+ #define SASL_MAX_MECHNAME_LEN 100
+ #define SASL_MAX_DATA_LEN (1024 * 1024)
+ 
+-static void spice_channel_set_detailed_authentication_error(SpiceChannel *channel)
+-{
+-    SpiceChannelPrivate *c = channel->priv;
+-
+-    if (c->auth_needs_username_and_password)
+-        g_set_error_literal(&c->error,
+-                            SPICE_CLIENT_ERROR,
+-                            SPICE_CLIENT_ERROR_AUTH_NEEDS_PASSWORD_AND_USERNAME,
+-                            _("Authentication failed: password and username are required"));
+-    else
+-        g_set_error_literal(&c->error,
+-                            SPICE_CLIENT_ERROR,
+-                            SPICE_CLIENT_ERROR_AUTH_NEEDS_PASSWORD,
+-                            _("Authentication failed: password is required"));
+-}
+-
+ /* Perform the SASL authentication process
+  */
+ static gboolean spice_channel_perform_auth_sasl(SpiceChannel *channel)
+@@ -1644,23 +1651,20 @@ restart:
+ complete:
+     CHANNEL_DEBUG(channel, "%s", "SASL authentication complete");
+     spice_channel_read(channel, &len, sizeof(len));
+-    if (len != SPICE_LINK_ERR_OK) {
+-        spice_channel_set_detailed_authentication_error(channel);
+-        g_coroutine_signal_emit(channel, signals[SPICE_CHANNEL_EVENT], 0, SPICE_CHANNEL_ERROR_AUTH);
++    if (len == SPICE_LINK_ERR_OK) {
++        ret = TRUE;
++        /* This must come *after* check-auth-result, because the former
++         * is defined to be sent unencrypted, and setting saslconn turns
++         * on the SSF layer encryption processing */
++        c->sasl_conn = saslconn;
++        goto cleanup;
+     }
+-    ret = len == SPICE_LINK_ERR_OK;
+-    /* This must come *after* check-auth-result, because the former
+-     * is defined to be sent unencrypted, and setting saslconn turns
+-     * on the SSF layer encryption processing */
+-    c->sasl_conn = saslconn;
+-    goto cleanup;
+ 
+ error:
+     if (saslconn)
+         sasl_dispose(&saslconn);
+-    spice_channel_set_detailed_authentication_error(channel);
+-    g_coroutine_signal_emit(channel, signals[SPICE_CHANNEL_EVENT], 0, SPICE_CHANNEL_ERROR_AUTH);
+-    c->has_error = TRUE; /* force disconnect */
++
++    spice_channel_failed_authentication(channel);
+     ret = FALSE;
+ 
+ cleanup:
+-- 
+2.1.0
+
diff --git a/0003-channel-do-not-enter-channel-iterate-on-early-error.patch b/0003-channel-do-not-enter-channel-iterate-on-early-error.patch
new file mode 100644
index 0000000..e67494e
--- /dev/null
+++ b/0003-channel-do-not-enter-channel-iterate-on-early-error.patch
@@ -0,0 +1,129 @@
+From 14ce8b8dabf47ec208148a83490503084445ba1d Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= <marcandre.lureau at redhat.com>
+Date: Tue, 16 Dec 2014 17:19:00 +0100
+Subject: [PATCH spice-gtk 3/6] channel: do not enter channel iterate on early
+ error
+
+There is no need to enter channel_iterate() if we found an early
+connection steps error.
+---
+ gtk/spice-channel.c | 32 +++++++++++++++++---------------
+ 1 file changed, 17 insertions(+), 15 deletions(-)
+
+diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c
+index d0e6df8..524a13e 100644
+--- a/gtk/spice-channel.c
++++ b/gtk/spice-channel.c
+@@ -1075,7 +1075,7 @@ static void spice_channel_failed_authentication(SpiceChannel *channel)
+ }
+ 
+ /* coroutine context */
+-static void spice_channel_recv_auth(SpiceChannel *channel)
++static gboolean spice_channel_recv_auth(SpiceChannel *channel)
+ {
+     SpiceChannelPrivate *c = channel->priv;
+     uint32_t link_res;
+@@ -1086,13 +1086,13 @@ static void spice_channel_recv_auth(SpiceChannel *channel)
+         CHANNEL_DEBUG(channel, "incomplete auth reply (%d/%" G_GSIZE_FORMAT ")",
+                     rc, sizeof(link_res));
+         g_coroutine_signal_emit(channel, signals[SPICE_CHANNEL_EVENT], 0, SPICE_CHANNEL_ERROR_LINK);
+-        return;
++        return FALSE;
+     }
+ 
+     if (link_res != SPICE_LINK_ERR_OK) {
+         CHANNEL_DEBUG(channel, "link result: reply %d", link_res);
+         spice_channel_failed_authentication(channel);
+-        return;
++        return FALSE;
+     }
+ 
+     c->state = SPICE_CHANNEL_STATE_READY;
+@@ -1105,6 +1105,8 @@ static void spice_channel_recv_auth(SpiceChannel *channel)
+ 
+     if (c->state != SPICE_CHANNEL_STATE_MIGRATING)
+         spice_channel_up(channel);
++
++    return TRUE;
+ }
+ 
+ G_GNUC_INTERNAL
+@@ -1678,14 +1680,14 @@ cleanup:
+ #endif /* HAVE_SASL */
+ 
+ /* coroutine context */
+-static void spice_channel_recv_link_msg(SpiceChannel *channel, gboolean *switch_tls)
++static gboolean spice_channel_recv_link_msg(SpiceChannel *channel, gboolean *switch_tls)
+ {
+     SpiceChannelPrivate *c;
+     int rc, num_caps, i;
+     uint32_t *caps;
+ 
+-    g_return_if_fail(channel != NULL);
+-    g_return_if_fail(channel->priv != NULL);
++    g_return_val_if_fail(channel != NULL, FALSE);
++    g_return_val_if_fail(channel->priv != NULL, FALSE);
+ 
+     c = channel->priv;
+ 
+@@ -1696,7 +1698,7 @@ static void spice_channel_recv_link_msg(SpiceChannel *channel, gboolean *switch_
+         g_critical("%s: %s: incomplete link reply (%d/%d)",
+                   c->name, __FUNCTION__, rc, c->peer_hdr.size);
+         g_coroutine_signal_emit(channel, signals[SPICE_CHANNEL_EVENT], 0, SPICE_CHANNEL_ERROR_LINK);
+-        return;
++        return FALSE;
+     }
+     switch (c->peer_msg->error) {
+     case SPICE_LINK_ERR_OK:
+@@ -1705,7 +1707,7 @@ static void spice_channel_recv_link_msg(SpiceChannel *channel, gboolean *switch_
+     case SPICE_LINK_ERR_NEED_SECURED:
+         *switch_tls = true;
+         CHANNEL_DEBUG(channel, "switching to tls");
+-        return;
++        return FALSE;
+     default:
+         g_warning("%s: %s: unhandled error %d",
+                 c->name, __FUNCTION__, c->peer_msg->error);
+@@ -1744,7 +1746,8 @@ static void spice_channel_recv_link_msg(SpiceChannel *channel, gboolean *switch_
+             CHANNEL_DEBUG(channel, "Choosing SASL mechanism");
+             auth.auth_mechanism = SPICE_COMMON_CAP_AUTH_SASL;
+             spice_channel_write(channel, &auth, sizeof(auth));
+-            spice_channel_perform_auth_sasl(channel);
++            if (!spice_channel_perform_auth_sasl(channel))
++                return FALSE;
+         } else
+ #endif
+         if (spice_channel_test_common_capability(channel, SPICE_COMMON_CAP_AUTH_SPICE)) {
+@@ -1759,11 +1762,12 @@ static void spice_channel_recv_link_msg(SpiceChannel *channel, gboolean *switch_
+     c->use_mini_header = spice_channel_test_common_capability(channel,
+                                                               SPICE_COMMON_CAP_MINI_HEADER);
+     CHANNEL_DEBUG(channel, "use mini header: %d", c->use_mini_header);
+-    return;
++    return TRUE;
+ 
+ error:
+     SPICE_CHANNEL_GET_CLASS(channel)->channel_disconnect(channel);
+     g_coroutine_signal_emit(channel, signals[SPICE_CHANNEL_EVENT], 0, SPICE_CHANNEL_ERROR_LINK);
++    return FALSE;
+ }
+ 
+ /* system context */
+@@ -2411,12 +2415,10 @@ connected:
+     }
+ 
+     spice_channel_send_link(channel);
+-    if (spice_channel_recv_link_hdr(channel, &switch_protocol) == FALSE)
+-        goto cleanup;
+-    spice_channel_recv_link_msg(channel, &switch_tls);
+-    if (switch_tls)
++    if (!spice_channel_recv_link_hdr(channel, &switch_protocol) ||
++        !spice_channel_recv_link_msg(channel, &switch_tls) ||
++        !spice_channel_recv_auth(channel))
+         goto cleanup;
+-    spice_channel_recv_auth(channel);
+ 
+     while (spice_channel_iterate(channel))
+         ;
+-- 
+2.1.0
+
diff --git a/0004-channel-introduce-SPICE_CHANNEL_STATE_RECONNECTING.patch b/0004-channel-introduce-SPICE_CHANNEL_STATE_RECONNECTING.patch
new file mode 100644
index 0000000..8fce97f
--- /dev/null
+++ b/0004-channel-introduce-SPICE_CHANNEL_STATE_RECONNECTING.patch
@@ -0,0 +1,107 @@
+From 9969d042bbedfddfb9589bdcc05d877a3bd1fa84 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= <marcandre.lureau at redhat.com>
+Date: Tue, 16 Dec 2014 17:56:37 +0100
+Subject: [PATCH spice-gtk 4/6] channel: introduce
+ SPICE_CHANNEL_STATE_RECONNECTING
+
+Add a new state that permits reconnection, because it's < CONNECTING.
+It also simplifies some code by removing unneeded variables in
+spice_channel_coroutine(): the channel.tls and session.protocol version
+properties are already modified during initial connection steps.
+---
+ gtk/spice-channel-priv.h |  1 +
+ gtk/spice-channel.c      | 19 ++++++++-----------
+ 2 files changed, 9 insertions(+), 11 deletions(-)
+
+diff --git a/gtk/spice-channel-priv.h b/gtk/spice-channel-priv.h
+index 671e9fe..bd7f490 100644
+--- a/gtk/spice-channel-priv.h
++++ b/gtk/spice-channel-priv.h
+@@ -67,6 +67,7 @@ struct _SpiceMsgIn {
+ enum spice_channel_state {
+     SPICE_CHANNEL_STATE_UNCONNECTED = 0,
+     SPICE_CHANNEL_STATE_FAILED_AUTHENTICATION,
++    SPICE_CHANNEL_STATE_RECONNECTING,
+     SPICE_CHANNEL_STATE_CONNECTING,
+     SPICE_CHANNEL_STATE_READY,
+     SPICE_CHANNEL_STATE_SWITCHING,
+diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c
+index 524a13e..4081e0b 100644
+--- a/gtk/spice-channel.c
++++ b/gtk/spice-channel.c
+@@ -1183,12 +1183,11 @@ static void spice_channel_send_link(SpiceChannel *channel)
+ }
+ 
+ /* coroutine context */
+-static gboolean spice_channel_recv_link_hdr(SpiceChannel *channel, gboolean *switch_protocol)
++static gboolean spice_channel_recv_link_hdr(SpiceChannel *channel)
+ {
+     SpiceChannelPrivate *c = channel->priv;
+     int rc;
+ 
+-    *switch_protocol = FALSE;
+     rc = spice_channel_read(channel, &c->peer_hdr, sizeof(c->peer_hdr));
+     if (rc != sizeof(c->peer_hdr)) {
+         g_warning("incomplete link header (%d/%" G_GSIZE_FORMAT ")",
+@@ -1221,7 +1220,7 @@ error:
+        incompatible. Try with the oldest protocol in this case: */
+     if (c->link_hdr.major_version != 1) {
+         SPICE_DEBUG("%s: error, switching to protocol 1 (spice 0.4)", c->name);
+-        *switch_protocol = TRUE;
++        c->state = SPICE_CHANNEL_STATE_RECONNECTING;
+         g_object_set(c->session, "protocol", 1, NULL);
+         return FALSE;
+     }
+@@ -1680,7 +1679,7 @@ cleanup:
+ #endif /* HAVE_SASL */
+ 
+ /* coroutine context */
+-static gboolean spice_channel_recv_link_msg(SpiceChannel *channel, gboolean *switch_tls)
++static gboolean spice_channel_recv_link_msg(SpiceChannel *channel)
+ {
+     SpiceChannelPrivate *c;
+     int rc, num_caps, i;
+@@ -1705,8 +1704,9 @@ static gboolean spice_channel_recv_link_msg(SpiceChannel *channel, gboolean *swi
+         /* nothing */
+         break;
+     case SPICE_LINK_ERR_NEED_SECURED:
+-        *switch_tls = true;
++        c->state = SPICE_CHANNEL_STATE_RECONNECTING;
+         CHANNEL_DEBUG(channel, "switching to tls");
++        c->tls = TRUE;
+         return FALSE;
+     default:
+         g_warning("%s: %s: unhandled error %d",
+@@ -2280,8 +2280,6 @@ static void *spice_channel_coroutine(void *data)
+     SpiceChannelPrivate *c = channel->priv;
+     guint verify;
+     int rc, delay_val = 1;
+-    gboolean switch_tls = FALSE;
+-    gboolean switch_protocol = FALSE;
+     /* When some other SSL/TLS version becomes obsolete, add it to this
+      * variable. */
+     long ssl_options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
+@@ -2415,8 +2413,8 @@ connected:
+     }
+ 
+     spice_channel_send_link(channel);
+-    if (!spice_channel_recv_link_hdr(channel, &switch_protocol) ||
+-        !spice_channel_recv_link_msg(channel, &switch_tls) ||
++    if (!spice_channel_recv_link_hdr(channel) ||
++        !spice_channel_recv_link_msg(channel) ||
+         !spice_channel_recv_auth(channel))
+         goto cleanup;
+ 
+@@ -2428,8 +2426,7 @@ cleanup:
+ 
+     SPICE_CHANNEL_GET_CLASS(channel)->channel_disconnect(channel);
+ 
+-    if (switch_protocol || (switch_tls && !c->tls)) {
+-        c->tls = switch_tls;
++    if (c->state == SPICE_CHANNEL_STATE_RECONNECTING) {
+         spice_channel_connect(channel);
+         g_object_unref(channel);
+     } else
+-- 
+2.1.0
+
diff --git a/0005-channel-throw-auth-error-when-coroutine-ends.patch b/0005-channel-throw-auth-error-when-coroutine-ends.patch
new file mode 100644
index 0000000..607170b
--- /dev/null
+++ b/0005-channel-throw-auth-error-when-coroutine-ends.patch
@@ -0,0 +1,54 @@
+From 201a8c2e36e250f49d5c729b11563730dfa4c484 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= <marcandre.lureau at redhat.com>
+Date: Tue, 16 Dec 2014 18:04:53 +0100
+Subject: [PATCH spice-gtk 5/6] channel: throw auth error when coroutine ends
+
+It is common that clients attempt to reconnect during the
+SPICE_CHANNEL_ERROR_AUTH callback.  However, the channel must exit
+the coroutine first before reconnection can happen.
+---
+ gtk/spice-channel.c | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c
+index 4081e0b..c00bb42 100644
+--- a/gtk/spice-channel.c
++++ b/gtk/spice-channel.c
+@@ -1069,8 +1069,6 @@ static void spice_channel_failed_authentication(SpiceChannel *channel)
+ 
+     c->state = SPICE_CHANNEL_STATE_FAILED_AUTHENTICATION;
+ 
+-    g_coroutine_signal_emit(channel, signals[SPICE_CHANNEL_EVENT], 0, SPICE_CHANNEL_ERROR_AUTH);
+-
+     c->has_error = TRUE; /* force disconnect */
+ }
+ 
+@@ -2182,6 +2180,9 @@ static gboolean spice_channel_delayed_unref(gpointer data)
+ 
+     g_return_val_if_fail(c->coroutine.coroutine.exited == TRUE, FALSE);
+ 
++    if (c->state == SPICE_CHANNEL_STATE_FAILED_AUTHENTICATION)
++        g_coroutine_signal_emit(channel, signals[SPICE_CHANNEL_EVENT], 0, SPICE_CHANNEL_ERROR_AUTH);
++
+     g_object_unref(G_OBJECT(data));
+ 
+     return FALSE;
+@@ -2458,6 +2459,7 @@ static gboolean connect_delayed(gpointer data)
+     return FALSE;
+ }
+ 
++/* any context */
+ static gboolean channel_connect(SpiceChannel *channel)
+ {
+     SpiceChannelPrivate *c = channel->priv;
+@@ -2639,7 +2641,6 @@ static void channel_disconnect(SpiceChannel *channel)
+     if (c->state == SPICE_CHANNEL_STATE_READY)
+         g_coroutine_signal_emit(channel, signals[SPICE_CHANNEL_EVENT], 0, SPICE_CHANNEL_CLOSED);
+ 
+-    c->state = SPICE_CHANNEL_STATE_UNCONNECTED;
+     spice_channel_reset(channel, FALSE);
+ 
+     g_return_if_fail(SPICE_IS_CHANNEL(channel));
+-- 
+2.1.0
+
diff --git a/0006-channel-clear-channel-error-after-auth-error.patch b/0006-channel-clear-channel-error-after-auth-error.patch
new file mode 100644
index 0000000..f34b7ac
--- /dev/null
+++ b/0006-channel-clear-channel-error-after-auth-error.patch
@@ -0,0 +1,37 @@
+From 43fc492149767bdade118d586572a2ce5abe63fc Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= <marcandre.lureau at redhat.com>
+Date: Tue, 16 Dec 2014 18:30:02 +0100
+Subject: [PATCH spice-gtk 6/6] channel: clear channel error after auth error
+
+When entered authentication details are wrong, spice-gtk will reset
+channel error, which will result in the following warning:
+
+(remote-viewer:20753): GLib-WARNING **: GError set over the top of a
+previous GError or uninitialized memory.
+This indicates a bug in someone's code. You must ensure an error is NULL
+before it's set.
+
+Clear channel error after reporting authentication error.
+---
+ gtk/spice-channel.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c
+index c00bb42..fb7b0d5 100644
+--- a/gtk/spice-channel.c
++++ b/gtk/spice-channel.c
+@@ -2180,8 +2180,10 @@ static gboolean spice_channel_delayed_unref(gpointer data)
+ 
+     g_return_val_if_fail(c->coroutine.coroutine.exited == TRUE, FALSE);
+ 
+-    if (c->state == SPICE_CHANNEL_STATE_FAILED_AUTHENTICATION)
++    if (c->state == SPICE_CHANNEL_STATE_FAILED_AUTHENTICATION) {
+         g_coroutine_signal_emit(channel, signals[SPICE_CHANNEL_EVENT], 0, SPICE_CHANNEL_ERROR_AUTH);
++        g_clear_error(&c->error);
++    }
+ 
+     g_object_unref(G_OBJECT(data));
+ 
+-- 
+2.1.0
+
diff --git a/spice-gtk.spec b/spice-gtk.spec
index 671d3e3..7052269 100644
--- a/spice-gtk.spec
+++ b/spice-gtk.spec
@@ -7,7 +7,7 @@
 
 Name:           spice-gtk
 Version:        0.27
-Release:        1%{?dist}
+Release:        2%{?dist}
 Summary:        A GTK+ widget for SPICE clients
 
 Group:          System Environment/Libraries
@@ -15,6 +15,12 @@ License:        LGPLv2+
 URL:            http://spice-space.org/page/Spice-Gtk
 #VCS:           git:git://anongit.freedesktop.org/spice/spice-gtk
 Source0:        http://www.spice-space.org/download/gtk/%{name}-%{version}%{?_version_suffix}.tar.bz2
+Patch0001:      0001-session-keep-main-channel-on-reconnect.patch
+Patch0002:      0002-channel-factorize-failed-authentication.patch
+Patch0003:      0003-channel-do-not-enter-channel-iterate-on-early-error.patch
+Patch0004:      0004-channel-introduce-SPICE_CHANNEL_STATE_RECONNECTING.patch
+Patch0005:      0005-channel-throw-auth-error-when-coroutine-ends.patch
+Patch0006:      0006-channel-clear-channel-error-after-auth-error.patch
 
 BuildRequires: intltool
 BuildRequires: gtk2-devel >= 2.14
@@ -137,12 +143,19 @@ spicy-screenshot is a tool to capture screen-shots of a SPICE desktop.
 
 %prep
 %setup -q -n spice-gtk-%{version}%{?_version_suffix} -c
+
 if [ -n '%{?_version_suffix}' ]; then
   mv spice-gtk-%{version}%{?_version_suffix} spice-gtk-%{version}
 fi
 
 pushd spice-gtk-%{version}
 find . -name '*.stamp' | xargs touch
+%patch0001 -p1
+%patch0002 -p1
+%patch0003 -p1
+%patch0004 -p1
+%patch0005 -p1
+%patch0006 -p1
 popd
 
 %if %{with_gtk3}
@@ -264,6 +277,9 @@ rm -rf %{buildroot}%{_datadir}/pkgconfig/spice-protocol.pc
 %{_bindir}/spicy-stats
 
 %changelog
+* Tue Dec 16 2014 Marc-André Lureau <marcandre.lureau at redhat.com> 0.27-2
+- Fix authentication error handling regression.
+
 * Thu Dec 11 2014 Marc-André Lureau <marcandre.lureau at redhat.com> 0.27-1
 - Update to spice-gtk v0.27
 


More information about the scm-commits mailing list