changeset 24954:365eb0b68d5f

Update Jingle raw-udp to latest spec version.
author Mike Ruprecht <maiku@soc.pidgin.im>
date Fri, 07 Nov 2008 03:00:09 +0000
parents b9d4ab2c84c6
children c6c6dc6ccdc7
files libpurple/protocols/jabber/jingle/rawudp.c libpurple/protocols/jabber/jingle/rawudp.h libpurple/protocols/jabber/jingle/rtp.c
diffstat 3 files changed, 225 insertions(+), 120 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/jabber/jingle/rawudp.c	Thu Nov 06 08:26:14 2008 +0000
+++ b/libpurple/protocols/jabber/jingle/rawudp.c	Fri Nov 07 03:00:09 2008 +0000
@@ -26,10 +26,8 @@
 
 struct _JingleRawUdpPrivate
 {
-	guint generation;
-	gchar *id;
-	gchar *ip;
-	guint port;
+	GList *local_candidates;
+	GList *remote_candidates;
 };
 
 #define JINGLE_RAWUDP_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), JINGLE_TYPE_RAWUDP, JingleRawUdpPrivate))
@@ -46,12 +44,54 @@
 
 enum {
 	PROP_0,
-	PROP_GENERATION,
-	PROP_ID,
-	PROP_IP,
-	PROP_PORT,
+	PROP_LOCAL_CANDIDATES,
+	PROP_REMOTE_CANDIDATES,
 };
 
+static JingleRawUdpCandidate *
+jingle_rawudp_candidate_copy(JingleRawUdpCandidate *candidate)
+{
+	JingleRawUdpCandidate *new_candidate = g_new0(JingleRawUdpCandidate, 1);
+	new_candidate->generation = candidate->generation;
+	new_candidate->component = candidate->component;
+	new_candidate->id = g_strdup(candidate->id);
+	new_candidate->ip = g_strdup(candidate->ip);
+	new_candidate->port = candidate->port;
+	return new_candidate;
+}
+
+static void
+jingle_rawudp_candidate_free(JingleRawUdpCandidate *candidate)
+{
+	g_free(candidate->id);
+	g_free(candidate->ip);
+}
+
+GType
+jingle_rawudp_candidate_get_type()
+{
+	static GType type = 0;
+
+	if (type == 0) {
+		type = g_boxed_type_register_static("JingleRawUdpCandidate",
+				(GBoxedCopyFunc)jingle_rawudp_candidate_copy,
+				(GBoxedFreeFunc)jingle_rawudp_candidate_free);
+	}
+	return type;
+}
+
+JingleRawUdpCandidate *
+jingle_rawudp_candidate_new(const gchar *id, guint generation, guint component, const gchar *ip, guint port)
+{
+	JingleRawUdpCandidate *candidate = g_new0(JingleRawUdpCandidate, 1);
+	candidate->generation = generation;
+	candidate->component = component;
+	candidate->id = g_strdup(id);
+	candidate->ip = g_strdup(ip);
+	candidate->port = port;
+	return candidate;
+}
+
 GType
 jingle_rawudp_get_type()
 {
@@ -88,37 +128,17 @@
 	klass->parent_class.parse = jingle_rawudp_parse_internal;
 	klass->parent_class.transport_type = JINGLE_TRANSPORT_RAWUDP;
 
-	g_object_class_install_property(gobject_class, PROP_GENERATION,
-			g_param_spec_uint("generation",
-			"Generation",
-			"The generation for this transport.",
-			0,
-			G_MAXUINT,
-			0,
-			G_PARAM_READWRITE));
-
-	g_object_class_install_property(gobject_class, PROP_ID,
-			g_param_spec_string("id",
-			"Id",
-			"The id for this transport.",
-			NULL,
-			G_PARAM_READWRITE));
+	g_object_class_install_property(gobject_class, PROP_LOCAL_CANDIDATES,
+			g_param_spec_pointer("local-candidates",
+			"Local candidates",
+			"The local candidates for this transport.",
+			G_PARAM_READABLE));
 
-	g_object_class_install_property(gobject_class, PROP_IP,
-			g_param_spec_string("ip",
-			"IP Address",
-			"The IP address for this transport.",
-			NULL,
-			G_PARAM_READWRITE));
-
-	g_object_class_install_property(gobject_class, PROP_PORT,
-			g_param_spec_uint("port",
-			"Port",
-			"The port for this transport.",
-			0,
-			65535,
-			0,
-			G_PARAM_READWRITE));
+	g_object_class_install_property(gobject_class, PROP_REMOTE_CANDIDATES,
+			g_param_spec_pointer("remote-candidates",
+			"Remote candidates",
+			"The remote candidates for this transport.",
+			G_PARAM_READABLE));
 
 	g_type_class_add_private(klass, sizeof(JingleRawUdpPrivate));
 }
@@ -133,11 +153,8 @@
 static void
 jingle_rawudp_finalize (GObject *rawudp)
 {
-	JingleRawUdpPrivate *priv = JINGLE_RAWUDP_GET_PRIVATE(rawudp);
+/*	JingleRawUdpPrivate *priv = JINGLE_RAWUDP_GET_PRIVATE(rawudp); */
 	purple_debug_info("jingle","jingle_rawudp_finalize\n");
-
-	g_free(priv->id);
-	g_free(priv->ip);
 }
 
 static void
@@ -149,19 +166,13 @@
 	rawudp = JINGLE_RAWUDP(object);
 
 	switch (prop_id) {
-		case PROP_GENERATION:
-			rawudp->priv->generation = g_value_get_uint(value);
-			break;
-		case PROP_ID:
-			g_free(rawudp->priv->id);
-			rawudp->priv->id = g_value_dup_string(value);
+		case PROP_LOCAL_CANDIDATES:
+			rawudp->priv->local_candidates =
+					g_value_get_pointer(value);
 			break;
-		case PROP_IP:
-			g_free(rawudp->priv->ip);
-			rawudp->priv->ip = g_value_dup_string(value);
-			break;
-		case PROP_PORT:
-			rawudp->priv->port = g_value_get_uint(value);
+		case PROP_REMOTE_CANDIDATES:
+			rawudp->priv->remote_candidates =
+					g_value_get_pointer(value);
 			break;
 		default:	
 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -178,17 +189,11 @@
 	rawudp = JINGLE_RAWUDP(object);
 
 	switch (prop_id) {
-		case PROP_GENERATION:
-			g_value_set_uint(value, rawudp->priv->generation);
-			break;
-		case PROP_ID:
-			g_value_set_string(value, rawudp->priv->id);
+		case PROP_LOCAL_CANDIDATES:
+			g_value_set_pointer(value, rawudp->priv->local_candidates);
 			break;
-		case PROP_IP:
-			g_value_set_string(value, rawudp->priv->ip);
-			break;
-		case PROP_PORT:
-			g_value_set_uint(value, rawudp->priv->port);
+		case PROP_REMOTE_CANDIDATES:
+			g_value_set_pointer(value, rawudp->priv->remote_candidates);
 			break;
 		default:	
 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);	
@@ -196,21 +201,91 @@
 	}
 }
 
-JingleRawUdp *
-jingle_rawudp_create(guint generation, const gchar *id, const gchar *ip, guint port)
+void
+jingle_rawudp_add_local_candidate(JingleRawUdp *rawudp, JingleRawUdpCandidate *candidate)
+{
+	GList *iter = rawudp->priv->local_candidates;
+
+	for (; iter; iter = g_list_next(iter)) {
+		JingleRawUdpCandidate *c = iter->data;
+		if (!strcmp(c->id, candidate->id)) {
+			guint generation = c->generation + 1;
+
+			g_boxed_free(JINGLE_TYPE_RAWUDP_CANDIDATE, c);
+			rawudp->priv->local_candidates = g_list_delete_link(
+					rawudp->priv->local_candidates, iter);
+
+			candidate->generation = generation;
+
+			rawudp->priv->local_candidates = g_list_append(
+					rawudp->priv->local_candidates, candidate);
+			return;
+		}
+	}
+
+	rawudp->priv->local_candidates = g_list_append(
+			rawudp->priv->local_candidates, candidate);
+}
+
+GList *
+jingle_rawudp_get_remote_candidates(JingleRawUdp *rawudp)
 {
-	return g_object_new(jingle_rawudp_get_type(),
-			"generation", generation,
-			"id", id,
-			"ip", ip,
-			"port", port, NULL);
+	return g_list_copy(rawudp->priv->remote_candidates);
+}
+
+static JingleRawUdpCandidate *
+jingle_rawudp_get_remote_candidate_by_id(JingleRawUdp *rawudp, gchar *id)
+{
+	GList *iter = rawudp->priv->remote_candidates;
+	for (; iter; iter = g_list_next(iter)) {
+		JingleRawUdpCandidate *candidate = iter->data;
+		if (!strcmp(candidate->id, id)) {
+			return candidate;
+		}
+	}
+	return NULL;
+}
+
+static void
+jingle_rawudp_add_remote_candidate(JingleRawUdp *rawudp, JingleRawUdpCandidate *candidate)
+{
+	JingleRawUdpPrivate *priv = JINGLE_RAWUDP_GET_PRIVATE(rawudp);
+	JingleRawUdpCandidate *rawudp_candidate =
+			jingle_rawudp_get_remote_candidate_by_id(rawudp, candidate->id);
+	if (rawudp_candidate != NULL) {
+		priv->remote_candidates = g_list_remove(
+				priv->remote_candidates, rawudp_candidate);
+		g_boxed_free(JINGLE_TYPE_RAWUDP_CANDIDATE, rawudp_candidate);
+	}
+	priv->remote_candidates = g_list_append(priv->remote_candidates, candidate);
 }
 
 static JingleTransport *
 jingle_rawudp_parse_internal(xmlnode *rawudp)
 {
 	JingleTransport *transport = parent_class->parse(rawudp);
-	
+	JingleRawUdpPrivate *priv = JINGLE_RAWUDP_GET_PRIVATE(transport);
+	xmlnode *candidate = xmlnode_get_child(rawudp, "candidate");
+	JingleRawUdpCandidate *rawudp_candidate;
+
+	for (; candidate; candidate = xmlnode_get_next_twin(candidate)) {
+		rawudp_candidate = jingle_rawudp_candidate_new(
+				xmlnode_get_attrib(candidate, "id"),
+				atoi(xmlnode_get_attrib(candidate, "generation")),
+				atoi(xmlnode_get_attrib(candidate, "component")),
+				xmlnode_get_attrib(candidate, "ip"),
+				atoi(xmlnode_get_attrib(candidate, "port")));
+		jingle_rawudp_add_remote_candidate(JINGLE_RAWUDP(transport), rawudp_candidate);
+	}
+
+	if (g_list_length(priv->remote_candidates) == 1) {
+		/* manufacture rtcp candidate */
+		rawudp_candidate = g_boxed_copy(JINGLE_TYPE_RAWUDP_CANDIDATE, rawudp_candidate);
+		rawudp_candidate->component = 2;
+		rawudp_candidate->port = rawudp_candidate->port + 1;
+		jingle_rawudp_add_remote_candidate(JINGLE_RAWUDP(transport), rawudp_candidate);
+	}
+
 	return transport;
 }
 
@@ -220,18 +295,26 @@
 	xmlnode *node = parent_class->to_xml(transport, content, action);
 
 	if (action == JINGLE_SESSION_INITIATE || action == JINGLE_TRANSPORT_INFO) {
-		xmlnode *xmltransport = xmlnode_new_child(node, "candidate");
 		JingleRawUdpPrivate *priv = JINGLE_RAWUDP_GET_PRIVATE(transport);
-		gchar *generation = g_strdup_printf("%d", priv->generation);
-		gchar *port = g_strdup_printf("%d", priv->port);
+		GList *iter = priv->local_candidates;
+
+		for (; iter; iter = g_list_next(iter)) {
+			JingleRawUdpCandidate *candidate = iter->data;
 
-		xmlnode_set_attrib(xmltransport, "generation", generation);
-		xmlnode_set_attrib(xmltransport, "id", priv->id);
-		xmlnode_set_attrib(xmltransport, "ip", priv->ip);
-		xmlnode_set_attrib(xmltransport, "port", port);
+			xmlnode *xmltransport = xmlnode_new_child(node, "candidate");
+			gchar *generation = g_strdup_printf("%d", candidate->generation);
+			gchar *component = g_strdup_printf("%d", candidate->component);
+			gchar *port = g_strdup_printf("%d", candidate->port);
 
-		g_free(port);
-		g_free(generation);
+			xmlnode_set_attrib(xmltransport, "generation", generation);
+			xmlnode_set_attrib(xmltransport, "component", component);
+			xmlnode_set_attrib(xmltransport, "id", candidate->id);
+			xmlnode_set_attrib(xmltransport, "ip", candidate->ip);
+			xmlnode_set_attrib(xmltransport, "port", port);
+
+			g_free(port);
+			g_free(generation);
+		}
 	}
 
 	return node;
--- a/libpurple/protocols/jabber/jingle/rawudp.h	Thu Nov 06 08:26:14 2008 +0000
+++ b/libpurple/protocols/jabber/jingle/rawudp.h	Fri Nov 07 03:00:09 2008 +0000
@@ -29,6 +29,7 @@
 G_BEGIN_DECLS
 
 #define JINGLE_TYPE_RAWUDP            (jingle_rawudp_get_type())
+#define JINGLE_TYPE_RAWUDP_CANDIDATE  (jingle_rawudp_candidate_get_type())
 #define JINGLE_RAWUDP(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), JINGLE_TYPE_RAWUDP, JingleRawUdp))
 #define JINGLE_RAWUDP_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), JINGLE_TYPE_RAWUDP, JingleRawUdpClass))
 #define JINGLE_IS_RAWUDP(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), JINGLE_TYPE_RAWUDP))
@@ -41,6 +42,8 @@
 typedef struct _JingleRawUdpClass JingleRawUdpClass;
 /** @copydoc _JingleRawUdpPrivate */
 typedef struct _JingleRawUdpPrivate JingleRawUdpPrivate;
+/** @copydoc _JingleRawUdpCandidate */
+typedef struct _JingleRawUdpCandidate JingleRawUdpCandidate;
 
 /** The rawudp class */
 struct _JingleRawUdpClass
@@ -58,10 +61,21 @@
 	JingleRawUdpPrivate *priv;      /**< The private data of this object. */
 };
 
+struct _JingleRawUdpCandidate
+{
+	guint generation;
+	guint component;
+	gchar *id;
+	gchar *ip;
+	guint port;
+};
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+GType jingle_rawudp_candidate_get_type(void);
+
 /**
  * Gets the rawudp class's GType
  *
@@ -69,7 +83,10 @@
  */
 GType jingle_rawudp_get_type(void);
 
-JingleRawUdp *jingle_rawudp_create(guint generation, const gchar *id, const gchar *ip, guint port);
+JingleRawUdpCandidate *jingle_rawudp_candidate_new(const gchar *id,
+		guint generation, guint component, const gchar *ip, guint port);
+void jingle_rawudp_add_local_candidate(JingleRawUdp *rawudp, JingleRawUdpCandidate *candidate);
+GList *jingle_rawudp_get_remote_candidates(JingleRawUdp *rawudp);
 
 #ifdef __cplusplus
 }
--- a/libpurple/protocols/jabber/jingle/rtp.c	Thu Nov 06 08:26:14 2008 +0000
+++ b/libpurple/protocols/jabber/jingle/rtp.c	Fri Nov 07 03:00:09 2008 +0000
@@ -206,11 +206,22 @@
 }
 
 static JingleTransport *
-jingle_rtp_candidate_to_transport(GType type, guint generation, FsCandidate *candidate)
+jingle_rtp_candidates_to_transport(JingleSession *session, GType type, guint generation, GList *candidates)
 {
 	if (type == JINGLE_TYPE_RAWUDP) {
-		return JINGLE_TRANSPORT(jingle_rawudp_create(generation,
-				candidate->foundation, candidate->ip, candidate->port));
+		gchar *id = jabber_get_next_id(jingle_session_get_js(session));
+		JingleTransport *transport = jingle_transport_create(JINGLE_TRANSPORT_RAWUDP);
+		JingleRawUdpCandidate *rawudp_candidate;
+		for (; candidates; candidates = g_list_next(candidates)) {
+			FsCandidate *candidate = candidates->data;
+			id = jabber_get_next_id(jingle_session_get_js(session));
+			rawudp_candidate = jingle_rawudp_candidate_new(id,
+					generation, candidate->component_id,
+					candidate->ip, candidate->port);
+			jingle_rawudp_add_local_candidate(JINGLE_RAWUDP(transport), rawudp_candidate);
+		}
+		g_free(id);
+		return transport;
 #if 0
 	} else if (type == JINGLE_TYPE_ICEUDP) {
 		return NULL;
@@ -220,16 +231,22 @@
 	}
 }
 
-static FsCandidate *
-jingle_rtp_transport_to_candidate(xmlnode *transport)
+static GList *
+jingle_rtp_transport_to_candidates(JingleTransport *transport)
 {
-	xmlnode *candidate = xmlnode_get_child(transport, "candidate");
-	const gchar *type = xmlnode_get_namespace(transport);
+	const gchar *type = jingle_transport_get_transport_type(transport);
+	GList *ret = NULL;
 	if (!strcmp(type, JINGLE_TRANSPORT_RAWUDP)) {
-		return fs_candidate_new("", FS_COMPONENT_RTP,
-				FS_CANDIDATE_TYPE_SRFLX, FS_NETWORK_PROTOCOL_UDP,
-				xmlnode_get_attrib(candidate, "ip"),
-				atoi(xmlnode_get_attrib(candidate, "port")));
+		GList *candidates = jingle_rawudp_get_remote_candidates(JINGLE_RAWUDP(transport));
+
+		for (; candidates; candidates = g_list_delete_link(candidates, candidates)) {
+			JingleRawUdpCandidate *candidate = candidates->data;
+			ret = g_list_append(ret, fs_candidate_new("", candidate->component,
+					FS_CANDIDATE_TYPE_SRFLX, FS_NETWORK_PROTOCOL_UDP,
+					candidate->ip, candidate->port));
+		}
+
+		return ret;
 #if 0
 	} else if (type == JINGLE_TRANSPORT_ICEUDP) {
 		return NULL;
@@ -264,23 +281,23 @@
 jingle_rtp_new_candidate_cb(PurpleMedia *media, gchar *sid, gchar *name, FsCandidate *candidate, JingleSession *session)
 {
 	purple_debug_info("jingle-rtp", "jingle_rtp_new_candidate_cb\n");
-
-	if (candidate->component_id == 1) {
-		JingleContent *content = jingle_session_find_content(session, sid, "initiator");
-		JingleTransport *transport =
-				JINGLE_TRANSPORT(jingle_rtp_candidate_to_transport(
-				JINGLE_TYPE_RAWUDP, 0, candidate));
-		jingle_content_set_pending_transport(content, transport);
-		jingle_content_accept_transport(content);
-	}
 }
 
 static void
 jingle_rtp_candidates_prepared_cb(PurpleMedia *media, gchar *sid, gchar *name, JingleSession *session)
 {
 	JingleContent *content = jingle_session_find_content(session, sid, "initiator");
+	GList *candidates = purple_media_get_local_candidates(media, sid, name);
+	JingleTransport *transport =
+			JINGLE_TRANSPORT(jingle_rtp_candidates_to_transport(
+			session, JINGLE_TYPE_RAWUDP, 0, candidates));
+	g_list_free(candidates);
+
 	JINGLE_RTP_GET_PRIVATE(content)->candidates_ready = TRUE;
 
+	jingle_content_set_pending_transport(content, transport);
+	jingle_content_accept_transport(content);
+
 	if (jingle_rtp_ready_to_initiate(session, media))
 		jabber_iq_send(jingle_session_to_packet(session, JINGLE_SESSION_INITIATE));
 }
@@ -542,10 +559,10 @@
 		}
 		case JINGLE_SESSION_INITIATE: {
 			JingleSession *session = jingle_content_get_session(content);
+			JingleTransport *transport = jingle_transport_parse(
+					xmlnode_get_child(xmlcontent, "transport"));
 			xmlnode *description = xmlnode_get_child(xmlcontent, "description");
-			xmlnode *transport = xmlnode_get_child(xmlcontent, "transport");
-			FsCandidate *candidate = jingle_rtp_transport_to_candidate(transport);
-			GList *candidates = g_list_append(NULL, candidate);
+			GList *candidates = jingle_rtp_transport_to_candidates(transport);
 			GList *codecs = jingle_rtp_parse_codecs(description);
 
 			jingle_rtp_init_media(content);
@@ -554,12 +571,6 @@
 					jingle_content_get_name(content),
 					jingle_session_get_remote_jid(session), codecs);
 
-			/* manufacture rtcp candidate */
-			candidate = fs_candidate_copy(candidate);
-			candidate->component_id = 2;
-			candidate->port = candidate->port + 1;
-			candidates = g_list_append(candidates, candidate);
-
 			purple_media_add_remote_candidates(jingle_rtp_get_media(session),
 					jingle_content_get_name(content),
 					jingle_session_get_remote_jid(session),
@@ -580,15 +591,9 @@
 		}
 		case JINGLE_TRANSPORT_INFO: {
 			JingleSession *session = jingle_content_get_session(content);
-			xmlnode *transport = xmlnode_get_child(xmlcontent, "transport");
-			FsCandidate *candidate = jingle_rtp_transport_to_candidate(transport);
-			GList *candidates = g_list_append(NULL, candidate);
-
-			/* manufacture rtcp candidate */
-			candidate = fs_candidate_copy(candidate);
-			candidate->component_id = 2;
-			candidate->port = candidate->port + 1;
-			candidates = g_list_append(candidates, candidate);
+			JingleTransport *transport = jingle_transport_parse(
+					xmlnode_get_child(xmlcontent, "transport"));
+			GList *candidates = jingle_rtp_transport_to_candidates(transport);
 
 			purple_media_add_remote_candidates(jingle_rtp_get_media(session),
 					jingle_content_get_name(content),