# HG changeset patch # User Mike Ruprecht # Date 1224391043 0 # Node ID bd598b606ca4b04b35e8d877a25037c584a11728 # Parent 5a774d0817d808211989cb612c51bccddb79b134 Restructure Jingle code to more easily support multiple application types. Actually negotiate a rawudp transport rather than pretending to use iceudp. diff -r 5a774d0817d8 -r bd598b606ca4 libpurple/marshallers.list --- a/libpurple/marshallers.list Fri Sep 05 02:18:15 2008 +0000 +++ b/libpurple/marshallers.list Sun Oct 19 04:37:23 2008 +0000 @@ -1,3 +1,4 @@ VOID:BOXED,BOXED VOID:POINTER,POINTER,OBJECT BOOLEAN:OBJECT +VOID:STRING,STRING diff -r 5a774d0817d8 -r bd598b606ca4 libpurple/media.c --- a/libpurple/media.c Fri Sep 05 02:18:15 2008 +0000 +++ b/libpurple/media.c Sun Oct 19 04:37:23 2008 +0000 @@ -210,8 +210,8 @@ G_TYPE_POINTER, FS_TYPE_CANDIDATE); purple_media_signals[CANDIDATES_PREPARED] = g_signal_new("candidates-prepared", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); + purple_smarshal_VOID__STRING_STRING, + G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING); purple_media_signals[CANDIDATE_PAIR] = g_signal_new("candidate-pair", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, purple_smarshal_VOID__BOXED_BOXED, @@ -675,7 +675,7 @@ if (session->session == fssession) { g_signal_emit(session->media, purple_media_signals[CODECS_READY], - 0, &session->id); + 0, session->id); g_list_free(sessions); break; } @@ -1003,7 +1003,8 @@ g_object_get(stream, "participant", &participant, NULL); g_object_get(participant, "cname", &name, NULL); g_object_unref(participant); - g_signal_emit(session->media, purple_media_signals[CANDIDATES_PREPARED], 0); + g_signal_emit(session->media, purple_media_signals[CANDIDATES_PREPARED], + 0, session->id, name); g_free(name); } diff -r 5a774d0817d8 -r bd598b606ca4 libpurple/media.h --- a/libpurple/media.h Fri Sep 05 02:18:15 2008 +0000 +++ b/libpurple/media.h Sun Oct 19 04:37:23 2008 +0000 @@ -27,6 +27,8 @@ #ifndef __MEDIA_H_ #define __MEDIA_H_ +#include "internal.h" + #ifdef USE_VV #include diff -r 5a774d0817d8 -r bd598b606ca4 libpurple/mediamanager.h --- a/libpurple/mediamanager.h Fri Sep 05 02:18:15 2008 +0000 +++ b/libpurple/mediamanager.h Sun Oct 19 04:37:23 2008 +0000 @@ -27,6 +27,8 @@ #ifndef __MEDIA_MANAGER_H_ #define __MEDIA_MANAGER_H_ +#include "internal.h" + #ifdef USE_VV #include diff -r 5a774d0817d8 -r bd598b606ca4 libpurple/protocols/jabber/Makefile.am --- a/libpurple/protocols/jabber/Makefile.am Fri Sep 05 02:18:15 2008 +0000 +++ b/libpurple/protocols/jabber/Makefile.am Sun Oct 19 04:37:23 2008 +0000 @@ -19,8 +19,18 @@ iq.h \ jabber.c \ jabber.h \ - jingle.c \ - jingle.h \ + jingle/jingle.c \ + jingle/jingle.h \ + jingle/content.c \ + jingle/content.h \ + jingle/rawudp.c \ + jingle/rawudp.h \ + jingle/rtp.c \ + jingle/rtp.h \ + jingle/session.c \ + jingle/session.h \ + jingle/transport.c \ + jingle/transport.h \ jutil.c \ jutil.h \ message.c \ diff -r 5a774d0817d8 -r bd598b606ca4 libpurple/protocols/jabber/Makefile.mingw --- a/libpurple/protocols/jabber/Makefile.mingw Fri Sep 05 02:18:15 2008 +0000 +++ b/libpurple/protocols/jabber/Makefile.mingw Sun Oct 19 04:37:23 2008 +0000 @@ -52,6 +52,12 @@ google.c \ iq.c \ jabber.c \ + jingle/jingle.c \ + jingle/content.c \ + jingle/rawudp.c \ + jingle/rtp.c \ + jingle/session.c \ + jingle/transport.c \ jutil.c \ message.c \ oob.c \ diff -r 5a774d0817d8 -r bd598b606ca4 libpurple/protocols/jabber/disco.c --- a/libpurple/protocols/jabber/disco.c Fri Sep 05 02:18:15 2008 +0000 +++ b/libpurple/protocols/jabber/disco.c Sun Oct 19 04:37:23 2008 +0000 @@ -28,6 +28,7 @@ #include "iq.h" #include "disco.h" #include "jabber.h" +#include "jingle/jingle.h" #include "presence.h" #include "roster.h" #include "pep.h" @@ -157,10 +158,10 @@ SUPPORT_FEATURE("http://www.google.com/transport/p2p"); SUPPORT_FEATURE("http://www.google.com/transport/raw-udp"); SUPPORT_FEATURE("http://www.google.com/session/phone"); - SUPPORT_FEATURE("urn:xmpp:tmp:jingle"); - SUPPORT_FEATURE("urn:xmpp:tmp:jingle:apps:rtp#audio"); - SUPPORT_FEATURE("urn:xmpp:tmp:jingle:apps:rtp#video"); - SUPPORT_FEATURE("urn:xmpp:tmp:jingle:transports:ice-udp"); + SUPPORT_FEATURE(JINGLE); + SUPPORT_FEATURE(JINGLE_APP_RTP_SUPPORT_AUDIO); + SUPPORT_FEATURE(JINGLE_APP_RTP_SUPPORT_VIDEO); + SUPPORT_FEATURE(JINGLE_TRANSPORT_RAWUDP); #endif } else { const char *ext = NULL; diff -r 5a774d0817d8 -r bd598b606ca4 libpurple/protocols/jabber/iq.c --- a/libpurple/protocols/jabber/iq.c Fri Sep 05 02:18:15 2008 +0000 +++ b/libpurple/protocols/jabber/iq.c Sun Oct 19 04:37:23 2008 +0000 @@ -28,7 +28,7 @@ #include "disco.h" #include "google.h" #include "iq.h" -#include "jingle.h" +#include "jingle/jingle.h" #include "oob.h" #include "roster.h" #include "si.h" @@ -360,8 +360,8 @@ } #ifdef USE_VV - if (xmlnode_get_child_with_namespace(packet, "jingle", "urn:xmpp:tmp:jingle")) { - jabber_jingle_session_parse(js, packet); + if (xmlnode_get_child_with_namespace(packet, "jingle", JINGLE)) { + jingle_parse(js, packet); return; } #endif @@ -406,7 +406,7 @@ jabber_iq_register_handler("jabber:iq:register", jabber_register_parse); jabber_iq_register_handler("urn:xmpp:ping", urn_xmpp_ping_parse); #ifdef USE_VV - jabber_iq_register_handler("urn:xmpp:tmp:jingle", jabber_jingle_session_parse); + jabber_iq_register_handler(JINGLE, jingle_parse); #endif } diff -r 5a774d0817d8 -r bd598b606ca4 libpurple/protocols/jabber/jabber.c --- a/libpurple/protocols/jabber/jabber.c Fri Sep 05 02:18:15 2008 +0000 +++ b/libpurple/protocols/jabber/jabber.c Sun Oct 19 04:37:23 2008 +0000 @@ -56,13 +56,13 @@ #include "xdata.h" #include "pep.h" #include "adhoccommands.h" -#include "jingle.h" + +#include "jingle/jingle.h" +#include "jingle/rtp.h" #ifdef USE_VV #include -#define XEP_0167_AUDIO_CAP "urn:xmpp:tmp:jingle:apps:rtp#audio" -#define XEP_0167_VIDEO_CAP "urn:xmpp:tmp:jingle:apps:rtp#video" #define GTALK_CAP "http://www.google.com/session/phone" #endif @@ -1285,7 +1285,7 @@ #ifdef USE_VV /* Close all of the open Jingle sessions on this stream */ - jabber_jingle_session_terminate_sessions(js); + jingle_terminate_sessions(js); #endif /* Don't perform any actions on the ssl connection @@ -1923,7 +1923,7 @@ return; #ifdef USE_VV - jabber_jingle_session_terminate_session_media(js, who); + jingle_rtp_terminate_session(js, who); #endif if((jb = jabber_buddy_find(js, who, TRUE)) && (jbr = jabber_buddy_find_resource(jb, jid->resource))) { @@ -2408,7 +2408,7 @@ jabber_initiate_media(PurpleConnection *gc, const char *who, PurpleMediaSessionType type) { - return jabber_jingle_session_initiate_media(gc->proto_data, who, type); + return jingle_rtp_initiate_media(gc->proto_data, who, type); } gboolean jabber_can_do_media(PurpleConnection *gc, const char *who, @@ -2432,18 +2432,18 @@ if (type == (PURPLE_MEDIA_AUDIO | PURPLE_MEDIA_VIDEO)) { purple_debug_info("jabber", "Checking audio/video XEP support for %s\n", who); - return (jabber_buddy_has_capability(jb, XEP_0167_AUDIO_CAP) || + return (jabber_buddy_has_capability(jb, JINGLE_APP_RTP_SUPPORT_AUDIO) || jabber_buddy_has_capability(jb, GTALK_CAP)) && - jabber_buddy_has_capability(jb, XEP_0167_VIDEO_CAP); + jabber_buddy_has_capability(jb, JINGLE_APP_RTP_SUPPORT_VIDEO); } else if (type == (PURPLE_MEDIA_AUDIO)) { purple_debug_info("jabber", "Checking audio XEP support for %s\n", who); - return jabber_buddy_has_capability(jb, XEP_0167_AUDIO_CAP) || + return jabber_buddy_has_capability(jb, JINGLE_APP_RTP_SUPPORT_AUDIO) || jabber_buddy_has_capability(jb, GTALK_CAP); } else if (type == (PURPLE_MEDIA_VIDEO)) { purple_debug_info("jabber", "Checking video XEP support for %s\n", who); - return jabber_buddy_has_capability(jb, XEP_0167_VIDEO_CAP); + return jabber_buddy_has_capability(jb, JINGLE_APP_RTP_SUPPORT_VIDEO); } return FALSE; diff -r 5a774d0817d8 -r bd598b606ca4 libpurple/protocols/jabber/jabber.h --- a/libpurple/protocols/jabber/jabber.h Fri Sep 05 02:18:15 2008 +0000 +++ b/libpurple/protocols/jabber/jabber.h Sun Oct 19 04:37:23 2008 +0000 @@ -247,6 +247,7 @@ #ifdef USE_VV /* keep a hash table of JingleSessions */ GHashTable *sessions; + GHashTable *medias; #endif }; diff -r 5a774d0817d8 -r bd598b606ca4 libpurple/protocols/jabber/jingle.c --- a/libpurple/protocols/jabber/jingle.c Fri Sep 05 02:18:15 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1520 +0,0 @@ -/* - * purple - Jabber Protocol Plugin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -#include "config.h" -#include "purple.h" -#include "jingle.h" -#include "xmlnode.h" -#include "iq.h" - -#include -#include -#include - -#ifdef USE_VV - -#include - -#define JINGLE "urn:xmpp:tmp:jingle" -#define JINGLE_RTP "urn:xmpp:tmp:jingle:apps:rtp" -#define JINGLE_RTP_INFO "urn:xmpp:tmp:jingle:apps:rtp:info" -#define TRANSPORT_ICEUDP "urn:xmpp:tmp:jingle:transports:ice-udp" - -typedef enum { - PENDING, - GOT_ACK, - ACCEPTED, - ACTIVE -} JingleSessionState; - -typedef struct { - char *id; - JabberStream *js; - PurpleMedia *media; - char *remote_jid; - char *initiator; - gboolean is_initiator; - JingleSessionState state; - GHashTable *contents; /* JingleSessionContent table */ -} JingleSession; - -typedef struct { - gchar *name; - JingleSession *session; - gchar *creator; - gchar *sender; - gchar *transport_type; - gchar *type; - gchar *subtype; -} JingleSessionContent; - -static void -jabber_jingle_session_content_create_internal(JingleSession *session, - const gchar *name, - const gchar *creator, - const gchar *sender, - const gchar *transport_type, - const gchar *type, - const gchar *subtype) -{ - JingleSessionContent *content = g_new0(JingleSessionContent, 1); - content->session = session; - content->name = g_strdup(name); - content->creator = g_strdup(creator); - content->sender = g_strdup(sender); - content->transport_type = g_strdup(transport_type); - content->type = g_strdup(type); - content->subtype = g_strdup(subtype); - - if (!session->contents) { - purple_debug_info("jingle", "Creating hash table for contents\n"); - session->contents = g_hash_table_new(g_str_hash, g_str_equal); - } - purple_debug_info("jingle", "inserting content with name == \"%s\" into table\n", - content->name); - g_hash_table_insert(session->contents, content->name, content); -} - -static void -jabber_jingle_session_destroy_content(JingleSessionContent *content) -{ - purple_debug_info("jingle", "destroying content with name == \"%s\"\n", - content->name); - g_hash_table_remove(content->session->contents, content->name); - g_free(content->name); - g_free(content->creator); - g_free(content->sender); - g_free(content->transport_type); - g_free(content->type); - g_free(content->subtype); - g_free(content); -} - -static const gchar * -jabber_jingle_session_content_get_name(const JingleSessionContent *jsc) -{ - return jsc->name; -} - -static JingleSession * -jabber_jingle_session_content_get_session(const JingleSessionContent *jsc) -{ - return jsc->session; -} - -static const gchar * -jabber_jingle_session_content_get_creator(const JingleSessionContent *jsc) -{ - return jsc->creator; -} - -static const gchar * -jabber_jingle_session_content_get_sender(const JingleSessionContent *jsc) -{ - return jsc->sender; -} - -static const gchar * -jabber_jingle_session_content_get_transport_type(const JingleSessionContent *jsc) -{ - return jsc->transport_type; -} - -static gboolean -jabber_jingle_session_content_is_transport_type(const JingleSessionContent *jsc, - const gchar *transport_type) -{ - return !strcmp(jabber_jingle_session_content_get_transport_type(jsc), - transport_type); -} - -static const gchar * -jabber_jingle_session_content_get_type(const JingleSessionContent *jsc) -{ - return jsc->type; -} - -static gboolean -jabber_jingle_session_content_is_type(const JingleSessionContent *jsc, - const gchar *type) -{ - return !strcmp(jabber_jingle_session_content_get_type(jsc), type); -} - -static gchar * -jabber_jingle_session_content_get_subtype(const JingleSessionContent *jsc) -{ - return jsc->subtype; -} - -static gboolean -jabber_jingle_session_content_is_vv_type(const JingleSessionContent *jsc, - const gchar *media_type) -{ - return jabber_jingle_session_content_is_type(jsc, JINGLE_RTP) && - !strcmp(jabber_jingle_session_content_get_subtype(jsc), - media_type); -} - -static void -jabber_jingle_session_content_set_sender(JingleSessionContent *jsc, - const char *sender) -{ - if (jsc->sender) - g_free(jsc->sender); - jsc->sender = g_strdup(sender); -} - -static gboolean -jabber_jingle_session_equal(gconstpointer a, gconstpointer b) -{ - purple_debug_info("jingle", - "jabber_jingle_session_equal, comparing %s and %s\n", - ((JingleSession *)a)->id, - ((JingleSession *)b)->id); - return !strcmp(((JingleSession *) a)->id, ((JingleSession *) b)->id); -} - -static JingleSession * -jabber_jingle_session_create_internal(JabberStream *js, - const char *id) -{ - JingleSession *sess = g_new0(JingleSession, 1); - sess->js = js; - - if (id) { - sess->id = g_strdup(id); - } else if (js) { - /* init the session ID... */ - sess->id = jabber_get_next_id(js); - } - - /* insert it into the hash table */ - if (!js->sessions) { - purple_debug_info("jingle", "Creating hash table for sessions\n"); - js->sessions = g_hash_table_new(g_str_hash, g_str_equal); - } - purple_debug_info("jingle", "inserting session with key: %s into table\n", - sess->id); - g_hash_table_insert(js->sessions, sess->id, sess); - - sess->state = PENDING; - - return sess; -} - -static JabberStream * -jabber_jingle_session_get_js(const JingleSession *sess) -{ - return sess->js; -} - -static JingleSession * -jabber_jingle_session_create(JabberStream *js) -{ - JingleSession *sess = jabber_jingle_session_create_internal(js, NULL); - sess->is_initiator = TRUE; - return sess; -} - -static JingleSession * -jabber_jingle_session_create_by_id(JabberStream *js, const char *id) -{ - JingleSession *sess = jabber_jingle_session_create_internal(js, id); - sess->is_initiator = FALSE; - return sess; -} - -static const char * -jabber_jingle_session_get_id(const JingleSession *sess) -{ - return sess->id; -} - -static void -jabber_jingle_session_destroy(JingleSession *sess) -{ - GList *contents; - g_hash_table_remove(sess->js->sessions, sess->id); - g_free(sess->id); - g_free(sess->remote_jid); - g_free(sess->initiator); - - if (sess->media) - g_object_unref(sess->media); - - for (contents = g_hash_table_get_values(sess->contents); contents; - contents = g_list_delete_link(contents, contents)) - jabber_jingle_session_destroy_content(contents->data); - - g_free(sess); -} - -static JingleSession * -jabber_jingle_session_find_by_id(JabberStream *js, const char *id) -{ - purple_debug_info("jingle", "find_by_id %s\n", id); - purple_debug_info("jingle", "lookup: %p\n", (js->sessions) ? - g_hash_table_lookup(js->sessions, id) : NULL); - return (JingleSession *) (js->sessions) ? - g_hash_table_lookup(js->sessions, id) : NULL; -} - -static JingleSession * -jabber_jingle_session_find_by_jid(JabberStream *js, const char *jid) -{ - GList *values = (js->sessions) ? - g_hash_table_get_values(js->sessions) : NULL; - gboolean use_bare = strchr(jid, '/') == NULL; - - for (; values; values = g_list_delete_link(values, values)) { - JingleSession *session = (JingleSession *)values->data; - gchar *cmp_jid = use_bare ? jabber_get_bare_jid(session->remote_jid) - : g_strdup(session->remote_jid); - if (!strcmp(jid, cmp_jid)) { - g_free(cmp_jid); - g_list_free(values); - return session; - } - g_free(cmp_jid); - } - - return NULL; -} - -static GList * -jabber_jingle_get_codecs(xmlnode *description) -{ - GList *codecs = NULL; - xmlnode *codec_element = NULL; - const char *encoding_name,*id, *clock_rate; - FsCodec *codec; - const gchar *media = xmlnode_get_attrib(description, "media"); - FsMediaType type = !strcmp(media, "video") ? FS_MEDIA_TYPE_VIDEO : - !strcmp(media, "audio") ? FS_MEDIA_TYPE_AUDIO : 0; - - for (codec_element = xmlnode_get_child(description, "payload-type") ; - codec_element ; - codec_element = xmlnode_get_next_twin(codec_element)) { - xmlnode *param; - gchar *codec_str; - encoding_name = xmlnode_get_attrib(codec_element, "name"); - - id = xmlnode_get_attrib(codec_element, "id"); - clock_rate = xmlnode_get_attrib(codec_element, "clockrate"); - - codec = fs_codec_new(atoi(id), encoding_name, - type, - clock_rate ? atoi(clock_rate) : 0); - - for (param = xmlnode_get_child(codec_element, "parameter"); - param; param = xmlnode_get_next_twin(param)) { - fs_codec_add_optional_parameter(codec, - xmlnode_get_attrib(param, "name"), - xmlnode_get_attrib(param, "value")); - } - - codec_str = fs_codec_to_string(codec); - purple_debug_fatal("jingle", "received codec: %s\n", codec_str); - g_free(codec_str); - - codecs = g_list_append(codecs, codec); - } - return codecs; -} - -static GList * -jabber_jingle_get_candidates(const xmlnode *transport) -{ - GList *candidates = NULL; - xmlnode *candidate = NULL; - FsCandidate *c; - - for (candidate = xmlnode_get_child(transport, "candidate") ; - candidate ; - candidate = xmlnode_get_next_twin(candidate)) { - const char *type = xmlnode_get_attrib(candidate, "type"); - c = fs_candidate_new(xmlnode_get_attrib(candidate, "component"), - atoi(xmlnode_get_attrib(candidate, "component")), - strcmp(type, "host") == 0 ? - FS_CANDIDATE_TYPE_HOST : - strcmp(type, "prflx") == 0 ? - FS_CANDIDATE_TYPE_PRFLX : - strcmp(type, "relay") == 0 ? - FS_CANDIDATE_TYPE_RELAY : - strcmp(type, "srflx") == 0 ? - FS_CANDIDATE_TYPE_SRFLX : 0, - strcmp(xmlnode_get_attrib(candidate, "protocol"), - "udp") == 0 ? - FS_NETWORK_PROTOCOL_UDP : - FS_NETWORK_PROTOCOL_TCP, - xmlnode_get_attrib(candidate, "ip"), - atoi(xmlnode_get_attrib(candidate, "port"))); - candidates = g_list_append(candidates, c); - } - - return candidates; -} - -static JingleSessionContent * -jabber_jingle_session_get_content(const JingleSession *session, - const char *name) -{ - return (JingleSession *) name ? - g_hash_table_lookup(session->contents, name) : NULL; -} - -static GList * -jabber_jingle_session_get_contents(const JingleSession *session) -{ - return g_hash_table_get_values(session->contents); -} - -static PurpleMedia * -jabber_jingle_session_get_media(const JingleSession *sess) -{ - return sess->media; -} - -static void -jabber_jingle_session_set_media(JingleSession *sess, PurpleMedia *media) -{ - sess->media = media; -} - -static const char * -jabber_jingle_session_get_remote_jid(const JingleSession *sess) -{ - return sess->remote_jid; -} - -static void -jabber_jingle_session_set_remote_jid(JingleSession *sess, - const char *remote_jid) -{ - if (sess->remote_jid) - g_free(sess->remote_jid); - sess->remote_jid = g_strdup(remote_jid); -} - -static JingleSessionState -jabber_jingle_session_get_state(JingleSession *sess) -{ - return sess->state; -} - -static void -jabber_jingle_session_set_state(JingleSession *sess, - JingleSessionState state) -{ - sess->state = state; -} - - -static const char * -jabber_jingle_session_get_initiator(const JingleSession *sess) -{ - return sess->initiator; -} - -static void -jabber_jingle_session_set_initiator(JingleSession *sess, - const char *initiator) -{ - if (sess->initiator) - g_free(sess->initiator); - sess->initiator = g_strdup(initiator); -} - -static gboolean -jabber_jingle_session_is_initiator(const JingleSession *sess) -{ - return sess->is_initiator; -} - -static void -jabber_jingle_session_add_payload_types(const JingleSessionContent *jsc, - xmlnode *description, - GList *codecs) -{ - for (; codecs ; codecs = codecs->next) { - FsCodec *codec = (FsCodec*)codecs->data; - GList *iter = codec->optional_params; - char id[8], clockrate[10], channels[10]; - gchar *codec_str; - xmlnode *payload = xmlnode_new_child(description, "payload-type"); - - g_snprintf(id, sizeof(id), "%d", codec->id); - g_snprintf(clockrate, sizeof(clockrate), "%d", codec->clock_rate); - g_snprintf(channels, sizeof(channels), "%d", codec->channels); - - xmlnode_set_attrib(payload, "name", codec->encoding_name); - xmlnode_set_attrib(payload, "id", id); - xmlnode_set_attrib(payload, "clockrate", clockrate); - xmlnode_set_attrib(payload, "channels", channels); - - for (; iter; iter = g_list_next(iter)) { - FsCodecParameter *fsparam = iter->data; - xmlnode *param = xmlnode_new_child(payload, "parameter"); - xmlnode_set_attrib(param, "name", fsparam->name); - xmlnode_set_attrib(param, "value", fsparam->value); - } - - codec_str = fs_codec_to_string(codec); - purple_debug_fatal("jingle", "adding codec: %s\n", codec_str); - g_free(codec_str); - } -} - -static xmlnode * -jabber_jingle_session_add_description_vv(const JingleSessionContent *jsc, - xmlnode *description) -{ - xmlnode_set_attrib(description, "media", - jabber_jingle_session_content_get_subtype(jsc)); - return description; -} - -static xmlnode * -jabber_jingle_session_add_description(const JingleSessionContent *jsc, - xmlnode *content) -{ - xmlnode *description = xmlnode_new_child(content, "description"); - xmlnode_set_namespace(description, - jabber_jingle_session_content_get_type(jsc)); - - if (jabber_jingle_session_content_is_type(jsc, JINGLE_RTP)) - return jabber_jingle_session_add_description_vv(jsc, description); - else - return description; -} - -static xmlnode * -jabber_jingle_session_add_candidate_iceudp(xmlnode *transport, - FsCandidate *c, - FsCandidate *remote) -{ - char port[8]; - char prio[8]; - char component[8]; - xmlnode *candidate = xmlnode_new_child(transport, "candidate"); - - g_snprintf(port, sizeof(port), "%d", c->port); - g_snprintf(prio, sizeof(prio), "%d", c->priority); - g_snprintf(component, sizeof(component), "%d", c->component_id); - - xmlnode_set_attrib(candidate, "component", component); - xmlnode_set_attrib(candidate, "foundation", "1"); /* what about this? */ - xmlnode_set_attrib(candidate, "generation", "0"); /* ? */ - xmlnode_set_attrib(candidate, "ip", c->ip); - xmlnode_set_attrib(candidate, "network", "0"); /* ? */ - xmlnode_set_attrib(candidate, "port", port); - xmlnode_set_attrib(candidate, "priority", prio); /* Is this correct? */ - xmlnode_set_attrib(candidate, "protocol", - c->proto == FS_NETWORK_PROTOCOL_UDP ? - "udp" : "tcp"); - if (c->username) - xmlnode_set_attrib(transport, "ufrag", c->username); - if (c->password) - xmlnode_set_attrib(transport, "pwd", c->password); - - xmlnode_set_attrib(candidate, "type", - c->type == FS_CANDIDATE_TYPE_HOST ? - "host" : - c->type == FS_CANDIDATE_TYPE_PRFLX ? - "prflx" : - c->type == FS_CANDIDATE_TYPE_RELAY ? - "relay" : - c->type == FS_CANDIDATE_TYPE_SRFLX ? - "srflx" : NULL); - - /* relay */ - if (c->type == FS_CANDIDATE_TYPE_RELAY) { - /* set rel-addr and rel-port? How? */ - } - - if (remote) { - char remote_port[8]; - g_snprintf(remote_port, sizeof(remote_port), "%d", remote->port); - xmlnode_set_attrib(candidate, "rem-addr", remote->ip); - xmlnode_set_attrib(candidate, "rem-port", remote_port); - } - - return candidate; -} - -static xmlnode * -jabber_jingle_session_add_transport(const JingleSessionContent *jsc, - xmlnode *content) -{ - xmlnode *transport = xmlnode_new_child(content, "transport"); - const gchar *transport_type = jabber_jingle_session_content_get_transport_type(jsc); - xmlnode_set_namespace(transport, transport_type); - return transport; -} - -static xmlnode * -jabber_jingle_session_add_content(const JingleSessionContent *jsc, - xmlnode *jingle) -{ - xmlnode *content = xmlnode_new_child(jingle, "content"); - xmlnode_set_attrib(content, "creator", - jabber_jingle_session_content_get_creator(jsc)); - xmlnode_set_attrib(content, "name", - jabber_jingle_session_content_get_name(jsc)); - xmlnode_set_attrib(content, "sender", - jabber_jingle_session_content_get_sender(jsc)); - return content; -} - - -static xmlnode * -jabber_jingle_session_add_jingle(const JingleSession *sess, - JabberIq *iq, const char *action) -{ - xmlnode *jingle = iq ? xmlnode_new_child(iq->node, "jingle") : - xmlnode_new("jingle"); - xmlnode_set_namespace(jingle, JINGLE); - xmlnode_set_attrib(jingle, "action", action); - xmlnode_set_attrib(jingle, "initiator", - jabber_jingle_session_get_initiator(sess)); - if (jabber_jingle_session_is_initiator(sess)) - xmlnode_set_attrib(jingle, "responder", - jabber_jingle_session_get_remote_jid(sess)); - else { - gchar *responder = g_strdup_printf("%s@%s/%s", - sess->js->user->node, - sess->js->user->domain, - sess->js->user->resource); - xmlnode_set_attrib(jingle, "responder", responder); - g_free(responder); - } - xmlnode_set_attrib(jingle, "sid", jabber_jingle_session_get_id(sess)); - - return jingle; -} - -static JabberIq * -jabber_jingle_session_create_ack(JingleSession *session, xmlnode *jingle) -{ - JabberIq *result = jabber_iq_new( - jabber_jingle_session_get_js(session), - JABBER_IQ_RESULT); - xmlnode *packet = xmlnode_get_parent(jingle); - jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id")); - xmlnode_set_attrib(result->node, "from", xmlnode_get_attrib(packet, "to")); - xmlnode_set_attrib(result->node, "to", xmlnode_get_attrib(packet, "from")); - return result; -} - -static JabberIq * -jabber_jingle_session_create_iq(const JingleSession *session) -{ - JabberIq *result = jabber_iq_new(jabber_jingle_session_get_js(session), - JABBER_IQ_SET); - gchar *from = g_strdup_printf("%s@%s/%s", session->js->user->node, - session->js->user->domain, - session->js->user->resource); - xmlnode_set_attrib(result->node, "from", from); - g_free(from); - xmlnode_set_attrib(result->node, "to", - jabber_jingle_session_get_remote_jid(session)); - return result; -} -#if 0 -static xmlnode * -jabber_jingle_session_create_content_accept(const JingleSession *sess) -{ - xmlnode *jingle = - jabber_jingle_session_add_jingle(sess, NULL, "content-accept"); - - xmlnode *content = xmlnode_new_child(jingle, "content"); - xmlnode *description = jabber_jingle_session_create_description(sess); - - xmlnode_set_attrib(content, "creator", "initiator"); - xmlnode_set_attrib(content, "name", "audio-content"); - xmlnode_set_attrib(content, "profile", "RTP/AVP"); - - xmlnode_insert_child(content, description); - - return jingle; -} - -static xmlnode * -jabber_jingle_session_create_content_replace(const JingleSession *sess, - FsCandidate *native_candidate, - FsCandidate *remote_candidate) -{ - xmlnode *jingle = - jabber_jingle_session_add_jingle(sess, NULL, "content-replace"); - xmlnode *content = NULL; - xmlnode *transport = NULL; - - purple_debug_info("jingle", "creating content-modify for native candidate %s " \ - ", remote candidate %s\n", native_candidate->candidate_id, - remote_candidate->candidate_id); - - content = xmlnode_new_child(jingle, "content"); - xmlnode_set_attrib(content, "creator", "initiator"); - xmlnode_set_attrib(content, "name", "audio-content"); - xmlnode_set_attrib(content, "profile", "RTP/AVP"); - - /* get top codec from codec_intersection to put here... */ - /* later on this should probably handle changing codec */ - - xmlnode_insert_child(content, jabber_jingle_session_create_description(sess)); - - transport = xmlnode_new_child(content, "transport"); - xmlnode_set_namespace(transport, TRANSPORT_ICEUDP); - jabber_jingle_session_add_candidate_iceudp(transport, native_candidate, - remote_candidate); - - purple_debug_info("jingle", "End create content modify\n"); - - return jingle; -} -#endif - -static JabberIq * -jabber_jingle_session_create_session_accept(const JingleSession *session) -{ - PurpleMedia *media = jabber_jingle_session_get_media(session); - const gchar *remote_jid = jabber_jingle_session_get_remote_jid(session); - JabberIq *request = jabber_jingle_session_create_iq(session); - xmlnode *jingle = - jabber_jingle_session_add_jingle(session, request, - "session-accept"); - GList *contents = jabber_jingle_session_get_contents(session); - - for (; contents; contents = contents->next) { - JingleSessionContent *jsc = contents->data; - const gchar *content_name = jabber_jingle_session_content_get_name(jsc); - xmlnode *content = jabber_jingle_session_add_content(jsc, jingle); - xmlnode *description = jabber_jingle_session_add_description(jsc, content); - xmlnode *transport = jabber_jingle_session_add_transport(jsc, content); - if (jabber_jingle_session_content_is_type(jsc, JINGLE_RTP)) { - GList *codecs = purple_media_get_negotiated_codecs(media, - content_name); - jabber_jingle_session_add_payload_types(jsc, description, codecs); - fs_codec_list_destroy(codecs); - } - if (jabber_jingle_session_content_is_transport_type(jsc, TRANSPORT_ICEUDP)) { - jabber_jingle_session_add_candidate_iceudp(transport, - purple_media_get_local_candidate(media, content_name, - remote_jid), - purple_media_get_remote_candidate(media, content_name, - remote_jid)); - } - } - - return request; -} - -static JabberIq * -jabber_jingle_session_create_session_info(const JingleSession *session, - const gchar *type) -{ - JabberIq *request = jabber_jingle_session_create_iq(session); - xmlnode *jingle = - jabber_jingle_session_add_jingle(session, request, - "session-info"); - xmlnode *info = xmlnode_new_child(jingle, type); - xmlnode_set_namespace(info, JINGLE_RTP_INFO); - return request; -} - -static JabberIq * -jabber_jingle_session_create_session_initiate(const JingleSession *session) -{ - JabberIq *request = jabber_jingle_session_create_iq(session); - xmlnode *jingle = - jabber_jingle_session_add_jingle(session, request, - "session-initiate"); - GList *contents = jabber_jingle_session_get_contents(session); - - for (; contents; contents = contents->next) { - JingleSessionContent *jsc = contents->data; - xmlnode *content = jabber_jingle_session_add_content(jsc, jingle); - xmlnode *description = jabber_jingle_session_add_description(jsc, content); - if (jabber_jingle_session_content_is_type(jsc, JINGLE_RTP)) { - PurpleMedia *media = jabber_jingle_session_get_media(session); - const gchar *content_name = - jabber_jingle_session_content_get_name(jsc); - GList *codecs = purple_media_get_local_codecs(media, content_name); - jabber_jingle_session_add_payload_types(jsc, description, codecs); - fs_codec_list_destroy(codecs); - } - jabber_jingle_session_add_transport(jsc, content); - } - - return request; -} - -static JabberIq * -jabber_jingle_session_create_session_terminate(const JingleSession *sess, - const char *reasoncode, - const char *reasontext) -{ - JabberIq *request = jabber_jingle_session_create_iq(sess); - xmlnode *jingle = - jabber_jingle_session_add_jingle(sess, request, - "session-terminate"); - xmlnode *reason = xmlnode_new_child(jingle, "reason"); - xmlnode_new_child(reason, reasoncode); - if (reasontext) { - xmlnode *text = xmlnode_new_child(reason, "text"); - xmlnode_insert_data(text, reasontext, strlen(reasontext)); - } - - return request; -} - -static JabberIq * -jabber_jingle_session_create_transport_info(const JingleSessionContent *jsc, - FsCandidate *candidate) -{ - JingleSession *session = - jabber_jingle_session_content_get_session(jsc); - JabberIq *request = jabber_jingle_session_create_iq(session); - xmlnode *jingle = - jabber_jingle_session_add_jingle(session, request, - "transport-info"); - xmlnode *content = jabber_jingle_session_add_content(jsc, jingle); - xmlnode *transport = jabber_jingle_session_add_transport(jsc, content); - jabber_jingle_session_add_candidate_iceudp(transport, candidate, NULL); - return request; -} -#if 0 -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); -} -#endif - -static void -jabber_jingle_session_accept(JingleSession *session) -{ - if (jabber_jingle_session_get_state(session) == ACCEPTED && - purple_media_candidates_prepared( - jabber_jingle_session_get_media(session), - jabber_jingle_session_get_remote_jid(session))) { - jabber_iq_send(jabber_jingle_session_create_session_accept(session)); - - purple_debug_info("jingle", "Sent session accept.\n"); - jabber_jingle_session_set_state(session, ACTIVE); - } -} - -static void -jabber_jingle_session_send_session_accept(JingleSession *session) -{ - /* create transport-info packages */ - PurpleMedia *media = jabber_jingle_session_get_media(session); - GList *contents = jabber_jingle_session_get_contents(session); - const gchar *remote_jid = jabber_jingle_session_get_remote_jid(session); - for (; contents; contents = contents->next) { - JingleSessionContent *jsc = contents->data; - GList *candidates = purple_media_get_local_candidates( - media, - jabber_jingle_session_content_get_name(jsc), - remote_jid); - purple_debug_info("jingle", - "jabber_session_candidates_prepared: %d candidates\n", - g_list_length(candidates)); - for (; candidates; candidates = candidates->next) { - FsCandidate *candidate = candidates->data; - JabberIq *result = jabber_jingle_session_create_transport_info(jsc, - candidate); - jabber_iq_send(result); - } - fs_candidate_list_destroy(candidates); - - purple_debug_info("jingle", "codec intersection: %i\n", - g_list_length(purple_media_get_negotiated_codecs(media, - jabber_jingle_session_content_get_name(jsc)))); - } - - jabber_jingle_session_set_state(session, ACCEPTED); - jabber_jingle_session_accept(session); -} - -static void -jabber_jingle_session_send_session_reject(JingleSession *session) -{ - jabber_iq_send(jabber_jingle_session_create_session_terminate(session, - "decline", NULL)); - jabber_jingle_session_destroy(session); -} - -static void -jabber_jingle_session_send_session_terminate(JingleSession *session) -{ - jabber_iq_send(jabber_jingle_session_create_session_terminate(session, - "no-error", NULL)); - jabber_jingle_session_destroy(session); -} - -static void -jabber_jingle_session_content_create_media(JingleSession *session, - PurpleMediaSessionType type) -{ - gchar sender[10] = ""; - - if (type & PURPLE_MEDIA_AUDIO) { - if ((type & PURPLE_MEDIA_AUDIO) == PURPLE_MEDIA_SEND_AUDIO) - strcpy(sender, "initiator"); - else if ((type & PURPLE_MEDIA_AUDIO) == PURPLE_MEDIA_RECV_AUDIO) - strcpy(sender, "responder"); - else - strcpy(sender, "both"); - jabber_jingle_session_content_create_internal(session, - "audio-content", "initiator", sender, - TRANSPORT_ICEUDP, JINGLE_RTP, "audio"); - } - if (type & PURPLE_MEDIA_VIDEO) { - if ((type & PURPLE_MEDIA_VIDEO) == PURPLE_MEDIA_SEND_VIDEO) - strcpy(sender, "initiator"); - else if ((type & PURPLE_MEDIA_VIDEO) == PURPLE_MEDIA_RECV_VIDEO) - strcpy(sender, "responder"); - else - strcpy(sender, "both"); - jabber_jingle_session_content_create_internal(session, - "video-content", "initiator", sender, - TRANSPORT_ICEUDP, JINGLE_RTP, "video"); - } -} - -static void -jabber_jingle_session_content_create_parse(JingleSession *session, - xmlnode *content) -{ - xmlnode *description = xmlnode_get_child(content, "description"); - xmlnode *transport = xmlnode_get_child(content, "transport"); - - const gchar *creator = xmlnode_get_attrib(content, "creator"); - const gchar *sender = xmlnode_get_attrib(content, "sender"); - const gchar *subtype = xmlnode_get_attrib(description, "media"); - - jabber_jingle_session_content_create_internal(session, - xmlnode_get_attrib(content, "name"), - creator ? creator : "initiator", - sender ? sender : "both", - xmlnode_get_namespace(transport), - xmlnode_get_namespace(description), - subtype); -} - -static void -jabber_jingle_session_new_candidate_cb(PurpleMedia *media, - const gchar *session_id, - const gchar *name, - FsCandidate *candidate, - JingleSession *session) -{ - if (jabber_jingle_session_get_state(session) == GOT_ACK || - jabber_jingle_session_get_state(session) == ACTIVE) { - JingleSessionContent *jsc = jabber_jingle_session_get_content(session, - session_id); - jabber_iq_send(jabber_jingle_session_create_transport_info(jsc, - candidate)); - } -} - -/* callback called when a pair of transport candidates (local and remote) - has been established */ -static void -jabber_jingle_session_candidate_pair_established_cb(PurpleMedia *media, - FsCandidate *native_candidate, - FsCandidate *remote_candidate, - JingleSession *session) -{ - if (!jabber_jingle_session_is_initiator(session)) { - jabber_jingle_session_accept(session); - } -} - -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; - GList *contents; - - if (!session) { - /* respond with an error here */ - purple_debug_error("jingle", "Received session-initiate ack" - " to nonexistent session\n"); - return; - } - - media = session->media; - - 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 packages */ - contents = jabber_jingle_session_get_contents(session); - for (; contents; contents = contents->next) { - JingleSessionContent *jsc = contents->data; - GList *candidates = purple_media_get_local_candidates( - jabber_jingle_session_get_media(session), - jabber_jingle_session_content_get_name(jsc), - jabber_jingle_session_get_remote_jid(session)); - purple_debug_info("jingle", - "jabber_session_candidates_prepared: %d candidates\n", - g_list_length(candidates)); - for (; candidates; candidates = candidates->next) { - FsCandidate *candidate = candidates->data; - JabberIq *result = jabber_jingle_session_create_transport_info(jsc, - candidate); - jabber_iq_send(result); - } - fs_candidate_list_destroy(candidates); - } - - jabber_jingle_session_set_state(session, GOT_ACK); -} - -static void -jabber_jingle_session_codecs_ready_cb(PurpleMedia *media, - const gchar *sess_id, - JingleSession *session) -{ - GList *contents = jabber_jingle_session_get_contents(session); - for (; contents; contents = g_list_delete_link(contents, contents)) { - JingleSessionContent *jsc = contents->data; - if (!purple_media_codecs_ready(media, - jabber_jingle_session_content_get_name(jsc))) { - break; - } - } - - if (contents != NULL) - g_list_free(contents); - else if (jabber_jingle_session_is_initiator(session) - && jabber_jingle_session_get_state(session) == PENDING) { - JabberIq *request; - - /* create request */ - request = jabber_jingle_session_create_session_initiate(session); - jabber_iq_set_callback(request, jabber_jingle_session_initiate_result_cb, NULL); - - /* send request to other part */ - jabber_iq_send(request); - } else { - jabber_jingle_session_accept(session); - } -} - -static gboolean -jabber_jingle_session_initiate_media_internal(JingleSession *session, - const char *initiator, - const char *remote_jid) -{ - PurpleMedia *media = NULL; - GList *contents = jabber_jingle_session_get_contents(session); - - media = purple_media_manager_create_media(purple_media_manager_get(), - session->js->gc, "fsrtpconference", remote_jid); - - jabber_jingle_session_set_remote_jid(session, remote_jid); - jabber_jingle_session_set_initiator(session, initiator); - - if (!media) { - purple_debug_error("jingle", "Couldn't create media session\n"); - return FALSE; - } - - jabber_jingle_session_set_media(session, media); - - for (; contents; contents = g_list_delete_link(contents, contents)) { - JingleSessionContent *jsc = contents->data; - gboolean result = FALSE; - const gchar *sender = jabber_jingle_session_content_get_sender(jsc); - FsStreamDirection direction = FS_DIRECTION_NONE; - - if (!strcmp(sender, "initiator")) - direction = FS_DIRECTION_SEND; - else if(!strcmp(sender, "responder")) - direction = FS_DIRECTION_RECV; - else - direction = FS_DIRECTION_BOTH; - - if (!jabber_jingle_session_is_initiator(session) - && direction != FS_DIRECTION_BOTH) { - if (direction == FS_DIRECTION_SEND) - direction = FS_DIRECTION_RECV; - else - direction = FS_DIRECTION_SEND; - } - - /* these will need to be changed to "nice" once the libnice transmitter is finished */ - if (jabber_jingle_session_content_is_vv_type(jsc, "audio")) { - result = purple_media_add_stream(media, "audio-content", remote_jid, - purple_media_from_fs(FS_MEDIA_TYPE_AUDIO, direction), - "rawudp", 0, NULL); - purple_debug_info("jingle", "Created Jingle audio session\n"); - } - else if (jabber_jingle_session_content_is_vv_type(jsc, "video")) { - result = purple_media_add_stream(media, "video-content", remote_jid, - purple_media_from_fs(FS_MEDIA_TYPE_VIDEO, direction), - "rawudp", 0, NULL); - purple_debug_info("jingle", "Created Jingle video session\n"); - } - - if (!result) { - purple_debug_error("jingle", "Couldn't create stream\n"); - purple_media_hangup(media); - return FALSE; - } - } - - /* 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), "new-candidate", - G_CALLBACK(jabber_jingle_session_new_candidate_cb), session); - g_signal_connect(G_OBJECT(media), "candidate-pair", - G_CALLBACK(jabber_jingle_session_candidate_pair_established_cb), session); - g_signal_connect(G_OBJECT(media), "codecs-ready", - G_CALLBACK(jabber_jingle_session_codecs_ready_cb), session); - - purple_media_ready(media); - - return TRUE; -} - -PurpleMedia * -jabber_jingle_session_initiate_media(JabberStream *js, const char *who, - PurpleMediaSessionType type) -{ - /* create content negotiation */ - 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("jingle", "Could not find Jabber buddy\n"); - return NULL; - } - jbr = jabber_buddy_find_resource(jb, NULL); - if (!jbr) { - purple_debug_error("jingle", "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); - jabber_jingle_session_content_create_media(session, type); - - /* 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); - - return session->media; -} - -void -jabber_jingle_session_terminate_session_media(JabberStream *js, const gchar *who) -{ - JingleSession *session; - - session = jabber_jingle_session_find_by_jid(js, who); - - if (session) - purple_media_hangup(session->media); -} - -void -jabber_jingle_session_terminate_sessions(JabberStream *js) -{ - GList *values = js->sessions ? - g_hash_table_get_values(js->sessions) : NULL; - - for (; values; values = g_list_delete_link(values, values)) { - JingleSession *session = (JingleSession *)values->data; - purple_media_hangup(session->media); - } -} - -static void -jabber_jingle_session_handle_content_replace(JingleSession *session, xmlnode *jingle) -{ -#if 0 - 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); - } -#endif -} - -static void -jabber_jingle_session_handle_session_accept(JingleSession *session, xmlnode *jingle) -{ - xmlnode *content = xmlnode_get_child(jingle, "content"); - const char *action = xmlnode_get_attrib(jingle, "action"); - 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... */ - - for (content = xmlnode_get_child(jingle, "content"); content; - content = xmlnode_get_next_twin(content)) { - description = xmlnode_get_child(content, "description"); - transport = xmlnode_get_child(content, "transport"); - - /* fetch codecs from remote party */ - purple_debug_info("jingle", "get codecs from session-accept\n"); - remote_codecs = jabber_jingle_get_codecs(description); - purple_debug_info("jingle", "get transport candidates from session accept\n"); - remote_transports = jabber_jingle_get_candidates(transport); - - purple_debug_info("jingle", "Got %d codecs from responder\n", - g_list_length(remote_codecs)); - purple_debug_info("jingle", "Got %d transport candidates from responder\n", - g_list_length(remote_transports)); - - purple_debug_info("jingle", "Setting remote codecs on stream\n"); - - if (!purple_media_set_remote_codecs(session->media, - xmlnode_get_attrib(content, "name"), - jabber_jingle_session_get_remote_jid(session), - remote_codecs)) { - purple_media_reject(jabber_jingle_session_get_media(session)); - return; - } - - codec_intersection = purple_media_get_negotiated_codecs(session->media, - xmlnode_get_attrib(content, "name")); - purple_debug_info("jingle", "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("jingle", "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... */ - purple_media_set_send_codec( - jabber_jingle_session_get_media(session), - xmlnode_get_attrib(content, "name"), - codec_intersection->data); - } - /* 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_candidates(session->media, - xmlnode_get_attrib(content, "name"), - 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 */ - - } - - fs_codec_list_destroy(codec_intersection); - - } - - if (!strcmp(action, "session-accept")) { - purple_media_got_accept(jabber_jingle_session_get_media(session)); - purple_debug_info("jingle", "Got session-accept\n"); - } - - jabber_iq_send(jabber_jingle_session_create_ack(session, jingle)); - - jabber_jingle_session_set_state(session, ACTIVE); -} - -static void -jabber_jingle_session_handle_session_info(JingleSession *session, xmlnode *jingle) -{ - purple_debug_info("jingle", "got session-info\n"); - jabber_iq_send(jabber_jingle_session_create_ack(session, jingle)); -} - -static void -jabber_jingle_session_handle_session_initiate(JingleSession *session, xmlnode *jingle) -{ - xmlnode *content = NULL; - xmlnode *description = NULL; - xmlnode *transport = NULL; - const char *initiator = NULL; - GList *codecs = NULL; - - if (!jingle) { - purple_debug_error("jingle", "Malformed request\n"); - return; - } - - initiator = xmlnode_get_attrib(jingle, "initiator"); - - for (content = xmlnode_get_child(jingle, "content"); content; - content = xmlnode_get_next_twin(content)) { - /* init media */ - if (!content) { - purple_debug_error("jingle", "jingle tag must contain content tag\n"); - /* should send error here */ - return; - } - - description = xmlnode_get_child(content, "description"); - - if (!description) { - purple_debug_error("jingle", "content tag must contain description tag\n"); - /* we should create an error iq here */ - return; - } - - transport = xmlnode_get_child(content, "transport"); - - if (!transport) { - purple_debug_error("jingle", "content tag must contain transport tag\n"); - /* we should create an error iq here */ - return; - } - - jabber_jingle_session_content_create_parse(session, content); - } - - if (!jabber_jingle_session_initiate_media_internal(session, initiator, initiator)) { - purple_debug_error("jingle", "Couldn't start media session with %s\n", initiator); - jabber_jingle_session_send_session_reject(session); - return; - } - - for (content = xmlnode_get_child(jingle, "content"); content; - content = xmlnode_get_next_twin(content)) { - GList *codec_intersection = NULL; - - /* init media */ - if (!content) { - purple_debug_error("jingle", "jingle tag must contain content tag\n"); - /* should send error here */ - return; - } - - description = xmlnode_get_child(content, "description"); - - if (!description) { - purple_debug_error("jingle", "content tag must contain description tag\n"); - /* we should create an error iq here */ - return; - } - codecs = jabber_jingle_get_codecs(description); - - purple_media_set_remote_codecs(session->media, - xmlnode_get_attrib(content, "name"), - initiator, codecs); - - codec_intersection = purple_media_get_negotiated_codecs(session->media, - xmlnode_get_attrib(content, "name")); - purple_debug_info("jingle", "codec intersection: %i\n", - g_list_length(codec_intersection)); - - if (g_list_length(codec_intersection) > 0) { - purple_media_set_send_codec( - jabber_jingle_session_get_media(session), - xmlnode_get_attrib(content, "name"), - codec_intersection->data); - } - } - jabber_iq_send(jabber_jingle_session_create_ack(session, jingle)); - jabber_iq_send(jabber_jingle_session_create_session_info(session, "ringing")); - - purple_media_got_request(jabber_jingle_session_get_media(session)); -} - -static void -jabber_jingle_session_handle_session_terminate(JingleSession *session, xmlnode *jingle) -{ - if (!session) { - purple_debug_error("jingle", "jabber_handle_session_terminate couldn't find session\n"); - return; - } - - /* maybe we should look at the reasoncode to determine if it was - a hangup or a reject, and call different callbacks to purple_media */ - purple_media_got_hangup(jabber_jingle_session_get_media(session)); - jabber_iq_send(jabber_jingle_session_create_ack(session, jingle)); - jabber_jingle_session_destroy(session); -} - -static void -jabber_jingle_session_handle_transport_info(JingleSession *session, xmlnode *jingle) -{ - xmlnode *content = xmlnode_get_child(jingle, "content"); - xmlnode *transport = xmlnode_get_child(content, "transport"); - GList *remote_candidates = jabber_jingle_get_candidates(transport); - - if (!session) - purple_debug_error("jingle", "jabber_handle_session_candidates couldn't find session\n"); - - /* send acknowledement */ - jabber_iq_send(jabber_jingle_session_create_ack(session, jingle)); - - /* add candidates to our list of remote candidates */ - if (g_list_length(remote_candidates) > 0) { - purple_media_add_remote_candidates(session->media, - xmlnode_get_attrib(content, "name"), - xmlnode_get_attrib(xmlnode_get_parent(jingle), "from"), - remote_candidates); - fs_candidate_list_destroy(remote_candidates); - } -} - -void -jabber_jingle_session_parse(JabberStream *js, xmlnode *packet) -{ - const gchar *type = xmlnode_get_attrib(packet, "type"); - xmlnode *jingle; - const gchar *action; - const char *sid; - JingleSession *session; - - if (!type || strcmp(type, "set")) { - /* send iq error here */ - return; - } - - /* is this a Jingle package? */ - if (!(jingle = xmlnode_get_child(packet, "jingle"))) { - /* send iq error here */ - return; - } - - if (!(action = xmlnode_get_attrib(jingle, "action"))) { - /* send iq error here */ - return; - } - - purple_debug_info("jabber", "got Jingle package action = %s\n", - action); - - if (!(sid = xmlnode_get_attrib(jingle, "sid"))) { - /* send iq error here */ - return; - } - - if (!(session = jabber_jingle_session_find_by_id(js, sid)) - && strcmp(action, "session-initiate")) { - purple_debug_error("jingle", "jabber_jingle_session_parse couldn't find session\n"); - /* send iq error here */ - return; - } - - if (!strcmp(action, "session-initiate")) { - if (session) { - /* This should only happen if you start a session with yourself */ - purple_debug_error("jingle", "Jingle session with " - "id={%s} already exists\n", sid); - /* send iq error */ - } else if ((session = jabber_jingle_session_find_by_jid(js, - xmlnode_get_attrib(packet, "from")))) { - purple_debug_fatal("jingle", "Jingle session with " - "jid={%s} already exists\n", - xmlnode_get_attrib(packet, "from")); - /* send jingle redirect packet */ - return; - } else { - session = jabber_jingle_session_create_by_id(js, sid); - jabber_jingle_session_handle_session_initiate(session, jingle); - } - } else if (!strcmp(action, "session-accept") - || !strcmp(action, "content-accept")) { - jabber_jingle_session_handle_session_accept(session, jingle); - } else if (!strcmp(action, "session-info")) { - jabber_jingle_session_handle_session_info(session, jingle); - } else if (!strcmp(action, "session-terminate")) { - jabber_jingle_session_handle_session_terminate(session, jingle); - } else if (!strcmp(action, "transport-info")) { - jabber_jingle_session_handle_transport_info(session, jingle); - } else if (!strcmp(action, "content-replace")) { - jabber_jingle_session_handle_content_replace(session, jingle); - } -} - -#endif /* USE_VV */ - diff -r 5a774d0817d8 -r bd598b606ca4 libpurple/protocols/jabber/jingle.h --- a/libpurple/protocols/jabber/jingle.h Fri Sep 05 02:18:15 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA - */ - -#ifndef JINGLE_H -#define JINGLE_H - -#include "config.h" -#include "jabber.h" -#include "media.h" - -#include -#include - -/* - * When Jingle content types other than voice and video are implemented, - * this #ifdef others surrounding Jingle code should be changed to just - * be around the voice and video specific parts. - */ -#ifdef USE_VV - -G_BEGIN_DECLS - -void jabber_jingle_session_parse(JabberStream *js, xmlnode *packet); - -PurpleMedia *jabber_jingle_session_initiate_media(JabberStream *js, - const char *who, - PurpleMediaSessionType type); - -void jabber_jingle_session_terminate_session_media(JabberStream *js, const gchar *who); -void jabber_jingle_session_terminate_sessions(JabberStream *js); - -G_END_DECLS - -#endif /* USE_VV */ - -#endif /* JINGLE_H */ diff -r 5a774d0817d8 -r bd598b606ca4 libpurple/protocols/jabber/jingle/content.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/jabber/jingle/content.c Sun Oct 19 04:37:23 2008 +0000 @@ -0,0 +1,455 @@ +/** + * @file content.c + * + * purple + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#include "config.h" +#include "debug.h" +#include "content.h" +#include "jingle.h" + +#include + +struct _JingleContentPrivate +{ + JingleSession *session; + gchar *description_type; + gchar *creator; + gchar *disposition; + gchar *name; + gchar *senders; + JingleTransport *transport; + JingleTransport *pending_transport; +}; + +#define JINGLE_CONTENT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), JINGLE_TYPE_CONTENT, JingleContentPrivate)) + +static void jingle_content_class_init (JingleContentClass *klass); +static void jingle_content_init (JingleContent *content); +static void jingle_content_finalize (GObject *object); +static void jingle_content_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); +static void jingle_content_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); +static xmlnode *jingle_content_to_xml_internal(JingleContent *content, xmlnode *jingle, JingleActionType action); +static JingleContent *jingle_content_parse_internal(xmlnode *content); + +static GObjectClass *parent_class = NULL; + +enum { + PROP_0, + PROP_SESSION, + PROP_CREATOR, + PROP_DISPOSITION, + PROP_NAME, + PROP_SENDERS, + PROP_TRANSPORT, + PROP_PENDING_TRANSPORT, +}; + +GType +jingle_content_get_type() +{ + static GType type = 0; + + if (type == 0) { + static const GTypeInfo info = { + sizeof(JingleContentClass), + NULL, + NULL, + (GClassInitFunc) jingle_content_class_init, + NULL, + NULL, + sizeof(JingleContent), + 0, + (GInstanceInitFunc) jingle_content_init, + NULL + }; + type = g_type_register_static(G_TYPE_OBJECT, "JingleContent", &info, 0); + } + return type; +} + +static void +jingle_content_class_init (JingleContentClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass*)klass; + parent_class = g_type_class_peek_parent(klass); + + gobject_class->finalize = jingle_content_finalize; + gobject_class->set_property = jingle_content_set_property; + gobject_class->get_property = jingle_content_get_property; + klass->to_xml = jingle_content_to_xml_internal; + klass->parse = jingle_content_parse_internal; + + g_object_class_install_property(gobject_class, PROP_SESSION, + g_param_spec_object("session", + "Jingle Session", + "The jingle session parent of this content.", + JINGLE_TYPE_SESSION, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_CREATOR, + g_param_spec_string("creator", + "Creator", + "The participant that created this content.", + NULL, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_DISPOSITION, + g_param_spec_string("disposition", + "Disposition", + "The disposition of the content.", + NULL, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_NAME, + g_param_spec_string("name", + "Name", + "The name of this content.", + NULL, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_SENDERS, + g_param_spec_string("senders", + "Senders", + "The sender of this content.", + NULL, + G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_TRANSPORT, + g_param_spec_object("transport", + "transport", + "The transport of this content.", + JINGLE_TYPE_TRANSPORT, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_PENDING_TRANSPORT, + g_param_spec_object("pending-transport", + "Pending transport", + "The pending transport contained within this content", + JINGLE_TYPE_TRANSPORT, + G_PARAM_READWRITE)); + + g_type_class_add_private(klass, sizeof(JingleContentPrivate)); +} + +static void +jingle_content_init (JingleContent *content) +{ + content->priv = JINGLE_CONTENT_GET_PRIVATE(content); + memset(content->priv, 0, sizeof(content->priv)); +} + +static void +jingle_content_finalize (GObject *content) +{ + JingleContentPrivate *priv = JINGLE_CONTENT_GET_PRIVATE(content); + purple_debug_info("jingle","jingle_content_finalize\n"); + + g_free(priv->description_type); + g_free(priv->creator); + g_free(priv->disposition); + g_free(priv->name); + g_free(priv->senders); + g_object_unref(priv->transport); + if (priv->pending_transport) + g_object_unref(priv->pending_transport); + + parent_class->finalize(content); +} + +static void +jingle_content_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + JingleContent *content; + g_return_if_fail(JINGLE_IS_CONTENT(object)); + + content = JINGLE_CONTENT(object); + + switch (prop_id) { + case PROP_SESSION: + content->priv->session = g_value_get_object(value); + break; + case PROP_CREATOR: + g_free(content->priv->creator); + content->priv->creator = g_value_dup_string(value); + break; + case PROP_DISPOSITION: + g_free(content->priv->disposition); + content->priv->disposition = g_value_dup_string(value); + break; + case PROP_NAME: + g_free(content->priv->name); + content->priv->name = g_value_dup_string(value); + break; + case PROP_SENDERS: + g_free(content->priv->senders); + content->priv->senders = g_value_dup_string(value); + break; + case PROP_TRANSPORT: + if (content->priv->transport) + g_object_unref(content->priv->transport); + content->priv->transport = g_value_get_object(value); + g_object_ref(content->priv->transport); + break; + case PROP_PENDING_TRANSPORT: + if (content->priv->pending_transport) + g_object_unref(content->priv->pending_transport); + content->priv->pending_transport = g_value_get_object(value); + g_object_ref(content->priv->pending_transport); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +jingle_content_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + JingleContent *content; + g_return_if_fail(JINGLE_IS_CONTENT(object)); + + content = JINGLE_CONTENT(object); + + switch (prop_id) { + case PROP_SESSION: + g_value_set_object(value, content->priv->session); + break; + case PROP_CREATOR: + g_value_set_string(value, content->priv->creator); + break; + case PROP_DISPOSITION: + g_value_set_string(value, content->priv->disposition); + break; + case PROP_NAME: + g_value_set_string(value, content->priv->name); + break; + case PROP_SENDERS: + g_value_set_string(value, content->priv->senders); + break; + case PROP_TRANSPORT: + g_value_set_object(value, content->priv->transport); + break; + case PROP_PENDING_TRANSPORT: + g_value_set_object(value, content->priv->pending_transport); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +JingleContent * +jingle_content_create(const gchar *type, const gchar *creator, + const gchar *disposition, const gchar *name, + const gchar *senders, JingleTransport *transport) +{ + + + JingleContent *content = g_object_new(jingle_get_type(type), + "creator", creator, + "disposition", disposition != NULL ? disposition : "session", + "name", name, + "senders", senders != NULL ? senders : "both", + "transport", transport, + NULL); + return content; +} + +JingleSession *jingle_content_get_session(JingleContent *content) +{ + JingleSession *session; + g_object_get(content, "session", &session, NULL); + return session; +} + +const gchar * +jingle_content_get_description_type(JingleContent *content) +{ + return JINGLE_CONTENT_GET_CLASS(content)->description_type; +} + +gchar * +jingle_content_get_creator(JingleContent *content) +{ + gchar *creator; + g_object_get(content, "creator", &creator, NULL); + return creator; +} + +gchar * +jingle_content_get_disposition(JingleContent *content) +{ + gchar *disposition; + g_object_get(content, "disposition", &disposition, NULL); + return disposition; +} + +gchar * +jingle_content_get_name(JingleContent *content) +{ + gchar *name; + g_object_get(content, "name", &name, NULL); + return name; +} + +gchar * +jingle_content_get_senders(JingleContent *content) +{ + gchar *senders; + g_object_get(content, "senders", &senders, NULL); + return senders; +} + +JingleTransport * +jingle_content_get_transport(JingleContent *content) +{ + JingleTransport *transport; + g_object_get(content, "transport", &transport, NULL); + return transport; +} + +void +jingle_content_set_session(JingleContent *content, JingleSession *session) +{ + JINGLE_IS_CONTENT(content); + JINGLE_IS_SESSION(session); + g_object_set(content, "session", session, NULL); +} + +JingleTransport * +jingle_content_get_pending_transport(JingleContent *content) +{ + JingleTransport *pending_transport; + g_object_get(content, "pending_transport", &pending_transport, NULL); + return pending_transport; +} + +void +jingle_content_set_pending_transport(JingleContent *content, JingleTransport *transport) +{ + g_object_set(content, "pending-transport", transport, NULL); +} + +void +jingle_content_accept_transport(JingleContent *content) +{ + if (content->priv->transport) + g_object_unref(content->priv->transport); + content->priv->transport = content->priv->pending_transport; + content->priv->pending_transport = NULL; +} + +void +jingle_content_remove_pending_transport(JingleContent *content) +{ + if (content->priv->pending_transport) { + g_object_unref(content->priv->pending_transport); + content->priv->pending_transport = NULL; + } +} + +void +jingle_content_modify(JingleContent *content, const gchar *senders) +{ + g_object_set(content, "senders", senders, NULL); +} + +static JingleContent * +jingle_content_parse_internal(xmlnode *content) +{ + xmlnode *description = xmlnode_get_child(content, "description"); + const gchar *type = xmlnode_get_namespace(description); + const gchar *creator = xmlnode_get_attrib(content, "creator"); + const gchar *disposition = xmlnode_get_attrib(content, "disposition"); + const gchar *senders = xmlnode_get_attrib(content, "senders"); + const gchar *name = xmlnode_get_attrib(content, "name"); + JingleTransport *transport = + jingle_transport_parse(xmlnode_get_child(content, "transport")); + + if (senders == NULL) + senders = "both"; + + return jingle_content_create(type, creator, disposition, name, senders, transport); +} + +JingleContent * +jingle_content_parse(xmlnode *content) +{ + const gchar *type = xmlnode_get_namespace(xmlnode_get_child(content, "description")); + return JINGLE_CONTENT_CLASS(g_type_class_ref(jingle_get_type(type)))->parse(content); +} + +static xmlnode * +jingle_content_to_xml_internal(JingleContent *content, xmlnode *jingle, JingleActionType action) +{ + xmlnode *node = xmlnode_new_child(jingle, "content"); + gchar *creator = jingle_content_get_creator(content); + gchar *name = jingle_content_get_name(content); + gchar *senders = jingle_content_get_senders(content); + gchar *disposition = jingle_content_get_disposition(content); + + xmlnode_set_attrib(node, "creator", creator); + xmlnode_set_attrib(node, "name", name); + xmlnode_set_attrib(node, "senders", senders); + if (strcmp("session", disposition)) + xmlnode_set_attrib(node, "disposition", disposition); + + g_free(disposition); + g_free(senders); + g_free(name); + g_free(creator); + + if (action != JINGLE_CONTENT_REMOVE) { + JingleTransport *transport; + + if (action != JINGLE_TRANSPORT_ACCEPT && + action != JINGLE_TRANSPORT_INFO && + action != JINGLE_TRANSPORT_REJECT && + action != JINGLE_TRANSPORT_REPLACE) { + xmlnode *description = xmlnode_new_child(node, "description"); + + xmlnode_set_namespace(description, + jingle_content_get_description_type(content)); + } + + if (action != JINGLE_TRANSPORT_REJECT && action == JINGLE_TRANSPORT_REPLACE) + transport = jingle_content_get_pending_transport(content); + else + transport = jingle_content_get_transport(content); + + jingle_transport_to_xml(transport, node, action); + } + + return node; +} + +xmlnode * +jingle_content_to_xml(JingleContent *content, xmlnode *jingle, JingleActionType action) +{ + g_return_val_if_fail(JINGLE_IS_CONTENT(content), NULL); + return JINGLE_CONTENT_GET_CLASS(content)->to_xml(content, jingle, action); +} + +void +jingle_content_handle_action(JingleContent *content, xmlnode *jingle, JingleActionType action) +{ + g_return_if_fail(JINGLE_IS_CONTENT(content)); + JINGLE_CONTENT_GET_CLASS(content)->handle_action(content, jingle, action); +} + diff -r 5a774d0817d8 -r bd598b606ca4 libpurple/protocols/jabber/jingle/content.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/jabber/jingle/content.h Sun Oct 19 04:37:23 2008 +0000 @@ -0,0 +1,117 @@ +/** + * @file content.h + * + * purple + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifndef JINGLE_CONTENT_H +#define JINGLE_CONTENT_H + + +#include "jabber.h" +#include "jingle.h" +#include "session.h" +#include "transport.h" + +#include +#include + +G_BEGIN_DECLS + +#define JINGLE_TYPE_CONTENT (jingle_content_get_type()) +#define JINGLE_CONTENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), JINGLE_TYPE_CONTENT, JingleContent)) +#define JINGLE_CONTENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), JINGLE_TYPE_CONTENT, JingleContentClass)) +#define JINGLE_IS_CONTENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), JINGLE_TYPE_CONTENT)) +#define JINGLE_IS_CONTENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), JINGLE_TYPE_CONTENT)) +#define JINGLE_CONTENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), JINGLE_TYPE_CONTENT, JingleContentClass)) + +/** @copydoc _JingleContent */ +typedef struct _JingleContent JingleContent; +/** @copydoc _JingleContentClass */ +typedef struct _JingleContentClass JingleContentClass; +/** @copydoc _JingleContentPrivate */ +typedef struct _JingleContentPrivate JingleContentPrivate; + +/** The content class */ +struct _JingleContentClass +{ + GObjectClass parent_class; /**< The parent class. */ + + xmlnode *(*to_xml) (JingleContent *content, xmlnode *jingle, JingleActionType action); + JingleContent *(*parse) (xmlnode *content); + void (*handle_action) (JingleContent *content, xmlnode *jingle, JingleActionType action); + const gchar *description_type; +}; + +/** The content class's private data */ +struct _JingleContent +{ + GObject parent; /**< The parent of this object. */ + JingleContentPrivate *priv; /**< The private data of this object. */ +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Gets the content class's GType + * + * @return The content class's GType. + */ +GType jingle_content_get_type(void); + +JingleContent *jingle_content_create(const gchar *type, const gchar *creator, + const gchar *disposition, const gchar *name, + const gchar *senders, JingleTransport *transport); + +JingleSession *jingle_content_get_session(JingleContent *content); +const gchar *jingle_content_get_description_type(JingleContent *content); +gchar *jingle_content_get_creator(JingleContent *content); +gchar *jingle_content_get_disposition(JingleContent *content); +gchar *jingle_content_get_name(JingleContent *content); +gchar *jingle_content_get_senders(JingleContent *content); +JingleTransport *jingle_content_get_transport(JingleContent *content); +JingleTransport *jingle_content_get_pending_transport(JingleContent *content); + +void jingle_content_set_session(JingleContent *content, JingleSession *session); +void jingle_content_set_pending_transport(JingleContent *content, JingleTransport *transport); +void jingle_content_accept_transport(JingleContent *content); +void jingle_content_remove_pending_transport(JingleContent *content); +void jingle_content_modify(JingleContent *content, const gchar *senders); + +#define jingle_content_create_content_accept(session) \ + jingle_session_to_packet(session, JINGLE_CONTENT_ACCEPT) +#define jingle_content_create_content_add(session) \ + jingle_session_to_packet(session, JINGLE_CONTENT_ADD) +#define jingle_content_create_content_modify(session) \ + jingle_session_to_packet(session, JINGLE_CONTENT_MODIFY) +#define jingle_content_create_content_remove(session) \ + jingle_session_to_packet(session, JINGLE_CONTENT_REMOVE) + +JingleContent *jingle_content_parse(xmlnode *content); +xmlnode *jingle_content_to_xml(JingleContent *content, xmlnode *jingle, JingleActionType action); +void jingle_content_handle_action(JingleContent *content, xmlnode *jingle, JingleActionType action); + +#ifdef __cplusplus +} +#endif + +G_END_DECLS + +#endif /* JINGLE_CONTENT_H */ + diff -r 5a774d0817d8 -r bd598b606ca4 libpurple/protocols/jabber/jingle/jingle.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/jabber/jingle/jingle.c Sun Oct 19 04:37:23 2008 +0000 @@ -0,0 +1,432 @@ +/* + * @file jingle.c + * + * purple - Jabber Protocol Plugin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ + +#include "config.h" +#include "content.h" +#include "debug.h" +#include "jingle.h" +#include +#include "session.h" +#include "rawudp.h" +#include "rtp.h" + +const gchar * +jingle_get_action_name(JingleActionType action) +{ + switch (action) { + case JINGLE_CONTENT_ACCEPT: + return "content-accept"; + case JINGLE_CONTENT_ADD: + return "content-add"; + case JINGLE_CONTENT_MODIFY: + return "content-modify"; + case JINGLE_CONTENT_REMOVE: + return "content-remove"; + case JINGLE_SESSION_ACCEPT: + return "session-accept"; + case JINGLE_SESSION_INFO: + return "session-info"; + case JINGLE_SESSION_INITIATE: + return "session-initiate"; + case JINGLE_SESSION_TERMINATE: + return "session-terminate"; + case JINGLE_TRANSPORT_ACCEPT: + return "transport-accept"; + case JINGLE_TRANSPORT_INFO: + return "transport-info"; + case JINGLE_TRANSPORT_REPLACE: + return "transport-replace"; + default: + return "unknown-type"; + } +} + +JingleActionType +jingle_get_action_type(const gchar *action) +{ + if (!strcmp(action, "content-accept")) + return JINGLE_CONTENT_ACCEPT; + else if (!strcmp(action, "content-add")) + return JINGLE_CONTENT_ADD; + else if (!strcmp(action, "content-modify")) + return JINGLE_CONTENT_MODIFY; + else if (!strcmp(action, "content-remove")) + return JINGLE_CONTENT_REMOVE; + else if (!strcmp(action, "session-accept")) + return JINGLE_SESSION_ACCEPT; + else if (!strcmp(action, "session-info")) + return JINGLE_SESSION_INFO; + else if (!strcmp(action, "session-initiate")) + return JINGLE_SESSION_INITIATE; + else if (!strcmp(action, "session-terminate")) + return JINGLE_SESSION_TERMINATE; + else if (!strcmp(action, "transport-accept")) + return JINGLE_TRANSPORT_ACCEPT; + else if (!strcmp(action, "transport-info")) + return JINGLE_TRANSPORT_INFO; + else if (!strcmp(action, "transport-replace")) + return JINGLE_TRANSPORT_REPLACE; + else + return JINGLE_UNKNOWN_TYPE; +} + +GType +jingle_get_type(const gchar *type) +{ + if (!strcmp(type, JINGLE_APP_RTP)) + return JINGLE_TYPE_RTP; +#if 0 + else if (!strcmp(type, JINGLE_APP_FT)) + return JINGLE_TYPE_FT; + else if (!strcmp(type, JINGLE_APP_XML)) + return JINGLE_TYPE_XML; +#endif + else if (!strcmp(type, JINGLE_TRANSPORT_RAWUDP)) + return JINGLE_TYPE_RAWUDP; +#if 0 + else if (!strcmp(type, JINGLE_TRANSPORT_ICEUDP)) + return JINGLE_TYPE_ICEUDP; + else if (!strcmp(type, JINGLE_TRANSPORT_SOCKS)) + return JINGLE_TYPE_SOCKS; + else if (!strcmp(type, JINGLE_TRANSPORT_IBB)) + return JINGLE_TYPE_IBB; +#endif + else + return G_TYPE_NONE; +} + +static void +jingle_handle_content_accept(JingleSession *session, xmlnode *jingle) +{ + xmlnode *content = xmlnode_get_child(jingle, "content"); + jabber_iq_send(jingle_session_create_ack(session, jingle)); + + for (; content; content = xmlnode_get_next_twin(content)) { + const gchar *name = xmlnode_get_attrib(content, "name"); + const gchar *creator = xmlnode_get_attrib(content, "creator"); + jingle_session_accept_content(session, name, creator); + /* signal here */ + } +} + +static void +jingle_handle_content_add(JingleSession *session, xmlnode *jingle) +{ + xmlnode *content = xmlnode_get_child(jingle, "content"); + jabber_iq_send(jingle_session_create_ack(session, jingle)); + + for (; content; content = xmlnode_get_next_twin(content)) { + JingleContent *pending_content = + jingle_content_parse(content); + if (pending_content == NULL) { + purple_debug_error("jingle", + "Error parsing \"content-add\" content.\n"); + /* XXX: send error here */ + } else { + jingle_session_add_pending_content(session, + pending_content); + } + } + + /* XXX: signal here */ +} + +static void +jingle_handle_content_modify(JingleSession *session, xmlnode *jingle) +{ + xmlnode *content = xmlnode_get_child(jingle, "content"); + jabber_iq_send(jingle_session_create_ack(session, jingle)); + + for (; content; content = xmlnode_get_next_twin(content)) { + const gchar *name = xmlnode_get_attrib(content, "name"); + const gchar *creator = xmlnode_get_attrib(content, "creator"); + JingleContent *local_content = jingle_session_find_content(session, name, creator); + + if (content != NULL) { + const gchar *senders = xmlnode_get_attrib(content, "senders"); + gchar *local_senders = jingle_content_get_senders(local_content); + if (strcmp(senders, local_senders)) + jingle_content_modify(local_content, senders); + g_free(local_senders); + } else { + purple_debug_error("jingle", "content_modify: unknown content\n"); + /* XXX: send error */ + } + } +} + +static void +jingle_handle_content_reject(JingleSession *session, xmlnode *jingle) +{ + xmlnode *content = xmlnode_get_child(jingle, "content"); + jabber_iq_send(jingle_session_create_ack(session, jingle)); + + for (; content; content = xmlnode_get_next_twin(content)) { + const gchar *name = xmlnode_get_attrib(content, "name"); + const gchar *creator = xmlnode_get_attrib(content, "creator"); + jingle_session_remove_pending_content(session, name, creator); + /* signal here */ + } +} + +static void +jingle_handle_content_remove(JingleSession *session, xmlnode *jingle) +{ + xmlnode *content = xmlnode_get_child(jingle, "content"); + + jabber_iq_send(jingle_session_create_ack(session, jingle)); + + for (; content; content = xmlnode_get_next_twin(content)) { + const gchar *name = xmlnode_get_attrib(content, "name"); + const gchar *creator = xmlnode_get_attrib(content, "creator"); + jingle_session_remove_content(session, name, creator); + } +} + +static void +jingle_handle_session_accept(JingleSession *session, xmlnode *jingle) +{ + xmlnode *content = xmlnode_get_child(jingle, "content"); + + jabber_iq_send(jingle_session_create_ack(session, jingle)); + + jingle_session_accept_session(session); + + for (; content; content = xmlnode_get_next_twin(content)) { + const gchar *name = xmlnode_get_attrib(content, "name"); + const gchar *creator = xmlnode_get_attrib(content, "creator"); + JingleContent *content = jingle_session_find_content(session, name, creator); + if (content == NULL) { + purple_debug_error("jingle", "Error parsing content\n"); + /* XXX: send error */ + } else { + jingle_content_handle_action(content, jingle, + JINGLE_SESSION_ACCEPT); + } + } +} + +static void +jingle_handle_session_info(JingleSession *session, xmlnode *jingle) +{ + jabber_iq_send(jingle_session_create_ack(session, jingle)); + /* XXX: call signal */ +} + +static void +jingle_handle_session_initiate(JingleSession *session, xmlnode *jingle) +{ + xmlnode *content = xmlnode_get_child(jingle, "content"); + + for (; content; content = xmlnode_get_next_twin(content)) { + JingleContent *parsed_content = jingle_content_parse(content); + if (parsed_content == NULL) { + purple_debug_error("jingle", "Error parsing content\n"); + /* XXX: send error */ + } else { + jingle_session_add_content(session, parsed_content); + jingle_content_handle_action(parsed_content, jingle, + JINGLE_SESSION_INITIATE); + } + } + + jabber_iq_send(jingle_session_create_ack(session, jingle)); +} + +static void +jingle_handle_session_terminate(JingleSession *session, xmlnode *jingle) +{ + jabber_iq_send(jingle_session_create_ack(session, jingle)); + + jingle_session_handle_action(session, jingle, + JINGLE_SESSION_TERMINATE); + /* display reason? */ + g_object_unref(session); +} + +static void +jingle_handle_transport_accept(JingleSession *session, xmlnode *jingle) +{ + xmlnode *content = xmlnode_get_child(jingle, "content"); + + jabber_iq_send(jingle_session_create_ack(session, jingle)); + + for (; content; content = xmlnode_get_next_twin(content)) { + const gchar *name = xmlnode_get_attrib(content, "name"); + const gchar *creator = xmlnode_get_attrib(content, "creator"); + JingleContent *content = jingle_session_find_content(session, name, creator); + jingle_content_accept_transport(content); + } +} + +static void +jingle_handle_transport_info(JingleSession *session, xmlnode *jingle) +{ + xmlnode *content = xmlnode_get_child(jingle, "content"); + + jabber_iq_send(jingle_session_create_ack(session, jingle)); + + for (; content; content = xmlnode_get_next_twin(content)) { + const gchar *name = xmlnode_get_attrib(content, "name"); + const gchar *creator = xmlnode_get_attrib(content, "creator"); + JingleContent *content = jingle_session_find_content(session, name, creator); + if (content == NULL) { + purple_debug_error("jingle", "Error parsing content\n"); + /* XXX: send error */ + } else { + jingle_content_handle_action(content, jingle, + JINGLE_TRANSPORT_INFO); + } + } +} + +static void +jingle_handle_transport_reject(JingleSession *session, xmlnode *jingle) +{ + xmlnode *content = xmlnode_get_child(jingle, "content"); + + jabber_iq_send(jingle_session_create_ack(session, jingle)); + + for (; content; content = xmlnode_get_next_twin(content)) { + const gchar *name = xmlnode_get_attrib(content, "name"); + const gchar *creator = xmlnode_get_attrib(content, "creator"); + JingleContent *content = jingle_session_find_content(session, name, creator); + jingle_content_remove_pending_transport(content); + } +} + +static void +jingle_handle_transport_replace(JingleSession *session, xmlnode *jingle) +{ + xmlnode *content = xmlnode_get_child(jingle, "content"); + + jabber_iq_send(jingle_session_create_ack(session, jingle)); + + for (; content; content = xmlnode_get_next_twin(content)) { + const gchar *name = xmlnode_get_attrib(content, "name"); + const gchar *creator = xmlnode_get_attrib(content, "creator"); + xmlnode *xmltransport = xmlnode_get_child(content, "transport"); + JingleTransport *transport = jingle_transport_parse(xmltransport); + JingleContent *content = jingle_session_find_content(session, name, creator); + + jingle_content_set_pending_transport(content, transport); + } +} + + +void +jingle_parse(JabberStream *js, xmlnode *packet) +{ + const gchar *type = xmlnode_get_attrib(packet, "type"); + xmlnode *jingle; + const gchar *action; + const gchar *sid; + JingleSession *session; + + if (!type || strcmp(type, "set")) { + /* send iq error here */ + return; + } + + /* is this a Jingle package? */ + if (!(jingle = xmlnode_get_child(packet, "jingle"))) { + /* send iq error here */ + return; + } + + if (!(action = xmlnode_get_attrib(jingle, "action"))) { + /* send iq error here */ + return; + } + + purple_debug_info("jabber", "got Jingle package action = %s\n", + action); + + if (!(sid = xmlnode_get_attrib(jingle, "sid"))) { + /* send iq error here */ + return; + } + + if (!(session = jingle_session_find_by_sid(js, sid)) + && strcmp(action, "session-initiate")) { + purple_debug_error("jingle", "jabber_jingle_session_parse couldn't find session\n"); + /* send iq error here */ + return; + } + + if (!strcmp(action, "session-initiate")) { + if (session) { + /* This should only happen if you start a session with yourself */ + purple_debug_error("jingle", "Jingle session with " + "id={%s} already exists\n", sid); + /* send iq error */ + } else if ((session = jingle_session_find_by_jid(js, + xmlnode_get_attrib(packet, "from")))) { + purple_debug_fatal("jingle", "Jingle session with " + "jid={%s} already exists\n", + xmlnode_get_attrib(packet, "from")); + /* send jingle redirect packet */ + return; + } else { + session = jingle_session_create(js, sid, + xmlnode_get_attrib(packet, "to"), + xmlnode_get_attrib(packet, "from"), FALSE); + jingle_handle_session_initiate(session, jingle); + } + } else if (!strcmp(action, "content-accept")) { + jingle_handle_content_accept(session, jingle); + } else if (!strcmp(action, "content-add")) { + jingle_handle_content_add(session, jingle); + } else if (!strcmp(action, "content-modify")) { + jingle_handle_content_modify(session, jingle); + } else if (!strcmp(action, "content-reject")) { + jingle_handle_content_reject(session, jingle); + } else if (!strcmp(action, "content-remove")) { + jingle_handle_content_remove(session, jingle); + } else if (!strcmp(action, "session-accept")) { + jingle_handle_session_accept(session, jingle); + } else if (!strcmp(action, "session-info")) { + jingle_handle_session_info(session, jingle); + } else if (!strcmp(action, "session-terminate")) { + jingle_handle_session_terminate(session, jingle); + } else if (!strcmp(action, "transport-accept")) { + jingle_handle_transport_accept(session, jingle); + } else if (!strcmp(action, "transport-info")) { + jingle_handle_transport_info(session, jingle); + } else if (!strcmp(action, "transport-reject")) { + jingle_handle_transport_reject(session, jingle); + } else if (!strcmp(action, "transport-replace")) { + jingle_handle_transport_replace(session, jingle); + } +} + +void +jingle_terminate_sessions(JabberStream *js) +{ + GList *values = js->sessions ? + g_hash_table_get_values(js->sessions) : NULL; + + for (; values; values = g_list_delete_link(values, values)) { + JingleSession *session = (JingleSession *)values->data; + g_object_unref(session); + } +} + diff -r 5a774d0817d8 -r bd598b606ca4 libpurple/protocols/jabber/jingle/jingle.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/jabber/jingle/jingle.h Sun Oct 19 04:37:23 2008 +0000 @@ -0,0 +1,81 @@ +/* + * @file jingle.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA + */ + +#ifndef JINGLE_H +#define JINGLE_H + +#include "jabber.h" + +#include +#include + +G_BEGIN_DECLS + +#ifdef __cplusplus +extern "C" { +#endif + +#define JINGLE "urn:xmpp:jingle:0" +#define JINGLE_ERROR "urn:xmpp:jingle:errors:0" +#define JINGLE_APP_FT "urn:xmpp:jingle:apps:file-transfer:0" +#define JINGLE_APP_RTP "urn:xmpp:jingle:apps:rtp:0" +#define JINGLE_APP_RTP_ERROR "urn:xmpp:jingle:apps:rtp:errors:0" +#define JINGLE_APP_RTP_INFO "urn:xmpp:jingle:apps:rtp:info:0" +#define JINGLE_APP_RTP_SUPPORT_AUDIO "urn:xmpp:jingle:apps:rtp:audio" +#define JINGLE_APP_RTP_SUPPORT_VIDEO "urn:xmpp:jingle:apps:rtp:video" +#define JINGLE_APP_XML "urn:xmpp:tmp:jingle:apps:xmlstream" +#define JINGLE_DTMF "urn:xmpp:jingle:dtmf:0" +#define JINGLE_TRANSPORT_SOCKS "urn:xmpp:jingle:transports:bytestreams:0" +#define JINGLE_TRANSPORT_IBB "urn:xmpp:jingle:transports:ibb:0" +#define JINGLE_TRANSPORT_ICEUDP "urn:xmpp:jingle:transports:ice-udp:0" +#define JINGLE_TRANSPORT_RAWUDP "urn:xmpp:jingle:transports:raw-udp:0" +#define JINGLE_TRANSPORT_RAWUDP_INFO "urn:xmpp:jingle:transports:raw-udp:info:0" + +typedef enum { + JINGLE_UNKNOWN_TYPE, + JINGLE_CONTENT_ACCEPT, + JINGLE_CONTENT_ADD, + JINGLE_CONTENT_MODIFY, + JINGLE_CONTENT_REJECT, + JINGLE_CONTENT_REMOVE, + JINGLE_SESSION_ACCEPT, + JINGLE_SESSION_INFO, + JINGLE_SESSION_INITIATE, + JINGLE_SESSION_TERMINATE, + JINGLE_TRANSPORT_ACCEPT, + JINGLE_TRANSPORT_INFO, + JINGLE_TRANSPORT_REJECT, + JINGLE_TRANSPORT_REPLACE, +} JingleActionType; + +const gchar *jingle_get_action_name(JingleActionType action); +JingleActionType jingle_get_action_type(const gchar *action); + +GType jingle_get_type(const gchar *type); + +void jingle_parse(JabberStream *js, xmlnode *packet); + +void jingle_terminate_sessions(JabberStream *js); + +#ifdef __cplusplus +} +#endif + +G_END_DECLS + +#endif /* JINGLE_H */ diff -r 5a774d0817d8 -r bd598b606ca4 libpurple/protocols/jabber/jingle/session.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/jabber/jingle/session.c Sun Oct 19 04:37:23 2008 +0000 @@ -0,0 +1,588 @@ +/** + * @file session.c + * + * purple + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#include "config.h" +#include "content.h" +#include "debug.h" +#include "session.h" +#include "jingle.h" + +#include + +struct _JingleSessionPrivate +{ + gchar *sid; + JabberStream *js; + gchar *remote_jid; + gchar *local_jid; + gboolean is_initiator; + gboolean state; + GList *contents; + GList *pending_contents; +}; + +#define JINGLE_SESSION_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), JINGLE_TYPE_SESSION, JingleSessionPrivate)) + +static void jingle_session_class_init (JingleSessionClass *klass); +static void jingle_session_init (JingleSession *session); +static void jingle_session_finalize (GObject *object); +static void jingle_session_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); +static void jingle_session_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); + +static GObjectClass *parent_class = NULL; + +enum { + PROP_0, + PROP_SID, + PROP_JS, + PROP_REMOTE_JID, + PROP_LOCAL_JID, + PROP_IS_INITIATOR, + PROP_STATE, + PROP_CONTENTS, + PROP_PENDING_CONTENTS, +}; + +GType +jingle_session_get_type() +{ + static GType type = 0; + + if (type == 0) { + static const GTypeInfo info = { + sizeof(JingleSessionClass), + NULL, + NULL, + (GClassInitFunc) jingle_session_class_init, + NULL, + NULL, + sizeof(JingleSession), + 0, + (GInstanceInitFunc) jingle_session_init, + NULL + }; + type = g_type_register_static(G_TYPE_OBJECT, "JingleSession", &info, 0); + } + return type; +} + +static void +jingle_session_class_init (JingleSessionClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass*)klass; + parent_class = g_type_class_peek_parent(klass); + + gobject_class->finalize = jingle_session_finalize; + gobject_class->set_property = jingle_session_set_property; + gobject_class->get_property = jingle_session_get_property; + + g_object_class_install_property(gobject_class, PROP_SID, + g_param_spec_string("sid", + "Session ID", + "The unique session ID of the Jingle Session.", + NULL, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_JS, + g_param_spec_pointer("js", + "JabberStream", + "The Jabber stream associated with this session.", + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_REMOTE_JID, + g_param_spec_string("remote-jid", + "Remote JID", + "The JID of the remote participant.", + NULL, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_LOCAL_JID, + g_param_spec_string("local-jid", + "Local JID", + "The JID of the local participant.", + NULL, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_IS_INITIATOR, + g_param_spec_boolean("is-initiator", + "Is Initiator", + "Whether or not the local JID is the initiator of the session.", + FALSE, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_STATE, + g_param_spec_boolean("state", + "State", + "The state of the session (PENDING=FALSE, ACTIVE=TRUE).", + FALSE, + G_PARAM_READABLE)); + + g_object_class_install_property(gobject_class, PROP_CONTENTS, + g_param_spec_pointer("contents", + "Contents", + "The active contents contained within this session", + G_PARAM_READABLE)); + + g_object_class_install_property(gobject_class, PROP_PENDING_CONTENTS, + g_param_spec_pointer("pending-contents", + "Pending contents", + "The pending contents contained within this session", + G_PARAM_READABLE)); + + g_type_class_add_private(klass, sizeof(JingleSessionPrivate)); +} + +static void +jingle_session_init (JingleSession *session) +{ + session->priv = JINGLE_SESSION_GET_PRIVATE(session); + memset(session->priv, 0, sizeof(session->priv)); +} + +static void +jingle_session_finalize (GObject *session) +{ + JingleSessionPrivate *priv = JINGLE_SESSION_GET_PRIVATE(session); + purple_debug_info("jingle","jingle_session_finalize\n"); + + g_hash_table_remove(priv->js->sessions, priv->sid); + + g_free(priv->sid); + g_free(priv->remote_jid); + g_free(priv->local_jid); + + for (; priv->contents; priv->contents = + g_list_delete_link(priv->contents, priv->contents)) { + g_object_unref(priv->contents->data); + } + for (; priv->pending_contents; priv->pending_contents = + g_list_delete_link(priv->pending_contents, priv->pending_contents)) { + g_object_unref(priv->pending_contents->data); + } + + parent_class->finalize(session); +} + +static void +jingle_session_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + JingleSession *session; + g_return_if_fail(JINGLE_IS_SESSION(object)); + + session = JINGLE_SESSION(object); + + switch (prop_id) { + case PROP_SID: + g_free(session->priv->sid); + session->priv->sid = g_value_dup_string(value); + break; + case PROP_JS: + session->priv->js = g_value_get_pointer(value); + break; + case PROP_REMOTE_JID: + g_free(session->priv->remote_jid); + session->priv->remote_jid = g_value_dup_string(value); + break; + case PROP_LOCAL_JID: + g_free(session->priv->local_jid); + session->priv->local_jid = g_value_dup_string(value); + break; + case PROP_IS_INITIATOR: + session->priv->is_initiator = g_value_get_boolean(value); + break; + case PROP_STATE: + session->priv->state = g_value_get_boolean(value); + break; + case PROP_CONTENTS: + session->priv->contents = g_value_get_pointer(value); + break; + case PROP_PENDING_CONTENTS: + session->priv->pending_contents = g_value_get_pointer(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +jingle_session_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + JingleSession *session; + g_return_if_fail(JINGLE_IS_SESSION(object)); + + session = JINGLE_SESSION(object); + + switch (prop_id) { + case PROP_SID: + g_value_set_string(value, session->priv->sid); + break; + case PROP_JS: + g_value_set_pointer(value, session->priv->js); + break; + case PROP_REMOTE_JID: + g_value_set_string(value, session->priv->remote_jid); + break; + case PROP_LOCAL_JID: + g_value_set_string(value, session->priv->local_jid); + break; + case PROP_IS_INITIATOR: + g_value_set_boolean(value, session->priv->is_initiator); + break; + case PROP_STATE: + g_value_set_boolean(value, session->priv->state); + break; + case PROP_CONTENTS: + g_value_set_pointer(value, session->priv->contents); + break; + case PROP_PENDING_CONTENTS: + g_value_set_pointer(value, session->priv->pending_contents); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +JingleSession * +jingle_session_create(JabberStream *js, const gchar *sid, + const gchar *local_jid, const gchar *remote_jid, + gboolean is_initiator) +{ + JingleSession *session = g_object_new(jingle_session_get_type(), + "js", js, + "sid", sid, + "local-jid", local_jid, + "remote-jid", remote_jid, + "is_initiator", is_initiator, + NULL); + + /* insert it into the hash table */ + if (!js->sessions) { + purple_debug_info("jingle", + "Creating hash table for sessions\n"); + js->sessions = g_hash_table_new(g_str_hash, g_str_equal); + } + purple_debug_info("jingle", + "inserting session with key: %s into table\n", sid); + g_hash_table_insert(js->sessions, g_strdup(sid), session); + + return session; +} + +JabberStream * +jingle_session_get_js(JingleSession *session) +{ + JabberStream *js; + g_object_get(session, "js", &js, NULL); + return js; +} + +gchar * +jingle_session_get_sid(JingleSession *session) +{ + gchar *sid; + g_object_get(session, "sid", &sid, NULL); + return sid; +} + +gchar * +jingle_session_get_local_jid(JingleSession *session) +{ + gchar *local_jid; + g_object_get(session, "local-jid", &local_jid, NULL); + return local_jid; +} + +gchar * +jingle_session_get_remote_jid(JingleSession *session) +{ + gchar *remote_jid; + g_object_get(session, "remote-jid", &remote_jid, NULL); + return remote_jid; +} + +gboolean +jingle_session_is_initiator(JingleSession *session) +{ + gboolean is_initiator; + g_object_get(session, "is-initiator", &is_initiator, NULL); + return is_initiator; +} + +gboolean +jingle_session_get_state(JingleSession *session) +{ + gboolean state; + g_object_get(session, "state", &state, NULL); + return state; +} + +GList * +jingle_session_get_contents(JingleSession *session) +{ + GList *contents; + g_object_get(session, "contents", &contents, NULL); + return contents; +} + +GList * +jingle_session_get_pending_contents(JingleSession *session) +{ + GList *pending_contents; + g_object_get(session, "pending-contents", &pending_contents, NULL); + return pending_contents; +} + +JingleSession * +jingle_session_find_by_sid(JabberStream *js, const gchar *sid) +{ + purple_debug_info("jingle", "find_by_id %s\n", sid); + purple_debug_info("jingle", "lookup: %p\n", (js->sessions) ? + g_hash_table_lookup(js->sessions, sid) : NULL); + return (JingleSession *) (js->sessions) ? + g_hash_table_lookup(js->sessions, sid) : NULL; +} + +JingleSession * +jingle_session_find_by_jid(JabberStream *js, const gchar *jid) +{ + GList *values = (js->sessions) ? + g_hash_table_get_values(js->sessions) : NULL; + gboolean use_bare = strchr(jid, '/') == NULL; + + for (; values; values = g_list_delete_link(values, values)) { + JingleSession *session = (JingleSession *)values->data; + gchar *remote_jid = jingle_session_get_remote_jid(session); + gchar *cmp_jid = use_bare ? jabber_get_bare_jid(remote_jid) + : g_strdup(remote_jid); + g_free(remote_jid); + if (!strcmp(jid, cmp_jid)) { + g_free(cmp_jid); + g_list_free(values); + return session; + } + g_free(cmp_jid); + } + + return NULL; +} + +static xmlnode * +jingle_add_jingle_packet(JingleSession *session, + JabberIq *iq, JingleActionType action) +{ + xmlnode *jingle = iq ? + xmlnode_new_child(iq->node, "jingle") : + xmlnode_new("jingle"); + gchar *local_jid = jingle_session_get_local_jid(session); + gchar *remote_jid = jingle_session_get_remote_jid(session); + + xmlnode_set_namespace(jingle, JINGLE); + xmlnode_set_attrib(jingle, "action", jingle_get_action_name(action)); + + if (jingle_session_is_initiator(session)) { + xmlnode_set_attrib(jingle, "initiator", + jingle_session_get_local_jid(session)); + xmlnode_set_attrib(jingle, "responder", + jingle_session_get_remote_jid(session)); + } else { + xmlnode_set_attrib(jingle, "initiator", + jingle_session_get_remote_jid(session)); + xmlnode_set_attrib(jingle, "responder", + jingle_session_get_local_jid(session)); + } + + g_free(local_jid); + g_free(remote_jid); + + xmlnode_set_attrib(jingle, "sid", jingle_session_get_sid(session)); + + return jingle; +} + +JabberIq * +jingle_session_create_ack(JingleSession *session, const xmlnode *jingle) +{ + JabberIq *result = jabber_iq_new( + jingle_session_get_js(session), + JABBER_IQ_RESULT); + xmlnode *packet = xmlnode_get_parent(jingle); + jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id")); + xmlnode_set_attrib(result->node, "from", xmlnode_get_attrib(packet, "to")); + xmlnode_set_attrib(result->node, "to", xmlnode_get_attrib(packet, "from")); + return result; +} + +static JabberIq * +jingle_create_iq(JingleSession *session) +{ + JabberStream *js = jingle_session_get_js(session); + JabberIq *result = jabber_iq_new(js, JABBER_IQ_SET); + gchar *from = jingle_session_get_local_jid(session); + gchar *to = jingle_session_get_remote_jid(session); + + xmlnode_set_attrib(result->node, "from", from); + xmlnode_set_attrib(result->node, "to", to); + + g_free(from); + g_free(to); + return result; +} + +xmlnode * +jingle_session_to_xml(JingleSession *session, xmlnode *jingle, JingleActionType action) +{ + if (action != JINGLE_SESSION_INFO && action != JINGLE_SESSION_TERMINATE) { + GList *iter; + if (action == JINGLE_CONTENT_ACCEPT + || action == JINGLE_CONTENT_ADD + || action == JINGLE_CONTENT_REMOVE) + iter = jingle_session_get_pending_contents(session); + else + iter = jingle_session_get_contents(session); + + for (; iter; iter = g_list_next(iter)) { + jingle_content_to_xml(iter->data, jingle, action); + } + } + return jingle; +} + +JabberIq * +jingle_session_to_packet(JingleSession *session, JingleActionType action) +{ + JabberIq *iq = jingle_create_iq(session); + xmlnode *jingle = jingle_add_jingle_packet(session, iq, action); + jingle_session_to_xml(session, jingle, action); + return iq; +} + +void jingle_session_handle_action(JingleSession *session, xmlnode *jingle, JingleActionType action) +{ + GList *iter; + if (action == JINGLE_CONTENT_ADD || action == JINGLE_CONTENT_REMOVE) + iter = jingle_session_get_pending_contents(session); + else + iter = jingle_session_get_contents(session); + + for (; iter; iter = g_list_next(iter)) { + jingle_content_handle_action(iter->data, jingle, action); + } +} + +JingleContent * +jingle_session_find_content(JingleSession *session, const gchar *name, const gchar *creator) +{ + GList *iter = session->priv->contents; + for (; iter; iter = g_list_next(iter)) { + JingleContent *content = iter->data; + gchar *cname = jingle_content_get_name(content); + gchar *ccreator = jingle_content_get_creator(content); + gboolean result = (!strcmp(name, cname) && !strcmp(creator, ccreator)); + + g_free(cname); + g_free(ccreator); + + if (result == TRUE) + return content; + } + return NULL; +} + +JingleContent * +jingle_session_find_pending_content(JingleSession *session, const gchar *name, const gchar *creator) +{ + GList *iter = session->priv->pending_contents; + for (; iter; iter = g_list_next(iter)) { + JingleContent *content = iter->data; + gchar *cname = jingle_content_get_name(content); + gchar *ccreator = jingle_content_get_creator(content); + gboolean result = (!strcmp(name, cname) && !strcmp(creator, ccreator)); + + g_free(cname); + g_free(ccreator); + + if (result == TRUE) + return content; + } + return NULL; +} + +void +jingle_session_add_content(JingleSession *session, JingleContent* content) +{ + session->priv->contents = + g_list_append(session->priv->contents, content); + jingle_content_set_session(content, session); +} + +void +jingle_session_remove_content(JingleSession *session, const gchar *name, const gchar *creator) +{ + JingleContent *content = + jingle_session_find_content(session, name, creator); + + if (content) { + session->priv->contents = + g_list_remove(session->priv->contents, content); + g_object_unref(content); + } +} + +void +jingle_session_add_pending_content(JingleSession *session, JingleContent* content) +{ + session->priv->pending_contents = + g_list_append(session->priv->pending_contents, content); + jingle_content_set_session(content, session); +} + +void +jingle_session_remove_pending_content(JingleSession *session, const gchar *name, const gchar *creator) +{ + JingleContent *content = jingle_session_find_pending_content(session, name, creator); + + if (content) { + session->priv->pending_contents = + g_list_remove(session->priv->pending_contents, content); + g_object_unref(content); + } +} + +void +jingle_session_accept_content(JingleSession *session, const gchar *name, const gchar *creator) +{ + JingleContent *content = jingle_session_find_pending_content(session, name, creator); + + if (content) { + g_object_ref(content); + jingle_session_remove_pending_content(session, name, creator); + jingle_session_add_content(session, content); + } +} + +void +jingle_session_accept_session(JingleSession *session) +{ + session->priv->state = TRUE; +} + diff -r 5a774d0817d8 -r bd598b606ca4 libpurple/protocols/jabber/jingle/session.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/jabber/jingle/session.h Sun Oct 19 04:37:23 2008 +0000 @@ -0,0 +1,122 @@ +/** + * @file session.h + * + * purple + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifndef JINGLE_SESSION_H +#define JINGLE_SESSION_H + +#include "iq.h" +#include "jabber.h" + +#include +#include + +G_BEGIN_DECLS + +#define JINGLE_TYPE_SESSION (jingle_session_get_type()) +#define JINGLE_SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), JINGLE_TYPE_SESSION, JingleSession)) +#define JINGLE_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), JINGLE_TYPE_SESSION, JingleSessionClass)) +#define JINGLE_IS_SESSION(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), JINGLE_TYPE_SESSION)) +#define JINGLE_IS_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), JINGLE_TYPE_SESSION)) +#define JINGLE_SESSION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), JINGLE_TYPE_SESSION, JingleSessionClass)) + +/** @copydoc _JingleSession */ +typedef struct _JingleSession JingleSession; +/** @copydoc _JingleSessionClass */ +typedef struct _JingleSessionClass JingleSessionClass; +/** @copydoc _JingleSessionPrivate */ +typedef struct _JingleSessionPrivate JingleSessionPrivate; + +/** The session class */ +struct _JingleSessionClass +{ + GObjectClass parent_class; /**< The parent class. */ +}; + +/** The session class's private data */ +struct _JingleSession +{ + GObject parent; /**< The parent of this object. */ + JingleSessionPrivate *priv; /**< The private data of this object. */ +}; + +struct _JingleContent; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Gets the session class's GType + * + * @return The session class's GType. + */ +GType jingle_session_get_type(void); + +JingleSession *jingle_session_create(JabberStream *js, const gchar *sid, + const gchar *local_jid, const gchar *remote_jid, + gboolean is_initiator); +JabberStream *jingle_session_get_js(JingleSession *session); +gchar *jingle_session_get_sid(JingleSession *session); +gchar *jingle_session_get_local_jid(JingleSession *session); +gchar *jingle_session_get_remote_jid(JingleSession *session); +gboolean jingle_session_is_initiator(JingleSession *session); +gboolean jingle_session_get_state(JingleSession *session); + +GList *jingle_session_get_contents(JingleSession *session); +GList *jingle_session_get_pending_contents(JingleSession *session); + +JingleSession *jingle_session_find_by_sid(JabberStream *js, const gchar *sid); +JingleSession *jingle_session_find_by_jid(JabberStream *js, const gchar *jid); + +JabberIq *jingle_session_create_ack(JingleSession *session, const xmlnode *jingle); +xmlnode *jingle_session_to_xml(JingleSession *session, xmlnode *parent, JingleActionType action); +JabberIq *jingle_session_to_packet(JingleSession *session, JingleActionType action); + +void jingle_session_handle_action(JingleSession *session, xmlnode *jingle, JingleActionType action); + +#define jingle_session_create_session_accept(session) \ + jingle_session_to_packet(session, JINGLE_SESSION_ACCEPT) +#define jingle_session_create_session_info(session) \ + jingle_session_to_packet(session, JINGLE_SESSION_INFO) +#define jingle_session_create_session_initiate(session) \ + jingle_session_to_packet(session, JINGLE_SESSION_INITIATE) +#define jingle_session_create_session_terminate(session) \ + jingle_session_to_packet(session, JINGLE_SESSION_TERMINATE) + +struct _JingleContent *jingle_session_find_content(JingleSession *session, + const gchar *name, const gchar *creator); +struct _JingleContent *jingle_session_find_pending_content(JingleSession *session, + const gchar *name, const gchar *creator); + +void jingle_session_add_content(JingleSession *session, struct _JingleContent* content); +void jingle_session_remove_content(JingleSession *session, const gchar *name, const gchar *creator); +void jingle_session_add_pending_content(JingleSession *session, struct _JingleContent* content); +void jingle_session_remove_pending_content(JingleSession *session, const gchar *name, const gchar *creator); +void jingle_session_accept_content(JingleSession *session, const gchar *name, const gchar *creator); +void jingle_session_accept_session(JingleSession *session); + +#ifdef __cplusplus +} +#endif + +G_END_DECLS + +#endif /* JINGLE_SESSION_H */ + diff -r 5a774d0817d8 -r bd598b606ca4 libpurple/protocols/jabber/jingle/transport.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/jabber/jingle/transport.c Sun Oct 19 04:37:23 2008 +0000 @@ -0,0 +1,172 @@ +/** + * @file transport.c + * + * purple + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#include "transport.h" +#include "jingle.h" +#include "debug.h" + +#include + +struct _JingleTransportPrivate +{ + void *dummy; +}; + +#define JINGLE_TRANSPORT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), JINGLE_TYPE_TRANSPORT, JingleTransportPrivate)) + +static void jingle_transport_class_init (JingleTransportClass *klass); +static void jingle_transport_init (JingleTransport *transport); +static void jingle_transport_finalize (GObject *object); +static void jingle_transport_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); +static void jingle_transport_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); +JingleTransport *jingle_transport_parse_internal(xmlnode *transport); +xmlnode *jingle_transport_to_xml_internal(JingleTransport *transport, xmlnode *content, JingleActionType action); + +static GObjectClass *parent_class = NULL; + +enum { + PROP_0, +}; + +GType +jingle_transport_get_type() +{ + static GType type = 0; + + if (type == 0) { + static const GTypeInfo info = { + sizeof(JingleTransportClass), + NULL, + NULL, + (GClassInitFunc) jingle_transport_class_init, + NULL, + NULL, + sizeof(JingleTransport), + 0, + (GInstanceInitFunc) jingle_transport_init, + NULL + }; + type = g_type_register_static(G_TYPE_OBJECT, "JingleTransport", &info, 0); + } + return type; +} + +static void +jingle_transport_class_init (JingleTransportClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass*)klass; + parent_class = g_type_class_peek_parent(klass); + + gobject_class->finalize = jingle_transport_finalize; + gobject_class->set_property = jingle_transport_set_property; + gobject_class->get_property = jingle_transport_get_property; + klass->to_xml = jingle_transport_to_xml_internal; + klass->parse = jingle_transport_parse_internal; + + g_type_class_add_private(klass, sizeof(JingleTransportPrivate)); +} + +static void +jingle_transport_init (JingleTransport *transport) +{ + transport->priv = JINGLE_TRANSPORT_GET_PRIVATE(transport); + memset(transport->priv, 0, sizeof(transport->priv)); +} + +static void +jingle_transport_finalize (GObject *transport) +{ + /* JingleTransportPrivate *priv = JINGLE_TRANSPORT_GET_PRIVATE(transport); */ + purple_debug_info("jingle","jingle_transport_finalize\n"); + + parent_class->finalize(transport); +} + +static void +jingle_transport_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + JingleTransport *transport; + g_return_if_fail(JINGLE_IS_TRANSPORT(object)); + + transport = JINGLE_TRANSPORT(object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +jingle_transport_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + JingleTransport *transport; + g_return_if_fail(JINGLE_IS_TRANSPORT(object)); + + transport = JINGLE_TRANSPORT(object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +JingleTransport * +jingle_transport_create(const gchar *type) +{ + return g_object_new(jingle_get_type(type), NULL); +} + +const gchar * +jingle_transport_get_transport_type(JingleTransport *transport) +{ + return JINGLE_TRANSPORT_GET_CLASS(transport)->transport_type; +} + +JingleTransport * +jingle_transport_parse_internal(xmlnode *transport) +{ + const gchar *type = xmlnode_get_namespace(transport); + return jingle_transport_create(type); +} + +xmlnode * +jingle_transport_to_xml_internal(JingleTransport *transport, xmlnode *content, JingleActionType action) +{ + xmlnode *node = xmlnode_new_child(content, "transport"); + xmlnode_set_namespace(node, jingle_transport_get_transport_type(transport)); + return node; +} + +JingleTransport * +jingle_transport_parse(xmlnode *transport) +{ + const gchar *type = xmlnode_get_namespace(transport); + return JINGLE_TRANSPORT_CLASS(g_type_class_ref(jingle_get_type(type)))->parse(transport); +} + +xmlnode * +jingle_transport_to_xml(JingleTransport *transport, xmlnode *content, JingleActionType action) +{ + g_return_val_if_fail(JINGLE_IS_TRANSPORT(transport), NULL); + return JINGLE_TRANSPORT_GET_CLASS(transport)->to_xml(transport, content, action); +} + diff -r 5a774d0817d8 -r bd598b606ca4 libpurple/protocols/jabber/jingle/transport.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/jabber/jingle/transport.h Sun Oct 19 04:37:23 2008 +0000 @@ -0,0 +1,95 @@ +/** + * @file transport.h + * + * purple + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifndef JINGLE_TRANSPORT_H +#define JINGLE_TRANSPORT_H + +#include +#include + +#include "jingle.h" +#include "xmlnode.h" + +G_BEGIN_DECLS + +#define JINGLE_TYPE_TRANSPORT (jingle_transport_get_type()) +#define JINGLE_TRANSPORT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), JINGLE_TYPE_TRANSPORT, JingleTransport)) +#define JINGLE_TRANSPORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), JINGLE_TYPE_TRANSPORT, JingleTransportClass)) +#define JINGLE_IS_TRANSPORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), JINGLE_TYPE_TRANSPORT)) +#define JINGLE_IS_TRANSPORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), JINGLE_TYPE_TRANSPORT)) +#define JINGLE_TRANSPORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), JINGLE_TYPE_TRANSPORT, JingleTransportClass)) + +/** @copydoc _JingleTransport */ +typedef struct _JingleTransport JingleTransport; +/** @copydoc _JingleTransportClass */ +typedef struct _JingleTransportClass JingleTransportClass; +/** @copydoc _JingleTransportPrivate */ +typedef struct _JingleTransportPrivate JingleTransportPrivate; + +/** The transport class */ +struct _JingleTransportClass +{ + GObjectClass parent_class; /**< The parent class. */ + + const gchar *transport_type; + xmlnode *(*to_xml) (JingleTransport *transport, xmlnode *content, JingleActionType action); + JingleTransport *(*parse) (xmlnode *transport); +}; + +/** The transport class's private data */ +struct _JingleTransport +{ + GObject parent; /**< The parent of this object. */ + JingleTransportPrivate *priv; /**< The private data of this object. */ +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Gets the transport class's GType + * + * @return The transport class's GType. + */ +GType jingle_transport_get_type(void); + +JingleTransport *jingle_transport_create(const gchar *type); +const gchar *jingle_transport_get_transport_type(JingleTransport *transport); +void jingle_transport_add_candidate(); + +#define jingle_transport_create_transport_accept(session) \ + jingle_session_to_packet(session, JINGLE_TRANSPORT_ACCEPT) +#define jingle_transport_create_transport_info(session) \ + jingle_session_to_packet(session, JINGLE_TRANSPORT_INFO) +#define jingle_transport_create_transport_replace(session) \ + jingle_session_to_packet(session, JINGLE_TRANSPORT_REPLACE) + +JingleTransport *jingle_transport_parse(xmlnode *transport); +xmlnode *jingle_transport_to_xml(JingleTransport *transport, xmlnode *content, JingleActionType action); + +#ifdef __cplusplus +} +#endif + +G_END_DECLS + +#endif /* JINGLE_TRANSPORT_H */ + diff -r 5a774d0817d8 -r bd598b606ca4 pidgin/gtkconv.c --- a/pidgin/gtkconv.c Fri Sep 05 02:18:15 2008 +0000 +++ b/pidgin/gtkconv.c Sun Oct 19 04:37:23 2008 +0000 @@ -7770,6 +7770,8 @@ } gtkmedia = pidgin_media_new(media); + g_object_unref(media); + gtk_box_pack_start(GTK_BOX(gtkconv->topvbox), gtkmedia, FALSE, FALSE, 0); gtk_widget_show(gtkmedia); g_signal_connect(G_OBJECT(gtkmedia), "message", G_CALLBACK(pidgin_gtkmedia_message_cb), conv); diff -r 5a774d0817d8 -r bd598b606ca4 pidgin/gtkmedia.c --- a/pidgin/gtkmedia.c Fri Sep 05 02:18:15 2008 +0000 +++ b/pidgin/gtkmedia.c Sun Oct 19 04:37:23 2008 +0000 @@ -249,6 +249,7 @@ pidgin_media_finalize (GObject *media) { PidginMedia *gtkmedia = PIDGIN_MEDIA(media); + purple_debug_info("gtkmedia", "pidgin_media_finalize\n"); if (gtkmedia->priv->media) { pidgin_media_disconnect_levels(gtkmedia->priv->media, gtkmedia); g_object_unref(gtkmedia->priv->media);