[Telepathy-commits] [telepathy-gabble/master] GabbleMucFactory: implement GabbleChannelManager
Simon McVittie
simon.mcvittie at collabora.co.uk
Thu Aug 21 06:31:58 PDT 2008
20080805144828-53eee-b2c51c880d6398e96ad12733e1d08a325007a20d.gz
---
src/muc-factory.c | 321 ++++++++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 303 insertions(+), 18 deletions(-)
diff --git a/src/muc-factory.c b/src/muc-factory.c
index a8a29c2..7dcd06f 100644
--- a/src/muc-factory.c
+++ b/src/muc-factory.c
@@ -28,10 +28,13 @@
#include <dbus/dbus-glib-lowlevel.h>
#include <loudmouth/loudmouth.h>
#include <telepathy-glib/channel-factory-iface.h>
+#include <telepathy-glib/dbus.h>
#include <telepathy-glib/interfaces.h>
+#include <telepathy-glib/util.h>
#define DEBUG_FLAG GABBLE_DEBUG_MUC
+#include "channel-manager.h"
#include "connection.h"
#include "conn-olpc.h"
#include "debug.h"
@@ -45,10 +48,13 @@
static void gabble_muc_factory_iface_init (gpointer g_iface,
gpointer iface_data);
+static void channel_manager_iface_init (gpointer, gpointer);
G_DEFINE_TYPE_WITH_CODE (GabbleMucFactory, gabble_muc_factory, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_FACTORY_IFACE,
- gabble_muc_factory_iface_init));
+ gabble_muc_factory_iface_init);
+ G_IMPLEMENT_INTERFACE (GABBLE_TYPE_CHANNEL_MANAGER,
+ channel_manager_iface_init));
/* properties */
enum
@@ -68,13 +74,18 @@ struct _GabbleMucFactoryPrivate
GHashTable *text_channels;
/* GUINT_TO_POINTER(room_handle) => (GabbleTubesChannel *) */
GHashTable *tubes_channels;
- /* Tubes channel requests which will be satisfied when the corresponding
+ /* Tubes channels which will be considered ready when the corresponding
* text channel is created.
- * GabbleMucChannel * => GabbleTubesChannel * */
+ * Borrowed GabbleMucChannel => borrowed GabbleTubesChannel */
GHashTable *text_needed_for_tubes;
/* GabbleDiscoRequest * => NULL (used as a set) */
GHashTable *disco_requests;
+ /* Map from channels to the request-tokens of requests that they will satisfy
+ * when they're ready.
+ * Borrowed GabbleExportableChannel => GSList of gpointer */
+ GHashTable *queued_requests;
+
gboolean dispose_has_run;
};
@@ -100,6 +111,9 @@ gabble_muc_factory_init (GabbleMucFactory *fac)
priv->disco_requests = g_hash_table_new_full (g_direct_hash, g_direct_equal,
NULL, NULL);
+ priv->queued_requests = g_hash_table_new_full (g_direct_hash,
+ g_direct_equal, NULL, NULL);
+
priv->message_cb = NULL;
priv->presence_cb = NULL;
@@ -137,6 +151,7 @@ gabble_muc_factory_dispose (GObject *object)
g_assert (priv->text_channels == NULL);
g_assert (priv->tubes_channels == NULL);
g_assert (priv->text_needed_for_tubes == NULL);
+ g_assert (priv->queued_requests == NULL);
g_hash_table_foreach (priv->disco_requests, cancel_disco_request,
priv->conn->disco);
@@ -223,6 +238,9 @@ muc_channel_closed_cb (GabbleMucChannel *chan, gpointer user_data)
GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
TpHandle room_handle;
+ gabble_channel_manager_emit_channel_closed_for_object (fac,
+ GABBLE_EXPORTABLE_CHANNEL (chan));
+
if (priv->text_channels != NULL)
{
g_object_get (chan, "handle", &room_handle, NULL);
@@ -257,6 +275,9 @@ tubes_channel_closed_cb (GabbleTubesChannel *chan, gpointer user_data)
GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
TpHandle room_handle;
+ gabble_channel_manager_emit_channel_closed_for_object (fac,
+ GABBLE_EXPORTABLE_CHANNEL (chan));
+
if (priv->tubes_channels != NULL)
{
g_object_get (chan, "handle", &room_handle, NULL);
@@ -268,6 +289,25 @@ tubes_channel_closed_cb (GabbleTubesChannel *chan, gpointer user_data)
}
}
+
+static void
+gabble_muc_factory_emit_new_channel (GabbleMucFactory *self,
+ GabbleExportableChannel *channel)
+{
+ GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (self);
+ GSList *requests_satisfied;
+
+ tp_channel_factory_iface_emit_new_channel (self, TP_CHANNEL_IFACE (channel),
+ NULL);
+
+ requests_satisfied = g_hash_table_lookup (priv->queued_requests, channel);
+ g_hash_table_steal (priv->queued_requests, channel);
+ requests_satisfied = g_slist_reverse (requests_satisfied);
+ gabble_channel_manager_emit_new_channel (self, channel, requests_satisfied);
+ g_slist_free (requests_satisfied);
+}
+
+
static void
muc_ready_cb (GabbleMucChannel *chan,
gpointer data)
@@ -278,15 +318,14 @@ muc_ready_cb (GabbleMucChannel *chan,
DEBUG ("chan=%p", chan);
- tp_channel_factory_iface_emit_new_channel (fac, (TpChannelIface *) chan,
- NULL);
+ gabble_muc_factory_emit_new_channel (fac, GABBLE_EXPORTABLE_CHANNEL (chan));
tubes_chan = g_hash_table_lookup (priv->text_needed_for_tubes, chan);
if (tubes_chan != NULL)
{
g_hash_table_remove (priv->text_needed_for_tubes, chan);
- tp_channel_factory_iface_emit_new_channel (fac,
- (TpChannelIface *) tubes_chan, NULL);
+ gabble_muc_factory_emit_new_channel (fac,
+ GABBLE_EXPORTABLE_CHANNEL (tubes_chan));
}
}
@@ -897,8 +936,8 @@ muc_factory_presence_cb (LmMessageHandler *handler,
* have a well-defined initiator (they're a consensus) so use 0 */
tubes_chan = new_tubes_channel (fac, room_handle, muc_chan,
0);
- tp_channel_factory_iface_emit_new_channel (fac,
- (TpChannelIface *) tubes_chan, NULL);
+ gabble_muc_factory_emit_new_channel (fac,
+ GABBLE_EXPORTABLE_CHANNEL (tubes_chan));
}
handle = tp_handle_ensure (contact_repo, from,
@@ -920,12 +959,60 @@ muc_factory_presence_cb (LmMessageHandler *handler,
static void
+gabble_muc_factory_associate_request (GabbleMucFactory *self,
+ gpointer channel,
+ gpointer request)
+{
+ GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (self);
+ GSList *list = g_hash_table_lookup (priv->queued_requests, channel);
+
+ g_assert (GABBLE_IS_EXPORTABLE_CHANNEL (channel));
+
+ g_hash_table_steal (priv->queued_requests, channel);
+ list = g_slist_prepend (list, request);
+ g_hash_table_insert (priv->queued_requests, channel, list);
+}
+
+
+static gboolean
+cancel_queued_requests (gpointer k,
+ gpointer v,
+ gpointer d)
+{
+ GabbleMucFactory *self = GABBLE_MUC_FACTORY (d);
+ GSList *requests_satisfied = v;
+ GSList *iter;
+
+ requests_satisfied = g_slist_reverse (requests_satisfied);
+
+ for (iter = requests_satisfied; iter != NULL; iter = iter->next)
+ {
+ gabble_channel_manager_emit_request_failed (self,
+ iter->data, TP_ERRORS, TP_ERROR_DISCONNECTED,
+ "Unable to complete this channel request, we're disconnecting!");
+ }
+
+ g_slist_free (requests_satisfied);
+
+ return TRUE;
+}
+
+
+static void
gabble_muc_factory_close_all (GabbleMucFactory *fac)
{
GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
DEBUG ("closing channels");
+ if (priv->queued_requests != NULL)
+ {
+ g_hash_table_foreach_steal (priv->queued_requests,
+ cancel_queued_requests, fac);
+ g_hash_table_destroy (priv->queued_requests);
+ priv->queued_requests = NULL;
+ }
+
if (priv->text_needed_for_tubes != NULL)
{
g_hash_table_destroy (priv->text_needed_for_tubes);
@@ -1022,7 +1109,7 @@ gabble_muc_factory_constructor (GType type, guint n_props,
struct _ForeachData
{
- TpChannelFunc foreach;
+ GabbleExportableChannelFunc foreach;
gpointer user_data;
};
@@ -1030,17 +1117,17 @@ static void
_foreach_slave (gpointer key, gpointer value, gpointer user_data)
{
struct _ForeachData *data = (struct _ForeachData *) user_data;
- TpChannelIface *chan = TP_CHANNEL_IFACE (value);
+ GabbleExportableChannel *channel = GABBLE_EXPORTABLE_CHANNEL (value);
- data->foreach (chan, data->user_data);
+ data->foreach (channel, data->user_data);
}
static void
-gabble_muc_factory_iface_foreach (TpChannelFactoryIface *iface,
- TpChannelFunc foreach,
- gpointer user_data)
+gabble_muc_factory_foreach_channel (GabbleChannelManager *manager,
+ GabbleExportableChannelFunc foreach,
+ gpointer user_data)
{
- GabbleMucFactory *fac = GABBLE_MUC_FACTORY (iface);
+ GabbleMucFactory *fac = GABBLE_MUC_FACTORY (manager);
GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
struct _ForeachData data;
@@ -1051,6 +1138,7 @@ gabble_muc_factory_iface_foreach (TpChannelFactoryIface *iface,
g_hash_table_foreach (priv->tubes_channels, _foreach_slave, &data);
}
+
/**
* ensure_muc_channel:
*
@@ -1071,6 +1159,9 @@ ensure_muc_channel (GabbleMucFactory *fac,
if (*ret == NULL)
{
+ /* FIXME: using base_conn->self_handle causes Requested to be TRUE,
+ * which is incorrect if this is a side-effect of joining a Tubes
+ * channel */
*ret = new_muc_channel (fac, handle, FALSE, base_conn->self_handle, NULL);
return FALSE;
}
@@ -1109,10 +1200,13 @@ gabble_muc_factory_iface_request (TpChannelFactoryIface *iface,
if (ensure_muc_channel (fac, priv, handle, &text_chan))
{
*ret = TP_CHANNEL_IFACE (text_chan);
+ gabble_channel_manager_emit_request_already_satisfied (fac,
+ request, GABBLE_EXPORTABLE_CHANNEL (text_chan));
return TP_CHANNEL_FACTORY_REQUEST_STATUS_EXISTING;
}
else
{
+ gabble_muc_factory_associate_request (fac, text_chan, request);
return TP_CHANNEL_FACTORY_REQUEST_STATUS_QUEUED;
}
}
@@ -1123,6 +1217,8 @@ gabble_muc_factory_iface_request (TpChannelFactoryIface *iface,
if (tubes_chan != NULL)
{
*ret = TP_CHANNEL_IFACE (tubes_chan);
+ gabble_channel_manager_emit_request_already_satisfied (fac,
+ request, GABBLE_EXPORTABLE_CHANNEL (tubes_chan));
return TP_CHANNEL_FACTORY_REQUEST_STATUS_EXISTING;
}
else
@@ -1132,13 +1228,16 @@ gabble_muc_factory_iface_request (TpChannelFactoryIface *iface,
tubes_chan = new_tubes_channel (fac, handle, text_chan,
base_conn->self_handle);
*ret = TP_CHANNEL_IFACE (tubes_chan);
- tp_channel_factory_iface_emit_new_channel (fac, *ret, NULL);
+ gabble_muc_factory_associate_request (fac, tubes_chan, request);
+ gabble_muc_factory_emit_new_channel (fac,
+ GABBLE_EXPORTABLE_CHANNEL (tubes_chan));
return TP_CHANNEL_FACTORY_REQUEST_STATUS_CREATED;
}
else
{
tubes_chan = new_tubes_channel (fac, handle, text_chan,
base_conn->self_handle);
+ gabble_muc_factory_associate_request (fac, tubes_chan, request);
g_hash_table_insert (priv->text_needed_for_tubes,
text_chan, tubes_chan);
return TP_CHANNEL_FACTORY_REQUEST_STATUS_QUEUED;
@@ -1197,6 +1296,192 @@ gabble_muc_factory_iface_init (gpointer g_iface,
klass->close_all =
(TpChannelFactoryIfaceProc) gabble_muc_factory_close_all;
- klass->foreach = gabble_muc_factory_iface_foreach;
+ klass->foreach =
+ (TpChannelFactoryIfaceForeachImpl) gabble_muc_factory_foreach_channel;
klass->request = gabble_muc_factory_iface_request;
}
+
+
+static const gchar * const muc_channel_required_properties[] = {
+ TP_IFACE_CHANNEL ".TargetHandle",
+ NULL
+};
+
+static const gchar * const * muc_tubes_channel_required_properties =
+ muc_channel_required_properties;
+
+static const gchar * const no_properties[] = { NULL };
+
+
+static void
+gabble_muc_factory_foreach_channel_class (GabbleChannelManager *manager,
+ GabbleChannelManagerChannelClassFunc 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);
+ /* no string value yet - we'll change it for each channel class */
+ 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);
+
+ g_value_set_static_string (channel_type_value, TP_IFACE_CHANNEL_TYPE_TEXT);
+ func (manager, table, muc_channel_required_properties, no_properties,
+ user_data);
+
+ g_value_set_static_string (channel_type_value, TP_IFACE_CHANNEL_TYPE_TUBES);
+ func (manager, table, muc_tubes_channel_required_properties, no_properties,
+ user_data);
+
+ g_hash_table_destroy (table);
+}
+
+
+static gboolean
+gabble_muc_factory_request (GabbleMucFactory *self,
+ gpointer request_token,
+ GHashTable *request_properties,
+ gboolean require_new)
+{
+ GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_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;
+ GabbleMucChannel *text_chan;
+ GabbleTubesChannel *tubes_chan;
+
+ 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 (ensure_muc_channel (self, priv, handle, &text_chan))
+ {
+ if (require_new)
+ {
+ g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
+ "That channel has already been created (or requested)");
+ goto error;
+ }
+ else
+ {
+ gabble_channel_manager_emit_request_already_satisfied (self,
+ request_token, GABBLE_EXPORTABLE_CHANNEL (text_chan));
+ }
+ }
+ else
+ {
+ gabble_muc_factory_associate_request (self, text_chan,
+ request_token);
+ }
+
+ return TRUE;
+ }
+ else if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TUBES))
+ {
+ tubes_chan = g_hash_table_lookup (priv->tubes_channels,
+ GUINT_TO_POINTER (handle));
+
+ if (tubes_chan != 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
+ {
+ gabble_channel_manager_emit_request_already_satisfied (self,
+ request_token, GABBLE_EXPORTABLE_CHANNEL (tubes_chan));
+ }
+ }
+ else if (ensure_muc_channel (self, priv, handle, &text_chan))
+ {
+ tubes_chan = new_tubes_channel (self, handle, text_chan,
+ base_conn->self_handle);
+ gabble_muc_factory_associate_request (self, tubes_chan,
+ request_token);
+ gabble_muc_factory_emit_new_channel (self,
+ GABBLE_EXPORTABLE_CHANNEL (tubes_chan));
+ }
+ else
+ {
+ tubes_chan = new_tubes_channel (self, handle, text_chan,
+ base_conn->self_handle);
+ gabble_muc_factory_associate_request (self, tubes_chan,
+ request_token);
+ g_hash_table_insert (priv->text_needed_for_tubes, text_chan,
+ tubes_chan);
+ }
+
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+
+error:
+ gabble_channel_manager_emit_request_failed (self, request_token,
+ error->domain, error->code, error->message);
+ g_error_free (error);
+ return TRUE;
+}
+
+
+static gboolean
+gabble_muc_factory_create_channel (GabbleChannelManager *manager,
+ gpointer request_token,
+ GHashTable *request_properties)
+{
+ GabbleMucFactory *self = GABBLE_MUC_FACTORY (manager);
+
+ return gabble_muc_factory_request (self, request_token, request_properties,
+ TRUE);
+}
+
+
+static gboolean
+gabble_muc_factory_request_channel (GabbleChannelManager *manager,
+ gpointer request_token,
+ GHashTable *request_properties)
+{
+ GabbleMucFactory *self = GABBLE_MUC_FACTORY (manager);
+
+ return gabble_muc_factory_request (self, request_token, request_properties,
+ FALSE);
+}
+
+
+static void
+channel_manager_iface_init (gpointer g_iface,
+ gpointer iface_data)
+{
+ GabbleChannelManagerIface *iface = g_iface;
+
+ iface->foreach_channel = gabble_muc_factory_foreach_channel;
+ iface->foreach_channel_class = gabble_muc_factory_foreach_channel_class;
+ iface->request_channel = gabble_muc_factory_request_channel;
+ iface->create_channel = gabble_muc_factory_create_channel;
+}
--
1.5.6.3
More information about the Telepathy-commits
mailing list