cpw.malu.xmpp.jingle_ft: 36b35bf2: Started updating to the new S5B spec.
malu at pidgin.im
malu at pidgin.im
Fri Jul 31 20:35:34 EDT 2009
-----------------------------------------------------------------
Revision: 36b35bf20f2241715ed73acb6954bc0cb49ce7e0
Ancestor: 468cf307eee309836b031e57280c68e533048a01
Author: malu at pidgin.im
Date: 2009-08-01T00:34:07
Branch: im.pidgin.cpw.malu.xmpp.jingle_ft
URL: http://d.pidgin.im/viewmtn/revision/info/36b35bf20f2241715ed73acb6954bc0cb49ce7e0
Modified files:
libpurple/protocols/jabber/jingle/file-transfer.c
libpurple/protocols/jabber/jingle/s5b.c
libpurple/protocols/jabber/jingle/s5b.h
ChangeLog:
Started updating to the new S5B spec.
Doesn't yet send an <activate/> transport-info to the other client when
using a proxy.
There also seems to be some issue for the initiator to connect to the receiver.
And I'm sure I've broken some other stuff... :)
-------------- next part --------------
============================================================
--- libpurple/protocols/jabber/jingle/file-transfer.c 388bc00eea5d622ba62bce7ac1082bea09ac4c81
+++ libpurple/protocols/jabber/jingle/file-transfer.c 7ac68bb79e7f6c92979d194dd2883ad8dad5b4e2
@@ -549,7 +549,7 @@ jingle_file_transfer_xfer_init(PurpleXfe
jingle_s5b_set_failed_connect_callback(JINGLE_S5B(transport),
jingle_file_transfer_s5b_connect_failed_callback, content);
/* start local listen on the S5B transport */
- jingle_s5b_gather_streamhosts(session, JINGLE_S5B(transport));
+ jingle_s5b_gather_candidates(session, JINGLE_S5B(transport));
}
} else if (xfer->data) {
JingleContent *content = (JingleContent *) xfer->data;
@@ -597,7 +597,7 @@ jingle_file_transfer_xfer_init(PurpleXfe
jingle_file_transfer_s5b_error_callback, content);
jingle_s5b_set_failed_connect_callback(JINGLE_S5B(transport),
jingle_file_transfer_s5b_connect_failed_callback, content);
- jingle_s5b_gather_streamhosts(session, JINGLE_S5B(transport));
+ jingle_s5b_gather_candidates(session, JINGLE_S5B(transport));
}
g_object_unref(session);
g_object_unref(transport);
@@ -730,9 +730,9 @@ jingle_file_transfer_handle_action_inter
break;
}
} else if (JINGLE_IS_S5B(transport)) {
- /* add the receiver's streamhost (this must be done here since
- parse is not called on the existing transport */
- jingle_s5b_add_streamhosts(JINGLE_S5B(transport),
+ /* add the receiver's streamhost candidates (this must be done
+ here since parse is not called on the existing transport */
+ jingle_s5b_add_candidates(JINGLE_S5B(transport),
xmlnode_get_child(xmlcontent, "transport"));
/* attempt to connect bytestream */
jingle_s5b_attempt_connect(session, JINGLE_S5B(transport));
@@ -748,7 +748,6 @@ jingle_file_transfer_handle_action_inter
xmlnode *description = xmlnode_get_child(xmlcontent, "description");
JabberStream *js = jingle_session_get_js(session);
xmlnode *offer = xmlnode_get_child(description, "offer");
- PurpleXfer *xfer = NULL;
gchar *who = jingle_session_get_remote_jid(session);
if (offer) {
@@ -828,25 +827,19 @@ jingle_file_transfer_handle_action_inter
/* we should check for "stream-host" error (in the case of S5B) and
offer a transport-replace with IBB */
if (xmltransport) {
- xmlnode *streamhost_error =
- xmlnode_get_child(xmltransport, "streamhost-error");
+ xmlnode *candidate_error =
+ xmlnode_get_child(xmltransport, "candidate-error");
- if (streamhost_error) {
+ if (candidate_error) {
purple_debug_info("jingle-ft",
"got a streamhost-error, remote couldn't connect\n");
JINGLE_FT_GET_PRIVATE(JINGLE_FT(content))->
remote_failed_s5b = TRUE;
-
- /* if we have already tried connecting to all remote
- streamhost candidates, or there were none, trigger
- the replace callback directly */
- if (JINGLE_IS_S5B(transport) &&
- !jingle_s5b_has_remaining_remote_streamhosts(
- JINGLE_S5B(transport))) {
- jingle_file_transfer_s5b_connect_failed_callback(
- content);
- }
}
+ if (JINGLE_IS_S5B(transport)) {
+ jingle_s5b_handle_transport_info(JINGLE_S5B(transport),
+ session, xmltransport);
+ }
}
g_object_unref(transport);
@@ -884,12 +877,8 @@ jingle_file_transfer_handle_action_inter
"failed to open file for reading\n");
jingle_file_transfer_cancel_local(content);
}
- } else if (JINGLE_IS_S5B(transport)) {
- JingleS5B *s5b = JINGLE_S5B(transport);
- xmlnode *xmltransport = xmlnode_get_child(xmlcontent, "transport");
- jingle_s5b_handle_transport_accept(s5b, session, xmltransport);
}
-
+
g_object_unref(session);
g_object_unref(transport);
break;
@@ -901,7 +890,7 @@ jingle_file_transfer_handle_action_inter
xmlnode_get_attrib(xmlnode_get_child(xmlcontent,
"transport"), "sid");
gchar *who = jingle_session_get_remote_jid(session);
- const PurpleXfer *xfer = JINGLE_FT(content)->priv->xfer;
+ PurpleXfer *xfer = JINGLE_FT(content)->priv->xfer;
const gchar *filename = purple_xfer_get_local_filename(xfer);
JingleTransport *new_transport =
jingle_content_get_pending_transport(content);
@@ -983,13 +972,11 @@ jingle_file_transfer_new_xfer(PurpleConn
PurpleXfer *
jingle_file_transfer_new_xfer(PurpleConnection *gc, const gchar *who)
{
- JabberStream *js = gc->proto_data;
PurpleXfer *xfer = purple_xfer_new(gc->account, PURPLE_XFER_SEND, who);
purple_debug_info("jingle-ft", "jingle_file_transfer_new_xfer\n");
- if (xfer)
- {
+ if (xfer) {
purple_xfer_set_init_fnc(xfer, jingle_file_transfer_xfer_init);
purple_xfer_set_cancel_send_fnc(xfer, jingle_file_transfer_cancel_send);
purple_xfer_set_end_fnc(xfer, jingle_file_transfer_xfer_end);
@@ -1002,7 +989,6 @@ jingle_file_transfer_send(PurpleConnecti
void
jingle_file_transfer_send(PurpleConnection *gc, const char *who, const char *file)
{
- JabberStream *js = gc->proto_data;
PurpleXfer *xfer = jingle_file_transfer_new_xfer(gc, who);
purple_debug_info("jingle-ft", "jingle_file_transfer_send\n");
============================================================
--- libpurple/protocols/jabber/jingle/s5b.c c7b2af23652fc3f56bfd2f35cd5042a795f1c8c4
+++ libpurple/protocols/jabber/jingle/s5b.c d9f6d2cd6b34bf5a15da1a3fe69ceab44f0687bd
@@ -31,104 +31,182 @@
/* auxillary functions to handle JabberBytestreamsStreamhosts, maybe this
should be in a separtate module, used by si.c and other places as well */
-static JabberBytestreamsStreamhost *
-jingle_s5b_streamhost_create(const gchar *jid, const gchar *host, int port,
- const gchar *zeroconf)
+typedef enum {
+ JINGLE_S5B_CANDIDATE_TYPE_DIRECT,
+ JINGLE_S5B_CANDIDATE_TYPE_ASSISTED,
+ JINGLE_S5B_CANDIDATE_TYPE_TUNNEL,
+ JINGLE_S5B_CANDIDATE_TYPE_PROXY,
+ JINGLE_S5B_CANDIDATE_TYPE_UNKNOWN
+} JingleS5BCandidateType;
+
+typedef struct {
+ gchar *cid;
+ gchar *host;
+ gchar *jid;
+ guint16 port;
+ guint priority;
+ JingleS5BCandidateType type;
+} JingleS5BCandidate;
+
+static guint
+jingle_s5b_candidate_get_prio(JingleS5BCandidateType type)
{
- JabberBytestreamsStreamhost *sh = g_new0(JabberBytestreamsStreamhost, 1);
+ guint type_preference;
+
+ switch (type) {
+ case JINGLE_S5B_CANDIDATE_TYPE_DIRECT:
+ type_preference = 126;
+ break;
+ case JINGLE_S5B_CANDIDATE_TYPE_ASSISTED:
+ type_preference = 120;
+ break;
+ case JINGLE_S5B_CANDIDATE_TYPE_TUNNEL:
+ type_preference = 110;
+ break;
+ case JINGLE_S5B_CANDIDATE_TYPE_PROXY:
+ type_preference = 10;
+ break;
+ default:
+ type_preference = 0;
+ break;
+ }
+
+ /* we set "local preference" to 0 (see XEP-0260) */
+ return 65536 * type_preference;
+}
+
+static JingleS5BCandidate *
+jingle_s5b_candidate_create(JabberStream *js, const gchar *jid, const gchar *host,
+ guint16 port, JingleS5BCandidateType type)
+{
+ JingleS5BCandidate *candidate = g_new0(JingleS5BCandidate, 1);
- if (sh) {
- sh->jid = g_strdup(jid);
- sh->host = g_strdup(host);
- sh->port = port;
- if (zeroconf)
- sh->zeroconf = g_strdup(zeroconf);
+ if (candidate) {
+ candidate->cid = jabber_get_next_id(js);
+ candidate->jid = g_strdup(jid);
+ candidate->host = g_strdup(host);
+ candidate->port = port;
+ candidate->type = type;
+ candidate->priority = jingle_s5b_candidate_get_prio(type);
}
- return sh;
+ return candidate;
}
-static JabberBytestreamsStreamhost *
-jingle_s5b_streamhost_create_from_xml(xmlnode *streamhost)
+static JingleS5BCandidate *
+jingle_s5b_candidate_create_from_xml(xmlnode *candidate)
{
- JabberBytestreamsStreamhost *sh = NULL;
- const gchar *jid = xmlnode_get_attrib(streamhost, "jid");
- const gchar *host = xmlnode_get_attrib(streamhost, "host");
- const gchar *port = xmlnode_get_attrib(streamhost, "port");
- const gchar *zeroconf = xmlnode_get_attrib(streamhost, "zeroconf");
+ JingleS5BCandidate *cand = NULL;
+ const gchar *cid = xmlnode_get_attrib(candidate, "cid");
+ const gchar *jid = xmlnode_get_attrib(candidate, "jid");
+ const gchar *host = xmlnode_get_attrib(candidate, "host");
+ const gchar *port = xmlnode_get_attrib(candidate, "port");
+ const gchar *priority = xmlnode_get_attrib(candidate, "priority");
+ const gchar *type = xmlnode_get_attrib(candidate, "type");
- if (jid && host) {
- sh = g_new0(JabberBytestreamsStreamhost, 1);
- sh->jid = g_strdup(jid);
- sh->host = g_strdup(host);
+ if (cid && host && priority) {
+ cand = g_new0(JingleS5BCandidate, 1);
+ cand->cid = g_strdup(cid);
+ cand->jid = g_strdup(jid);
+ cand->host = g_strdup(host);
if (port)
- sh->port = atoi(port);
- if (zeroconf)
- sh->zeroconf = g_strdup(zeroconf);
+ cand->port = atoi(port);
+ if (priority)
+ cand->priority = atoi(priority);
+
+ if (purple_strequal(type, "direct"))
+ cand->type = JINGLE_S5B_CANDIDATE_TYPE_DIRECT;
+ else if (purple_strequal(type, "assisted"))
+ cand->type = JINGLE_S5B_CANDIDATE_TYPE_ASSISTED;
+ else if (purple_strequal(type, "tunnel"))
+ cand->type = JINGLE_S5B_CANDIDATE_TYPE_TUNNEL;
+ else if (purple_strequal(type, "proxy"))
+ cand->type = JINGLE_S5B_CANDIDATE_TYPE_PROXY;
+ else
+ cand->type = JINGLE_S5B_CANDIDATE_TYPE_UNKNOWN;
}
- return sh;
+ return cand;
}
static void
-jingle_s5b_streamhost_destroy(JabberBytestreamsStreamhost *sh)
+jingle_s5b_candidate_destroy(JingleS5BCandidate *candidate)
{
- g_free(sh->jid);
- g_free(sh->host);
- if (sh->zeroconf)
- g_free(sh->zeroconf);
- g_free(sh);
+ g_free(candidate->cid);
+ g_free(candidate->jid);
+ g_free(candidate->host);
+ g_free(candidate);
}
-static JabberBytestreamsStreamhost *
-jingle_s5b_streamhost_copy(const JabberBytestreamsStreamhost *sh)
+static JingleS5BCandidate *
+jingle_s5b_candidate_copy(const JingleS5BCandidate *candidate)
{
- JabberBytestreamsStreamhost *new_sh = g_new0(JabberBytestreamsStreamhost, 1);
+ JingleS5BCandidate *new_cand = g_new0(JingleS5BCandidate, 1);
- if (new_sh) {
- new_sh->jid = g_strdup(sh->jid);
- new_sh->host = g_strdup(sh->host);
- new_sh->port = sh->port;
- if (sh->zeroconf)
- new_sh->zeroconf = g_strdup(sh->zeroconf);
+ if (new_cand) {
+ new_cand->cid = g_strdup(candidate->cid);
+ new_cand->jid = g_strdup(candidate->jid);
+ new_cand->host = g_strdup(candidate->host);
+ new_cand->port = candidate->port;
+ new_cand->priority = candidate->priority;
+ new_cand->type = candidate->type;
}
- return new_sh;
+ return new_cand;
}
static void
-jingle_s5b_streamhost_add_xml_internal(xmlnode *node,
- const JabberBytestreamsStreamhost *sh)
+jingle_s5b_candidate_add_xml_internal(xmlnode *node,
+ const JingleS5BCandidate *cand)
{
gchar port[10];
+ gchar priority[10];
if (node) {
- g_snprintf(port, 10, "%d", sh->port);
- xmlnode_set_attrib(node, "jid", sh->jid);
- xmlnode_set_attrib(node, "host", sh->host);
+ g_snprintf(port, 10, "%d", cand->port);
+ g_snprintf(priority, 10, "%d", cand->priority);
+ xmlnode_set_attrib(node, "cid", cand->cid);
+ xmlnode_set_attrib(node, "jid", cand->jid);
+ xmlnode_set_attrib(node, "host", cand->host);
xmlnode_set_attrib(node, "port", port);
- if (sh->zeroconf)
- xmlnode_set_attrib(node, "zeroconf", sh->zeroconf);
+ xmlnode_set_attrib(node, "priority", priority);
+
+ switch (cand->type) {
+ case JINGLE_S5B_CANDIDATE_TYPE_DIRECT:
+ xmlnode_set_attrib(node, "type", "direct");
+ break;
+ case JINGLE_S5B_CANDIDATE_TYPE_ASSISTED:
+ xmlnode_set_attrib(node, "type", "assisted");
+ break;
+ case JINGLE_S5B_CANDIDATE_TYPE_TUNNEL:
+ xmlnode_set_attrib(node, "type", "tunnel");
+ break;
+ case JINGLE_S5B_CANDIDATE_TYPE_PROXY:
+ xmlnode_set_attrib(node, "type", "proxy");
+ break;
+ default:
+ break;
+ }
}
}
static xmlnode *
-jingle_s5b_streamhost_to_xml(const JabberBytestreamsStreamhost *sh)
+jingle_s5b_candidate_to_xml(const JingleS5BCandidate *cand)
{
- xmlnode *streamhost = xmlnode_new("streamhost");
+ xmlnode *candidate = xmlnode_new("candidate");
- jingle_s5b_streamhost_add_xml_internal(streamhost, sh);
+ jingle_s5b_candidate_add_xml_internal(candidate, cand);
- return streamhost;
+ return candidate;
}
static xmlnode *
-jingle_s5b_streamhost_to_xml_used(const JabberBytestreamsStreamhost *sh)
+jingle_s5b_candidate_to_xml_used(const JingleS5BCandidate *cand)
{
- xmlnode *streamhost_used = xmlnode_new("streamhost-used");
+ xmlnode *candidate_used = xmlnode_new("candidate-used");
+
+ xmlnode_set_attrib(candidate_used, "cid", cand->cid);
- jingle_s5b_streamhost_add_xml_internal(streamhost_used, sh);
-
- return streamhost_used;
+ return candidate_used;
}
struct _JingleS5BPrivate {
@@ -145,11 +223,11 @@ struct _JingleS5BPrivate {
char *rxqueue;
size_t rxlen;
gsize rxmaxlen;
- GList *remote_streamhosts;
- GList *local_streamhosts;
- GList *remaining_streamhosts; /* pointer to untested remote SHs */
- JabberBytestreamsStreamhost *successfull_remote_streamhost;
- JabberBytestreamsStreamhost *accepted_streamhost;
+ GList *remote_candidates;
+ GList *local_candidates;
+ GList *remaining_candidates; /* pointer to untested remote SHs */
+ JingleS5BCandidate *successful_remote_candidate;
+ JingleS5BCandidate *accepted_candidate;
JingleS5BConnectCallback *connect_cb;
JingleS5BErrorCallback *error_cb;
JingleS5BFailedConnectCallback *failed_cb;
@@ -262,19 +340,19 @@ jingle_s5b_finalize (GObject *s5b)
}
/* free the local streamhosts */
- while (priv->local_streamhosts) {
- jingle_s5b_streamhost_destroy(
- (JabberBytestreamsStreamhost *)priv->local_streamhosts->data);
- priv->local_streamhosts = g_list_delete_link(priv->local_streamhosts,
- priv->local_streamhosts);
+ while (priv->local_candidates) {
+ jingle_s5b_candidate_destroy(
+ (JingleS5BCandidate *)priv->local_candidates->data);
+ priv->local_candidates = g_list_delete_link(priv->local_candidates,
+ priv->local_candidates);
}
/* free the remote streamhosts */
- while (priv->local_streamhosts) {
- jingle_s5b_streamhost_destroy(
- (JabberBytestreamsStreamhost *)priv->remote_streamhosts->data);
- priv->remote_streamhosts = g_list_delete_link(priv->remote_streamhosts,
- priv->remote_streamhosts);
+ while (priv->remote_candidates) {
+ jingle_s5b_candidate_destroy(
+ (JingleS5BCandidate *)priv->remote_candidates->data);
+ priv->remote_candidates = g_list_delete_link(priv->remote_candidates,
+ priv->remote_candidates);
}
if (priv->ppi)
@@ -371,22 +449,37 @@ void jingle_s5b_set_failed_connect_callb
s5b->priv->failed_content = content;
}
+static void
+jingle_s5b_add_remote_candidate(JingleS5B *s5b, JingleS5BCandidate *cand)
+{
+ GList *iter = s5b->priv->remote_candidates;
+
+ for (; iter ; iter = g_list_next(iter)) {
+ JingleS5BCandidate *curr_cand = (JingleS5BCandidate *) iter->data;
+
+ if (cand->priority > curr_cand->priority)
+ break;
+ }
+
+ s5b->priv->remote_candidates =
+ g_list_insert_before(s5b->priv->remote_candidates, iter, cand);
+}
+
void
-jingle_s5b_add_streamhosts(JingleS5B *s5b, const xmlnode *transport)
+jingle_s5b_add_candidates(JingleS5B *s5b, const xmlnode *transport)
{
- xmlnode *streamhost;
+ xmlnode *candidate;
- for (streamhost = xmlnode_get_child(transport, "streamhost");
- streamhost;
- streamhost = xmlnode_get_next_twin(streamhost)) {
- JabberBytestreamsStreamhost *sh =
- jingle_s5b_streamhost_create_from_xml(streamhost);
- if (sh) {
+ for (candidate = xmlnode_get_child(transport, "candidate");
+ candidate;
+ candidate = xmlnode_get_next_twin(candidate)) {
+ JingleS5BCandidate *cand =
+ jingle_s5b_candidate_create_from_xml(candidate);
+ if (cand) {
purple_debug_info("jingle-s5b",
- "adding streamhost jid = %s, host = %s, port = %d to remote "
- "streamhosts\n", sh->jid, sh->host, sh->port);
- s5b->priv->remote_streamhosts =
- g_list_append(s5b->priv->remote_streamhosts, sh);
+ "adding streamhost cid = %s, jid = %s, host = %s, port = %d to remote "
+ "candidates\n", cand->cid, cand->jid, cand->host, cand->port);
+ jingle_s5b_add_remote_candidate(s5b, cand);
}
}
}
@@ -400,7 +493,7 @@ jingle_s5b_parse_internal(xmlnode *s5b)
jingle_s5b_set_sid(JINGLE_S5B(transport),
xmlnode_get_attrib(s5b, "sid"));
- jingle_s5b_add_streamhosts(JINGLE_S5B(transport), s5b);
+ jingle_s5b_add_candidates(JINGLE_S5B(transport), s5b);
return transport;
}
@@ -421,18 +514,20 @@ jingle_s5b_to_xml_internal(JingleTranspo
xmlnode_set_attrib(node, "mode", "tcp");
if (action == JINGLE_SESSION_INITIATE || action == JINGLE_SESSION_ACCEPT) {
- for (iter = JINGLE_S5B_GET_PRIVATE(s5b)->local_streamhosts;
+ for (iter = JINGLE_S5B_GET_PRIVATE(s5b)->local_candidates;
iter;
iter = g_list_next(iter)) {
- JabberBytestreamsStreamhost *sh =
- (JabberBytestreamsStreamhost *) iter->data;
- xmlnode_insert_child(node, jingle_s5b_streamhost_to_xml(sh));
+ JingleS5BCandidate *cand =
+ (JingleS5BCandidate *) iter->data;
+ xmlnode_insert_child(node, jingle_s5b_candidate_to_xml(cand));
}
- } else if (action == JINGLE_TRANSPORT_ACCEPT) {
+ } else if (action == JINGLE_TRANSPORT_INFO) {
/* should include the chosen streamhost here... */
- xmlnode_insert_child(node,
- jingle_s5b_streamhost_to_xml_used(
- s5b->priv->successfull_remote_streamhost));
+ if (s5b->priv->successful_remote_candidate) {
+ xmlnode_insert_child(node,
+ jingle_s5b_candidate_to_xml_used(
+ s5b->priv->successful_remote_candidate));
+ }
}
return node;
@@ -502,7 +597,7 @@ jingle_s5b_stop_connection_attempts(Jing
{
purple_debug_info("jingle-s5b", "stop connection attempts\n");
- s5b->priv->remaining_streamhosts = NULL;
+ s5b->priv->remaining_candidates = NULL;
if (s5b->priv->listen_data) {
purple_network_listen_cancel(s5b->priv->listen_data);
@@ -566,7 +661,7 @@ jingle_s5b_send_read_again_resp_cb(gpoin
s5b->priv->rxqueue = NULL;
/* Before actually starting sending the file, we need to wait until the
- * recipient sends the IQ result with <streamhost-used/>
+ * recipient sends the IQ result with <candidate-used/>
*/
purple_debug_info("jingle-s5b", "SOCKS5 connection negotiation completed. "
"Waiting for IQ result to start file transfer.\n");
@@ -926,20 +1021,22 @@ jingle_s5b_listen_cb(int sock, gpointer
local_port);
if (!purple_strequal(local_ip, "0.0.0.0")) {
- JabberBytestreamsStreamhost *sh =
- jingle_s5b_streamhost_create(jid, local_ip, local_port, NULL);
- JINGLE_S5B_GET_PRIVATE(s5b)->local_streamhosts =
- g_list_append(JINGLE_S5B_GET_PRIVATE(s5b)->local_streamhosts,
- sh);
+ JingleS5BCandidate *cand =
+ jingle_s5b_candidate_create(js, jid, local_ip, local_port,
+ JINGLE_S5B_CANDIDATE_TYPE_DIRECT);
+ JINGLE_S5B_GET_PRIVATE(s5b)->local_candidates =
+ g_list_append(JINGLE_S5B_GET_PRIVATE(s5b)->local_candidates,
+ cand);
}
if (!purple_strequal(local_ip, public_ip) &&
!purple_strequal(public_ip, "0.0.0.0")) {
- JabberBytestreamsStreamhost *sh =
- jingle_s5b_streamhost_create(jid, public_ip, local_port, NULL);
- JINGLE_S5B_GET_PRIVATE(s5b)->local_streamhosts =
- g_list_append(JINGLE_S5B_GET_PRIVATE(s5b)->local_streamhosts,
- sh);
+ JingleS5BCandidate *cand =
+ jingle_s5b_candidate_create(js, jid, public_ip, local_port,
+ JINGLE_S5B_CANDIDATE_TYPE_ASSISTED);
+ JINGLE_S5B_GET_PRIVATE(s5b)->local_candidates =
+ g_list_append(JINGLE_S5B_GET_PRIVATE(s5b)->local_candidates,
+ cand);
}
s5b->priv->local_fd = sock;
@@ -963,9 +1060,12 @@ jingle_s5b_listen_cb(int sock, gpointer
/* is this check really nessesary? si.c does it so... */
if (sh->jid && sh->host && sh->port > 0) {
- JINGLE_S5B_GET_PRIVATE(s5b)->local_streamhosts =
- g_list_append(JINGLE_S5B_GET_PRIVATE(s5b)->local_streamhosts,
- jingle_s5b_streamhost_copy(sh));
+ JingleS5BCandidate *cand =
+ jingle_s5b_candidate_create(js, sh->jid, sh->host, sh->port,
+ JINGLE_S5B_CANDIDATE_TYPE_PROXY);
+ JINGLE_S5B_GET_PRIVATE(s5b)->local_candidates =
+ g_list_append(JINGLE_S5B_GET_PRIVATE(s5b)->local_candidates,
+ cand);
}
}
/* note: even if we couldn't obtain a local port and no bytestream
@@ -992,7 +1092,7 @@ void
}
void
-jingle_s5b_gather_streamhosts(JingleSession *session, JingleS5B *s5b)
+jingle_s5b_gather_candidates(JingleSession *session, JingleS5B *s5b)
{
JingleS5BConnectData *data = g_new0(JingleS5BConnectData, 1);
data->session = session;
@@ -1009,70 +1109,23 @@ jingle_s5b_gather_streamhosts(JingleSess
}
}
-static void
-jingle_s5b_transport_accept_cb(JabberStream *js, const char *from,
- JabberIqType type, const char *id, xmlnode *packet, gpointer data)
-{
- JingleS5BConnectData *cd = (JingleS5BConnectData *) data;
- JingleSession *session = cd->session;
- JingleS5B *s5b = cd->s5b;
-
- purple_debug_info("jingle-s5b",
- "in jingle_s5b_transport_accept_cb: is connected to remote %d,"
- " remote is connected %d\n", jingle_s5b_is_connected_to_remote(s5b),
- jingle_s5b_remote_is_connected(s5b));
-
- if (type == JABBER_IQ_RESULT) {
- if (!(!jingle_session_is_initiator(session) &&
- jingle_s5b_is_connected_to_remote(s5b)) ||
- !jingle_s5b_remote_is_connected(s5b)) {
- /* unless we are the receiver and the receiver could connect,
- now we shall "surrender" to other side and signal the content
- to start */
- jingle_s5b_surrender(s5b);
- /* start transfer */
- if (s5b->priv->connect_cb && s5b->priv->connect_content) {
- s5b->priv->connect_cb(s5b->priv->connect_content);
- } else {
- /* some error? */
- }
- }
- }
-
- g_free(cd);
-}
-
static void jingle_s5b_attempt_connect_internal(gpointer data);
static gboolean
-jingle_s5b_streamhost_is_local(JabberStream *js, const gchar *jid)
+jingle_s5b_candidate_is_local(const JingleS5BCandidate *cand)
{
- gchar *me = g_strdup_printf("%s@%s/%s", js->user->node, js->user->domain,
- js->user->resource);
- gchar *me_bare = jabber_get_bare_jid(me);
- gchar *bare_jid = jabber_get_bare_jid(jid);
- gboolean equal = purple_strequal(bare_jid, me_bare);
-
- purple_debug_info("jingle-s5b",
- "jingle_s5b_streamhost_is_local: comparing JIDs %s and %s\n",
- me, jid);
-
- g_free(me);
- g_free(me_bare);
- g_free(bare_jid);
-
- return equal;
+ return cand->type != JINGLE_S5B_CANDIDATE_TYPE_PROXY;
}
static gboolean
-jingle_s5b_has_local_streamhosts(JabberStream *js, JingleS5B *s5b)
+jingle_s5b_has_local_candidates(JabberStream *js, JingleS5B *s5b)
{
- GList *iter = s5b->priv->local_streamhosts;
+ GList *iter = s5b->priv->local_candidates;
for (; iter; iter = g_list_next(iter)) {
- JabberBytestreamsStreamhost *sh =
- (JabberBytestreamsStreamhost *) iter->data;
- if (jingle_s5b_streamhost_is_local(js, sh->jid)) {
+ JingleS5BCandidate *cand =
+ (JingleS5BCandidate *) iter->data;
+ if (jingle_s5b_candidate_is_local(cand)) {
return TRUE;
}
}
@@ -1094,9 +1147,9 @@ jingle_s5b_connect_timeout_cb(gpointer d
s5b->priv->connect_timeout = 0;
/* advance streamhost "counter" */
- if (s5b->priv->remaining_streamhosts) {
- s5b->priv->remaining_streamhosts =
- g_list_next(s5b->priv->remaining_streamhosts);
+ if (s5b->priv->remaining_candidates) {
+ s5b->priv->remaining_candidates =
+ g_list_next(s5b->priv->remaining_candidates);
purple_debug_info("jingle-s5b", "trying next streamhost\n");
/* if remaining_streamhost is NULL here, this call will result in a
streamhost error (and potentially fallback to IBB) */
@@ -1145,23 +1198,22 @@ jingle_s5b_connect_cb(gpointer data, gin
/* set the currently tried streamhost as the successfull one */
- s5b->priv->successfull_remote_streamhost =
- (JabberBytestreamsStreamhost *) s5b->priv->remaining_streamhosts->data;
+ s5b->priv->successful_remote_candidate =
+ (JingleS5BCandidate *) s5b->priv->remaining_candidates->data;
/* should stop trying to connect */
jingle_s5b_stop_connection_attempts(s5b);
- /* should send transport-accept with streamhost-used */
- result = jingle_session_to_packet(session, JINGLE_TRANSPORT_ACCEPT);
- jabber_iq_set_callback(result, jingle_s5b_transport_accept_cb, cd);
+ /* should send transport-info with candidate-used */
+ result = jingle_session_to_packet(session, JINGLE_TRANSPORT_INFO);
jabber_iq_send(result);
+ g_free(cd);
}
static void
-jingle_s5b_connect_to_streamhost(JingleS5BConnectData *data,
- const JabberBytestreamsStreamhost *sh, gboolean is_proxy,
- void (*connect_cb)(gpointer, gint, const gchar *),
- GSourceFunc timeout_cb)
+jingle_s5b_connect_to_candidate(JingleS5BConnectData *data,
+ const JingleS5BCandidate *cand, gboolean is_local_proxy,
+ void (*connect_cb)(gpointer, gint, const gchar *), GSourceFunc timeout_cb)
{
JingleSession *session = data->session;
JingleS5B *s5b = data->s5b;
@@ -1170,20 +1222,19 @@ jingle_s5b_connect_to_streamhost(JingleS
JabberID *dstjid = jabber_id_new(who);
gchar *dstaddr = NULL;
gchar *hash = NULL;
- gboolean remote_is_proxy = !purple_strequal(sh->jid, who);
purple_debug_info("jingle-s5b",
"attempting to connect to streamhost: %s, port: %d\n",
- sh->host, sh->port);
+ cand->host, cand->port);
if (s5b->priv->ppi)
purple_proxy_info_destroy(s5b->priv->ppi);
s5b->priv->ppi = purple_proxy_info_new();
purple_proxy_info_set_type(s5b->priv->ppi, PURPLE_PROXY_SOCKS5);
- purple_proxy_info_set_host(s5b->priv->ppi, sh->host);
- purple_proxy_info_set_port(s5b->priv->ppi, sh->port);
+ purple_proxy_info_set_host(s5b->priv->ppi, cand->host);
+ purple_proxy_info_set_port(s5b->priv->ppi, cand->port);
- if (is_proxy)
+ if (is_local_proxy)
dstaddr = g_strdup_printf("%s%s@%s/%s%s@%s/%s", s5b->priv->sid,
js->user->node, js->user->domain, js->user->resource,
dstjid->node, dstjid->domain, dstjid->resource);
@@ -1208,77 +1259,11 @@ jingle_s5b_connect_to_streamhost(JingleS
is a proxy and we have local candidates ourselves, to allow the other
end a chance to connect to use before reverting to a proxy */
s5b->priv->connect_timeout =
- purple_timeout_add(
- remote_is_proxy && jingle_s5b_has_local_streamhosts(js, s5b) ?
- STREAMHOST_CONNECT_PROXY_TIMEOUT_MILLIS :
- STREAMHOST_CONNECT_TIMEOUT_MILLIS,
- timeout_cb, data);
+ purple_timeout_add(STREAMHOST_CONNECT_TIMEOUT_MILLIS, timeout_cb, data);
g_free(dstjid);
}
-static void
-jingle_s5b_attempt_connect_internal(gpointer data)
-{
- JingleSession *session = ((JingleS5BConnectData *) data)->session;
- JingleS5B *s5b = ((JingleS5BConnectData *) data)->s5b;
-
- if (s5b->priv->remaining_streamhosts) {
-
- JabberBytestreamsStreamhost *sh =
- (JabberBytestreamsStreamhost *) s5b->priv->remaining_streamhosts->data;
-
- jingle_s5b_connect_to_streamhost((JingleS5BConnectData *) data, sh,
- FALSE, jingle_s5b_connect_cb, jingle_s5b_connect_timeout_cb);
- } else {
- /* send streamhost error */
- JabberIq *streamhost_error =
- jingle_session_to_packet(session, JINGLE_TRANSPORT_INFO);
- xmlnode *jingle =
- xmlnode_get_child(streamhost_error->node, "jingle");
- xmlnode *content =
- xmlnode_get_child(jingle, "content");
- xmlnode *transport = xmlnode_get_child(content, "transport");
-
- xmlnode_insert_child(transport, xmlnode_new("streamhost-error"));
- jabber_iq_send(streamhost_error);
-
- /* signal to the content that S5B failed (from our side) */
- if (s5b->priv->failed_cb && s5b->priv->failed_content)
- s5b->priv->failed_cb(s5b->priv->failed_content);
-
- g_free(data);
- }
-}
-
-void
-jingle_s5b_attempt_connect(JingleSession *session, JingleS5B *s5b)
-{
- JingleS5BConnectData *data = g_new0(JingleS5BConnectData, 1);
-
- data->session = session;
- data->s5b = s5b;
- s5b->priv->remaining_streamhosts = s5b->priv->remote_streamhosts;
- jingle_s5b_attempt_connect_internal(data);
-}
-
-
-
-static JabberBytestreamsStreamhost *
-jingle_s5b_find_local_streamhost(JingleS5B *s5b, const gchar *jid)
-{
- const GList *iter = s5b->priv->local_streamhosts;
-
- for (; iter ; iter = g_list_next(iter)) {
- JabberBytestreamsStreamhost *sh =
- (JabberBytestreamsStreamhost *) iter->data;
- if (purple_strequal(sh->jid, jid)) {
- return sh;
- }
- }
- return NULL;
-}
-
static gboolean
jingle_s5b_proxy_timeout_cb(gpointer data)
{
@@ -1335,7 +1320,7 @@ jingle_s5b_proxy_connect_cb(gpointer dat
/* active the streamhost */
iq = jabber_iq_new_query(jingle_session_get_js(session), JABBER_IQ_SET,
"http://jabber.org/protocol/bytestreams");
- xmlnode_set_attrib(iq->node, "to", s5b->priv->accepted_streamhost->jid);
+ xmlnode_set_attrib(iq->node, "to", s5b->priv->accepted_candidate->jid);
query = xmlnode_get_child(iq->node, "query");
xmlnode_set_attrib(query, "sid", s5b->priv->sid);
activate = xmlnode_new_child(query, "activate");
@@ -1347,15 +1332,15 @@ jingle_s5b_connect_to_proxy(JingleSessio
static void
jingle_s5b_connect_to_proxy(JingleSession *session, JingleS5B *s5b,
- JabberBytestreamsStreamhost *sh)
+ JingleS5BCandidate *cand)
{
purple_debug_info("jingle-s5b", "in jingle_s5b_connect_to_proxy\n");
- if (sh) {
+ if (cand) {
JingleS5BConnectData *data = g_new0(JingleS5BConnectData, 1);
data->session = session;
data->s5b = s5b;
- jingle_s5b_connect_to_streamhost(data, sh, TRUE,
+ jingle_s5b_connect_to_candidate(data, cand, TRUE,
jingle_s5b_proxy_connect_cb, jingle_s5b_proxy_timeout_cb);
} else {
purple_debug_error("jingle-s5b",
@@ -1365,41 +1350,44 @@ jingle_s5b_connect_to_proxy(JingleSessio
}
}
-void
-jingle_s5b_handle_transport_accept(JingleS5B *s5b, JingleSession *session,
- xmlnode *transport)
+
+static void
+jingle_s5b_attempt_connect_internal(gpointer data)
{
- xmlnode *streamhost_used = xmlnode_get_child(transport, "streamhost-used");
+ JingleSession *session = ((JingleS5BConnectData *) data)->session;
+ JingleS5B *s5b = ((JingleS5BConnectData *) data)->s5b;
+
+ if (s5b->priv->remaining_candidates) {
+ JingleS5BCandidate *cand =
+ (JingleS5BCandidate *) s5b->priv->remaining_candidates->data;
- if (streamhost_used) {
- const gchar *jid = xmlnode_get_attrib(streamhost_used, "jid");
- JabberStream *js = jingle_session_get_js(session);
- JabberBytestreamsStreamhost *sh =
- jingle_s5b_find_local_streamhost(s5b, jid);
+ jingle_s5b_connect_to_candidate((JingleS5BConnectData *) data, cand,
+ FALSE, jingle_s5b_connect_cb, jingle_s5b_connect_timeout_cb);
+ } else {
+ /* send candidate error */
+ JabberIq *candidate_error =
+ jingle_session_to_packet(session, JINGLE_TRANSPORT_INFO);
+ xmlnode *jingle =
+ xmlnode_get_child(candidate_error->node, "jingle");
+ xmlnode *content =
+ xmlnode_get_child(jingle, "content");
+ xmlnode *transport = xmlnode_get_child(content, "transport");
+
+ xmlnode_insert_child(transport, xmlnode_new("candidate-error"));
+ jabber_iq_send(candidate_error);
- s5b->priv->accepted_streamhost = sh;
-
- purple_debug_info("jingle-ft", "got streamhost-used\n");
- /* stop connection attempts */
- jingle_s5b_stop_connection_attempts(s5b);
+ /* if the other end could connect to us (they sent a "candidate-used")
+ we should use that */
+ if (s5b->priv->accepted_candidate) {
+ JingleS5BCandidate *cand = s5b->priv->accepted_candidate;
- if (jingle_session_is_initiator(session) &&
- jingle_s5b_is_connected_to_remote(s5b)) {
- /* we are the initiator and both parties could connect,
- give up "ownership", see footnote 3 in XEP-0260 */
- jingle_s5b_surrender(s5b);
- } else {
- /* we are now the "owner" of the bytestream */
jingle_s5b_take_command(s5b);
-
- /* also when receiving a <streamhost-used/> we need to
- check if that is not one of our local streamhosts,
- in which case it is a proxy, and we should connect to that */
- if (jid && !jingle_s5b_streamhost_is_local(js, jid)) {
+
+ if (!jingle_s5b_candidate_is_local(cand)) {
purple_debug_info("jingle-ft",
- "got transport-accept on a proxy, "
- "need to connect to the proxy\n");
- jingle_s5b_connect_to_proxy(session, s5b, sh);
+ "the remote connected to us through a proxy, "
+ "need to connect to the proxy\n");
+ jingle_s5b_connect_to_proxy(session, s5b, cand);
} else {
/* start transfer */
if (s5b->priv->connect_cb && s5b->priv->connect_content) {
@@ -1408,12 +1396,137 @@ jingle_s5b_handle_transport_accept(Jingl
/* some error? */
}
}
+ } else {
+ /* signal to the content that S5B failed (from our side) */
+ if (s5b->priv->failed_cb && s5b->priv->failed_content)
+ s5b->priv->failed_cb(s5b->priv->failed_content);
}
+ g_free(data);
}
}
+void
+jingle_s5b_attempt_connect(JingleSession *session, JingleS5B *s5b)
+{
+ JingleS5BConnectData *data = g_new0(JingleS5BConnectData, 1);
+
+ data->session = session;
+ data->s5b = s5b;
+ s5b->priv->remaining_candidates = s5b->priv->remote_candidates;
+ jingle_s5b_attempt_connect_internal(data);
+}
+
+
+
+static JingleS5BCandidate *
+jingle_s5b_find_local_candidate(JingleS5B *s5b, const gchar *cid)
+{
+ const GList *iter = s5b->priv->local_candidates;
+
+ for (; iter ; iter = g_list_next(iter)) {
+ JingleS5BCandidate *cand =
+ (JingleS5BCandidate *) iter->data;
+ if (purple_strequal(cand->cid, cid)) {
+ return cand;
+ }
+ }
+ return NULL;
+}
+
+
+void
+jingle_s5b_handle_transport_info(JingleS5B *s5b, JingleSession *session,
+ xmlnode *transport)
+{
+ xmlnode *candidate_used = xmlnode_get_child(transport, "candidate-used");
+ xmlnode *candidate_error = xmlnode_get_child(transport, "candidate-error");
+
+ if (candidate_used) {
+ const gchar *cid = xmlnode_get_attrib(candidate_used, "cid");
+ JingleS5BCandidate *cand =
+ jingle_s5b_find_local_candidate(s5b, cid);
+ JingleS5BCandidate *next_to_try =
+ (JingleS5BCandidate *) s5b->priv->remaining_candidates->data;
+
+ s5b->priv->accepted_candidate = cand;
+
+ purple_debug_info("jingle-ft", "got candidate-used\n");
+ /* stop connection attempts, unless there is remaining candidates of
+ higher priority */
+ if (!next_to_try || next_to_try->priority < cand->priority) {
+ /* we don't have any remaining candidates to try, send a
+ candidate-error */
+ /* send candidate error */
+ JabberIq *candidate_error =
+ jingle_session_to_packet(session, JINGLE_TRANSPORT_INFO);
+ xmlnode *jingle =
+ xmlnode_get_child(candidate_error->node, "jingle");
+ xmlnode *content =
+ xmlnode_get_child(jingle, "content");
+ xmlnode *transport = xmlnode_get_child(content, "transport");
+
+ xmlnode_insert_child(transport, xmlnode_new("candidate-error"));
+ jabber_iq_send(candidate_error);
+
+ jingle_s5b_stop_connection_attempts(s5b);
+
+ /* if we could not connect to the remote, or if we could and that
+ candidate has a lower priority, we have "won",
+ if the candidates have the same priority, we have won if we are
+ the initiator */
+ if (!s5b->priv->remaining_candidates ||
+ (s5b->priv->successful_remote_candidate
+ && s5b->priv->successful_remote_candidate->priority < cand->priority) ||
+ (s5b->priv->successful_remote_candidate
+ && s5b->priv->successful_remote_candidate->priority == cand->priority
+ && jingle_session_is_initiator(session))) {
+ /* we are now the "owner" of the bytestream */
+ jingle_s5b_take_command(s5b);
+
+ /* also when receiving a <streamhost-used/> we need to
+ check if that is not one of our local streamhosts,
+ in which case it is a proxy, and we should connect to that */
+ if (!jingle_s5b_candidate_is_local(cand)) {
+ purple_debug_info("jingle-ft",
+ "got transport-accept on a proxy, "
+ "need to connect to the proxy\n");
+ jingle_s5b_connect_to_proxy(session, s5b, cand);
+ } else {
+ /* start transfer */
+ if (s5b->priv->connect_cb && s5b->priv->connect_content) {
+ s5b->priv->connect_cb(s5b->priv->connect_content);
+ } else {
+ /* some error? */
+ }
+ }
+ } else {
+ jingle_s5b_surrender(s5b);
+ /* start the transfer */
+ if (s5b->priv->connect_cb && s5b->priv->connect_content) {
+ s5b->priv->connect_cb(s5b->priv->connect_content);
+ } else {
+ /* some error? */
+ }
+ }
+ }
+ } else if (candidate_error) {
+ if (s5b->priv->failed_cb && !s5b->priv->successful_remote_candidate &&
+ !s5b->priv->remaining_candidates) {
+ s5b->priv->failed_cb(s5b->priv->failed_content);
+ } else if (s5b->priv->successful_remote_candidate) {
+ jingle_s5b_surrender(s5b);
+ /* start the transfer */
+ if (s5b->priv->connect_cb && s5b->priv->connect_content) {
+ s5b->priv->connect_cb(s5b->priv->connect_content);
+ } else {
+ /* some error? */
+ }
+ }
+ }
+}
+
gboolean
-jingle_s5b_has_remaining_remote_streamhosts(const JingleS5B *s5b)
+jingle_s5b_has_remaining_remote_candidates(const JingleS5B *s5b)
{
- return s5b->priv->remaining_streamhosts != NULL;
+ return s5b->priv->remaining_candidates != NULL;
}
============================================================
--- libpurple/protocols/jabber/jingle/s5b.h 3fcc6a8f0b8ec5667d84a4058beff9600b5ef2ea
+++ libpurple/protocols/jabber/jingle/s5b.h 9d993ebbc9da48a427e0022ed1251227340c62a4
@@ -82,18 +82,18 @@ void jingle_s5b_set_failed_connect_callb
void jingle_s5b_set_failed_connect_callback(JingleS5B *s5b,
JingleS5BFailedConnectCallback *cb, JingleContent *content);
-void jingle_s5b_add_streamhosts(JingleS5B *s5b, const xmlnode *transport);
+void jingle_s5b_add_candidates(JingleS5B *s5b, const xmlnode *transport);
/* start discovering streamhosts, initiator will send session-initiate when
done, receiver will send transport-info */
-void jingle_s5b_gather_streamhosts(JingleSession *session, JingleS5B *s5b);
+void jingle_s5b_gather_candidates(JingleSession *session, JingleS5B *s5b);
void jingle_s5b_attempt_connect(JingleSession *session, JingleS5B *s5b);
-void jingle_s5b_handle_transport_accept(JingleS5B *s5b, JingleSession *session,
+void jingle_s5b_handle_transport_info(JingleS5B *s5b, JingleSession *session,
xmlnode *transport);
-gboolean jingle_s5b_has_remaining_remote_streamhosts(const JingleS5B *s5b);
+gboolean jingle_s5b_has_remaining_remote_candidates(const JingleS5B *s5b);
void jingle_s5b_stop_connection_attempts(JingleS5B *s5b);
More information about the Commits
mailing list