/* * This file is part of telepathy-idle * * Copyright (C) 2009 Collabora Limited * * 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 * * Authors: * Jonathon Jongsma */ #include "config.h" #include "idle-roomlist-manager.h" #include #define IDLE_DEBUG_FLAG IDLE_DEBUG_ROOMLIST #include "idle-connection.h" #include "idle-debug.h" #include "idle-roomlist-channel.h" #include "idle-parser.h" static void _roomlist_manager_iface_init (gpointer g_iface, gpointer iface_data); static GObject * _roomlist_manager_constructor (GType type, guint n_props, GObjectConstructParam *props); static void _roomlist_manager_dispose (GObject *object); G_DEFINE_TYPE_WITH_CODE(IdleRoomlistManager, idle_roomlist_manager, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE(TP_TYPE_CHANNEL_MANAGER, _roomlist_manager_iface_init)); /* properties */ enum { PROP_CONNECTION = 1, LAST_PROPERTY_ENUM }; static const gchar * const roomlist_channel_fixed_properties[] = { TP_IFACE_CHANNEL ".ChannelType", TP_IFACE_CHANNEL ".TargetHandleType", NULL }; static const gchar * const roomlist_channel_allowed_properties[] = { NULL }; struct _IdleRoomlistManagerPrivate { IdleConnection *conn; IdleRoomlistChannel *channel; int status_changed_id; gboolean dispose_has_run; }; static void _roomlist_manager_close_all (IdleRoomlistManager *self); static void connection_status_changed_cb (IdleConnection* conn, guint status, guint reason, IdleRoomlistManager *self); static void _roomlist_manager_foreach (TpChannelManager *self, TpExportableChannelFunc func, gpointer user_data); static void _roomlist_manager_foreach_class (TpChannelManager *self, TpChannelManagerChannelClassFunc func, gpointer user_data); static gboolean _roomlist_manager_create_channel (TpChannelManager *self, gpointer request_token, GHashTable *request_properties); static gboolean _roomlist_manager_request_channel (TpChannelManager *self, gpointer request_token, GHashTable *request_properties); static gboolean _roomlist_manager_ensure_channel (TpChannelManager *self, gpointer request_token, GHashTable *request_properties); static gboolean _roomlist_manager_requestotron (IdleRoomlistManager *self, gpointer request_token, GHashTable *request_properties, gboolean require_new); static IdleRoomlistChannel *_roomlist_manager_new_channel (IdleRoomlistManager *self, gpointer request); static void _roomlist_channel_closed_cb (IdleRoomlistChannel *chan, gpointer user_data); static void idle_roomlist_manager_init (IdleRoomlistManager *self) { IdleRoomlistManagerPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE(self, IDLE_TYPE_ROOMLIST_MANAGER, IdleRoomlistManagerPrivate); self->priv = priv; priv->channel = NULL; priv->status_changed_id = 0; priv->dispose_has_run = FALSE; } static void idle_roomlist_manager_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IdleRoomlistManager *self = IDLE_ROOMLIST_MANAGER (object); IdleRoomlistManagerPrivate *priv = self->priv; 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_roomlist_manager_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IdleRoomlistManager *self = IDLE_ROOMLIST_MANAGER (object); IdleRoomlistManagerPrivate *priv = self->priv; 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_roomlist_manager_class_init (IdleRoomlistManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GParamSpec *param_spec; g_type_class_add_private (klass, sizeof (IdleRoomlistManagerPrivate)); object_class->constructor = _roomlist_manager_constructor; object_class->dispose = _roomlist_manager_dispose; object_class->get_property = idle_roomlist_manager_get_property; object_class->set_property = idle_roomlist_manager_set_property; param_spec = g_param_spec_object ("connection", "IdleConnection object", "The IdleConnection object that owns this Roomlist 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 GObject * _roomlist_manager_constructor (GType type, guint n_props, GObjectConstructParam *props) { GObject *obj; IdleRoomlistManager *self; IdleRoomlistManagerPrivate *priv; obj = G_OBJECT_CLASS (idle_roomlist_manager_parent_class)->constructor (type, n_props, props); self = IDLE_ROOMLIST_MANAGER (obj); priv = self->priv; g_return_val_if_fail (priv->conn, obj); priv->status_changed_id = g_signal_connect (priv->conn, "status-changed", (GCallback) connection_status_changed_cb, self); return obj; } static void _roomlist_manager_close_all (IdleRoomlistManager *self) { IdleRoomlistManagerPrivate *priv = self->priv; IdleRoomlistChannel *tmp; if (priv->channel != NULL) { /* use a temporary variable and set priv->channel to NULL first * because unreffing this channel will cause the * _roomlist_channel_closed_cb to fire, which will try to unref it * again if priv->channel is not NULL */ tmp = priv->channel; priv->channel = NULL; g_object_unref(tmp); } if (priv->status_changed_id != 0) { g_signal_handler_disconnect (priv->conn, priv->status_changed_id); priv->status_changed_id = 0; } } static void connection_status_changed_cb (IdleConnection* conn, guint status, guint reason, IdleRoomlistManager *self) { if (status == TP_CONNECTION_STATUS_DISCONNECTED) { _roomlist_manager_close_all (self); } } static void _roomlist_manager_foreach (TpChannelManager *manager, TpExportableChannelFunc func, gpointer user_data) { IdleRoomlistManager *self = IDLE_ROOMLIST_MANAGER (manager); IdleRoomlistManagerPrivate *priv = self->priv; if (!priv->channel) { IDLE_DEBUG ("Channel missing, ignoring..."); return; } func (TP_EXPORTABLE_CHANNEL (priv->channel), user_data); } static void _roomlist_manager_foreach_class (TpChannelManager *self, TpChannelManagerChannelClassFunc func, gpointer user_data) { GHashTable *table = tp_asv_new ( roomlist_channel_fixed_properties[0], G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_ROOM_LIST, roomlist_channel_fixed_properties[1], G_TYPE_UINT, TP_HANDLE_TYPE_NONE, NULL); func (self, table, roomlist_channel_allowed_properties, user_data); g_hash_table_destroy (table); } static gboolean _roomlist_manager_create_channel (TpChannelManager *self, gpointer request_token, GHashTable *request_properties) { IdleRoomlistManager *mgr = IDLE_ROOMLIST_MANAGER (self); return _roomlist_manager_requestotron (mgr, request_token, request_properties, TRUE); } static gboolean _roomlist_manager_request_channel (TpChannelManager *self, gpointer request_token, GHashTable *request_properties) { IdleRoomlistManager *mgr = IDLE_ROOMLIST_MANAGER (self); return _roomlist_manager_requestotron (mgr, request_token, request_properties, FALSE); } static gboolean _roomlist_manager_ensure_channel (TpChannelManager *self, gpointer request_token, GHashTable *request_properties) { IdleRoomlistManager *mgr = IDLE_ROOMLIST_MANAGER (self); return _roomlist_manager_requestotron (mgr, request_token, request_properties, FALSE); } static gboolean _roomlist_manager_requestotron (IdleRoomlistManager *self, gpointer request_token, GHashTable *request_properties, gboolean require_new) { IdleRoomlistManagerPrivate *priv = self->priv; GError *error = NULL; IDLE_DEBUG("requesting new room list channel"); if (tp_strdiff (tp_asv_get_string (request_properties, TP_IFACE_CHANNEL ".ChannelType"), TP_IFACE_CHANNEL_TYPE_ROOM_LIST)) return FALSE; if (tp_asv_get_uint32 (request_properties, TP_IFACE_CHANNEL ".TargetHandleType", NULL) != 0) return FALSE; /* Check if there are any other properties that we don't understand */ if (tp_channel_manager_asv_has_unknown_properties (request_properties, roomlist_channel_fixed_properties, roomlist_channel_allowed_properties, &error)) { goto error; } if (priv->channel == NULL) { _roomlist_manager_new_channel (self, request_token); return TRUE; } if (require_new) { g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Only one room list channel can be created"); goto error; } tp_channel_manager_emit_request_already_satisfied (self, request_token, TP_EXPORTABLE_CHANNEL (priv->channel)); return TRUE; error: tp_channel_manager_emit_request_failed (self, request_token, error->domain, error->code, error->message); g_error_free (error); return TRUE; } static void _roomlist_channel_closed_cb (IdleRoomlistChannel *chan, gpointer user_data) { IdleRoomlistManager *self = IDLE_ROOMLIST_MANAGER (user_data); IdleRoomlistManagerPrivate *priv = self->priv; tp_channel_manager_emit_channel_closed_for_object (self, TP_EXPORTABLE_CHANNEL (chan)); if (priv->channel) { g_assert (priv->channel == chan); g_object_unref (priv->channel); priv->channel = NULL; } } static IdleRoomlistChannel * _roomlist_manager_new_channel (IdleRoomlistManager *self, gpointer request) { IdleRoomlistManagerPrivate *priv = self->priv; IdleRoomlistChannel *chan; GSList *requests = NULL; g_assert (priv->channel == NULL); IDLE_DEBUG ("Requested room list channel"); chan = g_object_new (IDLE_TYPE_ROOMLIST_CHANNEL, "connection", priv->conn, NULL); if (request != NULL) requests = g_slist_prepend (requests, request); tp_channel_manager_emit_new_channel (self, TP_EXPORTABLE_CHANNEL (chan), requests); g_slist_free (requests); g_signal_connect (chan, "closed", G_CALLBACK (_roomlist_channel_closed_cb), self); priv->channel = chan; return chan; } static void _roomlist_manager_iface_init (gpointer g_iface, gpointer iface_data) { TpChannelManagerIface *iface = g_iface; iface->foreach_channel = _roomlist_manager_foreach; iface->foreach_channel_class = _roomlist_manager_foreach_class; iface->request_channel = _roomlist_manager_request_channel; iface->create_channel = _roomlist_manager_create_channel; iface->ensure_channel = _roomlist_manager_ensure_channel; } static void _roomlist_manager_dispose (GObject *object) { IdleRoomlistManager *self = IDLE_ROOMLIST_MANAGER (object); IdleRoomlistManagerPrivate *priv = self->priv; if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; _roomlist_manager_close_all (self); if (G_OBJECT_CLASS (idle_roomlist_manager_parent_class)->dispose) G_OBJECT_CLASS (idle_roomlist_manager_parent_class)->dispose (object); }