cpw.malu.xmpp.jingle_ft: 3daf09d6: New files for Jingle file transfer

malu at pidgin.im malu at pidgin.im
Mon Mar 16 17:00:34 EDT 2009


-----------------------------------------------------------------
Revision: 3daf09d652778145695915ec1e441c60d3fbfd7d
Ancestor: e29115f1b59b7b26e05784a49625bfae6ebbc265
Author: malu at pidgin.im
Date: 2009-03-16T20:58:22
Branch: im.pidgin.cpw.malu.xmpp.jingle_ft
URL: http://d.pidgin.im/viewmtn/revision/info/3daf09d652778145695915ec1e441c60d3fbfd7d

Added files:
        libpurple/protocols/jabber/jingle/file-transfer.c
        libpurple/protocols/jabber/jingle/file-transfer.h
        libpurple/protocols/jabber/jingle/ibbs.c
        libpurple/protocols/jabber/jingle/ibbs.h

ChangeLog: 

New files for Jingle file transfer

-------------- next part --------------
============================================================
--- libpurple/protocols/jabber/jingle/file-transfer.c	7ef67938e0c92232593ab91261651c00a48159b1
+++ libpurple/protocols/jabber/jingle/file-transfer.c	7ef67938e0c92232593ab91261651c00a48159b1
@@ -0,0 +1,645 @@
+/*
+ * 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 "internal.h"
+
+#include "jingle.h"
+#include "file-transfer.h"
+#include "ibbs.h"
+#include "debug.h"
+#include "xmlnode.h"
+#include "xfer.h"
+
+#include <string.h>
+
+struct _JingleFTPrivate {
+	PurpleXfer *xfer;
+	FILE *ibb_fp; /* used to read/write from/to IBB streams */
+};
+
+#define JINGLE_FT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), \
+									JINGLE_TYPE_FT, JingleFTPrivate))
+
+static void jingle_file_transfer_class_init (JingleFTClass *klass);
+static void jingle_file_transfer_init (JingleFT *rtp);
+static void jingle_file_transfer_finalize (GObject *object);
+static void jingle_file_transfer_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
+static void jingle_file_transfer_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
+
+static JingleContent *jingle_file_transfer_parse_internal(xmlnode *ft);
+static xmlnode *jingle_file_transfer_to_xml_internal(JingleContent *rtp, 
+	xmlnode *content, JingleActionType action);
+static void jingle_file_transfer_handle_action_internal(JingleContent *content, xmlnode *jingle, JingleActionType action);
+
+static JingleContentClass *parent_class = NULL;
+
+enum {
+	PROP_0,
+	PROP_XFER,
+};
+
+GType
+jingle_file_transfer_get_type()
+{
+	static GType type = 0;
+
+	if (type == 0) {
+		static const GTypeInfo info = {
+			sizeof(JingleFTClass),
+			NULL,
+			NULL,
+			(GClassInitFunc) jingle_file_transfer_class_init,
+			NULL,
+			NULL,
+			sizeof(JingleFT),
+			0,
+			(GInstanceInitFunc) jingle_file_transfer_init,
+			NULL
+		};
+		type = g_type_register_static(JINGLE_TYPE_CONTENT, "JingleFT", &info, 0);
+	}
+	return type;
+}
+
+static void
+jingle_file_transfer_class_init (JingleFTClass *klass)
+{
+	GObjectClass *gobject_class = (GObjectClass*)klass;
+	parent_class = g_type_class_peek_parent(klass);
+
+	gobject_class->finalize = jingle_file_transfer_finalize;
+	gobject_class->set_property = jingle_file_transfer_set_property;
+	gobject_class->get_property = jingle_file_transfer_get_property;
+	klass->parent_class.to_xml = jingle_file_transfer_to_xml_internal;
+	klass->parent_class.parse = jingle_file_transfer_parse_internal;
+	klass->parent_class.description_type = JINGLE_APP_FT;
+	klass->parent_class.handle_action = 
+		jingle_file_transfer_handle_action_internal;
+
+	/* Add properties here... */
+	g_object_class_install_property(gobject_class, PROP_XFER,
+			g_param_spec_pointer("xfer",
+			"PurpleXfer",
+			"The PurpleXfer corresponding to this content.",
+			G_PARAM_READABLE));
+	
+	g_type_class_add_private(klass, sizeof(JingleFTPrivate));
+}
+
+static void
+jingle_file_transfer_init (JingleFT *ft)
+{
+	ft->priv = JINGLE_FT_GET_PRIVATE(ft);
+	memset(ft->priv, 0, sizeof(ft->priv));
+}
+
+static void
+jingle_file_transfer_finalize (GObject *ft)
+{
+	JingleFTPrivate *priv = JINGLE_FT_GET_PRIVATE(ft);
+	purple_debug_info("jingle-ft","jingle_file_transfer_finalize\n");
+
+	if (priv->ibb_fp) {
+		fclose(priv->ibb_fp);
+	}
+}
+
+static void
+jingle_file_transfer_set_property (GObject *object, guint prop_id, 
+	const GValue *value, GParamSpec *pspec)
+{
+	JingleFT *ft;
+	g_return_if_fail(JINGLE_IS_FT(object));
+
+	ft = JINGLE_FT(object);
+
+	switch (prop_id) {
+		/* properties come here... */
+		case PROP_XFER:
+			ft->priv->xfer = g_value_get_pointer(value);
+			break;
+		default:	
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+			break;
+	}
+}
+
+static void
+jingle_file_transfer_get_property (GObject *object, guint prop_id, 
+	GValue *value, GParamSpec *pspec)
+{
+	JingleFT *ft;
+	g_return_if_fail(JINGLE_IS_FT(object));
+	
+	ft = JINGLE_FT(object);
+
+	switch (prop_id) {
+		/* properties comes here... */
+		case PROP_XFER:
+			g_value_set_pointer(value, ft->priv->xfer);
+			break;
+		default:	
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);	
+			break;
+	}
+}
+
+PurpleXfer *
+jingle_file_transfer_get_xfer(JingleContent *content)
+{
+	PurpleXfer *xfer;
+	g_object_get(content, "xfer", &xfer, NULL);
+	return xfer;
+}
+
+static void
+jingle_file_transfer_cancel_remote(JingleContent *content)
+{
+	PurpleXfer *xfer = JINGLE_FT_GET_PRIVATE(JINGLE_FT(content))->xfer;
+	FILE *ibb_fp = JINGLE_FT_GET_PRIVATE(JINGLE_FT(content))->ibb_fp;
+	
+	purple_debug_info("jingle-ft", "cancel remote transfer\n");
+	if (xfer) {
+		jabber_iq_send(jingle_session_to_packet(
+			jingle_content_get_session(content), JINGLE_SESSION_TERMINATE));
+		purple_xfer_cancel_remote(xfer);
+	}
+	if (ibb_fp) {
+		fclose(ibb_fp);
+	}
+}
+
+static void
+jingle_file_transfer_cancel_local(JingleContent *content)
+{
+	PurpleXfer *xfer = JINGLE_FT_GET_PRIVATE(JINGLE_FT(content))->xfer;
+	FILE *ibb_fp = JINGLE_FT_GET_PRIVATE(JINGLE_FT(content))->ibb_fp;
+	
+	purple_debug_info("jingle-ft", "cancel local trasfer\n");
+	if (xfer) {
+		jabber_iq_send(jingle_session_to_packet(
+			jingle_content_get_session(content), JINGLE_SESSION_TERMINATE));
+		purple_xfer_cancel_local(xfer);
+	}
+	if (ibb_fp) {
+		fclose(ibb_fp);
+	}
+}
+
+static void
+jingle_file_transfer_success(JingleContent *content)
+{
+	PurpleXfer *xfer = JINGLE_FT_GET_PRIVATE(JINGLE_FT(content))->xfer;
+	FILE *ibb_fp = JINGLE_FT_GET_PRIVATE(JINGLE_FT(content))->ibb_fp;
+	
+	purple_debug_info("jingle-ft", "transfer received successful!\n");
+	if (xfer) {
+		purple_xfer_set_completed(xfer, TRUE);
+		purple_xfer_end(xfer);
+	}
+	if (ibb_fp) {
+		fclose(ibb_fp);
+	}
+}
+
+static void
+jingle_file_transfer_end(JingleContent *content)
+{
+	PurpleXfer *xfer = JINGLE_FT_GET_PRIVATE(JINGLE_FT(content))->xfer;
+	FILE *ibb_fp = JINGLE_FT_GET_PRIVATE(JINGLE_FT(content))->ibb_fp;
+	
+	purple_debug_info("jingle-ft", "ending transfer\n");
+	if (xfer) {
+		jabber_iq_send(jingle_session_to_packet(jingle_content_get_session(content), 
+			JINGLE_SESSION_TERMINATE));
+		purple_xfer_set_completed(xfer, TRUE);
+		purple_xfer_end(xfer);
+	}
+	if (ibb_fp) {
+		fclose(ibb_fp);
+	}
+}
+
+static void
+jingle_file_transfer_ibb_send_data(JingleContent *content)
+{
+	PurpleXfer *xfer = JINGLE_FT_GET_PRIVATE(JINGLE_FT(content))->xfer;
+	FILE *fp = JINGLE_FT_GET_PRIVATE(JINGLE_FT(content))->ibb_fp;
+	gsize remaining = purple_xfer_get_bytes_remaining(xfer);
+	gsize block_size = 
+		jingle_ibb_get_block_size(JINGLE_IBB(
+			jingle_content_get_transport(content)));
+	gsize packet_size = remaining < block_size ? remaining : block_size;
+	gpointer data = g_malloc(packet_size);
+	int res;
+
+	purple_debug_info("jingle-ft", 
+		"IBB: about to read %" G_GSIZE_FORMAT " bytes from file %p\n",
+		packet_size, fp);
+	res = fread(data, packet_size, 1, fp);
+
+	if (res == 1) {
+		jingle_ibb_send_data(JINGLE_IBB(jingle_content_get_transport(content)),
+			data, packet_size);
+		purple_xfer_set_bytes_sent(xfer,
+			purple_xfer_get_bytes_sent(xfer) + packet_size);
+		purple_xfer_update_progress(xfer);
+	} else {
+		jingle_file_transfer_cancel_local(content);
+	}
+	g_free(data);
+}
+
+/* callback functions for IBB */
+static void
+jingle_file_transfer_ibb_data_sent_callback(JingleContent *content)
+{
+	PurpleXfer *xfer = JINGLE_FT_GET_PRIVATE(JINGLE_FT(content))->xfer;
+	gsize remaining = purple_xfer_get_bytes_remaining(xfer);
+
+	if (remaining == 0) {
+		/* close the session */
+		jingle_file_transfer_end(content);
+	} else {
+		/* send more... */
+		jingle_file_transfer_ibb_send_data(content);
+	}
+}
+
+static void
+jingle_file_transfer_ibb_data_recv_callback(JingleContent *content, 
+	gconstpointer data, gsize size)
+{
+	JingleFT *ft = JINGLE_FT(content);
+	PurpleXfer *xfer = JINGLE_FT_GET_PRIVATE(ft)->xfer;
+	FILE *fp = JINGLE_FT_GET_PRIVATE(ft)->ibb_fp;
+
+	if (size <= purple_xfer_get_bytes_remaining(xfer)) {
+		purple_debug_info("jingle-ft", 
+			"about to write %" G_GSIZE_FORMAT " bytes from IBB stream\n",
+			size);
+		if(!fwrite(data, size, 1, fp)) {
+			purple_debug_error("jingle-ft", "error writing to file\n");
+			purple_xfer_cancel_remote(xfer);
+			return;
+		}
+		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) {
+			jingle_file_transfer_success(content);
+		}
+	} else {
+		/* sending more than intended */
+		purple_debug_error("jingle-ft",
+			"IBB file transfer send more data than expected\n");
+		jingle_file_transfer_cancel_remote(content);
+	}
+}
+
+static void
+jingle_file_transfer_ibb_error_callback(JingleContent *content)
+{
+	PurpleXfer *xfer = JINGLE_FT_GET_PRIVATE(JINGLE_FT(content))->xfer;
+	JingleSession *session = jingle_content_get_session(content);
+	JabberStream *js = 
+		jingle_session_get_js(jingle_content_get_session(content));
+	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,
+		jingle_session_get_remote_jid(session),
+		_("An error occured on the in-band bytestream transfer\n"));
+	purple_xfer_cancel_remote(xfer);
+}
+
+static void
+jingle_file_transfer_xfer_init(PurpleXfer *xfer)
+{
+	JingleContent *content = (JingleContent *) xfer->data;
+	JingleSession *session = jingle_content_get_session(content);
+	JingleTransport *transport = jingle_content_get_transport(content);
+	
+	if (jingle_session_is_initiator(session)) {
+		jabber_iq_send(jingle_session_to_packet(session, 
+			JINGLE_SESSION_INITIATE));
+	} else {
+		jabber_iq_send(jingle_session_to_packet(session,
+			JINGLE_SESSION_ACCEPT));
+		/* ready to receive */
+		if (JINGLE_IS_IBB(transport)) {
+			/* open file and prepare for IBB */
+			/* open file to write to */
+			JingleIBB *ibb = JINGLE_IBB(transport);
+			const gchar *filename = purple_xfer_get_local_filename(xfer);
+			JINGLE_FT_GET_PRIVATE(JINGLE_FT(content))->ibb_fp = 
+				g_fopen(filename, "wb");
+			if (JINGLE_FT_GET_PRIVATE(JINGLE_FT(content))->ibb_fp == NULL) {
+				purple_debug_error("jabber", 
+					"failed to open file %s for writing: %s\n", filename, 
+					g_strerror(errno));
+				purple_xfer_cancel_remote(xfer);
+				jabber_iq_send(jingle_session_to_packet(session,
+						JINGLE_SESSION_TERMINATE));
+				g_object_unref(session);
+				return;
+			}
+
+			/* setup callbacks */
+			jingle_ibb_set_data_received_callback(ibb, 
+				jingle_file_transfer_ibb_data_recv_callback);
+			jingle_ibb_set_error_callback(ibb,
+				jingle_file_transfer_ibb_error_callback);
+	
+			/* start the transfer */
+			purple_xfer_start(xfer, 0, NULL, 0);
+		}
+	}
+}
+
+static void
+jingle_file_transfer_cancel_send(PurpleXfer *xfer)
+{
+	JingleSession *session = 
+		jingle_content_get_session((JingleContent *)xfer->data);
+	
+	jabber_iq_send(jingle_session_to_packet(session, JINGLE_SESSION_TERMINATE));
+	g_object_unref(session);
+}
+
+static void
+jingle_file_transfer_cancel_recv(PurpleXfer *xfer)
+{
+	JingleSession *session = 
+		jingle_content_get_session((JingleContent *)xfer->data);
+
+	jabber_iq_send(jingle_session_to_packet(session, JINGLE_SESSION_TERMINATE));
+	g_object_unref(session);
+}
+
+static void
+jingle_file_transfer_xfer_end(PurpleXfer *xfer)
+{
+	JingleSession *session = 
+		jingle_content_get_session((JingleContent *)xfer->data);
+
+	g_object_unref(session);
+}
+
+static JingleContent *
+jingle_file_transfer_parse_internal(xmlnode *ft)
+{
+	JingleContent *content = parent_class->parse(ft);
+	xmlnode *description = xmlnode_get_child(ft, "description");
+	purple_debug_info("jingle-ft", "ft parse\n");
+	return content;
+}
+
+static xmlnode *
+jingle_file_transfer_to_xml_internal(JingleContent *ft, xmlnode *content, 
+	JingleActionType action)
+{
+	xmlnode *node = parent_class->to_xml(ft, content, action);
+	xmlnode *description = xmlnode_get_child(node, "description");
+	if (description != NULL) {
+		xmlnode *offer = xmlnode_new_child(description, "offer");
+		const PurpleXfer *xfer = jingle_file_transfer_get_xfer(ft);
+		
+		xmlnode_insert_child(offer, jabber_xfer_create_file_element(xfer));
+	}
+	return node;
+}
+
+static void
+jingle_file_transfer_handle_action_internal(JingleContent *content, 
+	xmlnode *xmlcontent, JingleActionType action)
+{
+	switch (action) {
+		case JINGLE_SESSION_ACCEPT: {
+			JingleSession *session = jingle_content_get_session(content);
+			xmlnode *description = xmlnode_get_child(xmlcontent, "description");
+			JingleTransport *transport = jingle_content_get_transport(content);
+			PurpleXfer *xfer = 
+				JINGLE_FT_GET_PRIVATE(JINGLE_FT(content))->xfer;
+			
+			/* do stuff here, start the transfer, etc... */
+			if (JINGLE_IS_IBB(transport)) {
+				JingleFT *ft = JINGLE_FT(content);
+				/* open the file for reading */
+				JINGLE_FT_GET_PRIVATE(ft)->ibb_fp = 
+					g_fopen(purple_xfer_get_local_filename(xfer), "rb");
+				
+				if (JINGLE_FT_GET_PRIVATE(ft)->ibb_fp) {
+					/* send first data */
+					purple_xfer_start(xfer, 0, NULL, 0);
+					purple_xfer_set_bytes_sent(xfer, 0);
+					purple_xfer_update_progress(xfer);
+					jingle_file_transfer_ibb_send_data(content);
+				} else {
+					purple_debug_error("jingle-ft", 
+						"failed to open file for reading\n");
+					jingle_file_transfer_cancel_local(content);
+				}
+			}
+			
+			g_object_unref(session);
+			break;
+		}
+		case JINGLE_SESSION_INITIATE: {
+			JingleSession *session = jingle_content_get_session(content);
+			JingleTransport *transport = jingle_transport_parse(
+					xmlnode_get_child(xmlcontent, "transport"));
+			xmlnode *description = xmlnode_get_child(xmlcontent, "description");
+			JabberStream *js = jingle_session_get_js(session);
+			xmlnode *offer = xmlnode_get_child(description, "offer");
+			PurpleXfer *xfer = NULL;
+			const gchar *who = jingle_session_get_remote_jid(session);
+	
+			if (offer) {
+				/* do stuff here... create the PurpleXfer etc... */
+				xmlnode *file = xmlnode_get_child(offer, "file");
+				PurpleXfer *xfer = NULL;
+
+				if (file)
+					xfer = jabber_xfer_create_from_xml(js->gc->account, file, 
+						who, session);
+				if (xfer) {
+					GList *contents = jingle_session_get_contents(session);
+					JingleContent *content = (JingleContent *) contents->data;
+					JINGLE_FT_GET_PRIVATE(JINGLE_FT(content))->xfer = xfer;
+					xfer->data = content;
+					/* setup callbacks */
+					purple_xfer_set_init_fnc(xfer, 
+						jingle_file_transfer_xfer_init);
+					purple_xfer_set_end_fnc(xfer,
+						jingle_file_transfer_xfer_end);
+					purple_xfer_set_cancel_recv_fnc(xfer,
+						jingle_file_transfer_cancel_recv);
+					purple_xfer_request(xfer);
+				} else {
+					jabber_iq_send(jingle_session_to_packet(session,
+						JINGLE_SESSION_TERMINATE));
+					g_object_unref(session);
+					break;
+				}
+				
+				if (JINGLE_IS_IBB(transport)) {
+					JingleIBB *ibb = JINGLE_IBB(transport);
+					const gchar *sid = 
+						xmlnode_get_attrib(xmlnode_get_child(xmlcontent, 
+							"transport"), "sid");
+					const gchar *filename = 
+						purple_xfer_get_local_filename(xfer);
+					jingle_ibb_create_session(ibb, content, sid, who);
+					
+					
+				}	
+			}
+
+			g_object_unref(session);
+			break;
+		}
+		case JINGLE_SESSION_TERMINATE: {
+			JingleSession *session = jingle_content_get_session(content);
+			GList *contents = jingle_session_get_contents(session);
+			JingleContent *content = (JingleContent *) contents->data;
+			PurpleXfer *xfer = 
+				jingle_file_transfer_get_xfer(content);
+
+			/* do stuff here... close transfer etc... */
+			if (xfer) {
+				purple_debug_info("jingle", 
+					"got session-terminate, ending transfer\n");
+				purple_xfer_end(xfer);
+				JINGLE_FT_GET_PRIVATE(JINGLE_FT(content))->xfer = NULL;
+			}
+	
+			g_object_unref(session);
+			break;
+		}
+		case JINGLE_TRANSPORT_INFO: {
+			JingleSession *session = jingle_content_get_session(content);
+			JingleTransport *transport = jingle_transport_parse(
+					xmlnode_get_child(xmlcontent, "transport"));
+			
+			/* do stuff... */
+			
+			/* we should check for "stream-host" error (in the case of S5B) and
+			 offer a transport-replace with IBB */
+			
+			g_object_unref(session);
+			break;
+		}
+		case JINGLE_TRANSPORT_REPLACE: {
+			JingleSession *session = jingle_content_get_session(content);
+			
+			/* fallback to IBB etc... */
+			
+			g_object_unref(session);
+			break;
+		}
+		default:
+			break;
+	}
+}
+
+
+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)
+	{
+		JingleSession *session;
+		JingleContent *content;
+		JingleTransport *transport;
+		gchar *jid = NULL, *me = NULL, *sid = NULL;
+
+		/* construct JID to send to */
+		JabberBuddy *jb = jabber_buddy_find(js, who, FALSE);
+		JabberBuddyResource *jbr;
+		
+		if (!jb) {
+			purple_debug_error("jingle-rtp", "Could not find Jabber buddy\n");
+			return NULL;
+		}
+		jbr = jabber_buddy_find_resource(jb, NULL);
+		if (!jbr) {
+			purple_debug_error("jingle-rtp", "Could not find buddy's resource\n");
+		}
+		
+		if ((strchr(who, '/') == NULL) && jbr && (jbr->name != NULL)) {
+			jid = g_strdup_printf("%s/%s", who, jbr->name);
+		} else {
+			jid = g_strdup(who);
+		}
+
+		/* set ourselves as initiator */
+		me = g_strdup_printf("%s@%s/%s", js->user->node, js->user->domain, js->user->resource);
+
+		sid = jabber_get_next_id(js);
+		session = jingle_session_create(js, sid, me, jid, TRUE);
+		g_free(sid);
+		
+		/* add the content */
+		/* for now just do IBB... */
+		transport = jingle_transport_create(JINGLE_TRANSPORT_IBB);
+		content = jingle_content_create(JINGLE_APP_FT, "initiator", "session",
+			"ft-session", "sender", transport);
+		jingle_session_add_content(session, content);
+		JINGLE_FT_GET_PRIVATE(JINGLE_FT(content))->xfer = xfer;
+
+		sid = jabber_get_next_id(js);
+		jingle_ibb_create_session(JINGLE_IBB(transport), content, sid, jid);
+		jingle_ibb_set_data_sent_callback(JINGLE_IBB(transport),
+			jingle_file_transfer_ibb_data_sent_callback);
+		jingle_ibb_set_error_callback(JINGLE_IBB(transport),
+			jingle_file_transfer_ibb_error_callback);
+		
+		xfer->data = content;
+		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);
+
+		js->file_transfers = g_list_append(js->file_transfers, xfer);
+
+		g_free(jid);
+		g_free(me);
+	}
+
+	return xfer;
+}
+
+
+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");
+	
+	if (file)
+		purple_xfer_request_accepted(xfer, file);
+	else
+		purple_xfer_request(xfer);
+}
============================================================
--- libpurple/protocols/jabber/jingle/file-transfer.h	5d85365937ca3af195ba553d223585dfdb390e6b
+++ libpurple/protocols/jabber/jingle/file-transfer.h	5d85365937ca3af195ba553d223585dfdb390e6b
@@ -0,0 +1,88 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301,  USA
+ */
+ 
+#ifndef JINGLE_FILE_TRANSFER_H
+#define JINGLE_FILE_TRANSFER_H
+
+#include "config.h"
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "content.h"
+#include "xmlnode.h"
+#include "ft.h"
+
+G_BEGIN_DECLS
+
+#define JINGLE_TYPE_FT            (jingle_file_transfer_get_type())
+#define JINGLE_FT(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), \
+									JINGLE_TYPE_FT, JingleFT))
+#define JINGLE_FT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), \
+									JINGLE_TYPE_FT, JingleFTClass))
+#define JINGLE_IS_FT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), \
+									JINGLE_TYPE_FT))
+#define JINGLE_IS_FT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), \
+									JINGLE_TYPE_FT))
+#define JINGLE_FT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), \
+									JINGLE_TYPE_FT, JingleFTClass))
+
+/** @copydoc _JingleFT */
+typedef struct _JingleFT JingleFT;
+/** @copydoc _JingleFTClass */
+typedef struct _JingleFTClass JingleFTClass;
+/** @copydoc _JingleFTPrivate */
+typedef struct _JingleFTPrivate JingleFTPrivate;
+
+/** The rtp class */
+struct _JingleFTClass
+{
+	JingleContentClass parent_class;     /**< The parent class. */
+};
+
+/** The rtp class's private data */
+struct _JingleFT
+{
+	JingleContent parent;                /**< The parent of this object. */
+	JingleFTPrivate *priv;      /**< The private data of this object. */
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Gets the rtp class's GType
+ *
+ * @return The rtp class's GType.
+ */
+GType jingle_file_transfer_get_type(void);
+
+PurpleXfer *jingle_file_transfer_new_xfer(PurpleConnection *gc, const gchar *who);
+
+void jingle_file_transfer_send(PurpleConnection *gc, const gchar *who, 
+	const gchar *file);
+
+PurpleXfer *jingle_file_transfer_get_xfer(JingleContent *content);
+
+#ifdef __cplusplus
+}
+#endif
+
+G_END_DECLS
+
+
+#endif /* JINGLE_FILE_TRANSFER_H */
\ No newline at end of file
============================================================
--- libpurple/protocols/jabber/jingle/ibbs.c	8db74a4a5513f15de0f81fac442a422dbdb4e766
+++ libpurple/protocols/jabber/jingle/ibbs.c	8db74a4a5513f15de0f81fac442a422dbdb4e766
@@ -0,0 +1,309 @@
+/*
+ * 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 "internal.h"
+
+#include "../ibb.h"
+#include "jingle.h"
+#include "session.h"
+#include "content.h"
+#include "ibbs.h"
+#include "debug.h"
+#include "xmlnode.h"
+
+struct _JingleIBBPrivate {
+	JabberIBBSession *session;
+	JingleIBBSentCallback *sent_cb;
+	JingleIBBDataCallback *recv_cb;
+	JingleIBBErrorCallback *error_cb;
+};
+
+#define JINGLE_IBB_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), JINGLE_TYPE_IBB, JingleIBBPrivate))
+
+static void jingle_ibb_class_init (JingleIBBClass *klass);
+static void jingle_ibb_init (JingleIBB *ibb);
+static void jingle_ibb_finalize (GObject *object);
+static void jingle_ibb_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
+static void jingle_ibb_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
+static JingleTransport *jingle_ibb_parse_internal(xmlnode *ibb);
+static xmlnode *jingle_ibb_to_xml_internal(JingleTransport *transport, xmlnode *content, JingleActionType action);
+
+static JingleTransportClass *parent_class = NULL;
+
+enum {
+	PROP_0,
+	PROP_IBB_SESSION
+};
+
+GType
+jingle_ibb_get_type()
+{
+	static GType type = 0;
+
+	if (type == 0) {
+		static const GTypeInfo info = {
+			sizeof(JingleIBBClass),
+			NULL,
+			NULL,
+			(GClassInitFunc) jingle_ibb_class_init,
+			NULL,
+			NULL,
+			sizeof(JingleIBB),
+			0,
+			(GInstanceInitFunc) jingle_ibb_init,
+			NULL
+		};
+		type = g_type_register_static(JINGLE_TYPE_TRANSPORT, "JingleIBB", &info, 0);
+	}
+	return type;
+}
+
+static void
+jingle_ibb_class_init (JingleIBBClass *klass)
+{
+	GObjectClass *gobject_class = (GObjectClass*)klass;
+	parent_class = g_type_class_peek_parent(klass);
+
+	gobject_class->finalize = jingle_ibb_finalize;
+	gobject_class->set_property = jingle_ibb_set_property;
+	gobject_class->get_property = jingle_ibb_get_property;
+	klass->parent_class.to_xml = jingle_ibb_to_xml_internal;
+	klass->parent_class.parse = jingle_ibb_parse_internal;
+	klass->parent_class.transport_type = JINGLE_TRANSPORT_IBB;
+
+	g_object_class_install_property(gobject_class, PROP_IBB_SESSION,
+			g_param_spec_pointer("ibb-session",
+			"IBB session",
+			"The IBB session for this transport.",
+			G_PARAM_READABLE));
+
+	g_type_class_add_private(klass, sizeof(JingleIBBPrivate));
+}
+
+static void
+jingle_ibb_init (JingleIBB *ibb)
+{	
+	ibb->priv = JINGLE_IBB_GET_PRIVATE(ibb);
+	memset(ibb->priv, 0, sizeof(ibb->priv));
+	
+	/* create IBB session */
+}
+
+static void
+jingle_ibb_finalize (GObject *ibb)
+{
+	JingleIBBPrivate *priv = JINGLE_IBB_GET_PRIVATE(ibb);
+	purple_debug_info("jingle","jingle_ibb_finalize\n");
+	
+	if (priv->session) {
+		/* should manually close the IBB session */
+		jabber_ibb_session_destroy(priv->session);
+		priv->session = NULL;
+	}
+}
+
+static void
+jingle_ibb_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+	JingleIBB *ibb;
+	g_return_if_fail(JINGLE_IS_IBB(object));
+
+	ibb = JINGLE_IBB(object);
+
+	switch (prop_id) {
+		case PROP_IBB_SESSION:
+			ibb->priv->session = g_value_get_pointer(value);
+			break;
+		default:	
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+			break;
+	}
+}
+
+static void
+jingle_ibb_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+	JingleIBB *ibb;
+	g_return_if_fail(JINGLE_IS_IBB(object));
+	
+	ibb = JINGLE_IBB(object);
+
+	switch (prop_id) {
+		case PROP_IBB_SESSION:
+			g_value_set_pointer(value, ibb->priv->session);
+			break;
+		default:	
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);	
+			break;
+	}
+}
+
+static JingleTransport *
+jingle_ibb_parse_internal(xmlnode *ibb)
+{
+	JingleTransport *transport = parent_class->parse(ibb);
+	JingleIBBPrivate *priv = JINGLE_IBB_GET_PRIVATE(transport);
+	xmlnode *jingle = xmlnode_get_parent(xmlnode_get_parent(ibb));
+	const gchar *who = xmlnode_get_attrib(jingle, "initiator");
+	const gchar *sid = xmlnode_get_attrib(ibb, "sid");
+	
+	return transport;
+}
+
+static xmlnode *
+jingle_ibb_to_xml_internal(JingleTransport *transport, xmlnode *content, 
+	JingleActionType action)
+{
+	xmlnode *node = parent_class->to_xml(transport, content, action);
+
+	purple_debug_info("jingle", "jingle_ibb_to_xml_internal\n");
+
+	if (action == JINGLE_SESSION_INITIATE || action == JINGLE_TRANSPORT_INFO) {
+		JingleIBBPrivate *priv = JINGLE_IBB_GET_PRIVATE(transport);
+		if (priv->session) {
+			gchar *block_size = 
+				g_strdup_printf("%d", 
+					jabber_ibb_session_get_block_size(priv->session));
+			xmlnode_set_attrib(node, "sid", 
+				jabber_ibb_session_get_sid(priv->session));
+			xmlnode_set_attrib(node, "block-size", block_size);
+			/* we will always send as <iq/> */
+			xmlnode_set_attrib(node, "stanza", "iq");
+			g_free(block_size);
+		} else {
+			purple_debug_error("jingle", 
+				"trying to send IBB transport before creating IBB session\n");
+		}
+	}
+	return node;
+}
+
+static void
+jingle_ibb_data_sent_callback(JabberIBBSession *sess)
+{
+	JingleContent *content = 
+		(JingleContent *) jabber_ibb_session_get_user_data(sess);
+
+	if (content) {
+		JingleTransport *transport = jingle_content_get_transport(content);
+
+		JingleIBBSentCallback *sent_cb = 
+			JINGLE_IBB_GET_PRIVATE(JINGLE_IBB(transport))->sent_cb;
+
+		if (sent_cb) {
+			sent_cb(content);
+		}
+	}
+}
+
+static void
+jingle_ibb_data_recv_callback(JabberIBBSession *sess, const gpointer data,
+	gsize size)
+{
+	JingleContent *content = 
+		(JingleContent *) jabber_ibb_session_get_user_data(sess);
+	
+	if (content) {
+		JingleTransport *transport = jingle_content_get_transport(content);
+
+		JingleIBBDataCallback *recv_cb = 
+			JINGLE_IBB_GET_PRIVATE(JINGLE_IBB(transport))->recv_cb;
+			
+		if (recv_cb) {
+			recv_cb(content, data, size);
+		}
+	}
+}
+
+static void
+jingle_ibb_error_callback(JabberIBBSession *sess)
+{
+	JingleContent *content = 
+		(JingleContent *) jabber_ibb_session_get_user_data(sess);
+	
+	if (content) {
+		JingleTransport *transport = jingle_content_get_transport(content);
+
+		JingleIBBErrorCallback *error_cb = 
+				JINGLE_IBB_GET_PRIVATE(JINGLE_IBB(transport))->error_cb;
+			
+		if (error_cb) {
+			error_cb(content);
+		}
+	}
+}
+
+void
+jingle_ibb_create_session(JingleIBB *ibb, JingleContent *content, 
+	const gchar *sid, const gchar *who)
+{
+	JingleSession *sess = jingle_content_get_session(content);
+	JabberIBBSession *session = 
+		jabber_ibb_session_create(jingle_session_get_js(sess), sid, who,
+			content);
+
+	purple_debug_info("jingle-ibb", "creating IBB session with sid: %s,\n"
+		" who: %s\n", sid, who);
+
+	/* we will set the IBB session to "open" immediatly, since the Jingle
+	 negotiation defines the "open" state */
+	jabber_ibb_session_set_state(session, JABBER_IBB_SESSION_OPENED);
+	/* set callbacks... */
+	jabber_ibb_session_set_data_sent_callback(session, 
+		jingle_ibb_data_sent_callback);
+	jabber_ibb_session_set_data_received_callback(session,
+		jingle_ibb_data_recv_callback);
+	jabber_ibb_session_set_error_callback(session, jingle_ibb_error_callback);
+	JINGLE_IBB_GET_PRIVATE(ibb)->session = session;
+}
+
+void 
+jingle_ibb_set_data_sent_callback(JingleIBB *ibb, JingleIBBSentCallback *cb)
+{
+	JINGLE_IBB_GET_PRIVATE(ibb)->sent_cb = cb;
+}
+
+void 
+jingle_ibb_set_data_received_callback(JingleIBB *ibb, JingleIBBDataCallback *cb)
+{
+	JINGLE_IBB_GET_PRIVATE(ibb)->recv_cb = cb;
+}
+
+void 
+jingle_ibb_set_error_callback(JingleIBB *ibb, JingleIBBErrorCallback *cb)
+{
+	JINGLE_IBB_GET_PRIVATE(ibb)->error_cb = cb;
+}
+
+void
+jingle_ibb_send_data(JingleIBB *ibb, gconstpointer data, gsize size)
+{
+	JabberIBBSession *session = JINGLE_IBB_GET_PRIVATE(ibb)->session;
+
+	if (session) {
+		jabber_ibb_session_send_data(session, data, size);
+	} else {
+		purple_debug_error("jingle", 
+			"jingle_ibb_send_data: no IBB session created\n");
+	}
+}
+
+gsize
+jingle_ibb_get_block_size(const JingleIBB *ibb)
+{
+	return jabber_ibb_session_get_block_size(
+		JINGLE_IBB_GET_PRIVATE(ibb)->session);
+}
\ No newline at end of file
============================================================
--- libpurple/protocols/jabber/jingle/ibbs.h	c8e75845fbc502bd96af3dddd711243aa7d38c5f
+++ libpurple/protocols/jabber/jingle/ibbs.h	c8e75845fbc502bd96af3dddd711243aa7d38c5f
@@ -0,0 +1,95 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301,  USA
+ */
+ 
+ #ifndef JINGLE_IBB_H
+ #define JINGLE_IBB_H
+ 
+ #include <glib.h>
+#include <glib-object.h>
+
+#include "transport.h"
+
+G_BEGIN_DECLS
+
+#define JINGLE_TYPE_IBB            (jingle_ibb_get_type())
+#define JINGLE_IBB(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), JINGLE_TYPE_IBB, JingleIBB))
+#define JINGLE_IBB_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), JINGLE_TYPE_IBB, JingleIBBClass))
+#define JINGLE_IS_IBB(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), JINGLE_TYPE_IBB))
+#define JINGLE_IS_IBB_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), JINGLE_TYPE_IBB))
+#define JINGLE_IBB_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), JINGLE_TYPE_IBB, JingleIBBClass))
+
+/** @copydoc _JingleIBB */
+typedef struct _JingleIBB JingleIBB;
+/** @copydoc _JingleIBBClass */
+typedef struct _JingleIBBClass JingleIBBClass;
+/** @copydoc _JingleIBBPrivate */
+typedef struct _JingleIBBPrivate JingleIBBPrivate;
+ 
+ /** The IBB class */
+struct _JingleIBBClass
+{
+	JingleTransportClass parent_class;     /**< The parent class. */
+
+	xmlnode *(*to_xml) (JingleTransport *transport, xmlnode *content, JingleActionType action);
+	JingleTransport *(*parse) (xmlnode *transport);
+};
+
+/** The IBB class's private data */
+struct _JingleIBB
+{
+	JingleTransport parent;                /**< The parent of this object. */
+	JingleIBBPrivate *priv;      /**< The private data of this object. */
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Gets the IBB class's GType
+ *
+ * @return The IBB class's GType.
+ */
+GType jingle_ibb_get_type(void);
+
+/* Set up the internal JabberIBBSession as being initiator */
+void jingle_ibb_create_session(JingleIBB *ibb, JingleContent *content, 
+	const gchar *sid, const gchar *who);
+
+/* Callback handling */
+typedef void (JingleIBBSentCallback)(JingleContent *);
+typedef void (JingleIBBDataCallback)(JingleContent *, gconstpointer, gsize); 
+typedef void (JingleIBBErrorCallback)(JingleContent *);
+
+void jingle_ibb_set_data_sent_callback(JingleIBB *ibb, 
+	JingleIBBSentCallback *cb);
+void jingle_ibb_set_data_received_callback(JingleIBB *ibb,
+	JingleIBBDataCallback *cb);
+void jingle_ibb_set_error_callback(JingleIBB *ibb, JingleIBBErrorCallback *cb);
+
+/* send a packet of data through the transport */
+void jingle_ibb_send_data(JingleIBB *ibb, gconstpointer data, gsize size);
+
+/* get the (max) block size of the IBB session */
+gsize jingle_ibb_get_block_size(const JingleIBB *ibb);
+
+#ifdef __cplusplus
+}
+#endif
+
+G_END_DECLS
+ 
+ #endif /* JINGLE_IBB_H */
\ No newline at end of file


More information about the Commits mailing list