summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonny Lamb <jonny.lamb@collabora.co.uk>2012-06-06 11:42:44 +0100
committerJonny Lamb <jonny.lamb@collabora.co.uk>2012-06-06 11:42:44 +0100
commit7ab3404c9b3b3f154c94c92b366c2a005d78f250 (patch)
tree13e2c3e128402a3479305a4cd50010368d1f8b26
parent30478a5e6274ae8208a595b3e865eafb62a4dda3 (diff)
muc-manager: only signal channels when they're 'ready'
It's nicer to only give the channel back when it's actually ready and joined instead of before hand. If it is returned too early gibber can be made to assert on its connection status. Gabble already does this. Signed-off-by: Jonny Lamb <jonny.lamb@collabora.co.uk>
-rw-r--r--src/muc-channel.c6
-rw-r--r--src/muc-channel.h2
-rw-r--r--src/muc-manager.c162
3 files changed, 165 insertions, 5 deletions
diff --git a/src/muc-channel.c b/src/muc-channel.c
index 1b0c5886..2abc5907 100644
--- a/src/muc-channel.c
+++ b/src/muc-channel.c
@@ -1812,3 +1812,9 @@ salut_muc_channel_bytestream_offered (SalutMucChannel *self,
salut_tube_iface_add_bytestream (tube, bytestream);
}
+
+gboolean
+salut_muc_channel_is_ready (SalutMucChannel *self)
+{
+ return self->priv->connected;
+}
diff --git a/src/muc-channel.h b/src/muc-channel.h
index 8c888d18..f5992768 100644
--- a/src/muc-channel.h
+++ b/src/muc-channel.h
@@ -100,6 +100,8 @@ void salut_muc_channel_bytestream_offered (SalutMucChannel *self,
GibberBytestreamIface *bytestream,
WockyStanza *msg);
+gboolean salut_muc_channel_is_ready (SalutMucChannel *self);
+
G_END_DECLS
#endif /* #ifndef __SALUT_MUC_CHANNEL_H__*/
diff --git a/src/muc-manager.c b/src/muc-manager.c
index 36d77ef0..ca28004b 100644
--- a/src/muc-manager.c
+++ b/src/muc-manager.c
@@ -87,6 +87,12 @@ struct _SalutMucManagerPrivate
/* tube ID => owned SalutTubeIface */
GHashTable *tubes;
+ /* borrowed TpExportableChannel => owned GSList of gpointer */
+ GHashTable *queued_requests;
+
+ /* borrowed SalutMucChannel => owned GSList of borrowed SalutTubeIface */
+ GHashTable *text_needed_for_tube;
+
gboolean dispose_has_run;
};
@@ -106,6 +112,11 @@ salut_muc_manager_init (SalutMucManager *obj)
/* allocate any data required by the object here */
priv->text_channels = g_hash_table_new_full (g_direct_hash, g_direct_equal,
NULL, g_object_unref);
+
+ priv->queued_requests = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+ NULL, (GDestroyNotify) g_slist_free);
+ priv->text_needed_for_tube = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+ NULL, (GDestroyNotify) g_slist_free);
}
static void
@@ -149,6 +160,23 @@ salut_muc_manager_set_property (GObject *object,
}
static void
+cancel_queued_requests (gpointer k,
+ gpointer v,
+ gpointer d)
+{
+ SalutMucManager *self = SALUT_MUC_MANAGER (d);
+ GSList *requests_satisfied = v;
+ GSList *iter;
+
+ for (iter = requests_satisfied; iter != NULL; iter = iter->next)
+ {
+ tp_channel_manager_emit_request_failed (self,
+ iter->data, TP_ERROR, TP_ERROR_DISCONNECTED,
+ "Unable to complete this channel request, we're disconnecting!");
+ }
+}
+
+static void
salut_muc_manager_close_all (SalutMucManager *self)
{
SalutMucManagerPrivate *priv = SALUT_MUC_MANAGER_GET_PRIVATE (self);
@@ -161,6 +189,13 @@ salut_muc_manager_close_all (SalutMucManager *self)
priv->status_changed_id = 0;
}
+ if (priv->queued_requests != NULL)
+ g_hash_table_foreach (priv->queued_requests,
+ cancel_queued_requests, self);
+
+ tp_clear_pointer (&priv->queued_requests, g_hash_table_unref);
+ tp_clear_pointer (&priv->text_needed_for_tube, g_hash_table_unref);
+
tp_clear_pointer (&priv->text_channels, g_hash_table_unref);
}
@@ -255,6 +290,8 @@ salut_muc_manager_dispose (GObject *object)
salut_muc_manager_close_all (self);
g_assert (priv->text_channels == NULL);
+ g_assert (priv->queued_requests == NULL);
+ g_assert (priv->text_needed_for_tube == NULL);
/* release any references held by the object here */
@@ -342,6 +379,32 @@ salut_muc_manager_type_foreach_channel_class (GType type,
g_hash_table_unref (table);
}
+static void
+associate_channel_to_data (GHashTable *table,
+ gpointer channel,
+ gpointer data)
+{
+ GSList *list;
+
+ if (data == NULL)
+ return;
+
+ /* yes it might be more 'efficient' to use prepend, then reverse the
+ * list before use but that's just annoying. I doubt there'll ever
+ * be more than one item in the list anyway. */
+
+ /* get the old list */
+ list = g_hash_table_lookup (table, channel);
+
+ /* add the data to it */
+ list = g_slist_append (list, data);
+
+ /* steal it so it doesn't get freed */
+ g_hash_table_steal (table, channel);
+
+ /* throw it back in */
+ g_hash_table_insert (table, channel, list);
+}
static void
muc_channel_closed_cb (SalutMucChannel *chan,
@@ -395,6 +458,80 @@ _get_connection (SalutMucManager *mgr,
protocol, parameters, error);
}
+static void
+muc_channel_ready_cb (SalutMucChannel *chan,
+ SalutMucManager *mgr)
+{
+ SalutMucManagerPrivate *priv = SALUT_MUC_MANAGER_GET_PRIVATE (mgr);
+ GSList *satisfied;
+ GSList *tube_channels;
+ GSList *l;
+
+ /* announce the text channel finally */
+ satisfied = g_hash_table_lookup (priv->queued_requests, chan);
+ tp_channel_manager_emit_new_channel (mgr,
+ TP_EXPORTABLE_CHANNEL (chan), satisfied);
+ g_hash_table_remove (priv->queued_requests, chan);
+
+ /* announce tube channels now */
+ tube_channels = g_hash_table_lookup (priv->text_needed_for_tube, chan);
+
+ for (l = tube_channels; l != NULL; l = l->next)
+ {
+ SalutTubeIface *tube = SALUT_TUBE_IFACE (l->data);
+ GSList *requests_satisfied;
+
+ requests_satisfied = g_hash_table_lookup (priv->queued_requests, tube);
+
+ tp_channel_manager_emit_new_channel (mgr,
+ TP_EXPORTABLE_CHANNEL (tube), requests_satisfied);
+
+ g_hash_table_remove (priv->queued_requests, tube);
+ }
+
+ g_hash_table_remove (priv->text_needed_for_tube, chan);
+}
+
+static void
+muc_channel_join_error_cb (SalutMucChannel *chan,
+ GError *error,
+ SalutMucManager *mgr)
+{
+ SalutMucManagerPrivate *priv = SALUT_MUC_MANAGER_GET_PRIVATE (mgr);
+ GSList *requests_satisfied;
+ GSList *tube_channels;
+ GSList *l;
+
+#define FAIL_REQUESTS(requests) \
+ { \
+ GSList *_l; \
+ for (_l = requests; _l != NULL; _l = _l->next) \
+ { \
+ tp_channel_manager_emit_request_failed (mgr, _l->data, \
+ error->domain, error->code, error->message); \
+ } \
+ }
+
+ /* first fail the text channel itself */
+ requests_satisfied = g_hash_table_lookup (priv->queued_requests, chan);
+ FAIL_REQUESTS(requests_satisfied);
+ g_hash_table_remove (priv->queued_requests, chan);
+
+ /* now fail all tube channel requests */
+ tube_channels = g_hash_table_lookup (priv->text_needed_for_tube, chan);
+
+ for (l = tube_channels; l != NULL; l = l->next)
+ {
+ TpExportableChannel *tube = TP_EXPORTABLE_CHANNEL (l->data);
+
+ requests_satisfied = g_hash_table_lookup (priv->queued_requests, tube);
+ FAIL_REQUESTS (requests_satisfied);
+ g_hash_table_remove (priv->queued_requests, tube);
+ }
+
+ g_hash_table_remove (priv->text_needed_for_tube, chan);
+}
+
static SalutMucChannel *
salut_muc_manager_new_muc_channel (SalutMucManager *mgr,
TpHandle handle,
@@ -433,6 +570,13 @@ salut_muc_manager_new_muc_channel (SalutMucManager *mgr,
g_hash_table_insert (priv->text_channels, GUINT_TO_POINTER (handle), chan);
+ if (salut_muc_channel_is_ready (chan))
+ muc_channel_ready_cb (chan, mgr);
+ else
+ g_signal_connect (chan, "ready", G_CALLBACK (muc_channel_ready_cb), mgr);
+
+ g_signal_connect (chan, "join-error", G_CALLBACK (muc_channel_join_error_cb), mgr);
+
return chan;
}
@@ -522,13 +666,21 @@ salut_muc_manager_request_new_muc_channel (SalutMucManager *mgr,
* succeed */
g_assert (r);
- if (request_token != NULL)
- tokens = g_slist_prepend (tokens, request_token);
-
if (announce)
{
- tp_channel_manager_emit_new_channel (mgr,
- TP_EXPORTABLE_CHANNEL (text_chan), tokens);
+ if (salut_muc_channel_is_ready (text_chan))
+ {
+ if (request_token != NULL)
+ tokens = g_slist_prepend (tokens, request_token);
+
+ tp_channel_manager_emit_new_channel (mgr,
+ TP_EXPORTABLE_CHANNEL (text_chan), tokens);
+ }
+ else
+ {
+ associate_channel_to_data (priv->queued_requests,
+ text_chan, request_token);
+ }
}
g_slist_free (tokens);