summaryrefslogtreecommitdiff
path: root/src/idle-muc-manager.c
diff options
context:
space:
mode:
authorJonathon Jongsma <jjongsma@gnome.org>2009-01-05 13:40:58 -0600
committerJonathon Jongsma <jonathon.jongsma@collabora.co.uk>2009-01-28 10:01:55 -0600
commitc90f550bd7840179f78278cb6d36bc63670afed6 (patch)
tree340b684211084fdc290a79931f25049337bc2f42 /src/idle-muc-manager.c
parent027f8d666388ddcdb47e99ebed448a519074db04 (diff)
downloadtelepathy-idle-c90f550bd7840179f78278cb6d36bc63670afed6.tar.gz
First pass at porting IdleMUCFactory to IdleMUCManager
Diffstat (limited to 'src/idle-muc-manager.c')
-rw-r--r--src/idle-muc-manager.c827
1 files changed, 827 insertions, 0 deletions
diff --git a/src/idle-muc-manager.c b/src/idle-muc-manager.c
new file mode 100644
index 0000000..b73a49a
--- /dev/null
+++ b/src/idle-muc-manager.c
@@ -0,0 +1,827 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006-2007 Collabora Limited
+ * Copyright (C) 2006-2007 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "idle-muc-manager.h"
+
+#include <time.h>
+
+#include <telepathy-glib/channel-manager.h>
+#include <telepathy-glib/dbus.h>
+#include <telepathy-glib/interfaces.h>
+
+#define IDLE_DEBUG_FLAG IDLE_DEBUG_MUC
+#include "idle-connection.h"
+#include "idle-ctcp.h"
+#include "idle-debug.h"
+#include "idle-muc-channel.h"
+#include "idle-parser.h"
+#include "idle-text.h"
+
+static void _muc_manager_iface_init(gpointer, gpointer);
+static GObject* _muc_manager_constructor(GType type, guint n_props, GObjectConstructParam *props);
+
+G_DEFINE_TYPE_WITH_CODE(IdleMUCManager, idle_muc_manager, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE(TP_TYPE_CHANNEL_MANAGER, _muc_manager_iface_init));
+
+/* properties */
+enum {
+ PROP_CONNECTION = 1,
+ LAST_PROPERTY_ENUM
+};
+
+typedef struct _IdleMUCManagerPrivate IdleMUCManagerPrivate;
+struct _IdleMUCManagerPrivate {
+ IdleConnection *conn;
+ GHashTable *channels;
+ gulong status_changed_id;
+ gboolean dispose_has_run;
+};
+
+#define IDLE_MUC_MANAGER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), IDLE_TYPE_MUC_MANAGER, IdleMUCManagerPrivate))
+
+static IdleParserHandlerResult _numeric_error_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data);
+static IdleParserHandlerResult _numeric_namereply_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data);
+static IdleParserHandlerResult _numeric_namereply_end_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data);
+static IdleParserHandlerResult _numeric_topic_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data);
+static IdleParserHandlerResult _numeric_topic_stamp_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data);
+
+static IdleParserHandlerResult _invite_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data);
+static IdleParserHandlerResult _join_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data);
+static IdleParserHandlerResult _kick_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data);
+static IdleParserHandlerResult _mode_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data);
+static IdleParserHandlerResult _nick_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data);
+static IdleParserHandlerResult _notice_privmsg_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data);
+static IdleParserHandlerResult _part_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data);
+static IdleParserHandlerResult _quit_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data);
+static IdleParserHandlerResult _topic_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data);
+
+static void connection_status_changed_cb (IdleConnection *conn, guint status, guint reason, IdleMUCManager *self);
+static void _muc_manager_close_all(IdleMUCManager *manager);
+static void _muc_manager_add_handlers(IdleMUCManager *manager);
+
+static void _muc_manager_foreach_channel(TpChannelManager *manager, TpExportableChannelFunc func, gpointer user_data);
+static void _muc_manager_foreach_channel_class (TpChannelManager *manager, TpChannelManagerChannelClassFunc func, gpointer user_data);
+
+static gboolean _muc_manager_create_channel (TpChannelManager *manager, gpointer request_token, GHashTable *request_properties);
+static gboolean _muc_manager_request_channel (TpChannelManager *manager, gpointer request_token, GHashTable *request_properties);
+static gboolean _muc_manager_ensure_channel (TpChannelManager *manager, gpointer request_token, GHashTable *request_properties);
+static gboolean _muc_manager_request (IdleMUCManager *self, gpointer request_token, GHashTable *request_properties, gboolean require_new);
+
+static IdleMUCChannel *_muc_manager_new_channel(IdleMUCManager *manager, TpHandle handle, gpointer request_token);
+
+static void _channel_closed_cb(IdleMUCChannel *chan, gpointer user_data);
+static void _channel_join_ready_cb(IdleMUCChannel *chan, guint err, gpointer user_data);
+
+
+static const gchar * const muc_channel_fixed_properties[] = {
+ TP_IFACE_CHANNEL ".ChannelType",
+ TP_IFACE_CHANNEL ".TargetHandleType",
+ NULL
+};
+
+static const gchar * const muc_channel_allowed_properties[] = {
+ TP_IFACE_CHANNEL ".TargetHandle",
+ TP_IFACE_CHANNEL ".TargetID",
+ NULL
+};
+
+
+static GObject*
+_muc_manager_constructor(GType type, guint n_props, GObjectConstructParam *props)
+{
+ GObject *obj;
+ IdleMUCManagerPrivate *priv;
+
+ obj = G_OBJECT_CLASS (idle_muc_manager_parent_class)-> constructor (type,
+ n_props,
+ props);
+
+ priv = IDLE_MUC_MANAGER_GET_PRIVATE (obj);
+
+ priv->status_changed_id =
+ g_signal_connect (priv->conn, "status-changed",
+ (GCallback) connection_status_changed_cb, obj);
+
+ return obj;
+}
+
+static void idle_muc_manager_init(IdleMUCManager *obj) {
+ IdleMUCManagerPrivate *priv = IDLE_MUC_MANAGER_GET_PRIVATE(obj);
+
+ priv->channels = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_object_unref);
+}
+
+static void idle_muc_manager_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) {
+ IdleMUCManager *fac = IDLE_MUC_MANAGER(object);
+ IdleMUCManagerPrivate *priv = IDLE_MUC_MANAGER_GET_PRIVATE(fac);
+
+ switch (property_id) {
+ case PROP_CONNECTION:
+ g_value_set_object(value, priv->conn);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ break;
+ }
+}
+
+static void idle_muc_manager_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) {
+ IdleMUCManager *fac = IDLE_MUC_MANAGER(object);
+ IdleMUCManagerPrivate *priv = IDLE_MUC_MANAGER_GET_PRIVATE(fac);
+
+ switch (property_id) {
+ case PROP_CONNECTION:
+ priv->conn = g_value_get_object(value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ break;
+ }
+}
+
+static void idle_muc_manager_class_init(IdleMUCManagerClass *klass) {
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+ GParamSpec *param_spec;
+
+ g_type_class_add_private(klass, sizeof(IdleMUCManagerPrivate));
+
+ object_class->constructor = _muc_manager_constructor;
+ object_class->get_property = idle_muc_manager_get_property;
+ object_class->set_property = idle_muc_manager_set_property;
+
+ param_spec = g_param_spec_object("connection", "IdleConnection object", "The IdleConnection object that owns this IM channel manager object.", IDLE_TYPE_CONNECTION, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
+ g_object_class_install_property(object_class, PROP_CONNECTION, param_spec);
+}
+
+static IdleParserHandlerResult _numeric_error_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data) {
+ IdleMUCManagerPrivate *priv = IDLE_MUC_MANAGER_GET_PRIVATE(user_data);
+ TpHandle room_handle = g_value_get_uint(g_value_array_get_nth(args, 0));
+
+ if (!priv->channels) {
+ IDLE_DEBUG("Channels hash table missing, ignoring...");
+ return IDLE_PARSER_HANDLER_RESULT_NOT_HANDLED;
+ }
+
+ IdleMUCChannel *chan = g_hash_table_lookup(priv->channels, GUINT_TO_POINTER(room_handle));
+
+ if (!chan)
+ return IDLE_PARSER_HANDLER_RESULT_HANDLED;
+
+ switch (code) {
+ case IDLE_PARSER_NUMERIC_BADCHANNELKEY:
+ idle_muc_channel_badchannelkey(chan);
+ break;
+
+ case IDLE_PARSER_NUMERIC_BANNEDFROMCHAN:
+ idle_muc_channel_join_error(chan, MUC_CHANNEL_JOIN_ERROR_BANNED);
+ break;
+
+ case IDLE_PARSER_NUMERIC_CHANNELISFULL:
+ idle_muc_channel_join_error(chan, MUC_CHANNEL_JOIN_ERROR_FULL);
+ break;
+
+ case IDLE_PARSER_NUMERIC_INVITEONLYCHAN:
+ idle_muc_channel_join_error(chan, MUC_CHANNEL_JOIN_ERROR_INVITE_ONLY);
+ break;
+
+ default:
+ g_assert_not_reached();
+ break;
+ }
+
+ return IDLE_PARSER_HANDLER_RESULT_HANDLED;
+}
+
+static IdleParserHandlerResult _numeric_topic_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data) {
+ IdleMUCManagerPrivate *priv = IDLE_MUC_MANAGER_GET_PRIVATE(user_data);
+ TpHandle room_handle = g_value_get_uint(g_value_array_get_nth(args, 0));
+ const gchar *topic = g_value_get_string(g_value_array_get_nth(args, 1));
+
+ if (!priv->channels) {
+ IDLE_DEBUG("Channels hash table missing, ignoring...");
+ return IDLE_PARSER_HANDLER_RESULT_NOT_HANDLED;
+ }
+
+ IdleMUCChannel *chan = g_hash_table_lookup(priv->channels, GUINT_TO_POINTER(room_handle));
+
+ if (chan)
+ idle_muc_channel_topic(chan, topic);
+
+ return IDLE_PARSER_HANDLER_RESULT_HANDLED;
+}
+
+static IdleParserHandlerResult _numeric_topic_stamp_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data) {
+ IdleMUCManagerPrivate *priv = IDLE_MUC_MANAGER_GET_PRIVATE(user_data);
+ TpHandle room_handle = g_value_get_uint(g_value_array_get_nth(args, 0));
+ TpHandle toucher_handle = g_value_get_uint(g_value_array_get_nth(args, 1));
+ time_t touched = g_value_get_uint(g_value_array_get_nth(args, 2));
+
+ if (!priv->channels) {
+ IDLE_DEBUG("Channels hash table missing, ignoring...");
+ return IDLE_PARSER_HANDLER_RESULT_NOT_HANDLED;
+ }
+
+ IdleMUCChannel *chan = g_hash_table_lookup(priv->channels, GUINT_TO_POINTER(room_handle));
+
+ idle_connection_emit_queued_aliases_changed(priv->conn);
+
+ if (chan)
+ idle_muc_channel_topic_touch(chan, toucher_handle, touched);
+
+ return IDLE_PARSER_HANDLER_RESULT_HANDLED;
+}
+
+static IdleParserHandlerResult _invite_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data) {
+ IdleMUCManager *manager = IDLE_MUC_MANAGER(user_data);
+ IdleMUCManagerPrivate *priv = IDLE_MUC_MANAGER_GET_PRIVATE(manager);
+ TpHandle inviter_handle = g_value_get_uint(g_value_array_get_nth(args, 0));
+ TpHandle invited_handle = g_value_get_uint(g_value_array_get_nth(args, 1));
+ TpHandle room_handle = g_value_get_uint(g_value_array_get_nth(args, 2));
+
+ if (invited_handle != priv->conn->parent.self_handle)
+ return IDLE_PARSER_HANDLER_RESULT_HANDLED;
+
+ if (!priv->channels) {
+ IDLE_DEBUG("Channels hash table missing, ignoring...");
+ return IDLE_PARSER_HANDLER_RESULT_NOT_HANDLED;
+ }
+
+ IdleMUCChannel *chan = g_hash_table_lookup(priv->channels, GUINT_TO_POINTER(room_handle));
+
+ idle_connection_emit_queued_aliases_changed(priv->conn);
+
+ if (!chan) {
+ chan = _muc_manager_new_channel(manager, room_handle, NULL);
+ tp_channel_manager_emit_new_channel(TP_CHANNEL_MANAGER(user_data), (TpExportableChannel *) chan, NULL);
+ idle_muc_channel_invited(chan, inviter_handle);
+ }
+
+ return IDLE_PARSER_HANDLER_RESULT_HANDLED;
+}
+
+static IdleParserHandlerResult _join_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data) {
+ IdleMUCManager *manager = IDLE_MUC_MANAGER(user_data);
+ IdleMUCManagerPrivate *priv = IDLE_MUC_MANAGER_GET_PRIVATE(manager);
+ TpHandle joiner_handle = g_value_get_uint(g_value_array_get_nth(args, 0));
+ TpHandle room_handle = g_value_get_uint(g_value_array_get_nth(args, 1));
+
+ idle_connection_emit_queued_aliases_changed(priv->conn);
+
+ if (!priv->channels) {
+ IDLE_DEBUG("Channels hash table missing, ignoring...");
+ return IDLE_PARSER_HANDLER_RESULT_NOT_HANDLED;
+ }
+
+ IdleMUCChannel *chan = g_hash_table_lookup(priv->channels, GUINT_TO_POINTER(room_handle));
+
+ if (!chan) {
+ /* XXX: emit request_already_satisified? use request_channel API? */
+ chan = _muc_manager_new_channel(manager, room_handle, NULL);
+ }
+
+ idle_muc_channel_join(chan, joiner_handle);
+
+ return IDLE_PARSER_HANDLER_RESULT_HANDLED;
+}
+
+static IdleParserHandlerResult _kick_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data) {
+ IdleMUCManagerPrivate *priv = IDLE_MUC_MANAGER_GET_PRIVATE(user_data);
+ TpHandle kicker_handle = g_value_get_uint(g_value_array_get_nth(args, 0));
+ TpHandle room_handle = g_value_get_uint(g_value_array_get_nth(args, 1));
+ TpHandle kicked_handle = g_value_get_uint(g_value_array_get_nth(args, 2));
+ const gchar *message = (args->n_values == 4) ? g_value_get_string(g_value_array_get_nth(args, 3)) : NULL;
+
+ if (!priv->channels) {
+ IDLE_DEBUG("Channels hash table missing, ignoring...");
+ return IDLE_PARSER_HANDLER_RESULT_NOT_HANDLED;
+ }
+
+ IdleMUCChannel *chan = g_hash_table_lookup(priv->channels, GUINT_TO_POINTER(room_handle));
+
+ if (chan)
+ idle_muc_channel_kick(chan, kicked_handle, kicker_handle, message);
+
+ return IDLE_PARSER_HANDLER_RESULT_HANDLED;
+}
+
+static IdleParserHandlerResult _numeric_namereply_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data) {
+ IdleMUCManagerPrivate *priv = IDLE_MUC_MANAGER_GET_PRIVATE(user_data);
+ TpHandle room_handle = g_value_get_uint(g_value_array_get_nth(args, 0));
+
+ if (!priv->channels) {
+ IDLE_DEBUG("Channels hash table missing, ignoring...");
+ return IDLE_PARSER_HANDLER_RESULT_NOT_HANDLED;
+ }
+
+ IdleMUCChannel *chan = g_hash_table_lookup(priv->channels, GUINT_TO_POINTER(room_handle));
+
+ if (chan)
+ idle_muc_channel_namereply(chan, args);
+
+ return IDLE_PARSER_HANDLER_RESULT_HANDLED;
+}
+
+static IdleParserHandlerResult _numeric_namereply_end_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data) {
+ IdleMUCManagerPrivate *priv = IDLE_MUC_MANAGER_GET_PRIVATE(user_data);
+ TpHandle room_handle = g_value_get_uint(g_value_array_get_nth(args, 0));
+
+ if (!priv->channels) {
+ IDLE_DEBUG("Channels hash table missing, ignoring...");
+ return IDLE_PARSER_HANDLER_RESULT_NOT_HANDLED;
+ }
+
+ IdleMUCChannel *chan = g_hash_table_lookup(priv->channels, GUINT_TO_POINTER(room_handle));
+
+ if (chan)
+ idle_muc_channel_namereply_end(chan);
+
+ return IDLE_PARSER_HANDLER_RESULT_HANDLED;
+}
+
+static IdleParserHandlerResult _mode_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data) {
+ IdleMUCManagerPrivate *priv = IDLE_MUC_MANAGER_GET_PRIVATE(user_data);
+ TpHandle room_handle = g_value_get_uint(g_value_array_get_nth(args, 0));
+
+ if (!priv->channels) {
+ IDLE_DEBUG("Channels hash table missing, ignoring...");
+ return IDLE_PARSER_HANDLER_RESULT_NOT_HANDLED;
+ }
+
+ IdleMUCChannel *chan = g_hash_table_lookup(priv->channels, GUINT_TO_POINTER(room_handle));
+
+ if (chan)
+ idle_muc_channel_mode(chan, args);
+
+ return IDLE_PARSER_HANDLER_RESULT_HANDLED;
+}
+
+typedef struct _ChannelRenameForeachData ChannelRenameForeachData;
+struct _ChannelRenameForeachData {
+ TpHandle old_handle, new_handle;
+};
+
+static void _channel_rename_foreach(TpExportableChannel *channel, gpointer user_data) {
+ IdleMUCChannel *muc_chan = IDLE_MUC_CHANNEL(channel);
+ ChannelRenameForeachData *data = user_data;
+
+ idle_muc_channel_rename(muc_chan, data->old_handle, data->new_handle);
+}
+
+static IdleParserHandlerResult _nick_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data) {
+ TpChannelManager *mgr = TP_CHANNEL_MANAGER(user_data);
+ TpHandle old_handle = g_value_get_uint(g_value_array_get_nth(args, 0));
+ TpHandle new_handle = g_value_get_uint(g_value_array_get_nth(args, 1));
+ ChannelRenameForeachData data = {old_handle, new_handle};
+
+ if (old_handle == new_handle)
+ return IDLE_PARSER_HANDLER_RESULT_NOT_HANDLED;
+
+ tp_channel_manager_foreach_channel(mgr, _channel_rename_foreach, &data);
+
+ return IDLE_PARSER_HANDLER_RESULT_NOT_HANDLED;
+}
+
+static IdleParserHandlerResult _notice_privmsg_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data) {
+ IdleMUCManager *manager = IDLE_MUC_MANAGER(user_data);
+ IdleMUCManagerPrivate *priv = IDLE_MUC_MANAGER_GET_PRIVATE(manager);
+ TpHandle sender_handle = (TpHandle) g_value_get_uint(g_value_array_get_nth(args, 0));
+ TpHandle room_handle = (TpHandle) g_value_get_uint(g_value_array_get_nth(args, 1));
+
+ if (!priv->channels) {
+ IDLE_DEBUG("Channels hash table missing, ignoring...");
+ return IDLE_PARSER_HANDLER_RESULT_NOT_HANDLED;
+ }
+
+ IdleMUCChannel *chan = g_hash_table_lookup(priv->channels, GUINT_TO_POINTER(room_handle));
+ /* XXX: just check for chan == NULL here and bail with NOT_HANDLED if room
+ * was not found ? Currently we go through all of the decoding of the
+ * message, but don't actually deliver the message to a channel if chan is
+ * NULL, and then we return 'HANDLED', which seems wrong
+ */
+
+ TpChannelTextMessageType type;
+ gchar *body;
+
+ if (code == IDLE_PARSER_PREFIXCMD_NOTICE_CHANNEL) {
+ type = TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE;
+ body = idle_ctcp_kill_blingbling(g_value_get_string(g_value_array_get_nth(args, 2)));
+ } else {
+ gboolean decoded = idle_text_decode(g_value_get_string(g_value_array_get_nth(args, 2)), &type, &body);
+ if (!decoded)
+ return IDLE_PARSER_HANDLER_RESULT_NOT_HANDLED;
+ }
+
+ if (chan)
+ idle_muc_channel_receive(chan, type, sender_handle, body);
+
+ g_free(body);
+
+ return IDLE_PARSER_HANDLER_RESULT_HANDLED;
+}
+
+
+static IdleParserHandlerResult _part_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data) {
+ IdleMUCManagerPrivate *priv = IDLE_MUC_MANAGER_GET_PRIVATE(user_data);
+ TpHandle leaver_handle = g_value_get_uint(g_value_array_get_nth(args, 0));
+ TpHandle room_handle = g_value_get_uint(g_value_array_get_nth(args, 1));
+ const gchar *message = (args->n_values == 3) ? g_value_get_string(g_value_array_get_nth(args, 2)) : NULL;
+
+ if (!priv->channels) {
+ IDLE_DEBUG("Channels hash table missing, ignoring...");
+ return IDLE_PARSER_HANDLER_RESULT_NOT_HANDLED;
+ }
+
+ IdleMUCChannel *chan = g_hash_table_lookup(priv->channels, GUINT_TO_POINTER(room_handle));
+
+ if (chan)
+ idle_muc_channel_part(chan, leaver_handle, message);
+
+ return IDLE_PARSER_HANDLER_RESULT_HANDLED;
+}
+
+typedef struct _ChannelQuitForeachData ChannelQuitForeachData;
+struct _ChannelQuitForeachData {
+ TpHandle handle;
+ const gchar *message;
+};
+
+static void _channel_quit_foreach(TpExportableChannel *channel, gpointer user_data) {
+ IdleMUCChannel *muc_chan = IDLE_MUC_CHANNEL(channel);
+ ChannelQuitForeachData *data = user_data;
+
+ idle_muc_channel_quit(muc_chan, data->handle, data->message);
+}
+
+static IdleParserHandlerResult _quit_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data) {
+ TpChannelManager *manager = TP_CHANNEL_MANAGER(user_data);
+ TpHandle leaver_handle = g_value_get_uint(g_value_array_get_nth(args, 0));
+ const gchar *message = (args->n_values == 2) ? g_value_get_string(g_value_array_get_nth(args, 1)) : NULL;
+ ChannelQuitForeachData data = {leaver_handle, message};
+
+ tp_channel_manager_foreach_channel(manager, _channel_quit_foreach, &data);
+
+ return IDLE_PARSER_HANDLER_RESULT_NOT_HANDLED;
+}
+
+static IdleParserHandlerResult _topic_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data) {
+ IdleMUCManagerPrivate *priv = IDLE_MUC_MANAGER_GET_PRIVATE(user_data);
+ TpHandle setter_handle = g_value_get_uint(g_value_array_get_nth(args, 0));
+ TpHandle room_handle = g_value_get_uint(g_value_array_get_nth(args, 1));
+ const gchar *topic = (args->n_values == 3) ? g_value_get_string(g_value_array_get_nth(args, 2)) : NULL;
+ time_t stamp = time(NULL);
+
+ if (!priv->channels) {
+ IDLE_DEBUG("Channels hash table missing, ignoring...");
+ return IDLE_PARSER_HANDLER_RESULT_NOT_HANDLED;
+ }
+
+ IdleMUCChannel *chan = g_hash_table_lookup(priv->channels, GUINT_TO_POINTER(room_handle));
+
+ if (chan) {
+ if (topic)
+ idle_muc_channel_topic_full(chan, setter_handle, stamp, topic);
+ else
+ idle_muc_channel_topic_unset(chan);
+ }
+
+ return IDLE_PARSER_HANDLER_RESULT_HANDLED;
+}
+
+static void _muc_manager_close_all(IdleMUCManager *manager)
+{
+ IdleMUCManagerPrivate *priv = IDLE_MUC_MANAGER_GET_PRIVATE(manager);
+
+ if (priv->status_changed_id != 0) {
+ g_signal_handler_disconnect (priv->conn, priv->status_changed_id);
+ priv->status_changed_id = 0;
+ }
+
+ if (!priv->channels) {
+ IDLE_DEBUG("Channels already closed, ignoring...");
+ return;
+ }
+
+ GHashTable *tmp = priv->channels;
+ priv->channels = NULL;
+ g_hash_table_destroy(tmp);
+}
+
+static void
+connection_status_changed_cb (IdleConnection *conn,
+ guint status,
+ guint reason,
+ IdleMUCManager *self)
+{
+ IdleMUCManagerPrivate *priv = IDLE_MUC_MANAGER_GET_PRIVATE(self);
+ switch (status)
+ {
+ case TP_CONNECTION_STATUS_CONNECTED:
+ _muc_manager_add_handlers(self);
+ break;
+ case TP_CONNECTION_STATUS_DISCONNECTED:
+ idle_parser_remove_handlers_by_data(priv->conn->parser, self);
+ _muc_manager_close_all(self);
+ break;
+ }
+}
+
+
+static void _muc_manager_add_handlers(IdleMUCManager *manager)
+{
+ IdleMUCManagerPrivate *priv = IDLE_MUC_MANAGER_GET_PRIVATE(manager);
+
+ idle_parser_add_handler(priv->conn->parser, IDLE_PARSER_NUMERIC_BADCHANNELKEY, _numeric_error_handler, manager);
+ idle_parser_add_handler(priv->conn->parser, IDLE_PARSER_NUMERIC_BANNEDFROMCHAN, _numeric_error_handler, manager);
+ idle_parser_add_handler(priv->conn->parser, IDLE_PARSER_NUMERIC_CHANNELISFULL, _numeric_error_handler, manager);
+ idle_parser_add_handler(priv->conn->parser, IDLE_PARSER_NUMERIC_INVITEONLYCHAN, _numeric_error_handler, manager);
+ idle_parser_add_handler(priv->conn->parser, IDLE_PARSER_NUMERIC_MODEREPLY, _mode_handler, manager);
+ idle_parser_add_handler(priv->conn->parser, IDLE_PARSER_NUMERIC_NAMEREPLY, _numeric_namereply_handler, manager);
+ idle_parser_add_handler(priv->conn->parser, IDLE_PARSER_NUMERIC_NAMEREPLY_END, _numeric_namereply_end_handler, manager);
+ idle_parser_add_handler(priv->conn->parser, IDLE_PARSER_NUMERIC_TOPIC, _numeric_topic_handler, manager);
+ idle_parser_add_handler(priv->conn->parser, IDLE_PARSER_NUMERIC_TOPIC_STAMP, _numeric_topic_stamp_handler, manager);
+
+ idle_parser_add_handler(priv->conn->parser, IDLE_PARSER_PREFIXCMD_INVITE, _invite_handler, manager);
+ idle_parser_add_handler(priv->conn->parser, IDLE_PARSER_PREFIXCMD_JOIN, _join_handler, manager);
+ idle_parser_add_handler(priv->conn->parser, IDLE_PARSER_PREFIXCMD_KICK, _kick_handler, manager);
+ idle_parser_add_handler(priv->conn->parser, IDLE_PARSER_PREFIXCMD_MODE_CHANNEL, _mode_handler, manager);
+ idle_parser_add_handler(priv->conn->parser, IDLE_PARSER_PREFIXCMD_NICK, _nick_handler, manager);
+ idle_parser_add_handler(priv->conn->parser, IDLE_PARSER_PREFIXCMD_NOTICE_CHANNEL, _notice_privmsg_handler, manager);
+ idle_parser_add_handler(priv->conn->parser, IDLE_PARSER_PREFIXCMD_PRIVMSG_CHANNEL, _notice_privmsg_handler, manager);
+ idle_parser_add_handler(priv->conn->parser, IDLE_PARSER_PREFIXCMD_PART, _part_handler, manager);
+ idle_parser_add_handler(priv->conn->parser, IDLE_PARSER_PREFIXCMD_QUIT, _quit_handler, manager);
+ idle_parser_add_handler(priv->conn->parser, IDLE_PARSER_PREFIXCMD_TOPIC, _topic_handler, manager);
+}
+
+struct _ForeachHelperData {
+ TpExportableChannelFunc func;
+ gpointer user_data;
+};
+
+static void _foreach_helper(gpointer key, gpointer value, gpointer user_data) {
+ struct _ForeachHelperData *data = user_data;
+ data->func(value, data->user_data);
+}
+
+static void _muc_manager_foreach_channel(TpChannelManager *iface, TpExportableChannelFunc func, gpointer user_data) {
+ IdleMUCManagerPrivate *priv = IDLE_MUC_MANAGER_GET_PRIVATE(iface);
+ struct _ForeachHelperData data = {func, user_data};
+
+ if (!priv->channels) {
+ IDLE_DEBUG("Channels hash table missing, ignoring...");
+ return;
+ }
+
+ g_hash_table_foreach(priv->channels, _foreach_helper, &data);
+}
+
+static void
+_muc_manager_foreach_channel_class (TpChannelManager *manager,
+ TpChannelManagerChannelClassFunc func,
+ gpointer user_data)
+{
+ GHashTable *table = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL, (GDestroyNotify) tp_g_value_slice_free);
+ GValue *channel_type_value, *handle_type_value;
+
+ channel_type_value = tp_g_value_slice_new (G_TYPE_STRING);
+ g_value_set_static_string (channel_type_value, TP_IFACE_CHANNEL_TYPE_TEXT);
+ g_hash_table_insert (table, TP_IFACE_CHANNEL ".ChannelType",
+ channel_type_value);
+
+ handle_type_value = tp_g_value_slice_new (G_TYPE_UINT);
+ g_value_set_uint (handle_type_value, TP_HANDLE_TYPE_ROOM);
+ g_hash_table_insert (table, TP_IFACE_CHANNEL ".TargetHandleType",
+ handle_type_value);
+
+ func (manager, table, muc_channel_allowed_properties,
+ user_data);
+
+ g_hash_table_destroy (table);
+}
+
+
+static IdleMUCChannel *_muc_manager_new_channel(IdleMUCManager *manager, TpHandle handle, gpointer request_token)
+{
+ IdleMUCManagerPrivate *priv = IDLE_MUC_MANAGER_GET_PRIVATE(manager);
+ IdleMUCChannel *chan;
+ gchar *object_path;
+
+ object_path = g_strdup_printf("%s/MucChannel%u", priv->conn->parent.object_path, handle);
+ chan = g_object_new(IDLE_TYPE_MUC_CHANNEL, "connection", priv->conn, "object-path", object_path, "handle", handle, NULL);
+
+ g_signal_connect(chan, "closed", (GCallback) _channel_closed_cb, manager);
+ g_signal_connect(chan, "join-ready", (GCallback) _channel_join_ready_cb, manager);
+
+ g_hash_table_insert(priv->channels, GUINT_TO_POINTER(handle), chan);
+
+ g_free(object_path);
+
+ GSList* tokens = NULL;
+ if (request_token != NULL)
+ tokens = g_slist_prepend (tokens, request_token);
+
+ tp_channel_manager_emit_new_channel (manager, TP_EXPORTABLE_CHANNEL (chan),
+ tokens);
+
+ g_slist_free (tokens);
+
+ return chan;
+}
+
+static void _channel_closed_cb(IdleMUCChannel *chan, gpointer user_data) {
+ IdleMUCManagerPrivate *priv = IDLE_MUC_MANAGER_GET_PRIVATE(user_data);
+
+ if (priv->channels) {
+ TpHandle handle;
+ g_object_get(chan, "handle", &handle, NULL);
+ g_hash_table_remove(priv->channels, GUINT_TO_POINTER(handle));
+ }
+}
+
+static void _channel_join_ready_cb(IdleMUCChannel *chan, guint err, gpointer user_data) {
+ TpChannelManager *manager = TP_CHANNEL_MANAGER(user_data);
+ IdleMUCManagerPrivate *priv = IDLE_MUC_MANAGER_GET_PRIVATE(user_data);
+
+ if (err == MUC_CHANNEL_JOIN_ERROR_NONE) {
+ tp_channel_manager_emit_new_channel(manager, (TpExportableChannel *) chan, NULL);
+ return;
+ }
+
+ gint err_code = 0;
+ const gchar* err_msg = NULL;
+ TpHandle handle;
+
+ g_object_get(chan, "handle", &handle, NULL);
+
+ switch (err) {
+ case MUC_CHANNEL_JOIN_ERROR_BANNED:
+ err_code = TP_ERROR_CHANNEL_BANNED;
+ err_msg = "You are banned from the channel.";
+ break;
+
+ case MUC_CHANNEL_JOIN_ERROR_FULL:
+ err_code = TP_ERROR_CHANNEL_FULL;
+ err_msg = "The channel is full.";
+ break;
+
+ case MUC_CHANNEL_JOIN_ERROR_INVITE_ONLY:
+ err_code = TP_ERROR_CHANNEL_INVITE_ONLY;
+ err_msg = "The channel is invite only.";
+ break;
+
+ default:
+ g_assert_not_reached();
+ break;
+ }
+
+ /* XXX: old: tp_channel_factory_iface_emit_channel_error(). is this a
+ * suitable replacement?
+ * FIXME: eventually we need to hook up the request_token so that this error
+ * is associated with the proper request
+ */
+ tp_channel_manager_emit_request_failed(manager, NULL, TP_ERRORS, err_code, err_msg);
+
+ if (priv->channels)
+ g_hash_table_remove(priv->channels, GUINT_TO_POINTER(handle));
+}
+
+
+static gboolean
+_muc_manager_create_channel (TpChannelManager *manager,
+ gpointer request_token,
+ GHashTable *request_properties)
+{
+ IdleMUCManager *self = IDLE_MUC_MANAGER (manager);
+
+ return _muc_manager_request (self, request_token, request_properties,
+ TRUE);
+}
+
+
+static gboolean
+_muc_manager_request_channel (TpChannelManager *manager,
+ gpointer request_token,
+ GHashTable *request_properties)
+{
+ IdleMUCManager *self = IDLE_MUC_MANAGER (manager);
+
+ return _muc_manager_request (self, request_token, request_properties,
+ FALSE);
+}
+
+
+static gboolean
+_muc_manager_ensure_channel (TpChannelManager *manager,
+ gpointer request_token,
+ GHashTable *request_properties)
+{
+ IdleMUCManager *self = IDLE_MUC_MANAGER (manager);
+
+ return _muc_manager_request (self, request_token, request_properties,
+ FALSE);
+}
+
+static gboolean
+_muc_manager_request (IdleMUCManager *self,
+ gpointer request_token,
+ GHashTable *request_properties,
+ gboolean require_new)
+{
+ IdleMUCManagerPrivate *priv = IDLE_MUC_MANAGER_GET_PRIVATE (self);
+ TpBaseConnection *base_conn = (TpBaseConnection *) priv->conn;
+ TpHandleRepoIface *room_repo = tp_base_connection_get_handles (base_conn,
+ TP_HANDLE_TYPE_ROOM);
+ GError *error = NULL;
+ TpHandle handle;
+ const gchar *channel_type;
+ IdleMUCChannel *channel;
+
+ if (tp_asv_get_uint32 (request_properties,
+ TP_IFACE_CHANNEL ".TargetHandleType", NULL) != TP_HANDLE_TYPE_ROOM)
+ return FALSE;
+
+ handle = tp_asv_get_uint32 (request_properties,
+ TP_IFACE_CHANNEL ".TargetHandle", NULL);
+
+ if (!tp_handle_is_valid (room_repo, handle, &error))
+ goto error;
+
+ channel_type = tp_asv_get_string (request_properties,
+ TP_IFACE_CHANNEL ".ChannelType");
+
+ if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TEXT))
+ {
+ if (tp_channel_manager_asv_has_unknown_properties (request_properties,
+ muc_channel_fixed_properties, muc_channel_allowed_properties,
+ &error))
+ goto error;
+
+ channel = g_hash_table_lookup (priv->channels,
+ GINT_TO_POINTER (handle));
+
+ if (channel != NULL)
+ {
+ if (require_new)
+ {
+ g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
+ "That channel has already been created (or requested)");
+ goto error;
+ }
+ else
+ {
+ tp_channel_manager_emit_request_already_satisfied (self,
+ request_token,
+ TP_EXPORTABLE_CHANNEL (channel));
+ }
+ }
+ else
+ {
+ channel = _muc_manager_new_channel (self, handle,
+ request_token);
+ }
+ idle_muc_channel_join_attempt(channel);
+
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+
+error:
+ tp_channel_manager_emit_request_failed (self, request_token,
+ error->domain, error->code, error->message);
+ g_error_free (error);
+ return TRUE;
+}
+
+
+static void _muc_manager_iface_init(gpointer g_iface, gpointer iface_data) {
+ TpChannelManagerIface *iface = g_iface;
+
+ iface->foreach_channel = _muc_manager_foreach_channel;
+ iface->foreach_channel_class = _muc_manager_foreach_channel_class;
+ iface->request_channel = _muc_manager_request_channel;
+ iface->create_channel = _muc_manager_create_channel;
+ iface->ensure_channel = _muc_manager_ensure_channel;
+}
+