/* * room-list.c - High level API for listing room channels * * Copyright (C) 2012 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ /** * SECTION:room-list * @title: TpRoomList * @short_description: proxy object for a room list channel * * #TpRoomList provides convenient API to list rooms. */ /** * TpRoomList: * * Data structure representing a #TpRoomList. * * Since: 0.19.0 */ /** * TpRoomListClass: * * The class of a #TpRoomList. * * Since: 0.19.0 */ #include #include "telepathy-glib/room-list.h" #include #include #include #define DEBUG_FLAG TP_DEBUG_CHANNEL #include "telepathy-glib/channel-internal.h" #include "telepathy-glib/debug-internal.h" #include #include static void async_initable_iface_init (GAsyncInitableIface *iface); G_DEFINE_TYPE_WITH_CODE (TpRoomList, tp_room_list, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init)) struct _TpRoomListPrivate { TpAccount *account; gchar *server; gboolean listing; TpChannel *channel; GSimpleAsyncResult *async_res; gulong invalidated_id; }; enum { PROP_ACCOUNT = 1, PROP_SERVER, PROP_LISTING, }; enum { SIG_GOT_ROOM, SIG_FAILED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL]; static void tp_room_list_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { TpRoomList *self = (TpRoomList *) object; switch (property_id) { case PROP_ACCOUNT: g_value_set_object (value, tp_room_list_get_account (self)); break; case PROP_SERVER: g_value_set_string (value, tp_room_list_get_server (self)); break; case PROP_LISTING: g_value_set_boolean (value, self->priv->listing); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void tp_room_list_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { TpRoomList *self = (TpRoomList *) object; switch (property_id) { case PROP_ACCOUNT: g_assert (self->priv->account == NULL); /* construct only */ self->priv->account = g_value_dup_object (value); break; case PROP_SERVER: g_assert (self->priv->server == NULL); /* construct only */ self->priv->server = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void got_rooms_cb (TpChannel *channel, const GPtrArray *rooms, gpointer user_data, GObject *weak_object) { TpRoomList *self = TP_ROOM_LIST (weak_object); guint i; for (i = 0; i < rooms->len; i++) { TpRoomInfo *room; room = _tp_room_info_new (g_ptr_array_index (rooms, i)); g_signal_emit (self, signals[SIG_GOT_ROOM], 0, room); g_object_unref (room); } } static void listing_rooms_cb (TpChannel *proxy, gboolean listing, gpointer user_data, GObject *weak_object) { TpRoomList *self = TP_ROOM_LIST (weak_object); if (self->priv->listing == listing) return; self->priv->listing = listing; g_object_notify (G_OBJECT (self), "listing"); } static void destroy_channel (TpRoomList *self) { if (self->priv->channel == NULL) return; DEBUG ("Destroying existing RoomList channel"); tp_channel_destroy_async (self->priv->channel, NULL, NULL); tp_clear_object (&self->priv->channel); } static void tp_room_list_dispose (GObject *object) { TpRoomList *self = TP_ROOM_LIST (object); void (*chain_up) (GObject *) = ((GObjectClass *) tp_room_list_parent_class)->dispose; if (self->priv->channel != NULL) g_signal_handler_disconnect (self->priv->channel, self->priv->invalidated_id); destroy_channel (self); g_clear_object (&self->priv->account); if (chain_up != NULL) chain_up (object); } static void tp_room_list_finalize (GObject *object) { TpRoomList *self = TP_ROOM_LIST (object); void (*chain_up) (GObject *) = ((GObjectClass *) tp_room_list_parent_class)->finalize; g_free (self->priv->server); if (chain_up != NULL) chain_up (object); } static void tp_room_list_class_init (TpRoomListClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GParamSpec *param_spec; gobject_class->get_property = tp_room_list_get_property; gobject_class->set_property = tp_room_list_set_property; gobject_class->dispose = tp_room_list_dispose; gobject_class->finalize = tp_room_list_finalize; /** * TpRoomList:account: * * The #TpAccount to use for the room listing. * * Since: 0.19.0 */ param_spec = g_param_spec_object ("account", "account", "TpAccount", TP_TYPE_ACCOUNT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (gobject_class, PROP_ACCOUNT, param_spec); /** * TpRoomList:server: * * The DNS name of the server whose rooms are listed by this channel, or * %NULL. * * Since: 0.19.0 */ param_spec = g_param_spec_string ("server", "Server", "The server associated with the channel", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (gobject_class, PROP_SERVER, param_spec); /** * TpRoomList:listing: * * %TRUE if the channel is currently listing rooms. * * This property is meaningless until the * %TP_ROOM_LIST_FEATURE_LISTING feature has been prepared. * * Since: 0.19.0 */ param_spec = g_param_spec_boolean ("listing", "Listing", "TRUE if the channel is listing rooms", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (gobject_class, PROP_LISTING, param_spec); /** * TpRoomList::got-room: * @self: a #TpRoomList * @room: a #TpRoomInfo * * Fired each time a room is found during the listing process. * User should take his own reference on @room if he plans to * continue using it once the signal callback has returned. * * Since: 0.19.0 */ signals[SIG_GOT_ROOM] = g_signal_new ("got-room", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, TP_TYPE_ROOM_INFO); /** * TpRoomList::failed: * @self: a #TpRoomList * @error: a #GError indicating the reason of the error * * Fired when something goes wrong while listing the channels; see @error * for details. * * Since: 0.19.0 */ signals[SIG_FAILED] = g_signal_new ("failed", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_ERROR); g_type_class_add_private (gobject_class, sizeof (TpRoomListPrivate)); } static void tp_room_list_init (TpRoomList *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), TP_TYPE_ROOM_LIST, TpRoomListPrivate); } /** * tp_room_list_get_account: * @self: a #TpRoomList * * Return the #TpRoomList:account property * * Returns: (transfer none): the value of #TpRoomList:account property * * Since: 0.19.0 */ TpAccount * tp_room_list_get_account (TpRoomList *self) { return self->priv->account; } /** * tp_room_list_get_server: * @self: a #TpRoomList * * Return the #TpRoomList:server property * * Returns: the value of #TpRoomList:server property * * Since: 0.19.0 */ const gchar * tp_room_list_get_server (TpRoomList *self) { return self->priv->server; } /** * tp_room_list_is_listing: * @self: a #TpRoomList * * Return the #TpRoomList:listing property * * Returns: the value of #TpRoomList:listing property * * Since: 0.19.0 */ gboolean tp_room_list_is_listing (TpRoomList *self) { return self->priv->listing; } static void list_rooms_cb (TpChannel *channel, const GError *error, gpointer user_data, GObject *weak_object) { TpRoomList *self = TP_ROOM_LIST (weak_object); if (error != NULL) { g_signal_emit (self, signals[SIG_FAILED], 0, error); } } /** * tp_room_list_start: * @self: a #TpRoomList * * Start listing rooms using @self. Use the TpRoomList::got-rooms * signal to get the rooms found. * Errors will be reported using the TpRoomList::failed signal. * * Since: 0.19.0 */ void tp_room_list_start (TpRoomList *self) { g_return_if_fail (self->priv->channel != NULL); tp_cli_channel_type_room_list_call_list_rooms (self->priv->channel, -1, list_rooms_cb, NULL, NULL, G_OBJECT (self)); } static void chan_invalidated_cb (TpChannel *channel, guint domain, gint code, gchar *message, TpRoomList *self) { GError *error = g_error_new_literal (domain, code, message); DEBUG ("RoomList channel invalidated: %s", message); g_signal_emit (self, signals[SIG_FAILED], 0, error); g_error_free (error); } static void create_channel_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { TpAccountChannelRequest *channel_request = TP_ACCOUNT_CHANNEL_REQUEST ( source_object); TpRoomList *self = user_data; GHashTable *properties; GError *error = NULL; const gchar *server; self->priv->channel = tp_account_channel_request_create_and_handle_channel_finish ( channel_request, result, NULL, &error); if (self->priv->channel == NULL) { DEBUG ("Failed to create RoomList channel: %s", error->message); goto out; } DEBUG ("Got channel: %s", tp_proxy_get_object_path (self->priv->channel)); self->priv->invalidated_id = tp_g_signal_connect_object (self->priv->channel, "invalidated", G_CALLBACK (chan_invalidated_cb), self, 0); if (tp_cli_channel_type_room_list_connect_to_got_rooms (self->priv->channel, got_rooms_cb, NULL, NULL, G_OBJECT (self), &error) == NULL) { DEBUG ("Failed to connect GotRooms signal: %s", error->message); goto out; } tp_cli_channel_type_room_list_connect_to_listing_rooms (self->priv->channel, listing_rooms_cb, NULL, NULL, G_OBJECT (self), &error); if (error != NULL) { DEBUG ("Failed to connect ListingRooms signal: %s", error->message); goto out; } properties = _tp_channel_get_immutable_properties (self->priv->channel); server = tp_asv_get_string (properties, TP_PROP_CHANNEL_TYPE_ROOM_LIST_SERVER); if (tp_strdiff (server, self->priv->server)) { if (tp_str_empty (server) && self->priv->server != NULL) { g_free (self->priv->server); self->priv->server = g_strdup (server); g_object_notify (G_OBJECT (self), "server"); } } out: if (error != NULL) { g_simple_async_result_set_from_error (self->priv->async_res, error); g_error_free (error); /* This function is safe if self->priv->channel is NULL. */ destroy_channel (self); } g_simple_async_result_complete (self->priv->async_res); tp_clear_object (&self->priv->async_res); } static void open_new_channel (TpRoomList *self) { GHashTable *request; TpAccountChannelRequest *channel_request; DEBUG ("Requesting new RoomList channel"); request = tp_asv_new ( TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_ROOM_LIST, NULL); if (self->priv->server != NULL) tp_asv_set_string (request, TP_PROP_CHANNEL_TYPE_ROOM_LIST_SERVER, self->priv->server); channel_request = tp_account_channel_request_new (self->priv->account, request, TP_USER_ACTION_TIME_NOT_USER_ACTION); tp_account_channel_request_create_and_handle_channel_async (channel_request, NULL, create_channel_cb, G_OBJECT (self)); g_object_unref (channel_request); g_hash_table_unref (request); } static void room_list_init_async (GAsyncInitable *initable, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { TpRoomList *self = TP_ROOM_LIST (initable); self->priv->async_res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, tp_room_list_new_async); open_new_channel (self); } static gboolean room_list_init_finish (GAsyncInitable *initable, GAsyncResult *res, GError **error) { if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) return FALSE; return TRUE; } static void async_initable_iface_init (GAsyncInitableIface *iface) { iface->init_async = room_list_init_async; iface->init_finish = room_list_init_finish; } /** * tp_room_list_new_async: * @account: a #TpAccount for the room listing * @server: the DNS name of the server whose rooms should listed * @callback: a #GAsyncReadyCallback to call when the initialization * is finished * @user_data: data to pass to the callback function * * * * Since: 0.19.0 */ void tp_room_list_new_async (TpAccount *account, const gchar *server, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (TP_IS_ACCOUNT (account)); g_async_initable_new_async (TP_TYPE_ROOM_LIST, G_PRIORITY_DEFAULT, NULL, callback, user_data, "account", account, "server", server, NULL); } /** * tp_room_list_new_finish: * @result: the #GAsyncResult from the callback * @error: a #GError location to store an error, or %NULL * * * * Returns: (transfer full): a new #TpRoomList object, or %NULL * in case of error. * * Since: 0.19.0 */ TpRoomList * tp_room_list_new_finish (GAsyncResult *result, GError **error) { GObject *object, *source_object; source_object = g_async_result_get_source_object (result); object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), result, error); g_object_unref (source_object); if (object != NULL) return TP_ROOM_LIST (object); else return NULL; }