diff options
author | Will Thompson <will.thompson@collabora.co.uk> | 2012-11-14 16:43:46 +0000 |
---|---|---|
committer | Will Thompson <will.thompson@collabora.co.uk> | 2012-11-14 17:10:54 +0000 |
commit | 39ee2f08cf4e2d46aae86307f6dfa023fe347248 (patch) | |
tree | fef2e4d2a0f40fed89c9eb619f5fd3e28c88a51e | |
parent | f51c7b9cd378a561a9926d0860c26b7581dd4dc0 (diff) |
muc-channel: implement Destroy(), make Close() respawn
This fixes the issue where empathy-chat crashing means you get kicked
out of all your channels.
It's technically backwards-incompatible but empathy-chat has been using
RemoveMembers() to leave rooms for ages, and it's a pretty destructive
and annoying bug, so let's just get on with it.
Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=24614
-rw-r--r-- | src/idle-muc-channel.c | 39 | ||||
-rw-r--r-- | src/idle-muc-manager.c | 14 | ||||
-rw-r--r-- | tests/twisted/Makefile.am | 1 | ||||
-rw-r--r-- | tests/twisted/channels/muc-destroy.py | 32 | ||||
-rw-r--r-- | tests/twisted/channels/requests-muc.py | 29 |
5 files changed, 111 insertions, 4 deletions
diff --git a/src/idle-muc-channel.c b/src/idle-muc-channel.c index d1c3e66..b6b6fb4 100644 --- a/src/idle-muc-channel.c +++ b/src/idle-muc-channel.c @@ -46,6 +46,7 @@ static void _password_iface_init(gpointer, gpointer); static void subject_iface_init(gpointer, gpointer); +static void destroyable_iface_init(gpointer, gpointer); static void idle_muc_channel_send (GObject *obj, TpMessage *message, TpMessageSendingFlags flags); static void idle_muc_channel_close (TpBaseChannel *base); @@ -59,6 +60,7 @@ G_DEFINE_TYPE_WITH_CODE(IdleMUCChannel, idle_muc_channel, TP_TYPE_BASE_CHANNEL, G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_SUBJECT, subject_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_ROOM_CONFIG, tp_base_room_config_iface_init); + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_DESTROYABLE, destroyable_iface_init); ) /* property enum */ @@ -131,6 +133,7 @@ static const gchar *muc_channel_interfaces[] = { TP_IFACE_CHANNEL_INTERFACE_ROOM, TP_IFACE_CHANNEL_INTERFACE_SUBJECT, TP_IFACE_CHANNEL_INTERFACE_ROOM_CONFIG, + TP_IFACE_CHANNEL_INTERFACE_DESTROYABLE, NULL }; @@ -1339,12 +1342,35 @@ idle_muc_channel_close ( IDLE_DEBUG ("called on %p", self); + if (priv->state == MUC_STATE_JOINED) { + tp_message_mixin_set_rescued (G_OBJECT (self)); + tp_base_channel_reopened (base, 0); + } else { + /* FIXME: this is wrong if called while JOIN is in flight. */ + tp_base_channel_destroyed (base); + } +} + + +static void +idle_muc_channel_destroy ( + TpSvcChannelInterfaceDestroyable *object, + DBusGMethodInvocation *context) +{ + TpBaseChannel *base = TP_BASE_CHANNEL (object); + IdleMUCChannel *self = IDLE_MUC_CHANNEL (object); + IdleMUCChannelPrivate *priv = self->priv; + + IDLE_DEBUG ("called on %p", self); + if (priv->state == MUC_STATE_JOINED) part_from_channel (self, NULL); /* FIXME: this is wrong if called while JOIN is in flight. */ if (priv->state < MUC_STATE_JOINED) tp_base_channel_destroyed (base); + + tp_svc_channel_interface_destroyable_return_from_destroy (context); } /** @@ -1537,3 +1563,16 @@ subject_iface_init ( IMPLEMENT (set_subject); #undef IMPLEMENT } + +static void +destroyable_iface_init ( + gpointer g_iface, + gpointer iface_data G_GNUC_UNUSED) +{ + TpSvcChannelInterfaceDestroyableClass *klass = g_iface; + +#define IMPLEMENT(x) \ + tp_svc_channel_interface_destroyable_implement_##x (klass, idle_muc_channel_##x) + IMPLEMENT (destroy); +#undef IMPLEMENT +} diff --git a/src/idle-muc-manager.c b/src/idle-muc-manager.c index 5cba4d7..faf66e9 100644 --- a/src/idle-muc-manager.c +++ b/src/idle-muc-manager.c @@ -676,6 +676,7 @@ static GSList *take_request_tokens(IdleMUCManager *manager, IdleMUCChannel *chan static void _channel_closed_cb(IdleMUCChannel *chan, gpointer user_data) { IdleMUCManager *manager = IDLE_MUC_MANAGER(user_data); IdleMUCManagerPrivate *priv = IDLE_MUC_MANAGER_GET_PRIVATE(manager); + TpBaseChannel *base = TP_BASE_CHANNEL (chan); GSList *reqs = take_request_tokens(user_data, chan); /* If there are any tokens for this channel when it closes, the request @@ -689,10 +690,17 @@ static void _channel_closed_cb(IdleMUCChannel *chan, gpointer user_data) { g_slist_free(reqs); + tp_channel_manager_emit_channel_closed_for_object (manager, + TP_EXPORTABLE_CHANNEL (chan)); + if (priv->channels) { - TpHandle handle; - g_object_get(chan, "handle", &handle, NULL); - g_hash_table_remove(priv->channels, GUINT_TO_POINTER(handle)); + TpHandle handle = tp_base_channel_get_target_handle (base); + + if (tp_base_channel_is_destroyed (base)) + g_hash_table_remove(priv->channels, GUINT_TO_POINTER(handle)); + else + tp_channel_manager_emit_new_channel (manager, TP_EXPORTABLE_CHANNEL (chan), + NULL); } } diff --git a/tests/twisted/Makefile.am b/tests/twisted/Makefile.am index 185ffec..8329514 100644 --- a/tests/twisted/Makefile.am +++ b/tests/twisted/Makefile.am @@ -16,6 +16,7 @@ TWISTED_TESTS = \ channels/requests-create.py \ channels/requests-muc.py \ channels/muc-channel-topic.py \ + channels/muc-destroy.py \ channels/room-list-channel.py \ channels/room-list-multiple.py \ messages/accept-invalid-nicks.py \ diff --git a/tests/twisted/channels/muc-destroy.py b/tests/twisted/channels/muc-destroy.py new file mode 100644 index 0000000..3513098 --- /dev/null +++ b/tests/twisted/channels/muc-destroy.py @@ -0,0 +1,32 @@ +""" +Tests Destroy()ing a MUC. +""" + +from servicetest import call_async, wrap_channel, EventPattern +from idletest import exec_test +import constants as cs + +def test(q, bus, conn, stream): + conn.Connect() + q.expect('dbus-signal', signal='StatusChanged', + args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) + + call_async(q, conn.Requests, "CreateChannel", { + cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, + cs.ROOM_NAME: "#everythingyoutouch", + }) + q.expect('stream-JOIN') + event = q.expect('dbus-return', method='CreateChannel') + path, props = event.value + chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text', + ['Destroyable']) + + call_async(q, chan.Destroyable, "Destroy") + q.expect_many( + EventPattern('stream-PART'), + EventPattern('dbus-signal', signal='Closed', path=path), + EventPattern('dbus-return', method='Destroy'), + ) + +if __name__ == '__main__': + exec_test(test) diff --git a/tests/twisted/channels/requests-muc.py b/tests/twisted/channels/requests-muc.py index 8d37526..27de685 100644 --- a/tests/twisted/channels/requests-muc.py +++ b/tests/twisted/channels/requests-muc.py @@ -3,7 +3,7 @@ Test connecting to a IRC channel via the Requests interface """ import functools -from idletest import exec_test, BaseIRCServer +from idletest import exec_test, BaseIRCServer, sync_stream from servicetest import ( EventPattern, call_async, sync_dbus, make_channel_proxy, assertEquals, assertSameSets, assertContains, @@ -89,6 +89,7 @@ def test(q, bus, conn, stream, use_room=False): cs.CHANNEL_IFACE_ROOM, cs.CHANNEL_IFACE_SUBJECT, cs.CHANNEL_IFACE_ROOM_CONFIG, + cs.CHANNEL_IFACE_DESTROYABLE, ], props[cs.INTERFACES]) assert props[cs.TARGET_HANDLE_TYPE] == cs.HT_ROOM assert props[cs.TARGET_ID] == '#idletest' @@ -123,6 +124,32 @@ def test(q, bus, conn, stream, use_room=False): assert chans[0] == (path, props) chan = make_channel_proxy(conn, path, 'Channel') + + # Make sure Close()ing the channel makes it respawn. This avoids the old + # bug where empathy-chat crashing booted you out of all your channels. + patterns = [EventPattern('stream-PART')] + q.forbid_events(patterns) + chan.Close() + q.expect('dbus-signal', signal='Closed', path=chan.object_path) + e = q.expect('dbus-signal', signal='NewChannels') + + path, props = e.args[0][0] + assertEquals(chan.object_path, path) + # We requested the channel originally, but we didn't request it popping + # back up. + assertEquals(0, props[cs.INITIATOR_HANDLE]) + assert not props[cs.REQUESTED] + + # Check that ensuring a respawned channel does what you'd expect. + ec_yours, ec_path, ec_props = conn.EnsureChannel(request, + dbus_interface=cs.CONN_IFACE_REQUESTS) + assert not ec_yours + assertEquals(chan.object_path, ec_path) + assertEquals(props, ec_props) + + sync_stream(q, stream) + q.unforbid_events(patterns) + chan.RemoveMembers([self_handle], "bye bye cruel\r\nworld", dbus_interface=cs.CHANNEL_IFACE_GROUP) |