cpw.malu.xmpp.ibb_ft: 1004ee62: Implements file transfers using in-band ...
malu at pidgin.im
malu at pidgin.im
Sat Sep 6 13:35:34 EDT 2008
-----------------------------------------------------------------
Revision: 1004ee62a91cd7c1f39c9d5aafdbe4de761879b0
Ancestor: 276af715e3b26cba28965e1fa14e8c6e5ac65a85
Author: malu at pidgin.im
Date: 2008-09-06T07:49:05
Branch: im.pidgin.cpw.malu.xmpp.ibb_ft
URL: http://d.pidgin.im/viewmtn/revision/info/1004ee62a91cd7c1f39c9d5aafdbe4de761879b0
Added files:
libpurple/protocols/jabber/ibb.c
libpurple/protocols/jabber/ibb.h
Modified files:
libpurple/protocols/jabber/Makefile.am
libpurple/protocols/jabber/Makefile.mingw
libpurple/protocols/jabber/disco.c
libpurple/protocols/jabber/iq.c
libpurple/protocols/jabber/libxmpp.c
libpurple/protocols/jabber/si.c
libpurple/protocols/jabber/si.h
ChangeLog:
Implements file transfers using in-band bytestreams for XMPP
using XEP-0047
Refs #6183
-------------- next part --------------
============================================================
--- libpurple/protocols/jabber/ibb.c c5bc8508d9003451d8a3f4ff87eccfd29e11323b
+++ libpurple/protocols/jabber/ibb.c c5bc8508d9003451d8a3f4ff87eccfd29e11323b
@@ -0,0 +1,514 @@
+/*
+ * 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
+ */
+
+#include "ibb.h"
+#include "debug.h"
+#include "xmlnode.h"
+
+#include <glib.h>
+#include <string.h>
+
+#define JABBER_IBB_SESSION_DEFAULT_BLOCK_SIZE 4096
+
+static GHashTable *jabber_ibb_sessions = NULL;
+static GList *open_handlers = NULL;
+
+JabberIBBSession *
+jabber_ibb_session_create(JabberStream *js, const gchar *sid, const gchar *who,
+ gpointer user_data)
+{
+ JabberIBBSession *sess = g_new0(JabberIBBSession, 1);
+
+ if (!sess) {
+ purple_debug_error("jabber", "Could not allocate IBB session object\n");
+ return NULL;
+ }
+
+ sess->js = js;
+ if (sid) {
+ sess->sid = g_strdup(sid);
+ } else {
+ sess->sid = g_strdup(jabber_get_next_id(js));
+ }
+ sess->who = g_strdup(who);
+ sess->block_size = JABBER_IBB_SESSION_DEFAULT_BLOCK_SIZE;
+ sess->state = JABBER_IBB_SESSION_NOT_OPENED;
+ sess->user_data = user_data;
+
+ g_hash_table_insert(jabber_ibb_sessions, sess->sid, sess);
+
+ return sess;
+}
+
+JabberIBBSession *
+jabber_ibb_session_create_from_xmlnode(JabberStream *js, xmlnode *packet,
+ gpointer user_data)
+{
+ JabberIBBSession *sess = NULL;
+ xmlnode *open = xmlnode_get_child_with_namespace(packet, "open",
+ XEP_0047_NAMESPACE);
+ const gchar *sid = xmlnode_get_attrib(open, "sid");
+ const gchar *block_size = xmlnode_get_attrib(open, "block-size");
+ gsize block_size_int;
+
+ if (!open) {
+ return NULL;
+ }
+
+ sess = g_new0(JabberIBBSession, 1);
+
+ if (!sess) {
+ purple_debug_error("jabber", "Could not allocate IBB session object\n");
+ return NULL;
+ }
+
+ if (!sid || !block_size) {
+ purple_debug_error("jabber",
+ "IBB session open tag requires sid and block-size attributes\n");
+ g_free(sess);
+ return NULL;
+ }
+
+ block_size_int = atoi(block_size);
+ sess->js = js;
+ sess->sid = g_strdup(sid);
+ sess->id = g_strdup(xmlnode_get_attrib(packet, "id"));
+ sess->who = g_strdup(xmlnode_get_attrib(packet, "from"));
+ sess->block_size = block_size_int;
+ /* if we create a session from an incoming <open/> request, it means the
+ session is immediatly open... */
+ sess->state = JABBER_IBB_SESSION_OPENED;
+ sess->user_data = user_data;
+
+ g_hash_table_insert(jabber_ibb_sessions, sess->sid, sess);
+
+ return sess;
+}
+
+void
+jabber_ibb_session_destroy(JabberIBBSession *sess)
+{
+ purple_debug_info("jabber", "IBB: destroying session %lx %s\n", sess,
+ sess->sid);
+
+ if (jabber_ibb_session_get_state(sess) == JABBER_IBB_SESSION_OPENED) {
+ jabber_ibb_session_close(sess);
+ }
+
+ purple_debug_info("jabber", "IBB: last_iq_id: %lx\n", sess->last_iq_id);
+ if (sess->last_iq_id) {
+ purple_debug_info("jabber", "IBB: removing callback for <iq/> %s\n",
+ sess->last_iq_id);
+ jabber_iq_remove_callback_by_id(jabber_ibb_session_get_js(sess),
+ sess->last_iq_id);
+ g_free(sess->last_iq_id);
+ sess->last_iq_id = NULL;
+ }
+
+ g_hash_table_remove(jabber_ibb_sessions, sess->sid);
+ g_free(sess->sid);
+ g_free(sess->who);
+ g_free(sess);
+}
+
+const gchar *
+jabber_ibb_session_get_sid(const JabberIBBSession *sess)
+{
+ return sess->sid;
+}
+
+JabberStream *
+jabber_ibb_session_get_js(JabberIBBSession *sess)
+{
+ return sess->js;
+}
+
+const gchar *
+jabber_ibb_session_get_who(const JabberIBBSession *sess)
+{
+ return sess->who;
+}
+
+guint16
+jabber_ibb_session_get_send_seq(const JabberIBBSession *sess)
+{
+ return sess->send_seq;
+}
+
+guint16
+jabber_ibb_session_get_recv_seq(const JabberIBBSession *sess)
+{
+ return sess->recv_seq;
+}
+
+JabberIBBSessionState
+jabber_ibb_session_get_state(const JabberIBBSession *sess)
+{
+ return sess->state;
+}
+
+gsize
+jabber_ibb_session_get_block_size(const JabberIBBSession *sess)
+{
+ return sess->block_size;
+}
+
+void
+jabber_ibb_session_set_block_size(JabberIBBSession *sess, gsize size)
+{
+ if (jabber_ibb_session_get_state(sess) == JABBER_IBB_SESSION_NOT_OPENED) {
+ sess->block_size = size;
+ } else {
+ purple_debug_error("jabber",
+ "Can't set block size on an open IBB session\n");
+ }
+}
+
+gpointer
+jabber_ibb_session_get_user_data(JabberIBBSession *sess)
+{
+ return sess->user_data;
+}
+
+void
+jabber_ibb_session_set_opened_callback(JabberIBBSession *sess,
+ JabberIBBOpenedCallback *cb)
+{
+ sess->opened_cb = cb;
+}
+
+void
+jabber_ibb_session_set_data_sent_callback(JabberIBBSession *sess,
+ JabberIBBSentCallback *cb)
+{
+ sess->data_sent_cb = cb;
+}
+
+void
+jabber_ibb_session_set_closed_callback(JabberIBBSession *sess,
+ JabberIBBClosedCallback *cb)
+{
+ sess->closed_cb = cb;
+}
+
+void
+jabber_ibb_session_set_data_received_callback(JabberIBBSession *sess,
+ JabberIBBDataCallback *cb)
+{
+ sess->data_received_cb = cb;
+}
+
+void
+jabber_ibb_session_set_error_callback(JabberIBBSession *sess,
+ JabberIBBErrorCallback *cb)
+{
+ sess->error_cb = cb;
+}
+
+static void
+jabber_ibb_session_opened_cb(JabberStream *js, xmlnode *packet, gpointer data)
+{
+ JabberIBBSession *sess = (JabberIBBSession *) data;
+
+ sess->state = JABBER_IBB_SESSION_OPENED;
+
+ if (sess->opened_cb) {
+ sess->opened_cb(sess);
+ }
+}
+
+void
+jabber_ibb_session_open(JabberIBBSession *sess)
+{
+ if (jabber_ibb_session_get_state(sess) != JABBER_IBB_SESSION_NOT_OPENED) {
+ purple_debug_error("jabber",
+ "jabber_ibb_session called on an already open stream\n");
+ } else {
+ JabberIq *set = jabber_iq_new(sess->js, JABBER_IQ_SET);
+ xmlnode *open = xmlnode_new("open");
+ gchar block_size[10];
+
+ xmlnode_set_attrib(set->node, "to", jabber_ibb_session_get_who(sess));
+ xmlnode_set_namespace(open, XEP_0047_NAMESPACE);
+ xmlnode_set_attrib(open, "sid", jabber_ibb_session_get_sid(sess));
+ g_snprintf(block_size, sizeof(block_size), "%ld",
+ jabber_ibb_session_get_block_size(sess));
+ xmlnode_set_attrib(open, "block-size", block_size);
+ xmlnode_insert_child(set->node, open);
+
+ jabber_iq_set_callback(set, jabber_ibb_session_opened_cb, sess);
+
+ jabber_iq_send(set);
+ }
+}
+
+void
+jabber_ibb_session_close(JabberIBBSession *sess)
+{
+ JabberIBBSessionState state = jabber_ibb_session_get_state(sess);
+
+ if (state != JABBER_IBB_SESSION_OPENED && state != JABBER_IBB_SESSION_ERROR) {
+ purple_debug_error("jabber",
+ "jabber_ibb_session_close called on a session that has not been"
+ "opened\n");
+ } else {
+ JabberIq *set = jabber_iq_new(jabber_ibb_session_get_js(sess),
+ JABBER_IQ_SET);
+ xmlnode *close = xmlnode_new("close");
+
+ xmlnode_set_attrib(set->node, "to", jabber_ibb_session_get_who(sess));
+ xmlnode_set_namespace(close, XEP_0047_NAMESPACE);
+ xmlnode_set_attrib(close, "sid", jabber_ibb_session_get_sid(sess));
+ xmlnode_insert_child(set->node, close);
+ jabber_iq_send(set);
+ sess->state = JABBER_IBB_SESSION_CLOSED;
+ }
+}
+
+void
+jabber_ibb_session_accept(JabberIBBSession *sess)
+{
+ JabberIq *result = jabber_iq_new(jabber_ibb_session_get_js(sess),
+ JABBER_IQ_RESULT);
+
+ xmlnode_set_attrib(result->node, "to", jabber_ibb_session_get_who(sess));
+ jabber_iq_set_id(result, sess->id);
+ jabber_iq_send(result);
+ sess->state = JABBER_IBB_SESSION_OPENED;
+}
+
+static void
+jabber_ibb_session_send_acknowledge_cb(JabberStream *js, xmlnode *packet, gpointer data)
+{
+ JabberIBBSession *sess = (JabberIBBSession *) data;
+ xmlnode *error = xmlnode_get_child(packet, "error");
+
+ if (sess) {
+ /* reset callback */
+ if (sess->last_iq_id) {
+ g_free(sess->last_iq_id);
+ sess->last_iq_id = NULL;
+ }
+
+ if (error) {
+ jabber_ibb_session_close(sess);
+ sess->state = JABBER_IBB_SESSION_ERROR;
+
+ if (sess->error_cb) {
+ sess->error_cb(sess);
+ }
+ } else {
+ if (sess->data_sent_cb) {
+ sess->data_sent_cb(sess);
+ }
+ }
+ } else {
+ /* the session has gone away, it was probably cancelled */
+ purple_debug_info("jabber",
+ "got response from send data, but IBB session is no longer active\n");
+ }
+}
+
+void
+jabber_ibb_session_send_data(JabberIBBSession *sess, gpointer data, gsize size)
+{
+ JabberIBBSessionState state = jabber_ibb_session_get_state(sess);
+
+ if (state != JABBER_IBB_SESSION_OPENED) {
+ purple_debug_error("jabber",
+ "trying to send data on a non-open IBB session\n");
+ } else if (size > jabber_ibb_session_get_block_size(sess)) {
+ purple_debug_error("jabber",
+ "trying to send a too large packet in the IBB session\n");
+ } else {
+ JabberIq *set = jabber_iq_new(jabber_ibb_session_get_js(sess),
+ JABBER_IQ_SET);
+ xmlnode *data_element = xmlnode_new("data");
+ char *base64 = purple_base64_encode(data, size);
+ char seq[10];
+ g_snprintf(seq, sizeof(seq), "%d", jabber_ibb_session_get_send_seq(sess));
+
+ xmlnode_set_attrib(set->node, "to", jabber_ibb_session_get_who(sess));
+ xmlnode_set_namespace(data_element, XEP_0047_NAMESPACE);
+ xmlnode_set_attrib(data_element, "sid", jabber_ibb_session_get_sid(sess));
+ xmlnode_set_attrib(data_element, "seq", seq);
+ xmlnode_insert_data(data_element, base64, strlen(base64));
+
+ xmlnode_insert_child(set->node, data_element);
+
+ purple_debug_info("jabber",
+ "IBB: setting send <iq/> callback for session %lx %s\n", sess,
+ sess->sid);
+ jabber_iq_set_callback(set, jabber_ibb_session_send_acknowledge_cb, sess);
+ sess->last_iq_id = g_strdup(xmlnode_get_attrib(set->node, "id"));
+ purple_debug_info("jabber", "IBB: set sess->last_iq_id: %lx %lx\n",
+ sess->last_iq_id, xmlnode_get_attrib(set->node, "id"));
+ jabber_iq_send(set);
+
+ g_free(base64);
+ (sess->send_seq)++;
+ }
+}
+
+void
+jabber_ibb_parse(JabberStream *js, xmlnode *packet)
+{
+ xmlnode *data = xmlnode_get_child_with_namespace(packet, "data",
+ XEP_0047_NAMESPACE);
+ xmlnode *close = xmlnode_get_child_with_namespace(packet, "close",
+ XEP_0047_NAMESPACE);
+ xmlnode *open = xmlnode_get_child_with_namespace(packet, "open",
+ XEP_0047_NAMESPACE);
+ const gchar *sid =
+ data ? xmlnode_get_attrib(data, "sid") :
+ close ? xmlnode_get_attrib(close, "sid") : NULL;
+ JabberIBBSession *sess =
+ sid ? g_hash_table_lookup(jabber_ibb_sessions, sid) : NULL;
+ const gchar *who = xmlnode_get_attrib(packet, "from");
+
+ if (sess) {
+
+ if (strcmp(who, jabber_ibb_session_get_who(sess)) != 0) {
+ /* the iq comes from a different JID than the remote JID of the
+ session, ignore it */
+ purple_debug_error("jabber",
+ "Got IBB iq from wrong JID, ignoring\n");
+ } else if (data) {
+ guint16 seq = atoi(xmlnode_get_attrib(data, "seq"));
+
+ /* reject the data, and set the session in error if we get an
+ out-of-order packet */
+ if (seq == jabber_ibb_session_get_recv_seq(sess)) {
+ /* sequence # is the expected... */
+ JabberIq *result = jabber_iq_new(js, JABBER_IQ_RESULT);
+
+ jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id"));
+ xmlnode_set_attrib(result->node, "to",
+ xmlnode_get_attrib(packet, "from"));
+
+ if (sess->data_received_cb) {
+ gchar *base64 = xmlnode_get_data(data);
+ gsize size;
+ gpointer rawdata = purple_base64_decode(base64, &size);
+
+ g_free(base64);
+
+ if (rawdata) {
+ if (size > jabber_ibb_session_get_block_size(sess)) {
+ purple_debug_error("jabber",
+ "IBB: received a too large packet\n");
+ } else {
+ sess->data_received_cb(sess, rawdata, size);
+ }
+ g_free(rawdata);
+ } else {
+ purple_debug_error("jabber",
+ "IBB: invalid BASE64 data received\n");
+ }
+ }
+
+ (sess->recv_seq)++;
+ jabber_iq_send(result);
+
+ } else {
+ purple_debug_error("jabber",
+ "Received an out-of-order IBB packet\n");
+ sess->state = JABBER_IBB_SESSION_ERROR;
+
+ if (sess->error_cb) {
+ sess->error_cb(sess);
+ }
+ }
+ } else if (close) {
+ sess->state = JABBER_IBB_SESSION_CLOSED;
+ purple_debug_info("jabber", "IBB: received close\n");
+
+ if (sess->closed_cb) {
+ purple_debug_info("jabber", "IBB: calling closed handler\n");
+ sess->closed_cb(sess);
+ }
+
+ } else {
+ /* this should never happen */
+ purple_debug_error("jabber", "Received bogus iq for IBB session\n");
+ }
+ } else if (open) {
+ JabberIq *result;
+ const GList *iterator;
+
+ /* run all open handlers registered until one returns true */
+ for (iterator = open_handlers ; iterator ;
+ iterator = g_list_next(iterator)) {
+ JabberIBBOpenHandler *handler = (JabberIBBOpenHandler *) iterator->data;
+
+ if (handler(js, packet)) {
+ result = jabber_iq_new(js, JABBER_IQ_RESULT);
+ xmlnode_set_attrib(result->node, "to",
+ xmlnode_get_attrib(packet, "from"));
+ jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id"));
+ jabber_iq_send(result);
+ return;
+ }
+ }
+ } else {
+ /* send error reply */
+ JabberIq *result = jabber_iq_new(js, JABBER_IQ_ERROR);
+ xmlnode *error = xmlnode_new("error");
+ xmlnode *item_not_found = xmlnode_new("item-not-found");
+
+ xmlnode_set_namespace(item_not_found,
+ "urn:ietf:params:xml:ns:xmpp-stanzas");
+ xmlnode_set_attrib(error, "code", "440");
+ xmlnode_set_attrib(error, "type", "cancel");
+ jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id"));
+ xmlnode_set_attrib(result->node, "to",
+ xmlnode_get_attrib(packet, "from"));
+ xmlnode_insert_child(error, item_not_found);
+ xmlnode_insert_child(result->node, error);
+
+ jabber_iq_send(result);
+ }
+}
+
+void
+jabber_ibb_register_open_handler(JabberIBBOpenHandler *cb)
+{
+ open_handlers = g_list_append(open_handlers, cb);
+}
+
+void
+jabber_ibb_unregister_open_handler(JabberIBBOpenHandler *cb)
+{
+ open_handlers = g_list_remove(open_handlers, cb);
+}
+
+void
+jabber_ibb_init(void)
+{
+ jabber_ibb_sessions = g_hash_table_new(g_str_hash, g_str_equal);
+}
+
+void
+jabber_ibb_uninit(void)
+{
+ g_hash_table_destroy(jabber_ibb_sessions);
+ g_list_free(open_handlers);
+ jabber_ibb_sessions = NULL;
+ open_handlers = NULL;
+}
+
+
+
============================================================
--- libpurple/protocols/jabber/ibb.h 79243d416a89295e16e8aee88f213e9412680d98
+++ libpurple/protocols/jabber/ibb.h 79243d416a89295e16e8aee88f213e9412680d98
@@ -0,0 +1,119 @@
+/*
+ * 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
+ */
+
+#include "jabber.h"
+#include "iq.h"
+
+#ifndef JABBER_IBB_SESSION
+#define JABBER_IBB_SESSION
+
+#define XEP_0047_NAMESPACE "http://jabber.org/protocol/ibb"
+
+typedef struct _JabberIBBSession JabberIBBSession;
+
+typedef void
+(JabberIBBDataCallback)(JabberIBBSession *, const gpointer data, gsize size);
+
+typedef void (JabberIBBOpenedCallback)(JabberIBBSession *);
+typedef void (JabberIBBClosedCallback)(JabberIBBSession *);
+typedef void (JabberIBBErrorCallback)(JabberIBBSession *);
+typedef void (JabberIBBSentCallback)(JabberIBBSession *);
+
+typedef gboolean (JabberIBBOpenHandler)(JabberStream *js, xmlnode *packet);
+
+typedef enum {
+ JABBER_IBB_SESSION_NOT_OPENED,
+ JABBER_IBB_SESSION_OPENED,
+ JABBER_IBB_SESSION_CLOSED,
+ JABBER_IBB_SESSION_ERROR
+} JabberIBBSessionState;
+
+struct _JabberIBBSession {
+ JabberStream *js;
+ gchar *who;
+ gchar *sid;
+ gchar *id;
+ guint16 send_seq;
+ guint16 recv_seq;
+ gsize block_size;
+
+ /* session state */
+ JabberIBBSessionState state;
+
+ /* user data (f.ex. a handle to a PurpleXfer) */
+ gpointer user_data;
+
+ /* callbacks */
+ JabberIBBOpenedCallback *opened_cb;
+ JabberIBBSentCallback *data_sent_cb;
+ JabberIBBClosedCallback *closed_cb;
+ /* callback for receiving data */
+ JabberIBBDataCallback *data_received_cb;
+ JabberIBBErrorCallback *error_cb;
+
+ /* store the last sent IQ (to permit cancel of callback) */
+ gchar *last_iq_id;
+};
+
+JabberIBBSession *jabber_ibb_session_create(JabberStream *js, const gchar *sid,
+ const gchar *who, gpointer user_data);
+JabberIBBSession *jabber_ibb_session_create_from_xmlnode(JabberStream *js,
+ xmlnode *packet, gpointer user_data);
+
+void jabber_ibb_session_destroy(JabberIBBSession *sess);
+
+void jabber_ibb_session_set_opened_callback(JabberIBBSession *sess,
+ JabberIBBOpenedCallback *cb);
+void jabber_ibb_session_set_data_sent_callback(JabberIBBSession *sess,
+ JabberIBBSentCallback *cb);
+void jabber_ibb_session_set_closed_callback(JabberIBBSession *sess,
+ JabberIBBClosedCallback *cb);
+void jabber_ibb_session_set_data_received_callback(JabberIBBSession *sess,
+ JabberIBBDataCallback *cb);
+void jabber_ibb_session_set_error_callback(JabberIBBSession *sess,
+ JabberIBBErrorCallback *cb);
+
+void jabber_ibb_session_open(JabberIBBSession *sess);
+void jabber_ibb_session_close(JabberIBBSession *sess);
+void jabber_ibb_session_accept(JabberIBBSession *sess);
+void jabber_ibb_session_send_data(JabberIBBSession *sess, gpointer data,
+ gsize size);
+
+const gchar *jabber_ibb_session_get_sid(const JabberIBBSession *sess);
+JabberStream *jabber_ibb_session_get_js(JabberIBBSession *sess);
+const gchar *jabber_ibb_session_get_who(const JabberIBBSession *sess);
+
+guint16 jabber_ibb_session_get_send_seq(const JabberIBBSession *sess);
+guint16 jabber_ibb_session_get_recv_seq(const JabberIBBSession *sess);
+
+JabberIBBSessionState jabber_ibb_session_get_state(const JabberIBBSession *sess);
+
+gsize jabber_ibb_session_get_block_size(const JabberIBBSession *sess);
+void jabber_ibb_session_set_block_size(JabberIBBSession *sess, gsize size);
+
+gpointer jabber_ibb_session_get_user_data(JabberIBBSession *sess);
+
+/* handle incoming packet */
+void jabber_ibb_parse(JabberStream *js, xmlnode *packet);
+
+/* add a handler for open session */
+void jabber_ibb_register_open_handler(JabberIBBOpenHandler *cb);
+void jabber_ibb_unregister_open_handler(JabberIBBOpenHandler *cb);
+
+void jabber_ibb_init(void);
+void jabber_ibb_uninit(void);
+
+#endif /* JABBER_IBB_SESSION */
============================================================
--- libpurple/protocols/jabber/Makefile.am c3548815c48a2264e4ed44e9023d8a124c3fc7f7
+++ libpurple/protocols/jabber/Makefile.am 874e08dc1e63543fa789f8cc336385588ee04b9c
@@ -15,6 +15,8 @@ JABBERSOURCES = auth.c \
disco.h \
google.c \
google.h \
+ ibb.c \
+ ibb.h \
iq.c \
iq.h \
jabber.c \
============================================================
--- libpurple/protocols/jabber/Makefile.mingw f3643645466cd2453f5c5ed6294505921b2c264d
+++ libpurple/protocols/jabber/Makefile.mingw db9da5163170896554308a94a0a3aa8583acfe22
@@ -50,6 +50,7 @@ C_SRC = \
chat.c \
disco.c \
google.c \
+ ibb.c \
iq.c \
jabber.c \
jutil.c \
============================================================
--- libpurple/protocols/jabber/disco.c 7f85a8a092ed6670ee147af38b8482042416e2ee
+++ libpurple/protocols/jabber/disco.c 9b83649d90d3fe61d4644a9936db1ece2d1d2bb7
@@ -132,9 +132,7 @@ void jabber_disco_info_parse(JabberStrea
SUPPORT_FEATURE("http://jabber.org/protocol/bytestreams")
SUPPORT_FEATURE("http://jabber.org/protocol/disco#info")
SUPPORT_FEATURE("http://jabber.org/protocol/disco#items")
-#if 0
- SUPPORT_FEATURE("http://jabber.org/protocol/ibb")
-#endif
+ SUPPORT_FEATURE("http://jabber.org/protocol/ibb");
SUPPORT_FEATURE("http://jabber.org/protocol/muc")
SUPPORT_FEATURE("http://jabber.org/protocol/muc#user")
SUPPORT_FEATURE("http://jabber.org/protocol/si")
@@ -273,6 +271,10 @@ void jabber_disco_info_parse(JabberStrea
else if(!strcmp(var, "http://jabber.org/protocol/commands")) {
capabilities |= JABBER_CAP_ADHOC;
}
+ else if(!strcmp(var, "http://jabber.org/protocol/ibb")) {
+ purple_debug_info("jabber", "remote supports IBB\n");
+ capabilities |= JABBER_CAP_IBB;
+ }
}
}
============================================================
--- libpurple/protocols/jabber/iq.c d9db8d4efa9a018e942073b30fae4846223d9cab
+++ libpurple/protocols/jabber/iq.c 99151de257a2f3cfd7c2d9477c99155690c1e846
@@ -33,6 +33,7 @@
#include "si.h"
#include "ping.h"
#include "adhoccommands.h"
+#include "ibb.h"
#ifdef _WIN32
#include "utsname.h"
@@ -355,6 +356,13 @@ void jabber_iq_parse(JabberStream *js, x
return;
}
+ if (xmlnode_get_child_with_namespace(packet, "data", XEP_0047_NAMESPACE)
+ || xmlnode_get_child_with_namespace(packet, "close", XEP_0047_NAMESPACE)
+ || xmlnode_get_child_with_namespace(packet, "open", XEP_0047_NAMESPACE)) {
+ jabber_ibb_parse(js, packet);
+ return;
+ }
+
/* If we get here, send the default error reply mandated by XMPP-CORE */
if(type && (!strcmp(type, "set") || !strcmp(type, "get"))) {
JabberIq *iq = jabber_iq_new(js, JABBER_IQ_ERROR);
============================================================
--- libpurple/protocols/jabber/libxmpp.c c2d1d7ec1fb55d2eb16df7154e83be9bc19a4d1b
+++ libpurple/protocols/jabber/libxmpp.c c52490b4a2bdf7baa2535eb1f2046ba0ce92d369
@@ -43,6 +43,7 @@
#include "pep.h"
#include "usertune.h"
#include "caps.h"
+#include "ibb.h"
static PurplePluginProtocolInfo prpl_info =
{
@@ -148,6 +149,9 @@ static gboolean unload_plugin(PurplePlug
purple_signal_unregister(plugin, "jabber-sending-xmlnode");
purple_signal_unregister(plugin, "jabber-sending-text");
+
+ jabber_si_uninit();
+ jabber_ibb_uninit();
return TRUE;
}
@@ -269,11 +273,15 @@ init_plugin(PurplePlugin *plugin)
jabber_tune_init();
jabber_caps_init();
+
+ jabber_ibb_init();
+ jabber_si_init();
jabber_add_feature("avatarmeta", AVATARNAMESPACEMETA, jabber_pep_namespace_only_when_pep_enabled_cb);
jabber_add_feature("avatardata", AVATARNAMESPACEDATA, jabber_pep_namespace_only_when_pep_enabled_cb);
jabber_add_feature("buzz", "http://www.xmpp.org/extensions/xep-0224.html#ns", jabber_buzz_isenabled);
-
+ jabber_add_feature("ibb", XEP_0047_NAMESPACE, NULL);
+
jabber_pep_register_handler("avatar", AVATARNAMESPACEMETA, jabber_buddy_avatar_update_metadata);
}
============================================================
--- libpurple/protocols/jabber/si.c 5ed8249db39deb22fcd1104fdebd1501c38a5fd4
+++ libpurple/protocols/jabber/si.c ec894e203fbbc29d2a62ce680770f095d891db83
@@ -35,6 +35,7 @@
#include "jabber.h"
#include "iq.h"
#include "si.h"
+#include "ibb.h"
#define STREAMHOST_CONNECT_TIMEOUT 15
@@ -64,8 +65,14 @@ typedef struct _JabberSIXfer {
size_t rxlen;
gsize rxmaxlen;
int local_streamhost_fd;
+
+ JabberIBBSession *ibb_session;
+ FILE *fp;
} JabberSIXfer;
+/* some forward declarations */
+static void jabber_si_xfer_ibb_send_init(JabberStream *js, PurpleXfer *xfer);
+
static PurpleXfer*
jabber_si_xfer_find(JabberStream *js, const char *sid, const char *from)
{
@@ -204,8 +211,25 @@ static void jabber_si_bytestreams_attemp
jabber_iq_send(iq);
- purple_xfer_cancel_local(xfer);
-
+ /* if IBB is available, revert to that before giving up... */
+ if (jsx->stream_method & STREAM_METHOD_IBB) {
+ /* if we are the initializer, init IBB */
+ purple_debug_info("jabber",
+ "jabber_si_bytestreams_attempt_connect: "
+ "no streamhosts found, trying IBB\n");
+ /* if we are the sender, open an IBB session. But not if we already
+ done it, since we could have received the error <iq/> from the
+ receiver already... */
+ if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND
+ && !jsx->ibb_session) {
+ jabber_si_xfer_ibb_send_init(jsx->js, xfer);
+ }
+ /* if we are the receiver, just wait for IBB open, callback is
+ already set up... */
+ } else {
+ purple_xfer_cancel_local(xfer);
+ }
+
return;
}
@@ -666,8 +690,29 @@ jabber_si_connect_proxy_cb(JabberStream
jsx = xfer->data;
if(!(type = xmlnode_get_attrib(packet, "type")) || strcmp(type, "result")) {
- if (type && !strcmp(type, "error"))
- purple_xfer_cancel_remote(xfer);
+ purple_debug_info("jabber",
+ "jabber_si_xfer_connect_proxy_cb: type = %s\n",
+ type);
+ if (type && !strcmp(type, "error")) {
+ /* if IBB is available, open IBB session */
+ purple_debug_info("jabber",
+ "jabber_si_xfer_connect_proxy_cb: got error, method: %d\n",
+ jsx->stream_method);
+ if (jsx->stream_method & STREAM_METHOD_IBB) {
+ purple_debug_info("jabber", "IBB is possible, try it\n");
+ /* if we are the sender and haven't already opened an IBB
+ session, do so now (we might already have failed to open
+ the bytestream proxy ourselves when receiving this <iq/> */
+ if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND
+ && !jsx->ibb_session) {
+ jabber_si_xfer_ibb_send_init(js, xfer);
+ }
+ /* if we are receiver, just wait for IBB open stanza, callback
+ is already set up */
+ } else {
+ purple_xfer_cancel_remote(xfer);
+ }
+ }
return;
}
@@ -694,8 +739,19 @@ jabber_si_connect_proxy_cb(JabberStream
purple_debug_info("jabber", "Got local SOCKS5 streamhost-used.\n");
purple_xfer_start(xfer, xfer->fd, NULL, -1);
} else {
- purple_debug_info("jabber", "streamhost-used does not match any proxy that was offered to target\n");
- purple_xfer_cancel_local(xfer);
+ /* if available, try to revert to IBB... */
+ if (jsx->stream_method & STREAM_METHOD_IBB) {
+ purple_debug_info("jabber",
+ "jabber_si_connect_proxy_cb: trying to revert to IBB\n");
+ if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) {
+ jabber_si_xfer_ibb_send_init(jsx->js, xfer);
+ }
+ /* if we are the receiver, we are already set up...*/
+ } else {
+ purple_debug_info("jabber",
+ "streamhost-used does not match any proxy that was offered to target\n");
+ purple_xfer_cancel_local(xfer);
+ }
}
g_free(my_jid);
return;
@@ -822,8 +878,23 @@ jabber_si_xfer_bytestreams_listen_cb(int
/* We have no way of transferring, cancel the transfer */
if (streamhost_count == 0) {
jabber_iq_free(iq);
- /* We should probably notify the target, but this really shouldn't ever happen */
- purple_xfer_cancel_local(xfer);
+
+ /* if available, revert to IBB */
+ if (jsx->stream_method & STREAM_METHOD_IBB) {
+ purple_debug_info("jabber",
+ "jabber_si_xfer_bytestreams_listen_cb: trying to revert to IBB\n");
+ if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) {
+ /* if we are the sender, init the IBB session... */
+ jabber_si_xfer_ibb_send_init(jsx->js, xfer);
+ }
+ /* if we are the receiver, we should just wait... the IBB open
+ handler has already been set up... */
+ } else {
+ /* We should probably notify the target,
+ but this really shouldn't ever happen */
+ purple_xfer_cancel_local(xfer);
+ }
+
return;
}
@@ -853,11 +924,200 @@ jabber_si_xfer_bytestreams_send_init(Pur
}
+/* forward declare some functions here... */
+static void jabber_si_xfer_cancel_recv(PurpleXfer *xfer);
+static void jabber_si_xfer_cancel_send(PurpleXfer *xfer);
+static void jabber_si_xfer_free(PurpleXfer *xfer);
+
+static void
+jabber_si_xfer_ibb_error_cb(JabberIBBSession *sess)
+{
+ PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
+ JabberStream *js = jabber_ibb_session_get_js(sess);
+ PurpleConnection *gc = js->gc;
+ PurpleAccount *account = purple_connection_get_account(gc);
+
+ purple_debug_error("jabber", "an error occured during IBB file transfer\n");
+ purple_xfer_error(purple_xfer_get_type(xfer), account,
+ jabber_ibb_session_get_who(sess),
+ _("An error occured on the in-band bytestream transfer\n"));
+ purple_xfer_end(xfer);
+}
+
+static void
+jabber_si_xfer_ibb_closed_cb(JabberIBBSession *sess)
+{
+ PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
+ JabberStream *js = jabber_ibb_session_get_js(sess);
+ PurpleConnection *gc = js->gc;
+ PurpleAccount *account = purple_connection_get_account(gc);
+
+ purple_debug_info("jabber", "the remote user closed the transfer\n");
+ if (purple_xfer_get_bytes_remaining(xfer) > 0) {
+ purple_xfer_error(purple_xfer_get_type(xfer), account,
+ jabber_ibb_session_get_who(sess), _("Transfer was closed."));
+ purple_xfer_end(xfer);
+ } else {
+ purple_xfer_set_completed(xfer, TRUE);
+ }
+}
+
+static void
+jabber_si_xfer_ibb_recv_data_cb(JabberIBBSession *sess, gpointer data,
+ gsize size)
+{
+ PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
+ JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
+
+ if (size <= purple_xfer_get_bytes_remaining(xfer)) {
+ fwrite(data, size, 1, jsx->fp);
+ purple_xfer_set_bytes_sent(xfer, purple_xfer_get_bytes_sent(xfer) + size);
+ purple_xfer_update_progress(xfer);
+
+ if (purple_xfer_get_bytes_remaining(xfer) == 0) {
+ purple_xfer_set_completed(xfer, TRUE);
+ }
+ } else {
+ /* trying to write past size of file transfers negotiated size,
+ reject transfer to protect against malicious behaviour */
+ purple_debug_error("jabber",
+ "IBB file transfer, trying to write past end of file\n");
+ jabber_si_xfer_cancel_recv(xfer);
+ }
+
+}
+
+static gboolean
+jabber_si_xfer_ibb_open_cb(JabberStream *js, xmlnode *packet)
+{
+ const gchar *who = xmlnode_get_attrib(packet, "from");
+ xmlnode *open = xmlnode_get_child(packet, "open");
+ const gchar *sid = xmlnode_get_attrib(open, "sid");
+ PurpleXfer *xfer = jabber_si_xfer_find(js, sid, who);
+ JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
+ JabberIBBSession *sess =
+ jabber_ibb_session_create_from_xmlnode(js, packet, xfer);
+
+ if (sess) {
+ /* setup callbacks here...*/
+ jabber_ibb_session_set_data_received_callback(sess,
+ jabber_si_xfer_ibb_recv_data_cb);
+ jabber_ibb_session_set_closed_callback(sess,
+ jabber_si_xfer_ibb_closed_cb);
+ jabber_ibb_session_set_error_callback(sess,
+ jabber_si_xfer_ibb_error_cb);
+
+ /* open the file to write to */
+ jsx->fp = g_fopen(purple_xfer_get_local_filename(xfer), "w");
+
+ jsx->ibb_session = sess;
+
+ /* start the transfer */
+ purple_xfer_start(xfer, 0, NULL, 0);
+ return TRUE;
+ } else {
+ /* failed to create IBB session */
+ purple_debug_error("jabber", "failed to create IBB session\n");
+ jabber_si_xfer_cancel_recv(xfer);
+ return FALSE;
+ }
+}
+
+static void
+jabber_si_xfer_ibb_send_data(JabberIBBSession *sess)
+{
+ PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
+ JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
+ gsize remaining = purple_xfer_get_bytes_remaining(xfer);
+ gsize packet_size = remaining < jabber_ibb_session_get_block_size(sess) ?
+ remaining : jabber_ibb_session_get_block_size(sess);
+ gpointer data = g_malloc(packet_size);
+ int res;
+
+ purple_debug_info("jabber", "IBB: about to read %d bytes from file %lx\n",
+ packet_size, jsx->fp);
+ res = fread(data, packet_size, 1, jsx->fp);
+
+ if (res == 1) {
+ jabber_ibb_session_send_data(sess, data, packet_size);
+ purple_xfer_set_bytes_sent(xfer,
+ purple_xfer_get_bytes_sent(xfer) + packet_size);
+ purple_xfer_update_progress(xfer);
+ } else {
+ purple_debug_error("jabber",
+ "jabber_si_xfer_ibb_opened_cb: error reading from file\n");
+ jabber_si_xfer_cancel_send(xfer);
+ }
+}
+
+static void
+jabber_si_xfer_ibb_sent_cb(JabberIBBSession *sess)
+{
+ PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
+ gsize remaining = purple_xfer_get_bytes_remaining(xfer);
+
+ if (remaining == 0) {
+ /* close the session */
+ jabber_ibb_session_close(sess);
+ purple_xfer_set_completed(xfer, TRUE);
+ } else {
+ /* send more... */
+ jabber_si_xfer_ibb_send_data(sess);
+ }
+}
+
+static void
+jabber_si_xfer_ibb_opened_cb(JabberIBBSession *sess)
+{
+ PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
+ JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
+
+ purple_xfer_start(xfer, 0, NULL, 0);
+ purple_xfer_set_bytes_sent(xfer, 0);
+ purple_xfer_update_progress(xfer);
+ jsx->fp = g_fopen(purple_xfer_get_local_filename(xfer), "r");
+ jabber_si_xfer_ibb_send_data(sess);
+}
+
+static void
+jabber_si_xfer_ibb_send_init(JabberStream *js, PurpleXfer *xfer)
+{
+ JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
+
+ purple_xfer_ref(xfer);
+
+ jsx->ibb_session = jabber_ibb_session_create(js, jsx->stream_id,
+ purple_xfer_get_remote_user(xfer), xfer);
+
+ if (jsx->ibb_session) {
+ /* should set callbacks here... */
+ jabber_ibb_session_set_opened_callback(jsx->ibb_session,
+ jabber_si_xfer_ibb_opened_cb);
+ jabber_ibb_session_set_data_sent_callback(jsx->ibb_session,
+ jabber_si_xfer_ibb_sent_cb);
+ jabber_ibb_session_set_closed_callback(jsx->ibb_session,
+ jabber_si_xfer_ibb_closed_cb);
+ jabber_ibb_session_set_error_callback(jsx->ibb_session,
+ jabber_si_xfer_ibb_error_cb);
+
+ /* open the IBB session */
+ jabber_ibb_session_open(jsx->ibb_session);
+
+ } else {
+ /* failed to create IBB session */
+ purple_xfer_unref(xfer);
+ purple_debug_error("jabber",
+ "failed to initiate IBB session for file transfer\n");
+ jabber_si_xfer_cancel_send(xfer);
+ }
+}
+
static void jabber_si_xfer_send_method_cb(JabberStream *js, xmlnode *packet,
gpointer data)
{
PurpleXfer *xfer = data;
xmlnode *si, *feature, *x, *field, *value;
+ gboolean found_method = FALSE;
if(!(si = xmlnode_get_child_with_namespace(packet, "si", "http://jabber.org/protocol/si"))) {
purple_xfer_cancel_remote(xfer);
@@ -876,20 +1136,33 @@ static void jabber_si_xfer_send_method_c
for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) {
const char *var = xmlnode_get_attrib(field, "var");
-
+ JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
+
if(var && !strcmp(var, "stream-method")) {
if((value = xmlnode_get_child(field, "value"))) {
char *val = xmlnode_get_data(value);
if(val && !strcmp(val, "http://jabber.org/protocol/bytestreams")) {
jabber_si_xfer_bytestreams_send_init(xfer);
- g_free(val);
- return;
+ jsx->stream_method |= STREAM_METHOD_BYTESTREAMS;
+ found_method = TRUE;
+ } else if (val && !strcmp(val, XEP_0047_NAMESPACE)) {
+ jsx->stream_method |= STREAM_METHOD_IBB;
+ if (!found_method) {
+ /* we haven't tried to init a bytestream session, yet
+ start IBB right away... */
+ jabber_si_xfer_ibb_send_init(js, xfer);
+ found_method = TRUE;
+ }
}
g_free(val);
}
}
}
- purple_xfer_cancel_remote(xfer);
+
+ if (!found_method) {
+ purple_xfer_cancel_remote(xfer);
+ }
+
}
static void jabber_si_xfer_send_request(PurpleXfer *xfer)
@@ -929,11 +1202,9 @@ static void jabber_si_xfer_send_request(
option = xmlnode_new_child(field, "option");
value = xmlnode_new_child(option, "value");
xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1);
- /*
option = xmlnode_new_child(field, "option");
value = xmlnode_new_child(option, "value");
xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1);
- */
jabber_iq_set_callback(iq, jabber_si_xfer_send_method_cb, xfer);
@@ -967,6 +1238,16 @@ static void jabber_si_xfer_free(PurpleXf
g_list_free(jsx->streamhosts);
}
+ if (jsx->ibb_session) {
+ purple_debug_info("jabber",
+ "jabber_si_xfer_free: destroying IBB session\n");
+ jabber_ibb_session_destroy(jsx->ibb_session);
+ }
+
+ if (jsx->fp) {
+ fclose(jsx->fp);
+ }
+
g_free(jsx->stream_id);
g_free(jsx->iq_id);
/* XXX: free other stuff */
@@ -979,6 +1260,12 @@ static void jabber_si_xfer_cancel_send(P
static void jabber_si_xfer_cancel_send(PurpleXfer *xfer)
{
+ JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
+
+ /* if there is an IBB session active, send close on that */
+ if (jsx->ibb_session) {
+ jabber_ibb_session_close(jsx->ibb_session);
+ }
jabber_si_xfer_free(xfer);
purple_debug(PURPLE_DEBUG_INFO, "jabber", "in jabber_si_xfer_cancel_send\n");
}
@@ -993,6 +1280,11 @@ static void jabber_si_xfer_cancel_recv(P
static void jabber_si_xfer_cancel_recv(PurpleXfer *xfer)
{
+ JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
+ /* if there is an IBB session active, send close */
+ if (jsx->ibb_session) {
+ jabber_ibb_session_close(jsx->ibb_session);
+ }
jabber_si_xfer_free(xfer);
purple_debug(PURPLE_DEBUG_INFO, "jabber", "in jabber_si_xfer_cancel_recv\n");
}
@@ -1007,9 +1299,16 @@ static void jabber_si_xfer_send_disco_cb
static void jabber_si_xfer_send_disco_cb(JabberStream *js, const char *who,
JabberCapabilities capabilities, gpointer data)
{
- PurpleXfer *xfer = data;
+ PurpleXfer *xfer = (PurpleXfer *) data;
+ JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
+
+ if (capabilities & JABBER_CAP_IBB) {
+ purple_debug_info("jabber",
+ "jabber_si_xfer_send_disco_cb: remote JID supports IBB\n");
+ jsx->stream_method |= STREAM_METHOD_IBB;
+ }
- if(capabilities & JABBER_CAP_SI_FILE_XFER) {
+ if (capabilities & JABBER_CAP_SI_FILE_XFER) {
jabber_si_xfer_send_request(xfer);
} else {
char *msg = g_strdup_printf(_("Unable to send file to %s, user does not support file transfers"), who);
@@ -1136,18 +1435,20 @@ static void jabber_si_xfer_init(PurpleXf
x = xmlnode_new_child(feature, "x");
xmlnode_set_namespace(x, "jabber:x:data");
xmlnode_set_attrib(x, "type", "submit");
-
field = xmlnode_new_child(x, "field");
xmlnode_set_attrib(field, "var", "stream-method");
-
- value = xmlnode_new_child(field, "value");
- if(jsx->stream_method & STREAM_METHOD_BYTESTREAMS)
+
+ /* we should maybe "remember" if bytestreams has failed before (in the
+ same session) with this JID, and only present IBB as an option to
+ avoid unnessesary timeout */
+ if (jsx->stream_method & STREAM_METHOD_BYTESTREAMS) {
+ value = xmlnode_new_child(field, "value");
xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1);
- /*
- else if(jsx->stream_method & STREAM_METHOD_IBB)
- xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1);
- */
-
+ } else if(jsx->stream_method & STREAM_METHOD_IBB) {
+ value = xmlnode_new_child(field, "value");
+ xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1);
+ }
+
jabber_iq_send(iq);
}
}
@@ -1167,6 +1468,9 @@ PurpleXfer *jabber_si_new_xfer(PurpleCon
xfer->data = jsx = g_new0(JabberSIXfer, 1);
jsx->js = js;
jsx->local_streamhost_fd = -1;
+
+ jsx->ibb_session = NULL;
+ jsx->fp = NULL;
purple_xfer_set_init_fnc(xfer, jabber_si_xfer_init);
purple_xfer_set_cancel_send_fnc(xfer, jabber_si_xfer_cancel_send);
@@ -1238,6 +1542,8 @@ void jabber_si_parse(JabberStream *js, x
jsx = g_new0(JabberSIXfer, 1);
jsx->local_streamhost_fd = -1;
+
+ jsx->ibb_session = NULL;
for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) {
const char *var = xmlnode_get_attrib(field, "var");
@@ -1249,10 +1555,8 @@ void jabber_si_parse(JabberStream *js, x
if((val = xmlnode_get_data(value))) {
if(!strcmp(val, "http://jabber.org/protocol/bytestreams")) {
jsx->stream_method |= STREAM_METHOD_BYTESTREAMS;
- /*
} else if(!strcmp(val, "http://jabber.org/protocol/ibb")) {
jsx->stream_method |= STREAM_METHOD_IBB;
- */
}
g_free(val);
}
@@ -1290,4 +1594,15 @@ void jabber_si_parse(JabberStream *js, x
}
}
+void
+jabber_si_init(void)
+{
+ jabber_ibb_register_open_handler(jabber_si_xfer_ibb_open_cb);
+}
+void
+jabber_si_uninit(void)
+{
+ jabber_ibb_unregister_open_handler(jabber_si_xfer_ibb_open_cb);
+}
+
============================================================
--- libpurple/protocols/jabber/si.h 5efdeacc163a9ff870a2234edc0678540596dc74
+++ libpurple/protocols/jabber/si.h 28bf67d72ad25206a4a2169ff89d4dd299282d96
@@ -30,5 +30,7 @@ void jabber_si_xfer_send(PurpleConnectio
void jabber_si_parse(JabberStream *js, xmlnode *packet);
PurpleXfer *jabber_si_new_xfer(PurpleConnection *gc, const char *who);
void jabber_si_xfer_send(PurpleConnection *gc, const char *who, const char *file);
+void jabber_si_init(void);
+void jabber_si_uninit(void);
#endif /* _PURPLE_JABBER_SI_H_ */
More information about the Commits
mailing list