changeset 23816:e73b03097664

Moved Jingle message handlers from jabber.c to jingle.c.
author Mike Ruprecht <maiku@soc.pidgin.im>
date Mon, 02 Jun 2008 20:59:20 +0000
parents 1f085713c281
children 41d6d4217d21 a7be3074923b
files libpurple/protocols/jabber/iq.c libpurple/protocols/jabber/jabber.c libpurple/protocols/jabber/jabber.h libpurple/protocols/jabber/jingle.c libpurple/protocols/jabber/jingle.h
diffstat 5 files changed, 508 insertions(+), 496 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/jabber/iq.c	Mon Jun 02 18:18:58 2008 +0000
+++ b/libpurple/protocols/jabber/iq.c	Mon Jun 02 20:59:20 2008 +0000
@@ -28,6 +28,7 @@
 #include "disco.h"
 #include "google.h"
 #include "iq.h"
+#include "jingle.h"
 #include "oob.h"
 #include "roster.h"
 #include "si.h"
@@ -368,16 +369,16 @@
 			purple_debug_info("jabber", "got Jingle package action = %s\n",
 							  action);
 			if (!strcmp(action, "session-initiate")) {
-				jabber_handle_session_initiate(js, packet);
+				jabber_jingle_session_handle_session_initiate(js, packet);
 			} else if (!strcmp(action, "session-accept")
 					   || !strcmp(action, "content-accept")) {
-				jabber_handle_session_accept(js, packet);
+				jabber_jingle_session_handle_session_accept(js, packet);
 			} else if (!strcmp(action, "session-terminate")) {
-				jabber_handle_session_terminate(js, packet);
+				jabber_jingle_session_handle_session_terminate(js, packet);
 			} else if (!strcmp(action, "transport-info")) {
-				jabber_handle_session_candidates(js, packet);
+				jabber_jingle_session_handle_transport_info(js, packet);
 			} else if (!strcmp(action, "content-replace")) {
-				jabber_handle_session_content_replace(js, packet);
+				jabber_jingle_session_handle_content_replace(js, packet);
 			}
 
 			return;
--- a/libpurple/protocols/jabber/jabber.c	Mon Jun 02 18:18:58 2008 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Mon Jun 02 20:59:20 2008 +0000
@@ -2382,264 +2382,11 @@
 
 #ifdef USE_VV
 
-static void
-jabber_session_send_accept(JingleSession *session)
-{
-	JabberIq *result = jabber_iq_new(jabber_jingle_session_get_js(session),
-									 JABBER_IQ_SET);
-	xmlnode *jingle = jabber_jingle_session_create_session_accept(session);
-	xmlnode_set_attrib(result->node, "to",
-					   jabber_jingle_session_get_remote_jid(session));
-
-	xmlnode_insert_child(result->node, jingle);
-	jabber_iq_send(result);
-	purple_debug_info("jabber", "Sent session accept, starting stream\n");
-	gst_element_set_state(purple_media_get_audio_pipeline(session->media), GST_STATE_PLAYING);
-
-	session->session_started = TRUE;
-}
-
-static void
-jabber_session_send_content_accept(JingleSession *session)
-{
-	JabberIq *result = jabber_iq_new(jabber_jingle_session_get_js(session),
-									 JABBER_IQ_SET);
-	xmlnode *jingle = jabber_jingle_session_create_content_accept(session);
-	xmlnode_set_attrib(result->node, "to",
-					   jabber_jingle_session_get_remote_jid(session));
-
-	xmlnode_insert_child(result->node, jingle);
-	jabber_iq_send(result);
-}
-
-static void
-jabber_session_send_reject(JingleSession *session)
-{
-	JabberIq *result = jabber_iq_new(jabber_jingle_session_get_js(session),
-									 JABBER_IQ_SET);
-	xmlnode *jingle = jabber_jingle_session_create_terminate(session,
-															 "decline", NULL);
-	xmlnode_set_attrib(result->node, "to", 
-					   jabber_jingle_session_get_remote_jid(session));
-	xmlnode_insert_child(result->node, jingle);
-	jabber_iq_send(result);
-	jabber_jingle_session_destroy(session);
-}
-
-static void
-jabber_session_send_terminate(JingleSession *session)
-{
-	JabberIq *result = jabber_iq_new(jabber_jingle_session_get_js(session),
-									 JABBER_IQ_SET);
-	xmlnode *jingle = jabber_jingle_session_create_terminate(session,
-															 "no-error", NULL);
-	xmlnode_set_attrib(result->node, "to",
-					   jabber_jingle_session_get_remote_jid(session));
-	xmlnode_insert_child(result->node, jingle);
-	jabber_iq_send(result);
-	jabber_jingle_session_destroy(session);
-}
-
-/* callback called when new local transport candidate(s) are available on the
-	Farsight stream */
-static void
-jabber_session_candidates_prepared(PurpleMedia *media, JingleSession *session)
-{
-	if (!jabber_jingle_session_is_initiator(session)) {
-		/* create transport-info package */
-		JabberIq *result = jabber_iq_new(jabber_jingle_session_get_js(session),
-						 JABBER_IQ_SET);
-		xmlnode *jingle = jabber_jingle_session_create_transport_info(session);
-		purple_debug_info("jabber", "jabber_session_candidates_prepared: %d candidates\n",
-				  g_list_length(purple_media_get_local_audio_candidates(session->media)));
-		xmlnode_set_attrib(result->node, "to",
-				   jabber_jingle_session_get_remote_jid(session));
-
-		xmlnode_insert_child(result->node, jingle);
-		jabber_iq_send(result);
-	}
-}
-
-/* callback called when a pair of transport candidates (local and remote)
-	has been established */
-static void
-jabber_session_candidate_pair_established(PurpleMedia *media,
-					  FsCandidate *native_candidate,
-					  FsCandidate *remote_candidate,
-					  JingleSession *session)
-{	
-	purple_debug_info("jabber", "jabber_candidate_pair_established called\n");
-	/* if we are the initiator, we should send a content-modify message */
-	if (jabber_jingle_session_is_initiator(session)) {
-		JabberIq *result;
-		xmlnode *jingle;
-		
-		purple_debug_info("jabber", "we are the initiator, let's send content-modify\n");
-
-		result = jabber_iq_new(jabber_jingle_session_get_js(session), JABBER_IQ_SET);
-
-		/* shall change this to a "content-replace" */
-		jingle =
-			jabber_jingle_session_create_content_replace(session,
-								     native_candidate,
-								     remote_candidate);
-		xmlnode_set_attrib(result->node, "to",
-						   jabber_jingle_session_get_remote_jid(session));
-		xmlnode_insert_child(result->node, jingle);
-		jabber_iq_send(result);
-	}
-}
-
-static gboolean
-jabber_initiate_media_internal(JingleSession *session, const char *initiator, const char *remote_jid)
-{
-	PurpleMedia *media = NULL;
-
-	media = purple_media_manager_create_media(purple_media_manager_get(), 
-						  session->js->gc, "fsrtpconference", remote_jid);
-
-	if (!media) {
-		purple_debug_error("jabber", "Couldn't create fsrtpconference\n");
-		return FALSE;
-	}
-
-	/* this will need to be changed to "nice" once the libnice transmitter is finished */
-	if (!purple_media_add_stream(media, remote_jid, PURPLE_MEDIA_AUDIO, "rawudp")) {
-		purple_debug_error("jabber", "Couldn't create audio stream\n");
-		purple_media_reject(media);
-		return FALSE;
-	}
-
-	jabber_jingle_session_set_remote_jid(session, remote_jid);
-	jabber_jingle_session_set_initiator(session, initiator);
-	jabber_jingle_session_set_media(session, media);
-	
-	/* connect callbacks */
-	g_signal_connect_swapped(G_OBJECT(media), "accepted", 
-				 G_CALLBACK(jabber_session_send_accept), session);
-	g_signal_connect_swapped(G_OBJECT(media), "reject", 
-				 G_CALLBACK(jabber_session_send_reject), session);
-	g_signal_connect_swapped(G_OBJECT(media), "hangup", 
-				 G_CALLBACK(jabber_session_send_terminate), session);
-	g_signal_connect(G_OBJECT(media), "candidates-prepared", 
-				 G_CALLBACK(jabber_session_candidates_prepared), session);
-	g_signal_connect(G_OBJECT(media), "candidate-pair", 
-				 G_CALLBACK(jabber_session_candidate_pair_established), session);
-
-	purple_media_ready(media);
-
-	return TRUE;
-}
-
-static void
-jabber_session_initiate_result_cb(JabberStream *js, xmlnode *packet, gpointer data)
-{
-	const char *from = xmlnode_get_attrib(packet, "from");
-	JingleSession *session = jabber_jingle_session_find_by_jid(js, from);
-	PurpleMedia *media = session->media;
-	JabberIq *result;
-	xmlnode *jingle;
-
-	if (!strcmp(xmlnode_get_attrib(packet, "type"), "error")) {
-		purple_media_got_hangup(media);
-		return;
-	}
-
-	/* catch errors */
-	if (xmlnode_get_child(packet, "error")) {
-		purple_media_got_hangup(media);
-		return;
-	}
-
-	/* create transport-info package */
-	result = jabber_iq_new(jabber_jingle_session_get_js(session), JABBER_IQ_SET);
-	jingle = jabber_jingle_session_create_transport_info(session);
-	purple_debug_info("jabber", "jabber_session_candidates_prepared: %d candidates\n",
-			  g_list_length(purple_media_get_local_audio_candidates(session->media)));
-	xmlnode_set_attrib(result->node, "to",
-			   jabber_jingle_session_get_remote_jid(session));
-
-	xmlnode_insert_child(result->node, jingle);
-	jabber_iq_send(result);
-}
-
 PurpleMedia *
 jabber_initiate_media(PurpleConnection *gc, const char *who, 
-								   PurpleMediaStreamType type)
+		      PurpleMediaStreamType type)
 {
-	/* create content negotiation */
-	JabberStream *js = gc->proto_data;
-	JabberIq *request = jabber_iq_new(js, JABBER_IQ_SET);
-	xmlnode *jingle, *content, *description, *transport;
-	GList *codecs;
-	JingleSession *session;
-	JabberBuddy *jb;
-	JabberBuddyResource *jbr;
-	
-	char *jid = NULL, *me = NULL;
-
-	/* construct JID to send to */
-	jb = jabber_buddy_find(js, who, FALSE);
-	if (!jb) {
-		purple_debug_error("jabber", "Could not find Jabber buddy\n");
-		return NULL;
-	}
-	jbr = jabber_buddy_find_resource(jb, NULL);
-	if (!jbr) {
-		purple_debug_error("jabber", "Could not find buddy's resource\n");
-	}
-
-	if ((strchr(who, '/') == NULL) && jbr && (jbr->name != NULL)) {
-		jid = g_strdup_printf("%s/%s", who, jbr->name);
-	} else {
-		jid = g_strdup(who);
-	}
-	
-	session = jabber_jingle_session_create(js);
-	/* set ourselves as initiator */
-	me = g_strdup_printf("%s@%s/%s", js->user->node, js->user->domain, js->user->resource);
-
-	if (!jabber_initiate_media_internal(session, me, jid)) {
-		g_free(jid);
-		g_free(me);
-		jabber_jingle_session_destroy(session);
-		return NULL;
-	}
-
-	g_free(jid);
-	g_free(me);
-
-	codecs = purple_media_get_local_audio_codecs(session->media);
-
-	/* create request */
-	
-	xmlnode_set_attrib(request->node, "to", 
-					   jabber_jingle_session_get_remote_jid(session));
-	jingle = xmlnode_new_child(request->node, "jingle");
-	xmlnode_set_namespace(jingle, "urn:xmpp:tmp:jingle");
-	xmlnode_set_attrib(jingle, "action", "session-initiate");
-	/* get our JID and a session id... */
-	xmlnode_set_attrib(jingle, "initiator", jabber_jingle_session_get_initiator(session));
-	xmlnode_set_attrib(jingle, "sid", jabber_jingle_session_get_id(session));
-	
-	content = xmlnode_new_child(jingle, "content");
-	xmlnode_set_attrib(content, "name", "audio-content");
-	xmlnode_set_attrib(content, "profile", "RTP/AVP");
-
-	description = jabber_jingle_session_create_description(session);
-	xmlnode_insert_child(content, description);
-
-	transport = xmlnode_new_child(content, "transport");
-	xmlnode_set_namespace(transport, "urn:xmpp:tmp:jingle:transports:ice-udp");
-
-	jabber_iq_set_callback(request, jabber_session_initiate_result_cb, NULL);
-
-	/* send request to other part */	
-	jabber_iq_send(request);
-
-	fs_codec_list_destroy(codecs);
-
-	return session->media;
+	return jabber_jingle_session_initiate_media(gc, who, type);
 }
 
 gboolean jabber_can_do_media(PurpleConnection *gc, const char *who, 
@@ -2685,234 +2432,6 @@
 	return FALSE;
 }
 
-
-void
-jabber_handle_session_accept(JabberStream *js, xmlnode *packet)
-{
-	JabberIq *result = jabber_iq_new(js, JABBER_IQ_RESULT);
-	xmlnode *jingle = xmlnode_get_child(packet, "jingle");
-	xmlnode *content = xmlnode_get_child(jingle, "content");
-	const char *sid = xmlnode_get_attrib(jingle, "sid");
-	const char *action = xmlnode_get_attrib(jingle, "action");
-	JingleSession *session = jabber_jingle_session_find_by_id(js, sid);
-	GList *remote_codecs = NULL;
-	GList *remote_transports = NULL;
-	GList *codec_intersection;
-	FsCodec *top = NULL;
-	xmlnode *description = NULL;
-	xmlnode *transport = NULL;
-
-	/* We should probably check validity of the incoming XML... */
-
-	xmlnode_set_attrib(result->node, "to",
-			jabber_jingle_session_get_remote_jid(session));
-	jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id"));
-
-	description = xmlnode_get_child(content, "description");
-	transport = xmlnode_get_child(content, "transport");
-
-	/* fetch codecs from remote party */
-	purple_debug_info("jabber", "get codecs from session-accept\n");
-	remote_codecs = jabber_jingle_get_codecs(description);
-	purple_debug_info("jabber", "get transport candidates from session accept\n");
-	remote_transports = jabber_jingle_get_candidates(transport);
-
-	purple_debug_info("jabber", "Got %d codecs from responder\n",
-			g_list_length(remote_codecs));
-	purple_debug_info("jabber", "Got %d transport candidates from responder\n",
-			g_list_length(remote_transports));
-
-	purple_debug_info("jabber", "Setting remote codecs on stream\n");
-
-	purple_media_set_remote_audio_codecs(session->media, 
-					     jabber_jingle_session_get_remote_jid(session),
-					     remote_codecs);
-
-	codec_intersection = purple_media_get_negotiated_audio_codecs(session->media);
-	purple_debug_info("jabber", "codec_intersection contains %d elems\n",
-			g_list_length(codec_intersection));
-	/* get the top codec */
-	if (g_list_length(codec_intersection) > 0) {
-		top = (FsCodec *) codec_intersection->data;
-		purple_debug_info("jabber", "Found a suitable codec on stream = %d\n",
-				top->id);
-
-		/* we have found a suitable codec, but we will not start the stream
-		   just yet, wait for transport negotiation to complete... */
-	}
-	/* if we also got transport candidates, add them to our streams
-	   list of known remote candidates */
-	if (g_list_length(remote_transports) > 0) {
-		purple_media_add_remote_audio_candidates(session->media,
-							 jabber_jingle_session_get_remote_jid(session),
-							 remote_transports);
-		fs_candidate_list_destroy(remote_transports);
-	}
-	if (g_list_length(codec_intersection) == 0 &&
-			g_list_length(remote_transports)) {
-		/* we didn't get any candidates and the codec intersection is empty,
-		   this means this was not a content-accept message and we couldn't
-		   find any suitable codecs, should return error and hang up */
-
-	}
-
-	g_list_free(codec_intersection);
-
-	if (!strcmp(action, "session-accept")) {
-		purple_media_got_accept(jabber_jingle_session_get_media(session));
-		purple_debug_info("jabber", "Got session-accept, starting stream\n");
-		gst_element_set_state(purple_media_get_audio_pipeline(session->media), GST_STATE_PLAYING);
-	}
-
-	jabber_iq_send(result);
-
-	session->session_started = TRUE;
-}
-
-void
-jabber_handle_session_terminate(JabberStream *js, xmlnode *packet)
-{
-	JabberIq *result = jabber_iq_new(js, JABBER_IQ_SET);
-	xmlnode *jingle = xmlnode_get_child(packet, "jingle");
-	const char *sid = xmlnode_get_attrib(jingle, "sid");
-	JingleSession *session = jabber_jingle_session_find_by_id(js, sid);
-
-	if(!session) {
-		purple_debug_error("jabber", "jabber_handle_session_terminate couldn't find session\n");
-		return;
-	}
-
-	xmlnode_set_attrib(result->node, "to",
-			jabber_jingle_session_get_remote_jid(session));
-	xmlnode_set_attrib(result->node, "id", xmlnode_get_attrib(packet, "id"));
-
-
-
-	/* maybe we should look at the reasoncode to determine if it was
-	   a hangup or a reject, and call different callbacks to purple_media */
-	gst_element_set_state(purple_media_get_audio_pipeline(session->media), GST_STATE_NULL);
-
-	purple_media_got_hangup(jabber_jingle_session_get_media(session));
-	jabber_iq_send(result);
-	jabber_jingle_session_destroy(session);
-}
-
-void
-jabber_handle_session_candidates(JabberStream *js, xmlnode *packet)
-{
-	JabberIq *result = jabber_iq_new(js, JABBER_IQ_RESULT);
-	xmlnode *jingle = xmlnode_get_child(packet, "jingle");
-	xmlnode *content = xmlnode_get_child(jingle, "content");
-	xmlnode *transport = xmlnode_get_child(content, "transport");
-	GList *remote_candidates = jabber_jingle_get_candidates(transport);
-	const char *sid = xmlnode_get_attrib(jingle, "sid");
-	JingleSession *session = jabber_jingle_session_find_by_id(js, sid);
-
-	if(!session)
-		purple_debug_error("jabber", "jabber_handle_session_candidates couldn't find session\n");
-
-	/* send acknowledement */
-	xmlnode_set_attrib(result->node, "id", xmlnode_get_attrib(packet, "id"));
-	xmlnode_set_attrib(result->node, "to", xmlnode_get_attrib(packet, "from"));
-	jabber_iq_send(result);
-
-	/* add candidates to our list of remote candidates */
-	if (g_list_length(remote_candidates) > 0) {
-		purple_media_add_remote_audio_candidates(session->media,
-							 xmlnode_get_attrib(packet, "from"),
-							 remote_candidates);
-		fs_candidate_list_destroy(remote_candidates);
-	}
-}
-
-/* change this to content-replace */
-void
-jabber_handle_session_content_replace(JabberStream *js, xmlnode *packet)
-{
-	xmlnode *jingle = xmlnode_get_child(packet, "jingle");
-	const char *sid = xmlnode_get_attrib(jingle, "sid");
-	JingleSession *session = jabber_jingle_session_find_by_id(js, sid);
-
-	if (!jabber_jingle_session_is_initiator(session) && session->session_started) {
-		JabberIq *result = jabber_iq_new(js, JABBER_IQ_RESULT);
-		JabberIq *accept = jabber_iq_new(js, JABBER_IQ_SET);
-		xmlnode *content_accept = NULL;
-
-		/* send acknowledement */
-		xmlnode_set_attrib(result->node, "id", xmlnode_get_attrib(packet, "id"));
-		xmlnode_set_attrib(result->node, "to", xmlnode_get_attrib(packet, "from"));
-		jabber_iq_send(result);
-
-		/* send content-accept */
-		content_accept = jabber_jingle_session_create_content_accept(session);
-		xmlnode_set_attrib(accept->node, "id", xmlnode_get_attrib(packet, "id"));
-		xmlnode_set_attrib(accept->node, "to", xmlnode_get_attrib(packet, "from"));
-		xmlnode_insert_child(accept->node, content_accept);
-
-		jabber_iq_send(accept);
-	}
-}
-
-void 
-jabber_handle_session_initiate(JabberStream *js, xmlnode *packet)
-{
-	JingleSession *session = NULL;
-	xmlnode *jingle = xmlnode_get_child(packet, "jingle");
-	xmlnode *content = NULL;
-	xmlnode *description = NULL;
-	const char *sid = NULL;
-	const char *initiator = NULL;
-	GList *codecs = NULL;
-	JabberIq *result = NULL;
-
-	if (!jingle) {
-		purple_debug_error("jabber", "Malformed request");
-		return;
-	}
-
-	sid = xmlnode_get_attrib(jingle, "sid");
-	initiator = xmlnode_get_attrib(jingle, "initiator");
-
-	if (jabber_jingle_session_find_by_id(js, sid)) {
-		/* This should only happen if you start a session with yourself */
-		purple_debug_error("jabber", "Jingle session with id={%s} already exists\n", sid);
-		return;
-	}
-	session = jabber_jingle_session_create_by_id(js, sid);
-
-	/* init media */
-	content = xmlnode_get_child(jingle, "content");
-	if (!content) {
-		purple_debug_error("jabber", "jingle tag must contain content tag\n");
-		/* should send error here */
-		return;
-	}
-
-	description = xmlnode_get_child(content, "description");
-
-	if (!description) {
-		purple_debug_error("jabber", "content tag must contain description tag\n");
-		/* we should create an error iq here */
-		return;
-	}
-
-	if (!jabber_initiate_media_internal(session, initiator, initiator)) {
-		purple_debug_error("jabber", "Couldn't start media session with %s\n", initiator);
-		jabber_jingle_session_destroy(session);
-		/* we should create an error iq here */
-		return;
-	}
-
-	codecs = jabber_jingle_get_codecs(description);
-
-	purple_media_set_remote_audio_codecs(session->media, initiator, codecs);
-
-	result = jabber_iq_new(js, JABBER_IQ_RESULT);
-	jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id"));
-	xmlnode_set_attrib(result->node, "to", xmlnode_get_attrib(packet, "from"));
-	jabber_iq_send(result);
-}
-
 #endif
 
 void jabber_register_commands(void)
--- a/libpurple/protocols/jabber/jabber.h	Mon Jun 02 18:18:58 2008 +0000
+++ b/libpurple/protocols/jabber/jabber.h	Mon Jun 02 20:59:20 2008 +0000
@@ -278,14 +278,6 @@
 #ifdef USE_VV
 PurpleMedia *jabber_initiate_media(PurpleConnection *gc, const char *who, PurpleMediaStreamType type);
 gboolean jabber_can_do_media(PurpleConnection *gc, const char *who, PurpleMediaStreamType type);
-
-/* Jingle handle session messages */
-void jabber_handle_session_initiate(JabberStream *js, xmlnode *packet);
-void jabber_handle_session_accept(JabberStream *js, xmlnode *packet);
-void jabber_handle_session_terminate(JabberStream *js, xmlnode *packet);
-void jabber_handle_session_candidates(JabberStream *js, xmlnode *packet);
-void jabber_handle_session_content_replace(JabberStream *js, xmlnode *packet);
-
 #endif
 
 #endif /* _PURPLE_JABBER_H_ */
--- a/libpurple/protocols/jabber/jingle.c	Mon Jun 02 18:18:58 2008 +0000
+++ b/libpurple/protocols/jabber/jingle.c	Mon Jun 02 20:59:20 2008 +0000
@@ -21,6 +21,7 @@
 #include "purple.h"
 #include "jingle.h"
 #include "xmlnode.h"
+#include "iq.h"
 
 #include <stdlib.h>
 #include <string.h>
@@ -487,4 +488,492 @@
 	return jingle;
 }
 
+static void
+jabber_jingle_session_send_content_accept(JingleSession *session)
+{
+	JabberIq *result = jabber_iq_new(jabber_jingle_session_get_js(session),
+					 JABBER_IQ_SET);
+	xmlnode *jingle = jabber_jingle_session_create_content_accept(session);
+	xmlnode_set_attrib(result->node, "to",
+			   jabber_jingle_session_get_remote_jid(session));
+
+	xmlnode_insert_child(result->node, jingle);
+	jabber_iq_send(result);
+}
+
+static void
+jabber_jingle_session_send_session_accept(JingleSession *session)
+{
+	JabberIq *result = jabber_iq_new(jabber_jingle_session_get_js(session),
+					 JABBER_IQ_SET);
+	xmlnode *jingle = jabber_jingle_session_create_session_accept(session);
+	xmlnode_set_attrib(result->node, "to",
+			   jabber_jingle_session_get_remote_jid(session));
+
+	xmlnode_insert_child(result->node, jingle);
+	jabber_iq_send(result);
+	purple_debug_info("jabber", "Sent session accept, starting stream\n");
+	gst_element_set_state(purple_media_get_audio_pipeline(session->media), GST_STATE_PLAYING);
+
+	session->session_started = TRUE;
+}
+
+static void
+jabber_jingle_session_send_session_reject(JingleSession *session)
+{
+	JabberIq *result = jabber_iq_new(jabber_jingle_session_get_js(session),
+					 JABBER_IQ_SET);
+	xmlnode *jingle = jabber_jingle_session_create_terminate(session,
+								 "decline", NULL);
+	xmlnode_set_attrib(result->node, "to", 
+			   jabber_jingle_session_get_remote_jid(session));
+	xmlnode_insert_child(result->node, jingle);
+	jabber_iq_send(result);
+	jabber_jingle_session_destroy(session);
+}
+
+static void
+jabber_jingle_session_send_session_terminate(JingleSession *session)
+{
+	JabberIq *result = jabber_iq_new(jabber_jingle_session_get_js(session),
+					 JABBER_IQ_SET);
+	xmlnode *jingle = jabber_jingle_session_create_terminate(session,
+								 "no-error", NULL);
+	xmlnode_set_attrib(result->node, "to",
+			   jabber_jingle_session_get_remote_jid(session));
+	xmlnode_insert_child(result->node, jingle);
+	jabber_iq_send(result);
+	jabber_jingle_session_destroy(session);
+}
+
+/* callback called when new local transport candidate(s) are available on the
+	Farsight stream */
+static void
+jabber_jingle_session_candidates_prepared(PurpleMedia *media, JingleSession *session)
+{
+	if (!jabber_jingle_session_is_initiator(session)) {
+		/* create transport-info package */
+		JabberIq *result = jabber_iq_new(jabber_jingle_session_get_js(session),
+						 JABBER_IQ_SET);
+		xmlnode *jingle = jabber_jingle_session_create_transport_info(session);
+		purple_debug_info("jabber", "jabber_session_candidates_prepared: %d candidates\n",
+				  g_list_length(purple_media_get_local_audio_candidates(session->media)));
+		xmlnode_set_attrib(result->node, "to",
+				   jabber_jingle_session_get_remote_jid(session));
+
+		xmlnode_insert_child(result->node, jingle);
+		jabber_iq_send(result);
+	}
+}
+
+/* callback called when a pair of transport candidates (local and remote)
+	has been established */
+static void
+jabber_jingle_session_candidate_pair_established(PurpleMedia *media,
+						 FsCandidate *native_candidate,
+						 FsCandidate *remote_candidate,
+						 JingleSession *session)
+{	
+	purple_debug_info("jabber", "jabber_candidate_pair_established called\n");
+	/* if we are the initiator, we should send a content-modify message */
+	if (jabber_jingle_session_is_initiator(session)) {
+		JabberIq *result;
+		xmlnode *jingle;
+		
+		purple_debug_info("jabber", "we are the initiator, let's send content-modify\n");
+
+		result = jabber_iq_new(jabber_jingle_session_get_js(session), JABBER_IQ_SET);
+
+		/* shall change this to a "content-replace" */
+		jingle = jabber_jingle_session_create_content_replace(session,
+								      native_candidate,
+								      remote_candidate);
+		xmlnode_set_attrib(result->node, "to",
+				   jabber_jingle_session_get_remote_jid(session));
+		xmlnode_insert_child(result->node, jingle);
+		jabber_iq_send(result);
+	}
+}
+
+static gboolean
+jabber_jingle_session_initiate_media_internal(JingleSession *session,
+					      const char *initiator,
+					      const char *remote_jid)
+{
+	PurpleMedia *media = NULL;
+
+	media = purple_media_manager_create_media(purple_media_manager_get(), 
+						  session->js->gc, "fsrtpconference", remote_jid);
+
+	if (!media) {
+		purple_debug_error("jabber", "Couldn't create fsrtpconference\n");
+		return FALSE;
+	}
+
+	/* this will need to be changed to "nice" once the libnice transmitter is finished */
+	if (!purple_media_add_stream(media, remote_jid, PURPLE_MEDIA_AUDIO, "rawudp")) {
+		purple_debug_error("jabber", "Couldn't create audio stream\n");
+		purple_media_reject(media);
+		return FALSE;
+	}
+
+	jabber_jingle_session_set_remote_jid(session, remote_jid);
+	jabber_jingle_session_set_initiator(session, initiator);
+	jabber_jingle_session_set_media(session, media);
+
+	/* connect callbacks */
+	g_signal_connect_swapped(G_OBJECT(media), "accepted", 
+				 G_CALLBACK(jabber_jingle_session_send_session_accept), session);
+	g_signal_connect_swapped(G_OBJECT(media), "reject", 
+				 G_CALLBACK(jabber_jingle_session_send_session_reject), session);
+	g_signal_connect_swapped(G_OBJECT(media), "hangup", 
+				 G_CALLBACK(jabber_jingle_session_send_session_terminate), session);
+	g_signal_connect(G_OBJECT(media), "candidates-prepared", 
+				 G_CALLBACK(jabber_jingle_session_candidates_prepared), session);
+	g_signal_connect(G_OBJECT(media), "candidate-pair", 
+				 G_CALLBACK(jabber_jingle_session_candidate_pair_established), session);
+
+	purple_media_ready(media);
+
+	return TRUE;
+}
+
+static void
+jabber_jingle_session_initiate_result_cb(JabberStream *js, xmlnode *packet, gpointer data)
+{
+	const char *from = xmlnode_get_attrib(packet, "from");
+	JingleSession *session = jabber_jingle_session_find_by_jid(js, from);
+	PurpleMedia *media = session->media;
+	JabberIq *result;
+	xmlnode *jingle;
+
+	if (!strcmp(xmlnode_get_attrib(packet, "type"), "error")) {
+		purple_media_got_hangup(media);
+		return;
+	}
+
+	/* catch errors */
+	if (xmlnode_get_child(packet, "error")) {
+		purple_media_got_hangup(media);
+		return;
+	}
+
+	/* create transport-info package */
+	result = jabber_iq_new(jabber_jingle_session_get_js(session), JABBER_IQ_SET);
+	jingle = jabber_jingle_session_create_transport_info(session);
+	purple_debug_info("jabber", "jabber_session_candidates_prepared: %d candidates\n",
+			  g_list_length(purple_media_get_local_audio_candidates(session->media)));
+	xmlnode_set_attrib(result->node, "to",
+			   jabber_jingle_session_get_remote_jid(session));
+
+	xmlnode_insert_child(result->node, jingle);
+	jabber_iq_send(result);
+}
+
+PurpleMedia *
+jabber_jingle_session_initiate_media(PurpleConnection *gc, const char *who, 
+				     PurpleMediaStreamType type)
+{
+	/* create content negotiation */
+	JabberStream *js = gc->proto_data;
+	JabberIq *request = jabber_iq_new(js, JABBER_IQ_SET);
+	xmlnode *jingle, *content, *description, *transport;
+	GList *codecs;
+	JingleSession *session;
+	JabberBuddy *jb;
+	JabberBuddyResource *jbr;
+	
+	char *jid = NULL, *me = NULL;
+
+	/* construct JID to send to */
+	jb = jabber_buddy_find(js, who, FALSE);
+	if (!jb) {
+		purple_debug_error("jabber", "Could not find Jabber buddy\n");
+		return NULL;
+	}
+	jbr = jabber_buddy_find_resource(jb, NULL);
+	if (!jbr) {
+		purple_debug_error("jabber", "Could not find buddy's resource\n");
+	}
+
+	if ((strchr(who, '/') == NULL) && jbr && (jbr->name != NULL)) {
+		jid = g_strdup_printf("%s/%s", who, jbr->name);
+	} else {
+		jid = g_strdup(who);
+	}
+	
+	session = jabber_jingle_session_create(js);
+	/* set ourselves as initiator */
+	me = g_strdup_printf("%s@%s/%s", js->user->node, js->user->domain, js->user->resource);
+
+	if (!jabber_jingle_session_initiate_media_internal(session, me, jid)) {
+		g_free(jid);
+		g_free(me);
+		jabber_jingle_session_destroy(session);
+		return NULL;
+	}
+
+	g_free(jid);
+	g_free(me);
+
+	codecs = purple_media_get_local_audio_codecs(session->media);
+
+	/* create request */
+	
+	xmlnode_set_attrib(request->node, "to", 
+			   jabber_jingle_session_get_remote_jid(session));
+	jingle = xmlnode_new_child(request->node, "jingle");
+	xmlnode_set_namespace(jingle, "urn:xmpp:tmp:jingle");
+	xmlnode_set_attrib(jingle, "action", "session-initiate");
+	/* get our JID and a session id... */
+	xmlnode_set_attrib(jingle, "initiator", jabber_jingle_session_get_initiator(session));
+	xmlnode_set_attrib(jingle, "sid", jabber_jingle_session_get_id(session));
+	
+	content = xmlnode_new_child(jingle, "content");
+	xmlnode_set_attrib(content, "name", "audio-content");
+	xmlnode_set_attrib(content, "profile", "RTP/AVP");
+
+	description = jabber_jingle_session_create_description(session);
+	xmlnode_insert_child(content, description);
+
+	transport = xmlnode_new_child(content, "transport");
+	xmlnode_set_namespace(transport, "urn:xmpp:tmp:jingle:transports:ice-udp");
+
+	jabber_iq_set_callback(request, jabber_jingle_session_initiate_result_cb, NULL);
+
+	/* send request to other part */	
+	jabber_iq_send(request);
+
+	fs_codec_list_destroy(codecs);
+
+	return session->media;
+}
+
+void
+jabber_jingle_session_handle_content_replace(JabberStream *js, xmlnode *packet)
+{
+	xmlnode *jingle = xmlnode_get_child(packet, "jingle");
+	const char *sid = xmlnode_get_attrib(jingle, "sid");
+	JingleSession *session = jabber_jingle_session_find_by_id(js, sid);
+
+	if (!jabber_jingle_session_is_initiator(session) && session->session_started) {
+		JabberIq *result = jabber_iq_new(js, JABBER_IQ_RESULT);
+		JabberIq *accept = jabber_iq_new(js, JABBER_IQ_SET);
+		xmlnode *content_accept = NULL;
+
+		/* send acknowledement */
+		xmlnode_set_attrib(result->node, "id", xmlnode_get_attrib(packet, "id"));
+		xmlnode_set_attrib(result->node, "to", xmlnode_get_attrib(packet, "from"));
+		jabber_iq_send(result);
+
+		/* send content-accept */
+		content_accept = jabber_jingle_session_create_content_accept(session);
+		xmlnode_set_attrib(accept->node, "id", xmlnode_get_attrib(packet, "id"));
+		xmlnode_set_attrib(accept->node, "to", xmlnode_get_attrib(packet, "from"));
+		xmlnode_insert_child(accept->node, content_accept);
+
+		jabber_iq_send(accept);
+	}
+}
+
+void
+jabber_jingle_session_handle_session_accept(JabberStream *js, xmlnode *packet)
+{
+	JabberIq *result = jabber_iq_new(js, JABBER_IQ_RESULT);
+	xmlnode *jingle = xmlnode_get_child(packet, "jingle");
+	xmlnode *content = xmlnode_get_child(jingle, "content");
+	const char *sid = xmlnode_get_attrib(jingle, "sid");
+	const char *action = xmlnode_get_attrib(jingle, "action");
+	JingleSession *session = jabber_jingle_session_find_by_id(js, sid);
+	GList *remote_codecs = NULL;
+	GList *remote_transports = NULL;
+	GList *codec_intersection;
+	FsCodec *top = NULL;
+	xmlnode *description = NULL;
+	xmlnode *transport = NULL;
+
+	/* We should probably check validity of the incoming XML... */
+
+	xmlnode_set_attrib(result->node, "to",
+			   jabber_jingle_session_get_remote_jid(session));
+	jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id"));
+
+	description = xmlnode_get_child(content, "description");
+	transport = xmlnode_get_child(content, "transport");
+
+	/* fetch codecs from remote party */
+	purple_debug_info("jabber", "get codecs from session-accept\n");
+	remote_codecs = jabber_jingle_get_codecs(description);
+	purple_debug_info("jabber", "get transport candidates from session accept\n");
+	remote_transports = jabber_jingle_get_candidates(transport);
+
+	purple_debug_info("jabber", "Got %d codecs from responder\n",
+			  g_list_length(remote_codecs));
+	purple_debug_info("jabber", "Got %d transport candidates from responder\n",
+			  g_list_length(remote_transports));
+
+	purple_debug_info("jabber", "Setting remote codecs on stream\n");
+
+	purple_media_set_remote_audio_codecs(session->media, 
+					     jabber_jingle_session_get_remote_jid(session),
+					     remote_codecs);
+
+	codec_intersection = purple_media_get_negotiated_audio_codecs(session->media);
+	purple_debug_info("jabber", "codec_intersection contains %d elems\n",
+			  g_list_length(codec_intersection));
+	/* get the top codec */
+	if (g_list_length(codec_intersection) > 0) {
+		top = (FsCodec *) codec_intersection->data;
+		purple_debug_info("jabber", "Found a suitable codec on stream = %d\n",
+				  top->id);
+
+		/* we have found a suitable codec, but we will not start the stream
+		   just yet, wait for transport negotiation to complete... */
+	}
+	/* if we also got transport candidates, add them to our streams
+	   list of known remote candidates */
+	if (g_list_length(remote_transports) > 0) {
+		purple_media_add_remote_audio_candidates(session->media,
+							 jabber_jingle_session_get_remote_jid(session),
+							 remote_transports);
+		fs_candidate_list_destroy(remote_transports);
+	}
+	if (g_list_length(codec_intersection) == 0 &&
+			g_list_length(remote_transports)) {
+		/* we didn't get any candidates and the codec intersection is empty,
+		   this means this was not a content-accept message and we couldn't
+		   find any suitable codecs, should return error and hang up */
+
+	}
+
+	g_list_free(codec_intersection);
+
+	if (!strcmp(action, "session-accept")) {
+		purple_media_got_accept(jabber_jingle_session_get_media(session));
+		purple_debug_info("jabber", "Got session-accept, starting stream\n");
+		gst_element_set_state(purple_media_get_audio_pipeline(session->media),
+				      GST_STATE_PLAYING);
+	}
+
+	jabber_iq_send(result);
+
+	session->session_started = TRUE;
+}
+
+void 
+jabber_jingle_session_handle_session_initiate(JabberStream *js, xmlnode *packet)
+{
+	JingleSession *session = NULL;
+	xmlnode *jingle = xmlnode_get_child(packet, "jingle");
+	xmlnode *content = NULL;
+	xmlnode *description = NULL;
+	const char *sid = NULL;
+	const char *initiator = NULL;
+	GList *codecs = NULL;
+	JabberIq *result = NULL;
+
+	if (!jingle) {
+		purple_debug_error("jabber", "Malformed request");
+		return;
+	}
+
+	sid = xmlnode_get_attrib(jingle, "sid");
+	initiator = xmlnode_get_attrib(jingle, "initiator");
+
+	if (jabber_jingle_session_find_by_id(js, sid)) {
+		/* This should only happen if you start a session with yourself */
+		purple_debug_error("jabber", "Jingle session with id={%s} already exists\n", sid);
+		return;
+	}
+	session = jabber_jingle_session_create_by_id(js, sid);
+
+	/* init media */
+	content = xmlnode_get_child(jingle, "content");
+	if (!content) {
+		purple_debug_error("jabber", "jingle tag must contain content tag\n");
+		/* should send error here */
+		return;
+	}
+
+	description = xmlnode_get_child(content, "description");
+
+	if (!description) {
+		purple_debug_error("jabber", "content tag must contain description tag\n");
+		/* we should create an error iq here */
+		return;
+	}
+
+	if (!jabber_jingle_session_initiate_media_internal(session, initiator, initiator)) {
+		purple_debug_error("jabber", "Couldn't start media session with %s\n", initiator);
+		jabber_jingle_session_destroy(session);
+		/* we should create an error iq here */
+		return;
+	}
+
+	codecs = jabber_jingle_get_codecs(description);
+
+	purple_media_set_remote_audio_codecs(session->media, initiator, codecs);
+
+	result = jabber_iq_new(js, JABBER_IQ_RESULT);
+	jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id"));
+	xmlnode_set_attrib(result->node, "to", xmlnode_get_attrib(packet, "from"));
+	jabber_iq_send(result);
+}
+
+void
+jabber_jingle_session_handle_session_terminate(JabberStream *js, xmlnode *packet)
+{
+	JabberIq *result = jabber_iq_new(js, JABBER_IQ_SET);
+	xmlnode *jingle = xmlnode_get_child(packet, "jingle");
+	const char *sid = xmlnode_get_attrib(jingle, "sid");
+	JingleSession *session = jabber_jingle_session_find_by_id(js, sid);
+
+	if (!session) {
+		purple_debug_error("jabber", "jabber_handle_session_terminate couldn't find session\n");
+		return;
+	}
+
+	xmlnode_set_attrib(result->node, "to",
+			   jabber_jingle_session_get_remote_jid(session));
+	xmlnode_set_attrib(result->node, "id", xmlnode_get_attrib(packet, "id"));
+
+
+
+	/* maybe we should look at the reasoncode to determine if it was
+	   a hangup or a reject, and call different callbacks to purple_media */
+	gst_element_set_state(purple_media_get_audio_pipeline(session->media), GST_STATE_NULL);
+
+	purple_media_got_hangup(jabber_jingle_session_get_media(session));
+	jabber_iq_send(result);
+	jabber_jingle_session_destroy(session);
+}
+
+void
+jabber_jingle_session_handle_transport_info(JabberStream *js, xmlnode *packet)
+{
+	JabberIq *result = jabber_iq_new(js, JABBER_IQ_RESULT);
+	xmlnode *jingle = xmlnode_get_child(packet, "jingle");
+	xmlnode *content = xmlnode_get_child(jingle, "content");
+	xmlnode *transport = xmlnode_get_child(content, "transport");
+	GList *remote_candidates = jabber_jingle_get_candidates(transport);
+	const char *sid = xmlnode_get_attrib(jingle, "sid");
+	JingleSession *session = jabber_jingle_session_find_by_id(js, sid);
+
+	if (!session)
+		purple_debug_error("jabber", "jabber_handle_session_candidates couldn't find session\n");
+
+	/* send acknowledement */
+	xmlnode_set_attrib(result->node, "id", xmlnode_get_attrib(packet, "id"));
+	xmlnode_set_attrib(result->node, "to", xmlnode_get_attrib(packet, "from"));
+	jabber_iq_send(result);
+
+	/* add candidates to our list of remote candidates */
+	if (g_list_length(remote_candidates) > 0) {
+		purple_media_add_remote_audio_candidates(session->media,
+							 xmlnode_get_attrib(packet, "from"),
+							 remote_candidates);
+		fs_candidate_list_destroy(remote_candidates);
+	}
+}
+
 #endif /* USE_VV */
--- a/libpurple/protocols/jabber/jingle.h	Mon Jun 02 18:18:58 2008 +0000
+++ b/libpurple/protocols/jabber/jingle.h	Mon Jun 02 20:59:20 2008 +0000
@@ -85,6 +85,17 @@
 
 GList *jabber_jingle_get_candidates(const xmlnode *transport);
 
+PurpleMedia *jabber_jingle_session_initiate_media(PurpleConnection *gc,
+						  const char *who,
+						  PurpleMediaStreamType type);
+
+/* Jingle message handlers */
+void jabber_jingle_session_handle_content_replace(JabberStream *js, xmlnode *packet);
+void jabber_jingle_session_handle_session_accept(JabberStream *js, xmlnode *packet);
+void jabber_jingle_session_handle_session_initiate(JabberStream *js, xmlnode *packet);
+void jabber_jingle_session_handle_session_terminate(JabberStream *js, xmlnode *packet);
+void jabber_jingle_session_handle_transport_info(JabberStream *js, xmlnode *packet);
+
 G_END_DECLS
 
 #endif /* USE_VV */