pidgin.vv: 398a4a38: Add automatic discovery of GTalk STUN se...
malu at pidgin.im
malu at pidgin.im
Tue Feb 3 16:40:28 EST 2009
-----------------------------------------------------------------
Revision: 398a4a3861f7eaf401b092a666b8899287ec070c
Ancestor: a04b6e057ca2d46a4b8019b1fc01b2393bcc9f8f
Author: malu at pidgin.im
Date: 2009-02-03T21:37:27
Branch: im.pidgin.pidgin.vv
URL: http://d.pidgin.im/viewmtn/revision/info/398a4a3861f7eaf401b092a666b8899287ec070c
Modified files:
libpurple/protocols/jabber/disco.c
libpurple/protocols/jabber/google.c
libpurple/protocols/jabber/google.h
libpurple/protocols/jabber/iq.c
libpurple/protocols/jabber/jabber.c
libpurple/protocols/jabber/jabber.h
libpurple/protocols/jabber/jingle/jingle.c
libpurple/protocols/jabber/jingle/jingle.h
libpurple/protocols/jabber/jingle/rtp.c
ChangeLog:
Add automatic discovery of GTalk STUN servers when using a Gtalk account
Is used for STUN candidate genration, unless a STUN server is set in prefs
Does not handle GTalk relay setup yet
-------------- next part --------------
============================================================
--- libpurple/protocols/jabber/disco.c 65145b787d79a1d35b70000df810a56120c7c519
+++ libpurple/protocols/jabber/disco.c b9d492eb4344876be4b08f398adcca601baa8037
@@ -452,7 +452,12 @@ jabber_disco_server_info_result_cb(Jabbe
if (!strcmp(name, "Google Talk")) {
purple_debug_info("jabber", "Google Talk!\n");
js->googletalk = TRUE;
- }
+
+ /* autodiscover stun and relays */
+ jabber_google_send_jingle_info(js);
+ } else {
+ /* TODO: add external service discovery here... */
+ }
}
for (child = xmlnode_get_child(query, "feature"); child;
============================================================
--- libpurple/protocols/jabber/google.c a853a056a9b84124a4d9777b3702f54345e0e17b
+++ libpurple/protocols/jabber/google.c fe2a041412aef8714f2d24984bad49fb14620d25
@@ -23,6 +23,8 @@
#include "mediamanager.h"
#include "util.h"
#include "privacy.h"
+#include "dnsquery.h"
+#include "network.h"
#include "buddy.h"
#include "google.h"
@@ -30,6 +32,8 @@
#include "presence.h"
#include "iq.h"
+#include "jingle/jingle.h"
+
#ifdef USE_VV
typedef struct {
@@ -124,7 +128,7 @@ google_session_send_candidates(PurpleMed
sess = google_session_create_xmlnode(session, "candidates");
xmlnode_insert_child(iq->node, sess);
xmlnode_set_attrib(iq->node, "to", session->remote_jid);
-
+
for (;candidates;candidates = candidates->next) {
char port[8];
char pref[8];
@@ -132,7 +136,7 @@ google_session_send_candidates(PurpleMed
if (!strcmp(transport->ip, "127.0.0.1"))
continue;
-
+
candidate = xmlnode_new("candidate");
g_snprintf(port, sizeof(port), "%d", transport->port);
@@ -162,7 +166,6 @@ google_session_send_candidates(PurpleMed
xmlnode_set_attrib(candidate, "generation", "0");
xmlnode_set_attrib(candidate, "network", "0");
xmlnode_insert_child(sess, candidate);
-
}
jabber_iq_send(iq);
}
@@ -246,6 +249,26 @@ google_session_state_changed_cb(PurpleMe
}
}
+static GParameter *
+jabber_google_session_get_params(JabberStream *js, guint *num)
+{
+ guint num_params;
+ GParameter *params = jingle_get_params(js, &num_params);
+ GParameter *new_params = g_new0(GParameter, num_params + 1);
+
+ memcpy(new_params, params, sizeof(GParameter) * num_params);
+
+ purple_debug_info("jabber", "setting Google jingle compatibility param\n");
+ new_params[num_params].name = "compatibility-mode";
+ g_value_init(&new_params[num_params].value, G_TYPE_UINT);
+ g_value_set_uint(&new_params[num_params].value, 1); /* NICE_COMPATIBILITY_GOOGLE */
+
+ g_free(params);
+ *num = num_params + 1;
+ return new_params;
+}
+
+
PurpleMedia*
jabber_google_session_initiate(JabberStream *js, const gchar *who, PurpleMediaSessionType type)
{
@@ -253,7 +276,8 @@ jabber_google_session_initiate(JabberStr
JabberBuddy *jb;
JabberBuddyResource *jbr;
gchar *jid;
- GParameter param;
+ GParameter *params;
+ guint num_params;
/* construct JID to send to */
jb = jabber_buddy_find(js, who, FALSE);
@@ -286,18 +310,15 @@ jabber_google_session_initiate(JabberStr
purple_media_manager_get(), js->gc,
"fsrtpconference", session->remote_jid, TRUE);
- /* GTalk requires the NICE_COMPATIBILITY_GOOGLE param */
- param.name = "compatibility-mode";
- memset(¶m.value, 0, sizeof(GValue));
- g_value_init(¶m.value, G_TYPE_UINT);
- g_value_set_uint(¶m.value, 1); /* NICE_COMPATIBILITY_GOOGLE */
+ params = jabber_google_session_get_params(js, &num_params);
if (purple_media_add_stream(session->media, "google-voice",
session->remote_jid, PURPLE_MEDIA_AUDIO,
- "nice", 1, ¶m) == FALSE) {
+ "nice", num_params, params) == FALSE) {
purple_media_error(session->media, "Error adding stream.");
purple_media_hangup(session->media);
google_session_destroy(session);
+ g_free(params);
return NULL;
}
@@ -310,6 +331,7 @@ jabber_google_session_initiate(JabberStr
sessions = g_hash_table_new(google_session_id_hash,
google_session_id_equal);
g_hash_table_insert(sessions, &(session->id), session);
+ g_free(params);
return session->media;
}
@@ -322,8 +344,9 @@ google_session_handle_initiate(JabberStr
xmlnode *desc_element, *codec_element;
PurpleMediaCodec *codec;
const char *id, *encoding_name, *clock_rate;
- GParameter param;
-
+ GParameter *params;
+ guint num_params;
+
if (session->state != UNINIT) {
purple_debug_error("jabber", "Received initiate for active session.\n");
return;
@@ -332,22 +355,21 @@ google_session_handle_initiate(JabberStr
session->media = purple_media_manager_create_media(purple_media_manager_get(), js->gc,
"fsrtpconference", session->remote_jid, FALSE);
- /* GTalk requires the NICE_COMPATIBILITY_GOOGLE param */
- param.name = "compatibility-mode";
- memset(¶m.value, 0, sizeof(GValue));
- g_value_init(¶m.value, G_TYPE_UINT);
- g_value_set_uint(¶m.value, 1); /* NICE_COMPATIBILITY_GOOGLE */
+ params = jabber_google_session_get_params(js, &num_params);
if (purple_media_add_stream(session->media, "google-voice", session->remote_jid,
- PURPLE_MEDIA_AUDIO, "nice", 1, ¶m) == FALSE) {
+ PURPLE_MEDIA_AUDIO, "nice", num_params, params) == FALSE) {
purple_media_error(session->media, "Error adding stream.");
purple_media_hangup(session->media);
google_session_send_terminate(session);
+ g_free(params);
return;
}
+ g_free(params);
+
desc_element = xmlnode_get_child(sess, "description");
-
+
for (codec_element = xmlnode_get_child(desc_element, "payload-type");
codec_element;
codec_element = xmlnode_get_next_twin(codec_element)) {
@@ -368,7 +390,7 @@ google_session_handle_initiate(JabberStr
G_CALLBACK(google_session_state_changed_cb), session);
purple_media_codec_list_free(codecs);
-
+
result = jabber_iq_new(js, JABBER_IQ_RESULT);
jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id"));
xmlnode_set_attrib(result->node, "to", session->remote_jid);
@@ -1025,3 +1047,105 @@ char *jabber_google_presence_outgoing(Pu
const char *attr = purple_status_get_attr_string(tune, PURPLE_TUNE_TITLE);
return attr ? g_strdup_printf("? %s", attr) : g_strdup("");
}
+
+static void
+jabber_google_stun_lookup_cb(GSList *hosts, gpointer data,
+ const char *error_message)
+{
+ JabberStream *js = (JabberStream *) data;
+
+ if (error_message) {
+ purple_debug_error("jabber", "Google STUN lookup failed: %s\n",
+ error_message);
+ g_slist_free(hosts);
+ return;
+ }
+
+ if (hosts && g_slist_next(hosts)) {
+ struct sockaddr *addr = g_slist_next(hosts)->data;
+ char dst[INET6_ADDRSTRLEN];
+ int port;
+
+ if (addr->sa_family == AF_INET6) {
+ inet_ntop(addr->sa_family, &((struct sockaddr_in6 *) addr)->sin6_addr,
+ dst, sizeof(dst));
+ port = ntohs(((struct sockaddr_in6 *) addr)->sin6_port);
+ } else {
+ inet_ntop(addr->sa_family, &((struct sockaddr_in *) addr)->sin_addr,
+ dst, sizeof(dst));
+ port = ntohs(((struct sockaddr_in *) addr)->sin_port);
+ }
+
+ if (js) {
+ if (js->stun_ip) {
+ g_free(js->stun_ip);
+ }
+ js->stun_ip = g_strdup(dst);
+ purple_debug_info("jabber", "set Google STUN IP address: %s\n", dst);
+ js->stun_port = port;
+ purple_debug_info("jabber", "set Google STUN port: %d\n", port);
+ purple_debug_info("jabber", "set Google STUN port: %d\n", port);
+ /* unmark ongoing query */
+ js->stun_query = NULL;
+ }
+ }
+
+ g_slist_free(hosts);
+}
+
+static void
+jabber_google_jingle_info_cb(JabberStream *js, xmlnode *result,
+ gpointer nullus)
+{
+ if (result) {
+ const xmlnode *query =
+ xmlnode_get_child_with_namespace(result, "query",
+ GOOGLE_JINGLE_INFO_NAMESPACE);
+
+ if (query) {
+ const xmlnode *stun = xmlnode_get_child(query, "stun");
+
+ purple_debug_info("jabber", "got google:jingleinfo\n");
+
+ if (stun) {
+ xmlnode *server = xmlnode_get_child(stun, "server");
+
+ if (server) {
+ const gchar *host = xmlnode_get_attrib(server, "host");
+ const gchar *udp = xmlnode_get_attrib(server, "udp");
+
+ if (host && udp) {
+ int port = atoi(udp);
+ /* if there, would already be an ongoing query,
+ cancel it */
+ if (js->stun_query)
+ purple_dnsquery_destroy(js->stun_query);
+
+ js->stun_query = purple_dnsquery_a(host, port,
+ jabber_google_stun_lookup_cb, js);
+ }
+ }
+ }
+ /* should perhaps handle relays later on, or maybe wait until
+ Google supports a common standard... */
+ }
+ }
+}
+
+void
+jabber_google_handle_jingle_info(JabberStream *js, xmlnode *packet)
+{
+ jabber_google_jingle_info_cb(js, packet, NULL);
+}
+
+void
+jabber_google_send_jingle_info(JabberStream *js)
+{
+ JabberIq *jingle_info =
+ jabber_iq_new_query(js, JABBER_IQ_GET, GOOGLE_JINGLE_INFO_NAMESPACE);
+
+ jabber_iq_set_callback(jingle_info, jabber_google_jingle_info_cb,
+ NULL);
+ purple_debug_info("jabber", "sending google:jingleinfo query\n");
+ jabber_iq_send(jingle_info);
+}
============================================================
--- libpurple/protocols/jabber/google.h efb1c5fbe4d135a9e2a17fae222d8f2490df4829
+++ libpurple/protocols/jabber/google.h 97e1d58285542849753713a8e138702613948bf9
@@ -27,6 +27,8 @@
#include "jabber.h"
#include "media.h"
+#define GOOGLE_JINGLE_INFO_NAMESPACE "google:jingleinfo"
+
void jabber_gmail_init(JabberStream *js);
void jabber_gmail_poke(JabberStream *js, xmlnode *node);
@@ -49,5 +51,7 @@ void jabber_google_session_parse(JabberS
PurpleMedia *jabber_google_session_initiate(JabberStream *js, const gchar *who, PurpleMediaSessionType type);
void jabber_google_session_parse(JabberStream *js, xmlnode *node);
+void jabber_google_handle_jingle_info(JabberStream *js, xmlnode *packet);
+void jabber_google_send_jingle_info(JabberStream *js);
#endif /* _PURPLE_GOOGLE_H_ */
============================================================
--- libpurple/protocols/jabber/iq.c 736e7af0145b220a0cf4f8f444d1b7ceedbeb2e4
+++ libpurple/protocols/jabber/iq.c d46af645f839c90fbaa0de713eeccba30f4080e9
@@ -448,6 +448,9 @@ void jabber_iq_init(void)
#ifdef USE_VV
jabber_iq_register_handler(JINGLE, jingle_parse);
#endif
+ /* handle Google jingleinfo */
+ jabber_iq_register_handler(GOOGLE_JINGLE_INFO_NAMESPACE,
+ jabber_google_handle_jingle_info);
}
void jabber_iq_uninit(void)
============================================================
--- libpurple/protocols/jabber/jabber.c 3b96839f0dda90301d9d21414b48da32c590dd29
+++ libpurple/protocols/jabber/jabber.c 985f7e79328037bc18df3cafc569983ef1452c44
@@ -735,20 +735,24 @@ jabber_login(PurpleAccount *account)
js->sessions = NULL;
#endif
+ js->stun_ip = NULL;
+ js->stun_port = 0;
+ js->stun_query = NULL;
+
if(!js->user) {
purple_connection_error_reason (gc,
PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
_("Invalid XMPP ID"));
return;
}
-
+
if (!js->user->domain || *(js->user->domain) == '\0') {
purple_connection_error_reason (gc,
PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
_("Invalid XMPP ID. Domain must be set."));
return;
}
-
+
if((my_jb = jabber_buddy_find(js, purple_account_get_username(account), TRUE)))
my_jb->subscription |= JABBER_SUB_BOTH;
@@ -1222,6 +1226,10 @@ void jabber_register_account(PurpleAccou
server = connect_server[0] ? connect_server : js->user->domain;
js->certificate_CN = g_strdup(server);
+ js->stun_ip = NULL;
+ js->stun_port = 0;
+ js->stun_query = NULL;
+
jabber_stream_set_state(js, JABBER_STREAM_CONNECTING);
if(purple_account_get_bool(account, "old_ssl", FALSE)) {
@@ -1425,6 +1433,15 @@ void jabber_close(PurpleConnection *gc)
g_free(js->srv_rec);
js->srv_rec = NULL;
+ g_free(js->stun_ip);
+ js->stun_ip = NULL;
+
+ /* cancel DNS query for STUN, if one is ongoing */
+ if (js->stun_query) {
+ purple_dnsquery_destroy(js->stun_query);
+ js->stun_query = NULL;
+ }
+
g_free(js);
gc->proto_data = NULL;
============================================================
--- libpurple/protocols/jabber/jabber.h 50a045bf1c95fd68419dcc43fa727127ee7d35cc
+++ libpurple/protocols/jabber/jabber.h b96ed9de1f0340bd808ab346ecff327fa6f4f914
@@ -58,6 +58,7 @@ typedef struct _JabberStream JabberStrea
#include "mediamanager.h"
#include "roomlist.h"
#include "sslconn.h"
+#include "dnsquery.h"
#include "jutil.h"
#include "xmlnode.h"
@@ -250,6 +251,12 @@ struct _JabberStream
#ifdef USE_VV
GHashTable *medias;
#endif
+
+ /* maybe this should only be present when USE_VV? */
+ gchar *stun_ip;
+ int stun_port;
+ PurpleDnsQueryData *stun_query;
+ /* later add stuff to handle TURN relays... */
};
typedef gboolean (JabberFeatureEnabled)(JabberStream *js, const gchar *shortname, const gchar *namespace);
============================================================
--- libpurple/protocols/jabber/jingle/jingle.c 48d1bfcbffbabf98b18860d95df332d61666eb73
+++ libpurple/protocols/jabber/jingle/jingle.c 4f2da1a1d4c58d27073f412a595e7b56da7ad4a5
@@ -20,6 +20,7 @@
*/
#include "internal.h"
+#include "network.h"
#include "content.h"
#include "debug.h"
@@ -438,3 +439,32 @@ jingle_terminate_sessions(JabberStream *
jingle_terminate_sessions_gh, NULL);
}
+GParameter *
+jingle_get_params(JabberStream *js, guint *num)
+{
+ /* don't set a STUN server if one is set globally in prefs, in that case
+ this will be handled in media.c */
+ gboolean has_account_stun = js->stun_ip && !purple_network_get_stun_ip();
+ guint num_params = has_account_stun ? 2 : 0;
+ GParameter *params = NULL;
+
+ if (num_params > 0) {
+ params = g_new0(GParameter, num_params);
+
+ purple_debug_info("jabber",
+ "setting param stun-ip for stream using Google auto-config: %s\n",
+ js->stun_ip);
+ params[0].name = "stun-ip";
+ g_value_init(¶ms[0].value, G_TYPE_STRING);
+ g_value_set_string(¶ms[0].value, js->stun_ip);
+ purple_debug_info("jabber",
+ "setting param stun-port for stream using Google auto-config: %d\n",
+ js->stun_port);
+ params[1].name = "stun-port";
+ g_value_init(¶ms[1].value, G_TYPE_UINT);
+ g_value_set_uint(¶ms[1].value, js->stun_port);
+ }
+
+ *num = num_params;
+ return params;
+}
============================================================
--- libpurple/protocols/jabber/jingle/jingle.h 9e27c85dda189559bffeeb670f8213ed4ef61d83
+++ libpurple/protocols/jabber/jingle/jingle.h 7d798cff01e7a5f418c2a7486b7bce6d25c69028
@@ -72,6 +72,10 @@ void jingle_terminate_sessions(JabberStr
void jingle_terminate_sessions(JabberStream *js);
+/* create a GParam array given autoconfigured STUN (and later perhaps TURN).
+ if google_talk is TRUE, set compatability mode to GOOGLE_TALK */
+GParameter *jingle_get_params(JabberStream *js, guint *num_params);
+
#ifdef __cplusplus
}
#endif
============================================================
--- libpurple/protocols/jabber/jingle/rtp.c 754d68f08d668e2170577e80d7b1e8f072c4668d
+++ libpurple/protocols/jabber/jingle/rtp.c 7b59484de69bb7150b13f150be58f736b3d91289
@@ -402,6 +402,8 @@ jingle_rtp_init_media(JingleContent *con
gboolean is_audio;
PurpleMediaSessionType type;
JingleTransport *transport;
+ GParameter *params = NULL;
+ guint num_params;
/* maybe this create ought to just be in initiate and handle initiate */
if (media == NULL)
@@ -436,13 +438,16 @@ jingle_rtp_init_media(JingleContent *con
type = is_audio == TRUE ? PURPLE_MEDIA_RECV_AUDIO
: PURPLE_MEDIA_RECV_VIDEO;
+ params =
+ jingle_get_params(jingle_session_get_js(session), &num_params);
purple_media_add_stream(media, name, remote_jid,
- type, transmitter, 0, NULL);
+ type, transmitter, num_params, params);
g_free(name);
g_free(media_type);
g_free(remote_jid);
g_free(senders);
+ g_free(params);
g_object_unref(session);
return TRUE;
More information about the Commits
mailing list