From 029fd8e852977ec7e8ce81a80f5cf7a07636d0ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 16 Dec 2014 18:49:11 +0100 Subject: [PATCH] Fix authentication error handling regression --- ...ssion-keep-main-channel-on-reconnect.patch | 115 ++++++++++++++++ ...nnel-factorize-failed-authentication.patch | 126 +++++++++++++++++ ...enter-channel-iterate-on-early-error.patch | 129 ++++++++++++++++++ ...uce-SPICE_CHANNEL_STATE_RECONNECTING.patch | 107 +++++++++++++++ ...throw-auth-error-when-coroutine-ends.patch | 54 ++++++++ ...clear-channel-error-after-auth-error.patch | 37 +++++ spice-gtk.spec | 18 ++- 7 files changed, 585 insertions(+), 1 deletion(-) create mode 100644 0001-session-keep-main-channel-on-reconnect.patch create mode 100644 0002-channel-factorize-failed-authentication.patch create mode 100644 0003-channel-do-not-enter-channel-iterate-on-early-error.patch create mode 100644 0004-channel-introduce-SPICE_CHANNEL_STATE_RECONNECTING.patch create mode 100644 0005-channel-throw-auth-error-when-coroutine-ends.patch create mode 100644 0006-channel-clear-channel-error-after-auth-error.patch 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?= +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?= +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?= +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?= +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?= +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?= +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 0.27-2 +- Fix authentication error handling regression. + * Thu Dec 11 2014 Marc-André Lureau 0.27-1 - Update to spice-gtk v0.27