[telepathy-gabble/master] Namespace contents by their creator.
Will Thompson
will.thompson at collabora.co.uk
Thu Jun 11 09:32:15 PDT 2009
Fixes fd.o#20763
---
src/jingle-session.c | 215 ++++++++++++++++++++++++++++++++-----------------
1 files changed, 140 insertions(+), 75 deletions(-)
diff --git a/src/jingle-session.c b/src/jingle-session.c
index 371bc35..2855630 100644
--- a/src/jingle-session.c
+++ b/src/jingle-session.c
@@ -80,7 +80,8 @@ struct _GabbleJingleSessionPrivate
/* GabbleJingleContent objects keyed by content name.
* Table owns references to these objects. */
- GHashTable *contents;
+ GHashTable *initiator_contents;
+ GHashTable *responder_contents;
JingleDialect dialect;
JingleState state;
@@ -150,7 +151,9 @@ gabble_jingle_session_init (GabbleJingleSession *obj)
GabbleJingleSessionPrivate);
obj->priv = priv;
- priv->contents = g_hash_table_new_full (g_str_hash, g_str_equal,
+ priv->initiator_contents = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, g_object_unref);
+ priv->responder_contents = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, g_object_unref);
priv->state = JS_STATE_PENDING_CREATED;
@@ -178,8 +181,11 @@ gabble_jingle_session_dispose (GObject *object)
(priv->state == JS_STATE_ENDED));
g_assert (priv->timer_id == 0);
- g_hash_table_destroy (priv->contents);
- priv->contents = NULL;
+ g_hash_table_destroy (priv->initiator_contents);
+ priv->initiator_contents = NULL;
+
+ g_hash_table_destroy (priv->responder_contents);
+ priv->responder_contents = NULL;
tp_handle_unref (contact_repo, sess->peer);
sess->peer = 0;
@@ -561,14 +567,63 @@ static GabbleJingleContent *_get_any_content (GabbleJingleSession *session);
#define SET_OUT_ORDER(txt...) g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_JINGLE_OUT_OF_ORDER, txt)
#define SET_CONFLICT(txt...) g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_CONFLICT, txt)
+static gboolean
+lookup_content (GabbleJingleSession *sess,
+ const gchar *name,
+ const gchar *creator,
+ gboolean fail_if_missing,
+ GabbleJingleContent **c,
+ GError **error)
+{
+ GabbleJingleSessionPrivate *priv = sess->priv;
+
+ if (name == NULL)
+ {
+ g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_BAD_REQUEST,
+ "'name' attribute unset");
+ return FALSE;
+ }
+
+ if (JINGLE_IS_GOOGLE_DIALECT (priv->dialect))
+ {
+ /* Only the initiator can create contents on GTalk. */
+ *c = g_hash_table_lookup (priv->initiator_contents, name);
+ }
+ else
+ {
+ if (!tp_strdiff (creator, "initiator"))
+ {
+ *c = g_hash_table_lookup (priv->initiator_contents, name);
+ }
+ else if (!tp_strdiff (creator, "responder"))
+ {
+ *c = g_hash_table_lookup (priv->responder_contents, name);
+ }
+ else
+ {
+ g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_BAD_REQUEST,
+ "'creator' attribute %s",
+ (creator == NULL ? "missing" : "invalid"));
+ return FALSE;
+ }
+ }
+
+ if (fail_if_missing && *c == NULL)
+ {
+ g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_BAD_REQUEST,
+ "Content '%s' (created by %s) does not exist", name, creator);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
static void
_foreach_content (GabbleJingleSession *sess, LmMessageNode *node,
ContentHandlerFunc func, GError **error)
{
- GabbleJingleSessionPrivate *priv = sess->priv;
GabbleJingleContent *c;
LmMessageNode *content_node;
- const gchar *name;
for (content_node = node->children;
NULL != content_node;
@@ -577,8 +632,11 @@ _foreach_content (GabbleJingleSession *sess, LmMessageNode *node,
if (tp_strdiff (lm_message_node_get_name (content_node), "content"))
continue;
- name = lm_message_node_get_attribute (content_node, "name");
- c = g_hash_table_lookup (priv->contents, name);
+ if (!lookup_content (sess,
+ lm_message_node_get_attribute (content_node, "name"),
+ lm_message_node_get_attribute (content_node, "creator"),
+ FALSE /* fail_if_missing */, &c, error))
+ return;
func (sess, c, content_node, error);
if (*error != NULL)
@@ -645,6 +703,7 @@ create_content (GabbleJingleSession *sess, GType content_type,
{
GabbleJingleSessionPrivate *priv = sess->priv;
GabbleJingleContent *c;
+ GHashTable *contents;
DEBUG ("session creating new content type, conn == %p, jf == %p", priv->conn, priv->conn->jingle_factory);
@@ -683,8 +742,23 @@ create_content (GabbleJingleSession *sess, GType content_type,
name = gabble_jingle_content_get_name (c);
}
- /* This will override existing content if it exists. */
- g_hash_table_replace (priv->contents, g_strdup (name), c);
+ if (priv->local_initiator == gabble_jingle_content_is_created_by_us (c))
+ {
+ DEBUG ("inserting content %s into initiator_contents", name);
+ contents = priv->initiator_contents;
+ }
+ else
+ {
+ DEBUG ("inserting content %s into responder_contents", name);
+ contents = priv->responder_contents;
+ }
+
+ /* If the content already existed, either we shouldn't have picked the name
+ * we did (if we're creating it) or _each_content_add should have already
+ * said no.
+ */
+ g_assert (g_hash_table_lookup (contents, name) == NULL);
+ g_hash_table_insert (contents, g_strdup (name), c);
g_signal_emit (sess, signals[NEW_CONTENT], 0, c);
return c;
}
@@ -729,26 +803,9 @@ _each_content_add (GabbleJingleSession *sess, GabbleJingleContent *c,
if (c != NULL)
{
- JingleContentState state;
-
- /* contents added by the session initiator may replace similarly-named
- * contents which we are trying to add (but haven't had acknowledged) */
-
- g_object_get (c, "state", &state, NULL);
- if (state < JINGLE_CONTENT_STATE_ACKNOWLEDGED)
- {
- if (priv->local_initiator)
- {
- SET_CONFLICT ("session initiator is creating a content "
- "named \"%s\" already", name);
- return;
- }
- }
- else
- {
- SET_CONFLICT ("content called \"%s\" already exists, rejecting", name);
- return;
- }
+ g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_BAD_REQUEST,
+ "content called \"%s\" already exists, rejecting", name);
+ return;
}
create_content (sess, content_type, JINGLE_MEDIA_TYPE_NONE,
@@ -856,8 +913,7 @@ on_session_initiate (GabbleJingleSession *sess, LmMessageNode *node,
return;
}
- if ((priv->dialect == JINGLE_DIALECT_GTALK3) ||
- (priv->dialect == JINGLE_DIALECT_GTALK4))
+ if (JINGLE_IS_GOOGLE_DIALECT (priv->dialect))
{
/* in this case we implicitly have just one content */
_each_content_add (sess, NULL, node, error);
@@ -960,24 +1016,28 @@ on_session_accept (GabbleJingleSession *sess, LmMessageNode *node,
}
static void
+mute_all_foreach (gpointer key,
+ gpointer value,
+ gpointer mute)
+{
+ if (G_OBJECT_TYPE (value) == GABBLE_TYPE_JINGLE_MEDIA_RTP)
+ g_object_set (value, "remote-mute", GPOINTER_TO_INT (mute), NULL);
+}
+
+static void
mute_all (GabbleJingleSession *sess,
gboolean mute)
{
- GHashTableIter iter;
- gpointer value;
-
- g_hash_table_iter_init (&iter, sess->priv->contents);
-
- while (g_hash_table_iter_next (&iter, NULL, &value))
- {
- if (G_OBJECT_TYPE (value) == GABBLE_TYPE_JINGLE_MEDIA_RTP)
- g_object_set (value, "remote-mute", mute, NULL);
- }
+ g_hash_table_foreach (sess->priv->initiator_contents, mute_all_foreach,
+ GINT_TO_POINTER (mute));
+ g_hash_table_foreach (sess->priv->responder_contents, mute_all_foreach,
+ GINT_TO_POINTER (mute));
}
static gboolean
set_mute (GabbleJingleSession *sess,
const gchar *name,
+ const gchar *creator,
gboolean mute,
GError **error)
{
@@ -989,14 +1049,9 @@ set_mute (GabbleJingleSession *sess,
return TRUE;
}
- c = g_hash_table_lookup (sess->priv->contents, name);
-
- if (c == NULL)
- {
- g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_ITEM_NOT_FOUND,
- "content '%s' does not exist", name);
- return FALSE;
- }
+ if (!lookup_content (sess, name, creator, TRUE /* fail if missing */, &c,
+ error))
+ return FALSE;
if (G_OBJECT_TYPE (c) != GABBLE_TYPE_JINGLE_MEDIA_RTP)
{
@@ -1031,7 +1086,8 @@ handle_payload (GabbleJingleSession *sess,
{
const gchar *ns = lm_message_node_get_namespace (payload);
const gchar *elt = lm_message_node_get_name (payload);
- const gchar *name_attr = lm_message_node_get_attribute (payload, "name");
+ const gchar *name = lm_message_node_get_attribute (payload, "name");
+ const gchar *creator = lm_message_node_get_attribute (payload, "creator");
if (tp_strdiff (ns, NS_JINGLE_RTP_INFO))
{
@@ -1065,11 +1121,11 @@ handle_payload (GabbleJingleSession *sess,
*/
else if (!tp_strdiff (elt, "mute"))
{
- return set_mute (sess, name_attr, TRUE, error);
+ return set_mute (sess, name, creator, TRUE, error);
}
else if (!tp_strdiff (elt, "unmute"))
{
- return set_mute (sess, name_attr, FALSE, error);
+ return set_mute (sess, name, creator, FALSE, error);
}
else
{
@@ -1122,7 +1178,7 @@ on_session_info (GabbleJingleSession *sess,
*/
if (understood_a_payload)
g_signal_emit (sess, signals[REMOTE_STATE_CHANGED], 0);
- else
+ else if (!hit_an_error)
g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_JINGLE_UNSUPPORTED_INFO,
"no recognized session-info payloads");
}
@@ -1196,11 +1252,10 @@ on_transport_info (GabbleJingleSession *sess, LmMessageNode *node,
/* We are certain that GTalk has only one content. It's not possible
* for session to have more than one content if in gtalk mode (if
* it happens, it's a bug in our code). */
- GList *cs = g_hash_table_get_values (priv->contents);
- g_assert (g_list_length (cs) == 1);
+ GList *cs = gabble_jingle_session_get_contents (sess);
+ g_assert (g_list_length (cs) == 1);
c = cs->data;
-
g_list_free (cs);
if (priv->dialect == JINGLE_DIALECT_GTALK4)
@@ -1226,17 +1281,13 @@ on_transport_info (GabbleJingleSession *sess, LmMessageNode *node,
}
else
{
- const gchar *name;
-
node = lm_message_node_get_child_any_ns (node, "content");
- name = lm_message_node_get_attribute (node, "name");
- c = g_hash_table_lookup (priv->contents, name);
- if (c == NULL)
- {
- SET_BAD_REQ ("content doesn't exist");
- return;
- }
+ if (!lookup_content (sess,
+ lm_message_node_get_attribute (node, "name"),
+ lm_message_node_get_attribute (node, "creator"),
+ TRUE /* fail_if_missing */, &c, error))
+ return;
/* we need transport child of content node */
node = lm_message_node_get_child_any_ns (node, "transport");
@@ -1671,7 +1722,8 @@ try_session_initiate_or_accept (GabbleJingleSession *sess)
JingleReplyHandler handler;
/* If there are no contents yet, we shouldn't have been called at all. */
- g_assert (g_hash_table_size (priv->contents) > 0);
+ g_assert (g_hash_table_size (priv->initiator_contents) +
+ g_hash_table_size (priv->responder_contents) > 0);
if (priv->local_initiator)
{
@@ -1875,7 +1927,11 @@ count_active_contents (GabbleJingleSession *sess)
GabbleJingleSessionPrivate *priv = sess->priv;
guint n_contents = 0;
- g_hash_table_foreach (priv->contents, _foreach_count_active_contents, &n_contents);
+ g_hash_table_foreach (priv->initiator_contents, _foreach_count_active_contents,
+ &n_contents);
+ g_hash_table_foreach (priv->responder_contents, _foreach_count_active_contents,
+ &n_contents);
+
return n_contents;
}
@@ -1884,13 +1940,15 @@ content_removed_cb (GabbleJingleContent *c, gpointer user_data)
{
GabbleJingleSession *sess = GABBLE_JINGLE_SESSION (user_data);
GabbleJingleSessionPrivate *priv = sess->priv;
- const gchar *name;
+ const gchar *name = gabble_jingle_content_get_name (c);
- g_object_get (c, "name", &name, NULL);
- g_hash_table_remove (priv->contents, name);
+ if (gabble_jingle_content_creator_is_initiator (c))
+ g_hash_table_remove (priv->initiator_contents, name);
+ else
+ g_hash_table_remove (priv->responder_contents, name);
if (priv->state == JS_STATE_ENDED)
- return;
+ return;
if (count_active_contents (sess) == 0)
gabble_jingle_session_terminate (sess, TP_CHANNEL_GROUP_CHANGE_REASON_NONE, NULL);
@@ -1921,14 +1979,16 @@ gabble_jingle_session_add_content (GabbleJingleSession *sess, JingleMediaType mt
GabbleJingleContent *c;
GType content_type;
gchar *name = NULL;
- gint id = g_hash_table_size (priv->contents) + 1;
+ GHashTable *contents = priv->local_initiator ? priv->initiator_contents
+ : priv->responder_contents;
+ guint id = g_hash_table_size (contents) + 1;
do
{
g_free (name);
name = g_strdup_printf ("stream%d", id++);
}
- while (g_hash_table_lookup (priv->contents, name) != NULL);
+ while (g_hash_table_lookup (contents, name) != NULL);
content_type = gabble_jingle_factory_lookup_content_type (
priv->conn->jingle_factory, content_ns);
@@ -1938,6 +1998,9 @@ gabble_jingle_session_add_content (GabbleJingleSession *sess, JingleMediaType mt
c = create_content (sess, content_type, mtype,
content_ns, transport_ns, name, NULL, NULL);
+ /* The new content better have ended up in the set we thought it would... */
+ g_assert (g_hash_table_lookup (contents, name) != NULL);
+
g_free (name);
return c;
@@ -1982,7 +2045,9 @@ GList *
gabble_jingle_session_get_contents (GabbleJingleSession *sess)
{
GabbleJingleSessionPrivate *priv = sess->priv;
- return g_hash_table_get_values (priv->contents);
+
+ return g_list_concat (g_hash_table_get_values (priv->initiator_contents),
+ g_hash_table_get_values (priv->responder_contents));
}
const gchar *
--
1.5.6.5
More information about the telepathy-commits
mailing list