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