im.pidgin.pidgin: 5727e9151416612d63670c49ed8982d3bb1704b4
datallah at pidgin.im
datallah at pidgin.im
Wed Nov 21 00:40:38 EST 2007
-----------------------------------------------------------------
Revision: 5727e9151416612d63670c49ed8982d3bb1704b4
Ancestor: ea658359572b6c8c913d4f099d0072fc46ba2977
Author: datallah at pidgin.im
Date: 2007-11-21T05:22:39
Branch: im.pidgin.pidgin
Modified files:
ChangeLog libpurple/protocols/jabber/disco.c
libpurple/protocols/jabber/iq.c
libpurple/protocols/jabber/jabber.c
libpurple/protocols/jabber/jabber.h
libpurple/protocols/jabber/libxmpp.c
libpurple/protocols/jabber/si.c
ChangeLog:
Implement more of XEP-0065 to support sending files through a proxy. To avoid adding strings this close to a release, it only supports using a proxy that is discovered from the server, but we'll include an account option to manually specify a ft proxy in the next release. Lots of this is based on a patch from galt - Fixes #3730, #116, #1768
-------------- next part --------------
============================================================
--- ChangeLog 5ee66d6907e39e1174020cc5207c95eb593b34dd
+++ ChangeLog 5e7b6f4e5e4abb4009ab2d749a573c19979ddeac
@@ -16,6 +16,11 @@ version 2.3.0 (11/20/2007):
implementation.
* XMPP password changes that return errors no longer cause the saved
password to be changed.
+ * XMPP file transfer support has been enhanced to support sending
+ files through a proxy when the server supports discovering a
+ a bytestream proxy. This should make file transfers much more
+ reliable. The next release will add support for manually specifying
+ a proxy when the server doesn't advertise one.
Pidgin:
* If a plugin says it can't be unloaded, we now display an error and
============================================================
--- libpurple/protocols/jabber/disco.c 6e2617ef5f81e13ede4c7f3ed8cf254dec716e34
+++ libpurple/protocols/jabber/disco.c 091b432ce26cf0eeed08aa0b0198e38098df1765
@@ -44,7 +44,38 @@ struct _jabber_disco_info_cb_data {
xmlnode_set_attrib(feature, "var", x); \
}
+static void
+jabber_disco_bytestream_server_cb(JabberStream *js, xmlnode *packet, gpointer data) {
+ JabberBytestreamsStreamhost *sh = data;
+ const char *from = xmlnode_get_attrib(packet, "from");
+ xmlnode *query = xmlnode_get_child_with_namespace(packet, "query",
+ "http://jabber.org/protocol/bytestreams");
+ if (from && !strcmp(from, sh->jid) && query != NULL) {
+ xmlnode *sh_node = xmlnode_get_child(query, "streamhost");
+ if (sh_node) {
+ const char *jid = xmlnode_get_attrib(sh_node, "jid");
+ const char *port = xmlnode_get_attrib(sh_node, "port");
+
+
+ if (jid == NULL || strcmp(jid, from) != 0)
+ purple_debug_error("jabber", "Invalid jid(%s) for bytestream.\n",
+ jid ? jid : "(null)");
+
+ sh->host = g_strdup(xmlnode_get_attrib(sh_node, "host"));
+ sh->zeroconf = g_strdup(xmlnode_get_attrib(sh_node, "zeroconf"));
+ if (port != NULL)
+ sh->port = atoi(port);
+ }
+ }
+
+ purple_debug_info("jabber", "Discovered bytestream proxy server: "
+ "jid='%s' host='%s' port='%d' zeroconf='%s'\n",
+ from ? from : "", sh->host ? sh->host : "",
+ sh->port, sh->zeroconf ? sh->zeroconf : "");
+}
+
+
void jabber_disco_info_parse(JabberStream *js, xmlnode *packet) {
const char *from = xmlnode_get_attrib(packet, "from");
const char *type = xmlnode_get_attrib(packet, "type");
@@ -191,10 +222,26 @@ void jabber_disco_info_parse(JabberStrea
if(!strcmp(category, "conference") && !strcmp(type, "text")) {
/* we found a groupchat or MUC server, add it to the list */
/* XXX: actually check for protocol/muc or gc-1.0 support */
- js->chat_servers = g_list_append(js->chat_servers, g_strdup(from));
+ js->chat_servers = g_list_prepend(js->chat_servers, g_strdup(from));
} else if(!strcmp(category, "directory") && !strcmp(type, "user")) {
/* we found a JUD */
- js->user_directories = g_list_append(js->user_directories, g_strdup(from));
+ js->user_directories = g_list_prepend(js->user_directories, g_strdup(from));
+ } else if(!strcmp(category, "proxy") && !strcmp(type, "bytestreams")) {
+ /* This is a bytestream proxy */
+ JabberIq *iq;
+ JabberBytestreamsStreamhost *sh;
+
+ purple_debug_info("jabber", "Found bytestream proxy server: %s\n", from);
+
+ sh = g_new0(JabberBytestreamsStreamhost, 1);
+ sh->jid = g_strdup(from);
+ js->bs_proxies = g_list_prepend(js->bs_proxies, sh);
+
+ iq = jabber_iq_new_query(js, JABBER_IQ_GET,
+ "http://jabber.org/protocol/bytestreams");
+ xmlnode_set_attrib(iq->node, "to", sh->jid);
+ jabber_iq_set_callback(iq, jabber_disco_bytestream_server_cb, sh);
+ jabber_iq_send(iq);
}
} else if(!strcmp(child->name, "feature")) {
@@ -344,8 +391,8 @@ jabber_disco_server_info_result_cb(Jabbe
g_free(js->server_name);
js->server_name = g_strdup(name);
if (!strcmp(name, "Google Talk")) {
- purple_debug_info("jabber", "Google Talk!\n");
- js->googletalk = TRUE;
+ purple_debug_info("jabber", "Google Talk!\n");
+ js->googletalk = TRUE;
}
}
@@ -422,8 +469,7 @@ void jabber_disco_items_server(JabberStr
jabber_iq_set_callback(iq, jabber_disco_server_items_result_cb, NULL);
jabber_iq_send(iq);
- iq = jabber_iq_new_query(js, JABBER_IQ_GET,
- "http://jabber.org/protocol/disco#info");
+ iq = jabber_iq_new_query(js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#info");
xmlnode_set_attrib(iq->node, "to", js->user->domain);
jabber_iq_set_callback(iq, jabber_disco_server_info_result_cb, NULL);
jabber_iq_send(iq);
============================================================
--- libpurple/protocols/jabber/iq.c 4d6e6f759f6124928998742da165598f635f713d
+++ libpurple/protocols/jabber/iq.c b464cdac0c130ac27678ea3a67d65d1966544dbd
@@ -399,5 +399,6 @@ void jabber_iq_uninit(void)
void jabber_iq_uninit(void)
{
g_hash_table_destroy(iq_handlers);
+ iq_handlers = NULL;
}
============================================================
--- libpurple/protocols/jabber/jabber.c 663fd9b52448a7223e5004b2d490e9d687bd5c87
+++ libpurple/protocols/jabber/jabber.c 37a356263832da85c1ce5dac41c9c1204cb8f407
@@ -1234,20 +1234,31 @@ void jabber_close(PurpleConnection *gc)
g_hash_table_destroy(js->buddies);
if(js->chats)
g_hash_table_destroy(js->chats);
+
while(js->chat_servers) {
g_free(js->chat_servers->data);
js->chat_servers = g_list_delete_link(js->chat_servers, js->chat_servers);
}
+
while(js->user_directories) {
g_free(js->user_directories->data);
js->user_directories = g_list_delete_link(js->user_directories, js->user_directories);
}
- if(js->stream_id)
- g_free(js->stream_id);
+
+ while(js->bs_proxies) {
+ JabberBytestreamsStreamhost *sh = js->bs_proxies->data;
+ g_free(sh->jid);
+ g_free(sh->host);
+ g_free(sh->zeroconf);
+ g_free(sh);
+ js->bs_proxies = g_list_delete_link(js->bs_proxies, js->bs_proxies);
+ }
+
+ g_free(js->stream_id);
if(js->user)
jabber_id_free(js->user);
- if(js->avatar_hash)
- g_free(js->avatar_hash);
+ g_free(js->avatar_hash);
+
purple_circ_buffer_destroy(js->write_buffer);
if(js->writeh)
purple_input_remove(js->writeh);
@@ -1256,11 +1267,9 @@ void jabber_close(PurpleConnection *gc)
sasl_dispose(&js->sasl);
if(js->sasl_mechs)
g_string_free(js->sasl_mechs, TRUE);
- if(js->sasl_cb)
- g_free(js->sasl_cb);
+ g_free(js->sasl_cb);
#endif
- if(js->serverFQDN)
- g_free(js->serverFQDN);
+ g_free(js->serverFQDN);
while(js->commands) {
JabberAdHocCommands *cmd = js->commands->data;
g_free(cmd->jid);
@@ -1272,21 +1281,14 @@ void jabber_close(PurpleConnection *gc)
g_free(js->server_name);
g_free(js->gmail_last_time);
g_free(js->gmail_last_tid);
- if(js->old_msg)
- g_free(js->old_msg);
- if(js->old_avatarhash)
- g_free(js->old_avatarhash);
- if(js->old_artist)
- g_free(js->old_artist);
- if(js->old_title)
- g_free(js->old_title);
- if(js->old_source)
- g_free(js->old_source);
- if(js->old_uri)
- g_free(js->old_uri);
- if(js->old_track)
- g_free(js->old_track);
-
+ g_free(js->old_msg);
+ g_free(js->old_avatarhash);
+ g_free(js->old_artist);
+ g_free(js->old_title);
+ g_free(js->old_source);
+ g_free(js->old_uri);
+ g_free(js->old_track);
+
g_free(js);
gc->proto_data = NULL;
============================================================
--- libpurple/protocols/jabber/jabber.h eeb9aecb59fd9307df205360db6744f417356a46
+++ libpurple/protocols/jabber/jabber.h 9f3858e49283499a451c4f78dff6a1d782c20467
@@ -117,7 +117,7 @@ struct _JabberStream
GHashTable *disco_callbacks;
int next_id;
-
+ GList *bs_proxies;
GList *oob_file_transfers;
GList *file_transfers;
@@ -146,7 +146,7 @@ struct _JabberStream
char *gmail_last_time;
char *gmail_last_tid;
- char *serverFQDN;
+ char *serverFQDN;
/* OK, this stays at the end of the struct, so plugins can depend
* on the rest of the stuff being in the right place
@@ -202,6 +202,13 @@ typedef struct _JabberFeature
JabberFeatureEnabled *is_enabled;
} JabberFeature;
+typedef struct _JabberBytestreamsStreamhost {
+ char *jid;
+ char *host;
+ int port;
+ char *zeroconf;
+} JabberBytestreamsStreamhost;
+
/* what kind of additional features as returned from disco#info are supported? */
extern GList *jabber_features;
============================================================
--- libpurple/protocols/jabber/libxmpp.c a93e1e14f07d615b2083778bdbc2d308db5dbf0e
+++ libpurple/protocols/jabber/libxmpp.c e90c78b600e55ee816eae95ffc4c0fd69fdd785b
@@ -233,7 +233,15 @@ init_plugin(PurplePlugin *plugin)
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
option);
-
+#if 0 /* TODO: Enable this when we're string unfrozen */
+ option = purple_account_option_string_new(_("File transfer proxies"),
+ "ft_proxies",
+ /* TODO: Is this an acceptable default? */
+ "proxy.jabber.org:7777");
+ prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
+ option);
+#endif
+
jabber_init_plugin(plugin);
purple_prefs_remove("/plugins/prpl/jabber");
============================================================
--- libpurple/protocols/jabber/si.c b424e7ec04876efe3efb16b52b95e45a2b5f2fd6
+++ libpurple/protocols/jabber/si.c 0addee72648bfa16a97fb3a8d326e11b675cff9d
@@ -36,19 +36,14 @@
#include "iq.h"
#include "si.h"
-#include "si.h"
+#define STREAMHOST_CONNECT_TIMEOUT 15
-struct bytestreams_streamhost {
- char *jid;
- char *host;
- int port;
-};
-
typedef struct _JabberSIXfer {
JabberStream *js;
PurpleProxyConnectData *connect_data;
PurpleNetworkListenData *listen_data;
+ guint connect_timeout;
gboolean accepted;
@@ -99,39 +94,82 @@ jabber_si_bytestreams_connect_cb(gpointe
JabberSIXfer *jsx = xfer->data;
JabberIq *iq;
xmlnode *query, *su;
- struct bytestreams_streamhost *streamhost = jsx->streamhosts->data;
+ JabberBytestreamsStreamhost *streamhost = jsx->streamhosts->data;
purple_proxy_info_destroy(jsx->gpi);
+ jsx->gpi = NULL;
jsx->connect_data = NULL;
+ if (jsx->connect_timeout > 0)
+ purple_timeout_remove(jsx->connect_timeout);
+ jsx->connect_timeout = 0;
+
if(source < 0) {
purple_debug_warning("jabber",
"si connection failed, jid was %s, host was %s, error was %s\n",
- streamhost->jid, streamhost->host, error_message);
+ streamhost->jid, streamhost->host,
+ error_message ? error_message : "(null)");
jsx->streamhosts = g_list_remove(jsx->streamhosts, streamhost);
g_free(streamhost->jid);
g_free(streamhost->host);
+ g_free(streamhost->zeroconf);
g_free(streamhost);
jabber_si_bytestreams_attempt_connect(xfer);
return;
}
- iq = jabber_iq_new_query(jsx->js, JABBER_IQ_RESULT, "http://jabber.org/protocol/bytestreams");
- xmlnode_set_attrib(iq->node, "to", xfer->who);
- jabber_iq_set_id(iq, jsx->iq_id);
- query = xmlnode_get_child(iq->node, "query");
- su = xmlnode_new_child(query, "streamhost-used");
- xmlnode_set_attrib(su, "jid", streamhost->jid);
+ /* unknown file transfer type is assumed to be RECEIVE */
+ if(xfer->type == PURPLE_XFER_SEND)
+ {
+ xmlnode *activate;
+ iq = jabber_iq_new_query(jsx->js, JABBER_IQ_SET, "http://jabber.org/protocol/bytestreams");
+ xmlnode_set_attrib(iq->node, "to", streamhost->jid);
+ query = xmlnode_get_child(iq->node, "query");
+ xmlnode_set_attrib(query, "sid", jsx->stream_id);
+ activate = xmlnode_new_child(query, "activate");
+ xmlnode_insert_data(activate, xfer->who, -1);
+ /* TODO: We need to wait for an activation result before starting */
+ }
+ else
+ {
+ iq = jabber_iq_new_query(jsx->js, JABBER_IQ_RESULT, "http://jabber.org/protocol/bytestreams");
+ xmlnode_set_attrib(iq->node, "to", xfer->who);
+ jabber_iq_set_id(iq, jsx->iq_id);
+ query = xmlnode_get_child(iq->node, "query");
+ su = xmlnode_new_child(query, "streamhost-used");
+ xmlnode_set_attrib(su, "jid", streamhost->jid);
+ }
+
jabber_iq_send(iq);
purple_xfer_start(xfer, source, NULL, -1);
}
+static gboolean
+connect_timeout_cb(gpointer data)
+{
+ PurpleXfer *xfer = data;
+ JabberSIXfer *jsx = xfer->data;
+
+ purple_debug_info("jabber", "Streamhost connection timeout of %d seconds exceeded.\n", STREAMHOST_CONNECT_TIMEOUT);
+
+ jsx->connect_timeout = 0;
+
+ if (jsx->connect_data != NULL)
+ purple_proxy_connect_cancel(jsx->connect_data);
+ jsx->connect_data = NULL;
+
+ /* Trigger the connect error manually */
+ jabber_si_bytestreams_connect_cb(xfer, -1, "Timeout Exceeded.");
+
+ return FALSE;
+}
+
static void jabber_si_bytestreams_attempt_connect(PurpleXfer *xfer)
{
JabberSIXfer *jsx = xfer->data;
- struct bytestreams_streamhost *streamhost;
+ JabberBytestreamsStreamhost *streamhost;
char *dstaddr, *p;
int i;
unsigned char hashval[20];
@@ -160,19 +198,29 @@ static void jabber_si_bytestreams_attemp
streamhost = jsx->streamhosts->data;
+ jsx->connect_data = NULL;
+ if (jsx->gpi != NULL)
+ purple_proxy_info_destroy(jsx->gpi);
+ jsx->gpi = NULL;
+
dstjid = jabber_id_new(xfer->who);
- if(dstjid != NULL) {
+ /* TODO: Deal with zeroconf */
+
+ if(dstjid != NULL && streamhost->host && streamhost->port > 0) {
jsx->gpi = purple_proxy_info_new();
purple_proxy_info_set_type(jsx->gpi, PURPLE_PROXY_SOCKS5);
purple_proxy_info_set_host(jsx->gpi, streamhost->host);
purple_proxy_info_set_port(jsx->gpi, streamhost->port);
+ /* unknown file transfer type is assumed to be RECEIVE */
+ if(xfer->type == PURPLE_XFER_SEND)
+ dstaddr = g_strdup_printf("%s%s@%s/%s%s@%s/%s", jsx->stream_id, jsx->js->user->node, jsx->js->user->domain,
+ jsx->js->user->resource, dstjid->node, dstjid->domain, dstjid->resource);
+ else
+ dstaddr = g_strdup_printf("%s%s@%s/%s%s@%s/%s", jsx->stream_id, dstjid->node, dstjid->domain, dstjid->resource,
+ jsx->js->user->node, jsx->js->user->domain, jsx->js->user->resource);
-
- dstaddr = g_strdup_printf("%s%s@%s/%s%s@%s/%s", jsx->stream_id, dstjid->node, dstjid->domain, dstjid->resource, jsx->js->user->node,
- jsx->js->user->domain, jsx->js->user->resource);
-
purple_cipher_digest_region("sha1", (guchar *)dstaddr, strlen(dstaddr),
sizeof(hashval), hashval, NULL);
g_free(dstaddr);
@@ -186,6 +234,11 @@ static void jabber_si_bytestreams_attemp
jabber_si_bytestreams_connect_cb, xfer);
g_free(dstaddr);
+ /* When selecting a streamhost, timeout after STREAMHOST_CONNECT_TIMEOUT seconds, otherwise it takes forever */
+ if (xfer->type != PURPLE_XFER_SEND && jsx->connect_data != NULL)
+ jsx->connect_timeout = purple_timeout_add_seconds(
+ STREAMHOST_CONNECT_TIMEOUT, connect_timeout_cb, xfer);
+
jabber_id_free(dstjid);
}
@@ -194,6 +247,7 @@ static void jabber_si_bytestreams_attemp
jsx->streamhosts = g_list_remove(jsx->streamhosts, streamhost);
g_free(streamhost->jid);
g_free(streamhost->host);
+ g_free(streamhost->zeroconf);
g_free(streamhost);
jabber_si_bytestreams_attempt_connect(xfer);
}
@@ -232,17 +286,19 @@ void jabber_bytestreams_parse(JabberStre
for(streamhost = xmlnode_get_child(query, "streamhost"); streamhost;
streamhost = xmlnode_get_next_twin(streamhost)) {
- const char *jid, *host, *port;
- int portnum;
+ const char *jid, *host = NULL, *port, *zeroconf;
+ int portnum = 0;
if((jid = xmlnode_get_attrib(streamhost, "jid")) &&
- (host = xmlnode_get_attrib(streamhost, "host")) &&
+ ((zeroconf = xmlnode_get_attrib(streamhost, "zeroconf")) ||
+ ((host = xmlnode_get_attrib(streamhost, "host")) &&
(port = xmlnode_get_attrib(streamhost, "port")) &&
- (portnum = atoi(port))) {
- struct bytestreams_streamhost *sh = g_new0(struct bytestreams_streamhost, 1);
+ (portnum = atoi(port))))) {
+ JabberBytestreamsStreamhost *sh = g_new0(JabberBytestreamsStreamhost, 1);
sh->jid = g_strdup(jid);
sh->host = g_strdup(host);
sh->port = portnum;
+ sh->zeroconf = g_strdup(zeroconf);
jsx->streamhosts = g_list_append(jsx->streamhosts, sh);
}
}
@@ -351,7 +407,7 @@ jabber_si_xfer_bytestreams_send_read_aga
jsx->js->user->resource, xfer->who);
purple_cipher_digest_region("sha1", (guchar *)dstaddr, strlen(dstaddr),
- sizeof(hashval), hashval, NULL);
+ sizeof(hashval), hashval, NULL);
g_free(dstaddr);
dstaddr = g_malloc(41);
p = dstaddr;
@@ -363,9 +419,12 @@ jabber_si_xfer_bytestreams_send_read_aga
purple_debug_error("jabber", "someone connected with the wrong info!\n");
close(source);
purple_xfer_cancel_remote(xfer);
+ g_free(dstaddr);
return;
}
+ g_free(dstaddr);
+
g_free(jsx->rxqueue);
host = purple_network_get_my_ip(jsx->js->fd);
@@ -523,7 +582,33 @@ jabber_si_xfer_bytestreams_send_read_cb(
source, PURPLE_INPUT_WRITE);
}
+static gint
+jabber_si_compare_jid(gconstpointer a, gconstpointer b)
+{
+ const JabberBytestreamsStreamhost *sh = a;
+
+ if(!a)
+ return -1;
+
+ return strcmp(sh->jid, (char *)b);
+}
+
+
static void
+jabber_si_free_streamhost(gpointer data, gpointer user_data)
+{
+ JabberBytestreamsStreamhost *sh = data;
+
+ if(!data)
+ return;
+
+ g_free(sh->jid);
+ g_free(sh->host);
+ g_free(sh->zeroconf);
+ g_free(sh);
+}
+
+static void
jabber_si_xfer_bytestreams_send_connected_cb(gpointer data, gint source,
PurpleInputCondition cond)
{
@@ -537,6 +622,7 @@ jabber_si_xfer_bytestreams_send_connecte
return;
else if(acceptfd == -1) {
purple_debug_warning("jabber", "accept: %s\n", g_strerror(errno));
+ /* TODO: This should cancel the ft */
return;
}
@@ -544,17 +630,74 @@ jabber_si_xfer_bytestreams_send_connecte
close(source);
xfer->watcher = purple_input_add(acceptfd, PURPLE_INPUT_READ,
- jabber_si_xfer_bytestreams_send_read_cb, xfer);
+ jabber_si_xfer_bytestreams_send_read_cb, xfer);
}
static void
+jabber_si_connect_proxy_cb(JabberStream *js, xmlnode *packet,
+ gpointer data)
+{
+ PurpleXfer *xfer = data;
+ JabberSIXfer *jsx = xfer->data;
+ xmlnode *query, *streamhost_used;
+ const char *from, *type, *jid;
+ GList *matched;
+
+ /* TODO: This need to send errors if we don't see what we're looking for */
+
+ /* In the case of a direct file transfer, this is expected to return */
+ if(!jsx)
+ return;
+
+ if(!(type = xmlnode_get_attrib(packet, "type")) || strcmp(type, "result"))
+ return;
+
+ if(!(from = xmlnode_get_attrib(packet, "from")))
+ return;
+
+ if(!(query = xmlnode_get_child(packet, "query")))
+ return;
+
+ if(!(streamhost_used = xmlnode_get_child(query, "streamhost-used")))
+ return;
+
+ if(!(jid = xmlnode_get_attrib(streamhost_used, "jid")))
+ return;
+
+ if(!(matched = g_list_find_custom(jsx->streamhosts, jid, jabber_si_compare_jid)))
+ {
+ gchar *my_jid = g_strdup_printf("%s@%s/%s", jsx->js->user->node,
+ jsx->js->user->domain, jsx->js->user->resource);
+ if (!strcmp(jid, my_jid))
+ purple_debug_info("jabber", "Got local SOCKS5 streamhost-used.\n");
+ else
+ purple_debug_info("jabber", "streamhost-used does not match any proxy that was offered to target\n");
+ g_free(my_jid);
+ return;
+ }
+
+ /* TODO: Clean up the local SOCKS5 proxy - it isn't going to be used.*/
+
+ jsx->streamhosts = g_list_remove_link(jsx->streamhosts, matched);
+ g_list_foreach(jsx->streamhosts, jabber_si_free_streamhost, NULL);
+ g_list_free(jsx->streamhosts);
+
+ jsx->streamhosts = matched;
+
+ jabber_si_bytestreams_attempt_connect(xfer);
+}
+
+static void
jabber_si_xfer_bytestreams_listen_cb(int sock, gpointer data)
{
PurpleXfer *xfer = data;
JabberSIXfer *jsx;
JabberIq *iq;
xmlnode *query, *streamhost;
- char *jid, *port;
+ char *jid, port[6];
+ const char *local_ip, *public_ip, *ft_proxies;
+ GList *tmp;
+ JabberBytestreamsStreamhost *sh, *sh2;
jsx = xfer->data;
jsx->listen_data = NULL;
@@ -578,27 +721,106 @@ jabber_si_xfer_bytestreams_listen_cb(int
xmlnode_set_attrib(query, "sid", jsx->stream_id);
- streamhost = xmlnode_new_child(query, "streamhost");
jid = g_strdup_printf("%s@%s/%s", jsx->js->user->node,
jsx->js->user->domain, jsx->js->user->resource);
- xmlnode_set_attrib(streamhost, "jid", jid);
+ xfer->local_port = purple_network_get_port_from_fd(sock);
+ g_snprintf(port, sizeof(port), "%hu", xfer->local_port);
+
+ /* TODO: Should there be an option to not use the local host as a ft proxy?
+ * (to prevent revealing IP address, etc.) */
+
+ /* Include the localhost's IP (for in-network transfers) */
+ local_ip = purple_network_get_local_system_ip(jsx->js->fd);
+ if (strcmp(local_ip, "0.0.0.0") != 0)
+ {
+ streamhost = xmlnode_new_child(query, "streamhost");
+ xmlnode_set_attrib(streamhost, "jid", jid);
+ xmlnode_set_attrib(streamhost, "host", local_ip);
+ xmlnode_set_attrib(streamhost, "port", port);
+ }
+
+ /* Include the public IP (assuming that there is a port mapped somehow) */
+ /* TODO: Check that it isn't the same as above and is a valid IP */
+ public_ip = purple_network_get_my_ip(jsx->js->fd);
+ if (strcmp(public_ip, local_ip) != 0)
+ {
+ streamhost = xmlnode_new_child(query, "streamhost");
+ xmlnode_set_attrib(streamhost, "jid", jid);
+ xmlnode_set_attrib(streamhost, "host", public_ip);
+ xmlnode_set_attrib(streamhost, "port", port);
+ }
+
g_free(jid);
- /* XXX: shouldn't we use the public IP or something? here */
- xmlnode_set_attrib(streamhost, "host",
- purple_network_get_my_ip(jsx->js->fd));
- xfer->local_port = purple_network_get_port_from_fd(sock);
- port = g_strdup_printf("%hu", xfer->local_port);
- xmlnode_set_attrib(streamhost, "port", port);
- g_free(port);
-
+ /* The listener for the local proxy */
xfer->watcher = purple_input_add(sock, PURPLE_INPUT_READ,
jabber_si_xfer_bytestreams_send_connected_cb, xfer);
- /* XXX: insert proxies here */
+ /* insert proxies here */
+ ft_proxies = purple_account_get_string(xfer->account, "ft_proxies", NULL);
+ if (ft_proxies) {
+ int i, portnum;
+ char *tmp;
+ gchar **ft_proxy_list = g_strsplit(ft_proxies, ",", 0);
- /* XXX: callback to find out which streamhost they used, or see if they
- * screwed it up */
+ g_list_foreach(jsx->streamhosts, jabber_si_free_streamhost, NULL);
+ g_list_free(jsx->streamhosts);
+ jsx->streamhosts = NULL;
+
+ for(i = 0; ft_proxy_list[i]; i++) {
+ g_strstrip(ft_proxy_list[i]);
+ if(!(*ft_proxy_list[i]))
+ continue;
+
+ if((tmp = strchr(ft_proxy_list[i], ':'))) {
+ portnum = atoi(tmp + 1);
+ *tmp = '\0';
+ } else
+ portnum = 7777;
+
+ g_snprintf(port, sizeof(port), "%hu", portnum);
+
+ streamhost = xmlnode_new_child(query, "streamhost");
+ xmlnode_set_attrib(streamhost, "jid", ft_proxy_list[i]);
+ xmlnode_set_attrib(streamhost, "host", ft_proxy_list[i]);
+ xmlnode_set_attrib(streamhost, "port", port);
+
+ sh = g_new0(JabberBytestreamsStreamhost, 1);
+ sh->jid = g_strdup(ft_proxy_list[i]);
+ sh->host = g_strdup(ft_proxy_list[i]);
+ sh->port = portnum;
+
+ jsx->streamhosts = g_list_prepend(jsx->streamhosts, sh);
+ }
+
+ g_strfreev(ft_proxy_list);
+ }
+
+ for (tmp = jsx->js->bs_proxies; tmp; tmp = tmp->next) {
+ sh = tmp->data;
+
+ /* TODO: deal with zeroconf proxies */
+
+ if (!(sh->host && sh->port > 0))
+ continue;
+
+ streamhost = xmlnode_new_child(query, "streamhost");
+ xmlnode_set_attrib(streamhost, "jid", sh->jid);
+ xmlnode_set_attrib(streamhost, "host", sh->host);
+ g_snprintf(port, sizeof(port), "%hu", sh->port);
+ xmlnode_set_attrib(streamhost, "port", port);
+
+ sh2 = g_new0(JabberBytestreamsStreamhost, 1);
+ sh2->jid = g_strdup(sh->jid);
+ sh2->host = g_strdup(sh->host);
+ sh2->zeroconf = g_strdup(sh->zeroconf);
+ sh2->port = sh->port;
+
+ jsx->streamhosts = g_list_prepend(jsx->streamhosts, sh2);
+ }
+
+ jabber_iq_set_callback(iq, jabber_si_connect_proxy_cb, xfer);
+
jabber_iq_send(iq);
}
@@ -688,8 +910,7 @@ static void jabber_si_xfer_send_request(
/* maybe later we'll do hash and date attribs */
feature = xmlnode_new_child(si, "feature");
- xmlnode_set_namespace(feature,
- "http://jabber.org/protocol/feature-neg");
+ xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg");
x = xmlnode_new_child(feature, "x");
xmlnode_set_namespace(x, "jabber:x:data");
xmlnode_set_attrib(x, "type", "form");
@@ -698,8 +919,7 @@ static void jabber_si_xfer_send_request(
xmlnode_set_attrib(field, "type", "list-single");
option = xmlnode_new_child(field, "option");
value = xmlnode_new_child(option, "value");
- xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams",
- -1);
+ xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1);
/*
option = xmlnode_new_child(field, "option");
value = xmlnode_new_child(option, "value");
@@ -729,6 +949,14 @@ static void jabber_si_xfer_free(PurpleXf
if (jsx->iq_id != NULL)
jabber_iq_remove_callback_by_id(js, jsx->iq_id);
+ if (jsx->connect_timeout > 0)
+ purple_timeout_remove(jsx->connect_timeout);
+
+ if (jsx->streamhosts) {
+ g_list_foreach(jsx->streamhosts, jabber_si_free_streamhost, NULL);
+ g_list_free(jsx->streamhosts);
+ }
+
g_free(jsx->stream_id);
g_free(jsx->iq_id);
/* XXX: free other stuff */
More information about the Commits
mailing list