/*
* 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 <jonathon.jongsma@collabora.co.uk>
*/
#include "config.h"
#include "idle-roomlist-channel.h"
#include <time.h>
#include <dbus/dbus-glib.h>
#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;
}
}
}