From 51bd7ee5d5ca65fde89e52d508ccc49eef5eb515 Mon Sep 17 00:00:00 2001 From: Debarshi Ray Date: Fri, 28 Oct 2011 01:39:47 +0300 Subject: [PATCH] MUCChannel: Queue SetSubject calls and return asynchronously Fixes: https://bugs.freedesktop.org/40734 --- src/idle-muc-channel.c | 147 +++++++++++++++++++++++++++++++++++++++++++++-- 1 files changed, 140 insertions(+), 7 deletions(-) diff --git a/src/idle-muc-channel.c b/src/idle-muc-channel.c index d8c5e21..682d78d 100644 --- a/src/idle-muc-channel.c +++ b/src/idle-muc-channel.c @@ -3,6 +3,7 @@ * * Copyright (C) 2006-2007 Collabora Limited * Copyright (C) 2006-2007 Nokia Corporation + * Copyright (C) 2011 Debarshi Ray * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License @@ -128,6 +129,11 @@ typedef enum { } IdleMUCChannelTPProperty; typedef struct { + const gchar *subject; + DBusGMethodInvocation *context; +} SubjectSet; + +typedef struct { const gchar *name; GType type; } TPPropertySignature; @@ -180,6 +186,9 @@ static void _free_prop_info_struct(gpointer data, gpointer user_data) } +static IdleParserHandlerResult topic_handler(IdleParser *parser, + IdleParserMessageCode code, GValueArray *args, gpointer user_data); + static gboolean add_member(GObject *gobj, TpHandle handle, const gchar *message, GError **error); static gboolean remove_member(GObject *gobj, TpHandle handle, const gchar *message, GError **error); @@ -204,6 +213,8 @@ struct _IdleMUCChannelPrivate { DBusGMethodInvocation *passwd_ctx; + GQueue *subject_sets; + /* NAMEREPLY MembersChanged aggregation */ TpHandleSet *namereply_set; @@ -283,6 +294,10 @@ idle_muc_channel_constructed (GObject *obj) initiator, TP_CHANNEL_GROUP_CHANGE_REASON_NONE); tp_intset_destroy (remote); } + + priv->subject_sets = g_queue_new(); + idle_parser_add_handler (IDLE_CONNECTION (conn)->parser, IDLE_PARSER_PREFIXCMD_TOPIC, + topic_handler, self); } static void @@ -410,16 +425,27 @@ static void idle_muc_channel_class_init (IdleMUCChannelClass *idle_muc_channel_c void idle_muc_channel_dispose (GObject *object) { IdleMUCChannel *self = IDLE_MUC_CHANNEL (object); IdleMUCChannelPrivate *priv = self->priv; + IdleConnection *conn; if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; + conn = IDLE_CONNECTION (tp_base_channel_get_connection (TP_BASE_CHANNEL (self))); + idle_parser_remove_handlers_by_data (conn->parser, self); + if (G_OBJECT_CLASS (idle_muc_channel_parent_class)->dispose) G_OBJECT_CLASS (idle_muc_channel_parent_class)->dispose (object); } +static void +subject_sets_foreach_free (gpointer data, + gpointer user_data) +{ + g_slice_free (SubjectSet, data); +} + void idle_muc_channel_finalize (GObject *object) { IdleMUCChannel *self = IDLE_MUC_CHANNEL (object); IdleMUCChannelPrivate *priv = self->priv; @@ -433,6 +459,9 @@ void idle_muc_channel_finalize (GObject *object) { muc_channel_tp_properties_destroy(self); g_free(priv->properties); + g_queue_foreach (priv->subject_sets, subject_sets_foreach_free, NULL); + g_queue_free (priv->subject_sets); + if (priv->namereply_set) tp_handle_set_destroy(priv->namereply_set); @@ -2103,6 +2132,63 @@ static void idle_muc_channel_set_properties (TpSvcPropertiesInterface *iface, co } static void +send_set_subject (IdleMUCChannel *chan, + SubjectSet *set) +{ + IdleMUCChannelPrivate *priv = chan->priv; + gchar cmd[IRC_MSG_MAXLEN + 2]; + + g_snprintf (cmd, IRC_MSG_MAXLEN + 2, "TOPIC %s :%s", + priv->channel_name, set->subject); + send_command (chan, cmd); +} + +static void +dequeue_set_subject (IdleMUCChannel *chan) +{ + IdleMUCChannelPrivate *priv = chan->priv; + SubjectSet *set = g_queue_pop_head (priv->subject_sets); + + g_free ((gpointer) set->subject); + g_slice_free (SubjectSet, set); + + if (g_queue_is_empty (priv->subject_sets)) + return; + + set = g_queue_peek_head (priv->subject_sets); + send_set_subject (chan, set); +} + +static void +queue_set_subject (IdleMUCChannel *chan, + const gchar *subject, + DBusGMethodInvocation *context) +{ + IdleMUCChannelPrivate *priv = chan->priv; + SubjectSet *set; + + set = g_slice_new0 (SubjectSet); + set->subject = tp_str_empty (subject) ? NULL : g_strdup (subject); + set->context = context; + + if (g_queue_is_empty (priv->subject_sets)) + send_set_subject (chan, set); + + g_queue_push_tail (priv->subject_sets, set); +} + +static void +return_from_set_subject (IdleMUCChannel *chan) +{ + IdleMUCChannelPrivate *priv = chan->priv; + SubjectSet *set = g_queue_peek_head (priv->subject_sets); + + tp_svc_channel_interface_subject_return_from_set_subject ( + set->context); + dequeue_set_subject (chan); +} + +static void idle_muc_channel_set_subject ( TpSvcChannelInterfaceSubject *iface, const gchar *subject, @@ -2127,16 +2213,63 @@ idle_muc_channel_set_subject ( } else { - gchar cmd[IRC_MSG_MAXLEN + 2]; - - g_snprintf (cmd, IRC_MSG_MAXLEN + 2, "TOPIC %s :%s", priv->channel_name, - subject); - send_command (self, cmd); - /* FIXME: don't return till we get a reply */ - tp_svc_channel_interface_subject_return_from_set_subject (context); + queue_set_subject (self, subject, context); } } +static SubjectSet * +get_matching_set_subject (IdleMUCChannel *chan, + GValueArray *args) +{ + IdleMUCChannelPrivate *priv = chan->priv; + TpBaseConnection *conn; + TpHandle chan_handle; + TpHandle room_handle; + TpHandle self_handle; + TpHandle setter_handle; + SubjectSet *set; + const gchar *topic; + + if (g_queue_is_empty (priv->subject_sets)) + return NULL; + + conn = tp_base_channel_get_connection (TP_BASE_CHANNEL (chan)); + self_handle = tp_base_connection_get_self_handle (conn); + setter_handle = g_value_get_uint (g_value_array_get_nth (args, 0)); + if (self_handle != setter_handle) + return NULL; + + chan_handle = tp_base_channel_get_target_handle (TP_BASE_CHANNEL (chan)); + room_handle = g_value_get_uint (g_value_array_get_nth (args, 1)); + if (chan_handle != room_handle) + return NULL; + + topic = (args->n_values == 3) + ? g_value_get_string (g_value_array_get_nth (args, 2)) + : NULL; + set = g_queue_peek_head (priv->subject_sets); + if (tp_strdiff (set->subject, topic)) + return NULL; + + return set; +} + +static IdleParserHandlerResult +topic_handler (IdleParser *parser, + IdleParserMessageCode code, + GValueArray *args, + gpointer user_data) +{ + IdleMUCChannel *self = IDLE_MUC_CHANNEL (user_data); + SubjectSet *set = get_matching_set_subject (self, args); + + if (set == NULL) + return IDLE_PARSER_HANDLER_RESULT_NOT_HANDLED; + + return_from_set_subject (self); + return IDLE_PARSER_HANDLER_RESULT_NOT_HANDLED; +} + gboolean idle_muc_channel_is_modechar(char c) { switch (c) { /* founder */ -- 1.7.6.4