# HG changeset patch # User Mike Ruprecht # Date 1212440360 0 # Node ID e73b03097664cef53782ea65438046e2991a56fe # Parent 1f085713c281cc73da6d6061b584dfdde1306405 Moved Jingle message handlers from jabber.c to jingle.c. diff -r 1f085713c281 -r e73b03097664 libpurple/protocols/jabber/iq.c --- 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; diff -r 1f085713c281 -r e73b03097664 libpurple/protocols/jabber/jabber.c --- 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) diff -r 1f085713c281 -r e73b03097664 libpurple/protocols/jabber/jabber.h --- 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_ */ diff -r 1f085713c281 -r e73b03097664 libpurple/protocols/jabber/jingle.c --- 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 #include @@ -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 */ diff -r 1f085713c281 -r e73b03097664 libpurple/protocols/jabber/jingle.h --- 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 */