/* * 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-channel.h" #include #include #include #define IDLE_DEBUG_FLAG IDLE_DEBUG_ROOMLIST #include "idle-connection.h" #include "idle-debug.h" #include "idle-text.h" static void idle_roomlist_channel_close (TpBaseChannel *channel); static void _roomlist_iface_init (gpointer, gpointer); static void connection_status_changed_cb (IdleConnection* conn, guint status, guint reason, IdleRoomlistChannel *self); static IdleParserHandlerResult _rpl_list_handler (IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data); static IdleParserHandlerResult _rpl_listend_handler (IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data); G_DEFINE_TYPE_WITH_CODE (IdleRoomlistChannel, idle_roomlist_channel, TP_TYPE_BASE_CHANNEL, G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_ROOM_LIST, _roomlist_iface_init); ) /* private structure */ struct _IdleRoomlistChannelPrivate { IdleConnection *connection; GPtrArray *rooms; TpHandleSet *handles; gboolean listing; gboolean closed; int status_changed_id; gboolean dispose_has_run; }; static void idle_roomlist_channel_init (IdleRoomlistChannel *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, IDLE_TYPE_ROOMLIST_CHANNEL, IdleRoomlistChannelPrivate); } static void idle_roomlist_channel_dispose (GObject *object); static void idle_roomlist_channel_finalize (GObject *object); static void idle_roomlist_channel_constructed (GObject *obj) { IdleRoomlistChannel *self = IDLE_ROOMLIST_CHANNEL (obj); IdleRoomlistChannelPrivate *priv = self->priv; TpBaseChannel *base = TP_BASE_CHANNEL (obj); G_OBJECT_CLASS (idle_roomlist_channel_parent_class)->constructed (obj); tp_base_channel_register (base); priv->connection = IDLE_CONNECTION (tp_base_channel_get_connection (TP_BASE_CHANNEL (obj))); priv->status_changed_id = g_signal_connect (priv->connection, "status-changed", (GCallback) connection_status_changed_cb, obj); idle_parser_add_handler (priv->connection->parser, IDLE_PARSER_NUMERIC_LIST, _rpl_list_handler, obj); idle_parser_add_handler (priv->connection->parser, IDLE_PARSER_NUMERIC_LISTEND, _rpl_listend_handler, obj); priv->rooms = g_ptr_array_new (); priv->handles = tp_handle_set_new(tp_base_connection_get_handles ( TP_BASE_CONNECTION(priv->connection), TP_HANDLE_TYPE_ROOM)); } static gchar * idle_roomlist_channel_get_path_suffix (TpBaseChannel *chan) { return g_strdup ("RoomListChannel"); } static void idle_roomlist_channel_get_roomlist_property ( GObject *object, GQuark iface, GQuark name, GValue *value, gpointer getter_data) { g_return_if_fail (iface == TP_IFACE_QUARK_CHANNEL_TYPE_ROOM_LIST); g_return_if_fail (name == g_quark_from_static_string ("Server")); g_return_if_fail (G_VALUE_HOLDS_STRING (value)); g_value_set_static_string (value, ""); } static void idle_roomlist_channel_fill_properties ( TpBaseChannel *chan, GHashTable *properties) { TpBaseChannelClass *klass = TP_BASE_CHANNEL_CLASS (idle_roomlist_channel_parent_class); klass->fill_immutable_properties (chan, properties); tp_dbus_properties_mixin_fill_properties_hash ( G_OBJECT (chan), properties, TP_IFACE_CHANNEL_TYPE_ROOM_LIST, "Server", NULL); } static void idle_roomlist_channel_class_init (IdleRoomlistChannelClass *idle_roomlist_channel_class) { GObjectClass *object_class = G_OBJECT_CLASS (idle_roomlist_channel_class); TpBaseChannelClass *base_channel_class = TP_BASE_CHANNEL_CLASS (idle_roomlist_channel_class); static TpDBusPropertiesMixinPropImpl roomlist_props[] = { { "Server", NULL, NULL }, { NULL } }; g_type_class_add_private (idle_roomlist_channel_class, sizeof (IdleRoomlistChannelPrivate)); object_class->constructed = idle_roomlist_channel_constructed; object_class->dispose = idle_roomlist_channel_dispose; object_class->finalize = idle_roomlist_channel_finalize; base_channel_class->channel_type = TP_IFACE_CHANNEL_TYPE_ROOM_LIST; base_channel_class->target_handle_type = TP_HANDLE_TYPE_NONE; base_channel_class->close = idle_roomlist_channel_close; base_channel_class->fill_immutable_properties = idle_roomlist_channel_fill_properties; base_channel_class->get_object_path_suffix = idle_roomlist_channel_get_path_suffix; tp_dbus_properties_mixin_implement_interface (object_class, TP_IFACE_QUARK_CHANNEL_TYPE_ROOM_LIST, idle_roomlist_channel_get_roomlist_property, NULL, roomlist_props); } void idle_roomlist_channel_dispose (GObject *object) { IdleRoomlistChannel *self = IDLE_ROOMLIST_CHANNEL (object); IdleRoomlistChannelPrivate *priv = self->priv; g_assert (object != NULL); if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; if (priv->status_changed_id != 0) { g_signal_handler_disconnect (priv->connection, priv->status_changed_id); priv->status_changed_id = 0; } if (priv->rooms) { g_ptr_array_free (priv->rooms, TRUE); priv->rooms = NULL; } if (G_OBJECT_CLASS (idle_roomlist_channel_parent_class)->dispose) G_OBJECT_CLASS (idle_roomlist_channel_parent_class)->dispose (object); } void idle_roomlist_channel_finalize (GObject *object) { IdleRoomlistChannel *self = IDLE_ROOMLIST_CHANNEL (object); IdleRoomlistChannelPrivate *priv = self->priv; if (priv->handles) { tp_handle_set_destroy (priv->handles); } G_OBJECT_CLASS (idle_roomlist_channel_parent_class)->finalize (object); } static void idle_roomlist_channel_close (TpBaseChannel *channel) { IdleRoomlistChannel *self = IDLE_ROOMLIST_CHANNEL (channel); IdleRoomlistChannelPrivate *priv = self->priv; idle_parser_remove_handlers_by_data (priv->connection->parser, channel); tp_base_channel_destroyed (channel); } /** * idle_roomlist_channel_get_listing_rooms * * Implements D-Bus method GetListingRooms * on interface org.freedesktop.Telepathy.Channel.Type.RoomList */ static void idle_roomlist_channel_get_listing_rooms (TpSvcChannelTypeRoomList *iface, DBusGMethodInvocation *context) { IdleRoomlistChannel *self = IDLE_ROOMLIST_CHANNEL (iface); IdleRoomlistChannelPrivate *priv = self->priv;; tp_svc_channel_type_room_list_return_from_get_listing_rooms ( context, priv->listing); } /** * idle_roomlist_channel_list_rooms * * Implements D-Bus method ListRooms * on interface org.freedesktop.Telepathy.Channel.Type.RoomList */ static void idle_roomlist_channel_list_rooms (TpSvcChannelTypeRoomList *iface, DBusGMethodInvocation *context) { IdleRoomlistChannel *self = IDLE_ROOMLIST_CHANNEL (iface); IdleRoomlistChannelPrivate *priv = self->priv; priv->listing = TRUE; tp_svc_channel_type_room_list_emit_listing_rooms (iface, TRUE); idle_connection_send(priv->connection, "LIST"); tp_svc_channel_type_room_list_return_from_list_rooms (context); } /** * idle_roomlist_channel_stop_listing * * Implements D-Bus method StopListing * on interface org.freedesktop.Telepathy.Channel.Type.RoomList */ static void idle_roomlist_channel_stop_listing (TpSvcChannelTypeRoomList *iface, DBusGMethodInvocation *context) { IdleRoomlistChannel *self = IDLE_ROOMLIST_CHANNEL (iface); GError error = { TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "Can't stop listing!" }; g_assert (IDLE_IS_ROOMLIST_CHANNEL (self)); dbus_g_method_return_error (context, &error); /* priv->listing = FALSE; tp_svc_channel_type_room_list_emit_listing_rooms (iface, FALSE); stop_listing (self); tp_svc_channel_type_room_list_return_from_stop_listing (context); */ } static void _roomlist_iface_init (gpointer g_iface, gpointer iface_data) { TpSvcChannelTypeRoomListClass *klass = (TpSvcChannelTypeRoomListClass *)(g_iface); #define IMPLEMENT(x) tp_svc_channel_type_room_list_implement_##x (\ klass, idle_roomlist_channel_##x) IMPLEMENT (get_listing_rooms); IMPLEMENT (list_rooms); IMPLEMENT (stop_listing); #undef IMPLEMENT } static IdleParserHandlerResult _rpl_list_handler (IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data) { IdleRoomlistChannel* self = IDLE_ROOMLIST_CHANNEL (user_data); IdleRoomlistChannelPrivate *priv = self->priv; GValue room = {0,}; GHashTable *keys; TpHandle room_handle = g_value_get_uint (g_value_array_get_nth (args, 0)); TpHandleRepoIface *handl_repo = tp_base_connection_get_handles(TP_BASE_CONNECTION(priv->connection), TP_HANDLE_TYPE_ROOM); const gchar *room_name = tp_handle_inspect(handl_repo, room_handle); guint num_users = g_value_get_uint (g_value_array_get_nth (args, 1)); /* topic is optional */ const gchar *topic = ""; if (args->n_values > 2) { topic = g_value_get_string (g_value_array_get_nth (args, 2)); } keys = tp_asv_new ( "handle-name", G_TYPE_STRING, room_name, "name", G_TYPE_STRING, room_name, "members", G_TYPE_UINT, num_users, "subject", G_TYPE_STRING, topic, NULL); g_value_init (&room, TP_STRUCT_TYPE_ROOM_INFO); g_value_take_boxed (&room, dbus_g_type_specialized_construct (TP_STRUCT_TYPE_ROOM_INFO)); dbus_g_type_struct_set (&room, 0, room_handle, 1, TP_IFACE_CHANNEL_TYPE_TEXT, 2, keys, G_MAXUINT); IDLE_DEBUG ("adding new room signal data to pending: %s", room_name); g_ptr_array_add (priv->rooms, g_value_get_boxed (&room)); /* retain a ref to the room handle */ tp_handle_set_add (priv->handles, room_handle); g_hash_table_destroy (keys); return IDLE_PARSER_HANDLER_RESULT_HANDLED; } static gboolean emit_room_signal (IdleRoomlistChannel *self) { IdleRoomlistChannelPrivate *priv = self->priv; if (!priv->listing) return FALSE; if (priv->rooms->len == 0) return TRUE; tp_svc_channel_type_room_list_emit_got_rooms ( (TpSvcChannelTypeRoomList *) self, priv->rooms); while (priv->rooms->len != 0) { gpointer boxed = g_ptr_array_index (priv->rooms, 0); g_boxed_free (TP_STRUCT_TYPE_ROOM_INFO, boxed); g_ptr_array_remove_index_fast (priv->rooms, 0); } return TRUE; } static IdleParserHandlerResult _rpl_listend_handler (IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data) { IdleRoomlistChannel* self = IDLE_ROOMLIST_CHANNEL (user_data); IdleRoomlistChannelPrivate *priv = self->priv; emit_room_signal (self); priv->listing = FALSE; tp_svc_channel_type_room_list_emit_listing_rooms ( (TpSvcChannelTypeRoomList *) self, FALSE); /* g_source_remove (priv->timer_source_id); priv->timer_source_id = 0; */ return IDLE_PARSER_HANDLER_RESULT_HANDLED; } static void connection_status_changed_cb (IdleConnection* conn, guint status, guint reason, IdleRoomlistChannel *self) { IdleRoomlistChannelPrivate *priv = self->priv; if (status == TP_CONNECTION_STATUS_DISCONNECTED) { idle_parser_remove_handlers_by_data (conn->parser, self); if (priv->status_changed_id != 0) { g_signal_handler_disconnect (conn, priv->status_changed_id); priv->status_changed_id = 0; } } }