--- a/CMakeLists.txt Mon Aug 16 23:36:49 2010 +0200
+++ b/CMakeLists.txt Mon Aug 16 23:37:31 2010 +0200
@@ -38,7 +38,7 @@
## Target definitions
add_subdirectory(jingle)
-add_subdirectory(jingle-filetransfer)
+add_subdirectory(jingle-ft)
add_subdirectory(jingle-ibb)
add_subdirectory(jingle-socks5)
--- a/README Mon Aug 16 23:36:49 2010 +0200
+++ b/README Mon Aug 16 23:37:31 2010 +0200
@@ -0,0 +1,16 @@
+mcabber-jingle is a set of modules implementing:
+* Jingle (XEP-0166), a XMPP extension for managing peer-to-peer sessions
+* Jingle File Transfert (XEP-0234), a Jingle application type for transferring
+ files between two entities.
+* Jingle In-Band Bytestreams (XEP-261), a Jingle transport method that sends data
+ using the In-Band Bytestreams (IBB) protocol (XEP-0047).
+
+====INSTALLATION====
+To build the modules, you will need loudmouth and mcabber headers files along
+with cmake. On Debian/Ubuntu, cmake is provided by the "cmake" package and the
+loudmouth header files in "loudmouth-dev".
+Once you have installed them, you can simply run "cmake .", then "make install"
+as root. This should install the 3 modules in /usr/lib/mcabber.
+To load them, type "/module load jingle", "/module load jingle-ft" and "module
+load jingle-ibb". You can also load them automatically by putting those three
+lines in your mcabberrc without the leading slash.
--- a/jingle-filetransfer/CMakeLists.txt Mon Aug 16 23:36:49 2010 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,4 +0,0 @@
-add_library(jingle-filetransfer MODULE filetransfer.c)
-set_target_properties(jingle-filetransfer PROPERTIES COMPILE_FLAGS "-Wall -O0 -g")
-include_directories(${CMAKE_SOURCE_DIR})
-install(TARGETS jingle-filetransfer DESTINATION lib/mcabber)
--- a/jingle-filetransfer/filetransfer.c Mon Aug 16 23:36:49 2010 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,705 +0,0 @@
-/*
- * filetransfer.c
- *
- * Copyrigth (C) 2010 Nicolas Cornu <nicolas.cornu@ensi-bourges.fr>
- *
- * 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
- * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- * USA
- */
-#include "config.h"
-
-#include <glib.h>
-#include <glib/gstdio.h>
-#include <string.h>
-
-#include <mcabber/modules.h>
-#include <mcabber/utils.h>
-#include <mcabber/xmpp_helper.h>
-#include <mcabber/settings.h>
-#include <mcabber/logprint.h>
-#include <mcabber/compl.h>
-#include <mcabber/commands.h>
-#include <mcabber/roster.h>
-#include <mcabber/utils.h>
-
-#include <jingle/jingle.h>
-#include <jingle/check.h>
-#include <jingle/register.h>
-#include <jingle/sessions.h>
-#include <jingle/send.h>
-
-#include "filetransfer.h"
-
-
-static gconstpointer check(JingleContent *cn, GError **err);
-static gboolean handle(JingleAction action, gconstpointer data, LmMessageNode *node);
-static void tomessage(gconstpointer data, LmMessageNode *node);
-static gboolean handle_data(gconstpointer data, const gchar *data2, guint len);
-static void start(session_content *sc);
-static void send(session_content *sc);
-static void stop(gconstpointer data);
-static gchar* info(gconstpointer data);
-
-static gboolean is_md5_hash(const gchar *hash);
-
-static void jingle_ft_init(void);
-static void jingle_ft_uninit(void);
-// Return must be free
-static gchar *_convert_size(guint64 size);
-static int _next_index(void);
-static void _free(JingleFT *jft);
-
-const gchar *deps[] = { "jingle", NULL };
-
-static GSList *info_list = NULL;
-static guint jft_cid = 0;
-
-const gchar* strstate[] = {
- "PENDING",
- "STARTING",
- "ENDING",
- "REJECT",
- "ERROR"
-};
-
-static JingleAppFuncs funcs = {
- .check = check,
- .handle = handle,
- .tomessage = tomessage,
- .handle_data = handle_data,
- .start = start,
- .send = send,
- .stop = stop,
- .info = info
-};
-
-module_info_t info_jingle_filetransfer = {
- .branch = MCABBER_BRANCH,
- .api = MCABBER_API_VERSION,
- .version = PROJECT_VERSION,
- .description = "Jingle File Transfer (XEP-0234)\n",
- .requires = deps,
- .init = jingle_ft_init,
- .uninit = jingle_ft_uninit,
- .next = NULL,
-};
-
-
-/**
- * \fn static gconstpointer check(JingleContent *cn, GError **err)
- * \brief Check if a node description with xmlns of JFT is correct
- *
- * \param cn the jinglecontent a node description
- * \param err contain an error of the domain JINGLE_CHECK_ERROR
- * \return a gconstpointer, which is a new allocated JingleFT
- */
-static gconstpointer check(JingleContent *cn, GError **err)
-{
- JingleFT *ft = NULL;
- LmMessageNode *node;
- gint64 tmpsize;
- const gchar *datestr, *sizestr;
-
- node = lm_message_node_get_child(cn->description, "offer");
-
- if (!node) {
- g_set_error(err, JINGLE_CHECK_ERROR, JINGLE_CHECK_ERROR_MISSING,
- "the offer element is missing");
- return NULL;
- }
-
- node = lm_message_node_get_child(node, "file");
- if (!node) {
- g_set_error(err, JINGLE_CHECK_ERROR, JINGLE_CHECK_ERROR_MISSING,
- "the file element is missing");
- return NULL;
- }
-
- if (g_strcmp0(lm_message_node_get_attribute(node, "xmlns"), NS_SI_FT)) {
- g_set_error(err, JINGLE_CHECK_ERROR, JINGLE_CHECK_ERROR_MISSING,
- "the file transfer offer has an invalid/unsupported namespace");
- return NULL;
- }
-
- ft = g_new0(JingleFT, 1);
- datestr = lm_message_node_get_attribute(node, "date");
- ft->hash = (gchar *) lm_message_node_get_attribute(node, "hash");
- ft->name = (gchar *) lm_message_node_get_attribute(node, "name");
- sizestr = lm_message_node_get_attribute(node, "size");
- ft->transmit = 0;
- ft->dir = JINGLE_FT_INCOMING;
-
- if (!ft->name || !sizestr) {
- g_set_error(err, JINGLE_CHECK_ERROR, JINGLE_CHECK_ERROR_MISSING,
- "an attribute of the file element is missing");
- g_free(ft);
- return NULL;
- }
-
- ft->date = from_iso8601(datestr, 1);
- tmpsize = g_ascii_strtoll(sizestr, NULL, 10);
-
- // the size attribute is a xs:integer an therefore can be negative.
- if (tmpsize < 0) {
- g_set_error(err, JINGLE_CHECK_ERROR, JINGLE_CHECK_ERROR_BADVALUE,
- "the offered file has a negative size");
- g_free(ft);
- return NULL;
- }
- ft->size = tmpsize;
-
- ft->name = g_path_get_basename(ft->name);
-
- if (settings_opt_get("jingle_ft_dir") != NULL)
- ft->name = g_build_filename(settings_opt_get("jingle_ft_dir"), ft->name, NULL);
- else
- ft->name = g_build_filename("/tmp", ft->name, NULL);
-
- if (!g_strcmp0(ft->name, ".")) {
- g_set_error(err, JINGLE_CHECK_ERROR, JINGLE_CHECK_ERROR_BADVALUE,
- "the offered file has an invalid filename");
- g_free(ft->name);
- g_free(ft);
- return NULL;
- }
-
- // check if the md5 hash is valid ([a-fA-F0-9){32})
- if (ft->hash != NULL && (strlen(ft->hash) != 32 || !is_md5_hash(ft->hash))) {
- g_set_error(err, JINGLE_CHECK_ERROR, JINGLE_CHECK_ERROR_BADVALUE,
- "the offered file has an invalid hash");
- g_free(ft->name);
- g_free(ft);
- return NULL;
- }
- ft->hash = g_strndup(ft->hash, 32);
-
- {
- JingleFTInfo *jfti = g_new0(JingleFTInfo, 1);
- jfti->index = _next_index();
- jfti->jft = ft;
- info_list = g_slist_append(info_list, jfti);
- }
-
- return (gconstpointer) ft;
-}
-
-/**
- * \fn static gboolean handle(JingleAction action, gconstpointer data, LmMessageNode *node)
- * \brief handle a function to handle action which are not "current"
- *
- * \param action the action which have been received
- * \param data contain the JingleFT of the content concerned
- * \param node the node himself
- * \return a gconstpointer, which is a new allocated JingleFT
- */
-static gboolean handle(JingleAction action, gconstpointer data,
- LmMessageNode *node)
-{
- if (action == JINGLE_SESSION_INFO) {
- if (!g_strcmp0(lm_message_node_get_attribute(node, "xmlns"),
- NS_JINGLE_APP_FT_INFO)
- && !g_strcmp0(node->name, "hash")) {
- ((JingleFT *)data)->hash = g_strdup(lm_message_node_get_value(node));
- return TRUE;
- }
- return FALSE;
- }
- return FALSE;
-}
-
-static gboolean is_md5_hash(const gchar *hash)
-{
- int i = 0;
- for (i = 0; i < 32 && hash[i]; i++)
- if (!g_ascii_isxdigit(hash[i])) break;
-
- if (i == 32)
- return TRUE;
- else
- return FALSE;
-}
-
-static gboolean handle_data(gconstpointer jingleft, const gchar *data, guint len)
-{
- JingleFT *jft = (JingleFT *) jingleft;
- GError *err = NULL;
- GIOStatus status;
- gsize bytes_written = 0;
-
- if (jft->dir != JINGLE_FT_INCOMING)
- return FALSE;
-
- if (jft->md5 == NULL) {
- jft->md5 = g_checksum_new(G_CHECKSUM_MD5);
- }
-
- g_checksum_update(jft->md5, (guchar*)data, (gsize)len);
-
- // TODO: check if the file already exist or if it was created
- // during the call to jingle_ft_check and handle_data
- if (jft->outfile == NULL) {
- jft->outfile = g_io_channel_new_file(jft->name, "w", &err);
- if (jft->outfile == NULL || err != NULL) {
- scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: %s %s", err->message,
- jft->name);
- //TODO: propagate the GError ?
- g_error_free(err);
- return FALSE;
- }
- jft->state = JINGLE_FT_STARTING;
- status = g_io_channel_set_encoding(jft->outfile, NULL, &err);
- if (status != G_IO_STATUS_NORMAL || err != NULL) {
- scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: %s %s", err->message,
- jft->name);
- g_error_free(err);
- return FALSE;
- }
- }
-
- jft->state = JINGLE_FT_STARTING;
-
- status = g_io_channel_write_chars(jft->outfile, data, (gssize) len,
- &bytes_written, &err);
- if (status != G_IO_STATUS_NORMAL || err != NULL) {
- scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: %s %s", err->message,
- jft->name);
- g_error_free(err);
- return FALSE;
- }
- status = g_io_channel_flush(jft->outfile, &err);
- if (status != G_IO_STATUS_NORMAL || err != NULL) {
- scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: %s %s", err->message,
- jft->name);
- g_error_free(err);
- return FALSE;
- }
-
- if (bytes_written != len) {
- // not supposed to happen if status is normal, unless outfile is non-blocking
- return FALSE;
- }
-
- jft->transmit += len;
- return TRUE;
-}
-
-
-static int _next_index(void)
-{
- static int a = 0;
- return a++;
-}
-
-static void do_sendfile(char *arg)
-{
- char **args = split_arg(arg, 3, 0);
- gchar *filename;
- struct stat fileinfo;
-
- if (!g_strcmp0(args[0], "send")) {
- if (!args[1]) {
- scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: give me a name!");
- free_arg_lst(args);
- return;
- }
-
- filename = expand_filename(args[1]); // expand ~ to HOME
-
- if (g_stat(filename, &fileinfo) != 0) {
- scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: unable to stat %s",
- args[1]);
- free_arg_lst(args);
- return;
- }
-
- if (!S_ISREG(fileinfo.st_mode) && !S_ISLNK(fileinfo.st_mode)) {
- scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: File doesn't exist!");
- free_arg_lst(args);
- return;
- }
-
- scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: Trying to send %s",
- args[1]);
-
- {
- JingleSession *sess;
- gchar *sid = jingle_generate_sid();
- gchar *ressource, *recipientjid;
- const gchar *namespaces[] = {NS_JINGLE, NS_JINGLE_APP_FT, NULL};
- const gchar *myjid = g_strdup(lm_connection_get_jid(lconnection));
- JingleFT *jft = g_new0(JingleFT, 1);
- GError *err = NULL;
-
- if (CURRENT_JID == NULL) { // CURRENT_JID = the jid of the user which has focus
- scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: Please, choose a valid JID in the roster");
- free_arg_lst(args);
- return;
- }
- ressource = jingle_find_compatible_res(CURRENT_JID, namespaces);
- if (ressource == NULL) {
- scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: Cannot send file, because there is no ressource available");
- free_arg_lst(args);
- return;
- }
-
- recipientjid = g_strdup_printf("%s/%s", CURRENT_JID, ressource);
-
- sess = session_new(sid, myjid, recipientjid, JINGLE_SESSION_OUTGOING);
- session_add_content(sess, "file", JINGLE_SESSION_STATE_PENDING);
-
- jft->desc = g_strdup(args[1]);
- jft->type = JINGLE_FT_OFFER;
- jft->name = g_path_get_basename(filename);
- jft->date = fileinfo.st_mtime;
- jft->size = fileinfo.st_size;
- jft->transmit = 0;
- jft->state = JINGLE_FT_PENDING;
- jft->dir = JINGLE_FT_OUTGOING;
- jft->outfile = g_io_channel_new_file (filename, "r", &err);
- if (jft->outfile == NULL || err != NULL) {
- scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: %s %s", err->message,
- args[1]);
- g_error_free(err);
- free_arg_lst(args);
- return;
- }
-
- g_io_channel_set_encoding(jft->outfile, NULL, &err);
- if (jft->outfile == NULL || err != NULL) {
- scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: %s %s", err->message,
- args[1]);
- g_error_free(err);
- free_arg_lst(args);
- return;
- }
-
- session_add_app(sess, "file", NS_JINGLE_APP_FT, jft);
-
- {
- JingleFTInfo *jfti = g_new0(JingleFTInfo, 1);
- jfti->index = _next_index();
- jfti->jft = jft;
- info_list = g_slist_append(info_list, jfti);
- }
-
- jingle_handle_app(sess, "file", NS_JINGLE_APP_FT, jft, recipientjid);
-
- g_free(ressource);
- g_free(sid);
- }
- } else if (!g_strcmp0(args[0], "info")) {
- GSList *el = info_list;
-
- if (!info_list)
- scr_LogPrint(LPRINT_LOGNORM, "JFT: no file");
-
- for (el = info_list; el; el = el->next) {
- JingleFTInfo *jftio = el->data;
- gchar *strsize = _convert_size(jftio->jft->size);
- scr_LogPrint(LPRINT_LOGNORM, "[%i]%s %s %s %.2f%%: %s %s", jftio->index,
- (jftio->jft->dir == JINGLE_FT_INCOMING)?"<==":"-->",
- jftio->jft->name, strsize,
- (gfloat)jftio->jft->transmit/(gfloat)jftio->jft->size * 100,
- jftio->jft->desc?jftio->jft->desc:"",
- strstate[jftio->jft->state]);
- g_free(strsize);
- }
- } else if (!g_strcmp0(args[0], "flush")) {
- GSList *el, *el2 = info_list;
- int count = 0;
- el = info_list;
- while (el) {
- JingleFTInfo *jftinf;
- jftinf = el->data;
- if (jftinf->jft->state == JINGLE_FT_ERROR ||
- jftinf->jft->state == JINGLE_FT_REJECT ||
- jftinf->jft->state == JINGLE_FT_ENDING) {
- count++;
- _free(jftinf->jft);
- info_list = g_slist_delete_link(info_list, el);
- if (info_list == NULL)
- break;
- el = el2;
- continue;
- }
- el2 = el;
- el = el->next;
- }
- scr_LogPrint(LPRINT_LOGNORM, "JFT: %i file%s removed", count, (count>1) ? "s" : "");
- } else {
- scr_LogPrint(LPRINT_LOGNORM, "/jft: %s is not a correct option.", args[1]);
- }
-
- free_arg_lst(args);
-}
-
-static void _free(JingleFT *jft)
-{
- g_free(jft->hash);
- g_free(jft->name);
- g_free(jft->desc);
- g_io_channel_unref(jft->outfile);
- if (jft->dir == JINGLE_FT_INCOMING)
- g_checksum_free(jft->md5);
- g_free(jft);
-}
-
-static void tomessage(gconstpointer data, LmMessageNode *node)
-{
- JingleFT *jft = (JingleFT*) data;
- gchar *size = NULL;
- gchar date[19];
-
- if (lm_message_node_get_child(node, "description") != NULL)
- return;
-
- LmMessageNode *node2 = lm_message_node_add_child(node, "description", NULL);
- lm_message_node_set_attribute(node2, "xmlns", NS_JINGLE_APP_FT);
- if (jft->type == JINGLE_FT_OFFER)
- node2 = lm_message_node_add_child(node2, "offer", NULL);
- else
- node2 = lm_message_node_add_child(node2, "request", NULL);
-
- node2 = lm_message_node_add_child(node2, "file", NULL);
-
- size = g_strdup_printf("%" G_GUINT64_FORMAT, jft->size);
-
- lm_message_node_set_attributes(node2, "xmlns", NS_SI_FT,
- "name", jft->name,
- "size", size,
- NULL);
- g_free(size);
-
- if (jft->hash != NULL)
- lm_message_node_set_attribute(node2, "hash", jft->hash);
-
- if (jft->date)
- if (!to_iso8601(date, jft->date))
- lm_message_node_set_attribute(node2, "date", date);
-
- if (jft->desc != NULL)
- lm_message_node_add_child(node2, "desc", jft->desc);
-
- //if (jft->data != 0)
-}
-
-static void send_hash(const gchar *sid, const gchar *to, const gchar *hash)
-{
- JingleAckHandle *ackhandle;
- GError *err = NULL;
- gboolean ret;
-
- LmMessage *r = lm_message_new_with_sub_type(to, LM_MESSAGE_TYPE_IQ,
- LM_MESSAGE_SUB_TYPE_SET);
- LmMessageNode *node = lm_message_get_node(r);
- lm_message_node_add_child(node, "jingle", NULL);
- node = lm_message_node_get_child(node, "jingle");
- lm_message_node_set_attributes(node, "xmlns", NS_JINGLE,
- "sid", sid,
- "action", "session-info",
- NULL);
- lm_message_node_add_child(node, "hash", hash);
- node = lm_message_node_get_child(node, "hash");
- lm_message_node_set_attribute(node, "xmlns", NS_JINGLE_APP_FT_INFO);
-
- ackhandle = g_new0(JingleAckHandle, 1);
- ackhandle->callback = NULL;
- ackhandle->user_data = NULL;
-
- ret = lm_connection_send_with_reply(lconnection, r,
- jingle_new_ack_handler(ackhandle), &err);
-
- // It's not really a problem, but we must tell it!
- if (ret == FALSE || err != NULL) {
- scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: cannot send hash: %s",
- err->message);
- g_error_free(err);
- }
-
- lm_message_unref(r);
-}
-
-static void send(session_content *sc)
-{
- JingleFT *jft;
- gchar buf[JINGLE_FT_SIZE_READ];
- gsize read;
- GIOStatus status;
- int count = 0;
- GError *err = NULL;
-
- JingleSession *sess = session_find_by_sid(sc->sid, sc->from);
- if (sess == NULL) {
- scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: error before transfer");
- // We haven't LmMessage: jingle_send_iq_error(jn->message, "cancel", "item-not-found", "unknown-session");
- return;
- }
-
- SessionContent *sc2 = session_find_sessioncontent(sess, sc->name);
-
- jft = (JingleFT*)sc2->description;
-
- if (jft->dir != JINGLE_FT_OUTGOING)
- return;
-
- do {
- count++;
- status = g_io_channel_read_chars(jft->outfile, (gchar*)buf,
- JINGLE_FT_SIZE_READ, &read, &err);
- } while (status == G_IO_STATUS_AGAIN && count < 10);
-
- if (status == G_IO_STATUS_AGAIN) {
- // TODO: something better
- scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: file unavailable");
- return;
- }
-
- if (status == G_IO_STATUS_ERROR || err != NULL) {
- scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: %s", err->message);
- g_error_free(err);
- return;
- }
-
- if (status == G_IO_STATUS_NORMAL) {
- jft->transmit += read;
- g_checksum_update(jft->md5, (guchar*)buf, read);
- // Call a handle in jingle who will call the trans
- handle_app_data(sc->sid, sc->from, sc->name, buf, read);
- }
-
- if (status == G_IO_STATUS_EOF) {
- handle_app_data(sc->sid, sc->from, sc->name, NULL, 0);
- scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: transfer finish (%s)",
- jft->name);
- jft->hash = g_strdup(g_checksum_get_string(jft->md5));
- jft->state = JINGLE_FT_ENDING;
- // Call a function to say state is ended
- session_changestate_sessioncontent(sess, sc2->name,
- JINGLE_SESSION_STATE_ENDED);
- // Send the hash
- send_hash(sess->sid, sess->to, jft->hash);
- g_checksum_free(jft->md5);
-
- if (!session_remove_sessioncontent(sess, sc2->name)) {
- jingle_send_session_terminate(sess, "success");
- session_delete(sess);
- }
- }
-}
-
-static void start(session_content *sc)
-{
- JingleFT *jft;
-
- JingleSession *sess = session_find_by_sid(sc->sid, sc->from);
- if (sess == NULL) {
- scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: error before transfer");
- return;
- }
-
- SessionContent *sc2 = session_find_sessioncontent(sess, sc->name);
-
- jft = (JingleFT*)sc2->description;
- jft->state = JINGLE_FT_STARTING;
- jft->md5 = g_checksum_new(G_CHECKSUM_MD5);
-
- scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: Transfer start (%s)",
- jft->name);
-
- sc2->appfuncs->send(sc);
-}
-
-// When we got a session-terminate
-static void stop(gconstpointer data)
-{
- JingleFT *jft = (JingleFT*)data;
- GError *err = NULL;
- GIOStatus status;
-
- jft->state = JINGLE_FT_ENDING;
- if (jft->outfile != NULL) {
- status = g_io_channel_shutdown(jft->outfile, TRUE, &err);
- if (status != G_IO_STATUS_NORMAL || err != NULL) {
- scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: %s",
- err->message);
- g_error_free(err);
- }
- }
-
- if (jft->hash != NULL && jft->md5 != NULL) {
- if (g_strcmp0(jft->hash, g_checksum_get_string(jft->md5))) {
- scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: File corrupt (%s)",
- jft->name);
- } else {
- scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: transfer finished (%s)"
- " and verified", jft->name);
- }
- } else {
- scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: transfer finished (%s)"
- " but not verified", jft->name);
- }
-}
-
-static gchar *_convert_size(guint64 size)
-{
- gchar *strsize;
-
- if (size < 1024)
- strsize = g_strdup_printf("%" G_GUINT64_FORMAT " B", size);
- else if (size < 1048576)
- strsize = g_strdup_printf("%.2lf KiB", size/1024.0);
- else if (size < 1073741824)
- strsize = g_strdup_printf("%.2lf MiB", size/1048576.0);
- else if (size < 1099511627776)
- strsize = g_strdup_printf("%.2lf GiB", size/1073741824.0);
-
- return strsize;
-}
-
-static gchar* info(gconstpointer data)
-{
- JingleFT *jft = (JingleFT *)data;
- gchar *info, *strsize = _convert_size(jft->size);
- info = g_strdup_printf("JFT: Receive %s (%s)", jft->name, strsize);
-
- g_free(strsize);
-
- return info;
-}
-
-static void jingle_ft_init(void)
-{
- jingle_register_app(NS_JINGLE_APP_FT, &funcs, JINGLE_TRANSPORT_STREAMING);
- xmpp_add_feature(NS_JINGLE_APP_FT);
- jft_cid = compl_new_category();
- if (jft_cid) {
- compl_add_category_word(jft_cid, "send");
- //compl_add_category_word(jft_cid, "request");
- compl_add_category_word(jft_cid, "info");
- compl_add_category_word(jft_cid, "flush");
- }
- /* Add command */
- cmd_add("jft", "Manage file transfer", jft_cid, 0, do_sendfile, NULL);
-}
-
-static void jingle_ft_uninit(void)
-{
- g_slist_free(info_list);
- xmpp_del_feature(NS_JINGLE_APP_FT);
- jingle_unregister_app(NS_JINGLE_APP_FT);
- cmd_del("file");
- if (jft_cid)
- compl_del_category(jft_cid);
-}
--- a/jingle-filetransfer/filetransfer.h Mon Aug 16 23:36:49 2010 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,126 +0,0 @@
-#ifndef __JINGLEFT_H__
-#define __JINGLEFT_H__ 1
-
-/**
- * \file filetransfer.h
- * \brief The main file of app-module to the file transfer XEP 234
- * \author Nicolas Cornu
- * \version 0.1
- */
-
-#define NS_JINGLE_APP_FT "urn:xmpp:jingle:apps:file-transfer:1"
-#define NS_JINGLE_APP_FT_INFO "urn:xmpp:jingle:apps:file-transfer:info:1"
-#define NS_SI_FT "http://jabber.org/protocol/si/profile/file-transfer"
-#define JINGLE_FT_SIZE_READ 2048
-
-/**
- * \enum JingleFTType
- * \brief type of the content
- */
-typedef enum {
- JINGLE_FT_OFFER,
- JINGLE_FT_REQUEST,
-} JingleFTType;
-
-/**
- * \enum JingleFTDirection
- * \brief Direction of the file
- */
-typedef enum {
- JINGLE_FT_INCOMING, /*!< We download the file */
- JINGLE_FT_OUTGOING /*!< We upload the file */
-} JingleFTDirection;
-
-/**
- * \enum JingleFTState
- * \brief state of the file
- */
-typedef enum {
- JINGLE_FT_PENDING, /*!< The file isn't yet accepted by the both parts */
- JINGLE_FT_STARTING, /*!< The file is transfering */
- JINGLE_FT_ENDING, /*!< The file has been transfered */
- JINGLE_FT_REJECT, /*!< The transfer has been refused */
- JINGLE_FT_ERROR /*!< And error occured during the transfer */
-} JingleFTState;
-
-/**
- * \struct JingleFT
- * \brief represent the file transfer himself
- */
-typedef struct {
- /**
- * the last modification of the file, optional
- */
- time_t date;
-
- /**
- * MD5 hash of the file, optional
- */
- gchar *hash;
-
- /**
- * the name of the file that the sender wishes to send
- */
- gchar *name;
-
- /**
- * the size, in bytes, of the data to be sent
- */
- guint64 size;
-
- /**
- * Data already send/receive
- */
- guint64 transmit;
-
- /**
- * descriptor to the output file
- */
- GIOChannel *outfile;
-
- /**
- * Is it an offer or a request ?
- */
- JingleFTType type;
-
- /**
- * Is it if the file is incoming or outgoing
- */
- JingleFTDirection dir;
-
- /**
- * The state of the file (PENDING, STARTING, ...)
- */
- JingleFTState state;
-
- /**
- * A little description of the transfer
- */
- gchar *desc;
-
- /**
- * Where we compute the hash
- */
- GChecksum *md5;
-
-} JingleFT;
-
-/**
- * \struct JingleFTInfo
- * \brief contain only a link to the gconstpointer
- *
- * We keep localy a list of all the files transfered to print infos
- *
- */
-typedef struct {
- /**
- * An index. Not use now, but will in the futur to cancel a transfer.
- */
- int index;
-
- /**
- * The link to the JingleFT in the session.
- */
- JingleFT *jft;
-} JingleFTInfo;
-#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jingle-ft/CMakeLists.txt Mon Aug 16 23:37:31 2010 +0200
@@ -0,0 +1,4 @@
+add_library(jingle-ft MODULE filetransfer.c)
+set_target_properties(jingle-ft PROPERTIES COMPILE_FLAGS "-Wall -O2 -g")
+include_directories(${CMAKE_SOURCE_DIR})
+install(TARGETS jingle-ft DESTINATION lib/mcabber)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jingle-ft/filetransfer.c Mon Aug 16 23:37:31 2010 +0200
@@ -0,0 +1,705 @@
+/*
+ * filetransfer.c
+ *
+ * Copyrigth (C) 2010 Nicolas Cornu <nicolas.cornu@ensi-bourges.fr>
+ *
+ * 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
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#include "config.h"
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <string.h>
+
+#include <mcabber/modules.h>
+#include <mcabber/utils.h>
+#include <mcabber/xmpp_helper.h>
+#include <mcabber/settings.h>
+#include <mcabber/logprint.h>
+#include <mcabber/compl.h>
+#include <mcabber/commands.h>
+#include <mcabber/roster.h>
+#include <mcabber/utils.h>
+
+#include <jingle/jingle.h>
+#include <jingle/check.h>
+#include <jingle/register.h>
+#include <jingle/sessions.h>
+#include <jingle/send.h>
+
+#include "filetransfer.h"
+
+
+static gconstpointer check(JingleContent *cn, GError **err);
+static gboolean handle(JingleAction action, gconstpointer data, LmMessageNode *node);
+static void tomessage(gconstpointer data, LmMessageNode *node);
+static gboolean handle_data(gconstpointer data, const gchar *data2, guint len);
+static void start(session_content *sc);
+static void send(session_content *sc);
+static void stop(gconstpointer data);
+static gchar* info(gconstpointer data);
+
+static gboolean is_md5_hash(const gchar *hash);
+
+static void jingle_ft_init(void);
+static void jingle_ft_uninit(void);
+// Return must be free
+static gchar *_convert_size(guint64 size);
+static int _next_index(void);
+static void _free(JingleFT *jft);
+
+const gchar *deps[] = { "jingle", NULL };
+
+static GSList *info_list = NULL;
+static guint jft_cid = 0;
+
+const gchar* strstate[] = {
+ "PENDING",
+ "STARTING",
+ "ENDING",
+ "REJECT",
+ "ERROR"
+};
+
+static JingleAppFuncs funcs = {
+ .check = check,
+ .handle = handle,
+ .tomessage = tomessage,
+ .handle_data = handle_data,
+ .start = start,
+ .send = send,
+ .stop = stop,
+ .info = info
+};
+
+module_info_t info_jingle_filetransfer = {
+ .branch = MCABBER_BRANCH,
+ .api = MCABBER_API_VERSION,
+ .version = PROJECT_VERSION,
+ .description = "Jingle File Transfer (XEP-0234)\n",
+ .requires = deps,
+ .init = jingle_ft_init,
+ .uninit = jingle_ft_uninit,
+ .next = NULL,
+};
+
+
+/**
+ * \fn static gconstpointer check(JingleContent *cn, GError **err)
+ * \brief Check if a node description with xmlns of JFT is correct
+ *
+ * \param cn the jinglecontent a node description
+ * \param err contain an error of the domain JINGLE_CHECK_ERROR
+ * \return a gconstpointer, which is a new allocated JingleFT
+ */
+static gconstpointer check(JingleContent *cn, GError **err)
+{
+ JingleFT *ft = NULL;
+ LmMessageNode *node;
+ gint64 tmpsize;
+ const gchar *datestr, *sizestr;
+
+ node = lm_message_node_get_child(cn->description, "offer");
+
+ if (!node) {
+ g_set_error(err, JINGLE_CHECK_ERROR, JINGLE_CHECK_ERROR_MISSING,
+ "the offer element is missing");
+ return NULL;
+ }
+
+ node = lm_message_node_get_child(node, "file");
+ if (!node) {
+ g_set_error(err, JINGLE_CHECK_ERROR, JINGLE_CHECK_ERROR_MISSING,
+ "the file element is missing");
+ return NULL;
+ }
+
+ if (g_strcmp0(lm_message_node_get_attribute(node, "xmlns"), NS_SI_FT)) {
+ g_set_error(err, JINGLE_CHECK_ERROR, JINGLE_CHECK_ERROR_MISSING,
+ "the file transfer offer has an invalid/unsupported namespace");
+ return NULL;
+ }
+
+ ft = g_new0(JingleFT, 1);
+ datestr = lm_message_node_get_attribute(node, "date");
+ ft->hash = (gchar *) lm_message_node_get_attribute(node, "hash");
+ ft->name = (gchar *) lm_message_node_get_attribute(node, "name");
+ sizestr = lm_message_node_get_attribute(node, "size");
+ ft->transmit = 0;
+ ft->dir = JINGLE_FT_INCOMING;
+
+ if (!ft->name || !sizestr) {
+ g_set_error(err, JINGLE_CHECK_ERROR, JINGLE_CHECK_ERROR_MISSING,
+ "an attribute of the file element is missing");
+ g_free(ft);
+ return NULL;
+ }
+
+ ft->date = from_iso8601(datestr, 1);
+ tmpsize = g_ascii_strtoll(sizestr, NULL, 10);
+
+ // the size attribute is a xs:integer an therefore can be negative.
+ if (tmpsize < 0) {
+ g_set_error(err, JINGLE_CHECK_ERROR, JINGLE_CHECK_ERROR_BADVALUE,
+ "the offered file has a negative size");
+ g_free(ft);
+ return NULL;
+ }
+ ft->size = tmpsize;
+
+ ft->name = g_path_get_basename(ft->name);
+
+ if (settings_opt_get("jingle_ft_dir") != NULL)
+ ft->name = g_build_filename(settings_opt_get("jingle_ft_dir"), ft->name, NULL);
+ else
+ ft->name = g_build_filename("/tmp", ft->name, NULL);
+
+ if (!g_strcmp0(ft->name, ".")) {
+ g_set_error(err, JINGLE_CHECK_ERROR, JINGLE_CHECK_ERROR_BADVALUE,
+ "the offered file has an invalid filename");
+ g_free(ft->name);
+ g_free(ft);
+ return NULL;
+ }
+
+ // check if the md5 hash is valid ([a-fA-F0-9){32})
+ if (ft->hash != NULL && (strlen(ft->hash) != 32 || !is_md5_hash(ft->hash))) {
+ g_set_error(err, JINGLE_CHECK_ERROR, JINGLE_CHECK_ERROR_BADVALUE,
+ "the offered file has an invalid hash");
+ g_free(ft->name);
+ g_free(ft);
+ return NULL;
+ }
+ ft->hash = g_strndup(ft->hash, 32);
+
+ {
+ JingleFTInfo *jfti = g_new0(JingleFTInfo, 1);
+ jfti->index = _next_index();
+ jfti->jft = ft;
+ info_list = g_slist_append(info_list, jfti);
+ }
+
+ return (gconstpointer) ft;
+}
+
+/**
+ * \fn static gboolean handle(JingleAction action, gconstpointer data, LmMessageNode *node)
+ * \brief handle a function to handle action which are not "current"
+ *
+ * \param action the action which have been received
+ * \param data contain the JingleFT of the content concerned
+ * \param node the node himself
+ * \return a gconstpointer, which is a new allocated JingleFT
+ */
+static gboolean handle(JingleAction action, gconstpointer data,
+ LmMessageNode *node)
+{
+ if (action == JINGLE_SESSION_INFO) {
+ if (!g_strcmp0(lm_message_node_get_attribute(node, "xmlns"),
+ NS_JINGLE_APP_FT_INFO)
+ && !g_strcmp0(node->name, "hash")) {
+ ((JingleFT *)data)->hash = g_strdup(lm_message_node_get_value(node));
+ return TRUE;
+ }
+ return FALSE;
+ }
+ return FALSE;
+}
+
+static gboolean is_md5_hash(const gchar *hash)
+{
+ int i = 0;
+ for (i = 0; i < 32 && hash[i]; i++)
+ if (!g_ascii_isxdigit(hash[i])) break;
+
+ if (i == 32)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+static gboolean handle_data(gconstpointer jingleft, const gchar *data, guint len)
+{
+ JingleFT *jft = (JingleFT *) jingleft;
+ GError *err = NULL;
+ GIOStatus status;
+ gsize bytes_written = 0;
+
+ if (jft->dir != JINGLE_FT_INCOMING)
+ return FALSE;
+
+ if (jft->md5 == NULL) {
+ jft->md5 = g_checksum_new(G_CHECKSUM_MD5);
+ }
+
+ g_checksum_update(jft->md5, (guchar*)data, (gsize)len);
+
+ // TODO: check if the file already exist or if it was created
+ // during the call to jingle_ft_check and handle_data
+ if (jft->outfile == NULL) {
+ jft->outfile = g_io_channel_new_file(jft->name, "w", &err);
+ if (jft->outfile == NULL || err != NULL) {
+ scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: %s %s", err->message,
+ jft->name);
+ //TODO: propagate the GError ?
+ g_error_free(err);
+ return FALSE;
+ }
+ jft->state = JINGLE_FT_STARTING;
+ status = g_io_channel_set_encoding(jft->outfile, NULL, &err);
+ if (status != G_IO_STATUS_NORMAL || err != NULL) {
+ scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: %s %s", err->message,
+ jft->name);
+ g_error_free(err);
+ return FALSE;
+ }
+ }
+
+ jft->state = JINGLE_FT_STARTING;
+
+ status = g_io_channel_write_chars(jft->outfile, data, (gssize) len,
+ &bytes_written, &err);
+ if (status != G_IO_STATUS_NORMAL || err != NULL) {
+ scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: %s %s", err->message,
+ jft->name);
+ g_error_free(err);
+ return FALSE;
+ }
+ status = g_io_channel_flush(jft->outfile, &err);
+ if (status != G_IO_STATUS_NORMAL || err != NULL) {
+ scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: %s %s", err->message,
+ jft->name);
+ g_error_free(err);
+ return FALSE;
+ }
+
+ if (bytes_written != len) {
+ // not supposed to happen if status is normal, unless outfile is non-blocking
+ return FALSE;
+ }
+
+ jft->transmit += len;
+ return TRUE;
+}
+
+
+static int _next_index(void)
+{
+ static int a = 0;
+ return a++;
+}
+
+static void do_sendfile(char *arg)
+{
+ char **args = split_arg(arg, 3, 0);
+ gchar *filename;
+ struct stat fileinfo;
+
+ if (!g_strcmp0(args[0], "send")) {
+ if (!args[1]) {
+ scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: give me a name!");
+ free_arg_lst(args);
+ return;
+ }
+
+ filename = expand_filename(args[1]); // expand ~ to HOME
+
+ if (g_stat(filename, &fileinfo) != 0) {
+ scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: unable to stat %s",
+ args[1]);
+ free_arg_lst(args);
+ return;
+ }
+
+ if (!S_ISREG(fileinfo.st_mode) && !S_ISLNK(fileinfo.st_mode)) {
+ scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: File doesn't exist!");
+ free_arg_lst(args);
+ return;
+ }
+
+ scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: Trying to send %s",
+ args[1]);
+
+ {
+ JingleSession *sess;
+ gchar *sid = jingle_generate_sid();
+ gchar *ressource, *recipientjid;
+ const gchar *namespaces[] = {NS_JINGLE, NS_JINGLE_APP_FT, NULL};
+ const gchar *myjid = g_strdup(lm_connection_get_jid(lconnection));
+ JingleFT *jft = g_new0(JingleFT, 1);
+ GError *err = NULL;
+
+ if (CURRENT_JID == NULL) { // CURRENT_JID = the jid of the user which has focus
+ scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: Please, choose a valid JID in the roster");
+ free_arg_lst(args);
+ return;
+ }
+ ressource = jingle_find_compatible_res(CURRENT_JID, namespaces);
+ if (ressource == NULL) {
+ scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: Cannot send file, because there is no ressource available");
+ free_arg_lst(args);
+ return;
+ }
+
+ recipientjid = g_strdup_printf("%s/%s", CURRENT_JID, ressource);
+
+ sess = session_new(sid, myjid, recipientjid, JINGLE_SESSION_OUTGOING);
+ session_add_content(sess, "file", JINGLE_SESSION_STATE_PENDING);
+
+ jft->desc = g_strdup(args[1]);
+ jft->type = JINGLE_FT_OFFER;
+ jft->name = g_path_get_basename(filename);
+ jft->date = fileinfo.st_mtime;
+ jft->size = fileinfo.st_size;
+ jft->transmit = 0;
+ jft->state = JINGLE_FT_PENDING;
+ jft->dir = JINGLE_FT_OUTGOING;
+ jft->outfile = g_io_channel_new_file (filename, "r", &err);
+ if (jft->outfile == NULL || err != NULL) {
+ scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: %s %s", err->message,
+ args[1]);
+ g_error_free(err);
+ free_arg_lst(args);
+ return;
+ }
+
+ g_io_channel_set_encoding(jft->outfile, NULL, &err);
+ if (jft->outfile == NULL || err != NULL) {
+ scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: %s %s", err->message,
+ args[1]);
+ g_error_free(err);
+ free_arg_lst(args);
+ return;
+ }
+
+ session_add_app(sess, "file", NS_JINGLE_APP_FT, jft);
+
+ {
+ JingleFTInfo *jfti = g_new0(JingleFTInfo, 1);
+ jfti->index = _next_index();
+ jfti->jft = jft;
+ info_list = g_slist_append(info_list, jfti);
+ }
+
+ jingle_handle_app(sess, "file", NS_JINGLE_APP_FT, jft, recipientjid);
+
+ g_free(ressource);
+ g_free(sid);
+ }
+ } else if (!g_strcmp0(args[0], "info")) {
+ GSList *el = info_list;
+
+ if (!info_list)
+ scr_LogPrint(LPRINT_LOGNORM, "JFT: no file");
+
+ for (el = info_list; el; el = el->next) {
+ JingleFTInfo *jftio = el->data;
+ gchar *strsize = _convert_size(jftio->jft->size);
+ scr_LogPrint(LPRINT_LOGNORM, "[%i]%s %s %s %.2f%%: %s %s", jftio->index,
+ (jftio->jft->dir == JINGLE_FT_INCOMING)?"<==":"-->",
+ jftio->jft->name, strsize,
+ (gfloat)jftio->jft->transmit/(gfloat)jftio->jft->size * 100,
+ jftio->jft->desc?jftio->jft->desc:"",
+ strstate[jftio->jft->state]);
+ g_free(strsize);
+ }
+ } else if (!g_strcmp0(args[0], "flush")) {
+ GSList *el, *el2 = info_list;
+ int count = 0;
+ el = info_list;
+ while (el) {
+ JingleFTInfo *jftinf;
+ jftinf = el->data;
+ if (jftinf->jft->state == JINGLE_FT_ERROR ||
+ jftinf->jft->state == JINGLE_FT_REJECT ||
+ jftinf->jft->state == JINGLE_FT_ENDING) {
+ count++;
+ _free(jftinf->jft);
+ info_list = g_slist_delete_link(info_list, el);
+ if (info_list == NULL)
+ break;
+ el = el2;
+ continue;
+ }
+ el2 = el;
+ el = el->next;
+ }
+ scr_LogPrint(LPRINT_LOGNORM, "JFT: %i file%s removed", count, (count>1) ? "s" : "");
+ } else {
+ scr_LogPrint(LPRINT_LOGNORM, "/jft: %s is not a correct option.", args[1]);
+ }
+
+ free_arg_lst(args);
+}
+
+static void _free(JingleFT *jft)
+{
+ g_free(jft->hash);
+ g_free(jft->name);
+ g_free(jft->desc);
+ g_io_channel_unref(jft->outfile);
+ if (jft->dir == JINGLE_FT_INCOMING)
+ g_checksum_free(jft->md5);
+ g_free(jft);
+}
+
+static void tomessage(gconstpointer data, LmMessageNode *node)
+{
+ JingleFT *jft = (JingleFT*) data;
+ gchar *size = NULL;
+ gchar date[19];
+
+ if (lm_message_node_get_child(node, "description") != NULL)
+ return;
+
+ LmMessageNode *node2 = lm_message_node_add_child(node, "description", NULL);
+ lm_message_node_set_attribute(node2, "xmlns", NS_JINGLE_APP_FT);
+ if (jft->type == JINGLE_FT_OFFER)
+ node2 = lm_message_node_add_child(node2, "offer", NULL);
+ else
+ node2 = lm_message_node_add_child(node2, "request", NULL);
+
+ node2 = lm_message_node_add_child(node2, "file", NULL);
+
+ size = g_strdup_printf("%" G_GUINT64_FORMAT, jft->size);
+
+ lm_message_node_set_attributes(node2, "xmlns", NS_SI_FT,
+ "name", jft->name,
+ "size", size,
+ NULL);
+ g_free(size);
+
+ if (jft->hash != NULL)
+ lm_message_node_set_attribute(node2, "hash", jft->hash);
+
+ if (jft->date)
+ if (!to_iso8601(date, jft->date))
+ lm_message_node_set_attribute(node2, "date", date);
+
+ if (jft->desc != NULL)
+ lm_message_node_add_child(node2, "desc", jft->desc);
+
+ //if (jft->data != 0)
+}
+
+static void send_hash(const gchar *sid, const gchar *to, const gchar *hash)
+{
+ JingleAckHandle *ackhandle;
+ GError *err = NULL;
+ gboolean ret;
+
+ LmMessage *r = lm_message_new_with_sub_type(to, LM_MESSAGE_TYPE_IQ,
+ LM_MESSAGE_SUB_TYPE_SET);
+ LmMessageNode *node = lm_message_get_node(r);
+ lm_message_node_add_child(node, "jingle", NULL);
+ node = lm_message_node_get_child(node, "jingle");
+ lm_message_node_set_attributes(node, "xmlns", NS_JINGLE,
+ "sid", sid,
+ "action", "session-info",
+ NULL);
+ lm_message_node_add_child(node, "hash", hash);
+ node = lm_message_node_get_child(node, "hash");
+ lm_message_node_set_attribute(node, "xmlns", NS_JINGLE_APP_FT_INFO);
+
+ ackhandle = g_new0(JingleAckHandle, 1);
+ ackhandle->callback = NULL;
+ ackhandle->user_data = NULL;
+
+ ret = lm_connection_send_with_reply(lconnection, r,
+ jingle_new_ack_handler(ackhandle), &err);
+
+ // It's not really a problem, but we must tell it!
+ if (ret == FALSE || err != NULL) {
+ scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: cannot send hash: %s",
+ err->message);
+ g_error_free(err);
+ }
+
+ lm_message_unref(r);
+}
+
+static void send(session_content *sc)
+{
+ JingleFT *jft;
+ gchar buf[JINGLE_FT_SIZE_READ];
+ gsize read;
+ GIOStatus status;
+ int count = 0;
+ GError *err = NULL;
+
+ JingleSession *sess = session_find_by_sid(sc->sid, sc->from);
+ if (sess == NULL) {
+ scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: error before transfer");
+ // We haven't LmMessage: jingle_send_iq_error(jn->message, "cancel", "item-not-found", "unknown-session");
+ return;
+ }
+
+ SessionContent *sc2 = session_find_sessioncontent(sess, sc->name);
+
+ jft = (JingleFT*)sc2->description;
+
+ if (jft->dir != JINGLE_FT_OUTGOING)
+ return;
+
+ do {
+ count++;
+ status = g_io_channel_read_chars(jft->outfile, (gchar*)buf,
+ JINGLE_FT_SIZE_READ, &read, &err);
+ } while (status == G_IO_STATUS_AGAIN && count < 10);
+
+ if (status == G_IO_STATUS_AGAIN) {
+ // TODO: something better
+ scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: file unavailable");
+ return;
+ }
+
+ if (status == G_IO_STATUS_ERROR || err != NULL) {
+ scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: %s", err->message);
+ g_error_free(err);
+ return;
+ }
+
+ if (status == G_IO_STATUS_NORMAL) {
+ jft->transmit += read;
+ g_checksum_update(jft->md5, (guchar*)buf, read);
+ // Call a handle in jingle who will call the trans
+ handle_app_data(sc->sid, sc->from, sc->name, buf, read);
+ }
+
+ if (status == G_IO_STATUS_EOF) {
+ handle_app_data(sc->sid, sc->from, sc->name, NULL, 0);
+ scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: transfer finish (%s)",
+ jft->name);
+ jft->hash = g_strdup(g_checksum_get_string(jft->md5));
+ jft->state = JINGLE_FT_ENDING;
+ // Call a function to say state is ended
+ session_changestate_sessioncontent(sess, sc2->name,
+ JINGLE_SESSION_STATE_ENDED);
+ // Send the hash
+ send_hash(sess->sid, sess->to, jft->hash);
+ g_checksum_free(jft->md5);
+
+ if (!session_remove_sessioncontent(sess, sc2->name)) {
+ jingle_send_session_terminate(sess, "success");
+ session_delete(sess);
+ }
+ }
+}
+
+static void start(session_content *sc)
+{
+ JingleFT *jft;
+
+ JingleSession *sess = session_find_by_sid(sc->sid, sc->from);
+ if (sess == NULL) {
+ scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: error before transfer");
+ return;
+ }
+
+ SessionContent *sc2 = session_find_sessioncontent(sess, sc->name);
+
+ jft = (JingleFT*)sc2->description;
+ jft->state = JINGLE_FT_STARTING;
+ jft->md5 = g_checksum_new(G_CHECKSUM_MD5);
+
+ scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: Transfer start (%s)",
+ jft->name);
+
+ sc2->appfuncs->send(sc);
+}
+
+// When we got a session-terminate
+static void stop(gconstpointer data)
+{
+ JingleFT *jft = (JingleFT*)data;
+ GError *err = NULL;
+ GIOStatus status;
+
+ jft->state = JINGLE_FT_ENDING;
+ if (jft->outfile != NULL) {
+ status = g_io_channel_shutdown(jft->outfile, TRUE, &err);
+ if (status != G_IO_STATUS_NORMAL || err != NULL) {
+ scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: %s",
+ err->message);
+ g_error_free(err);
+ }
+ }
+
+ if (jft->hash != NULL && jft->md5 != NULL) {
+ if (g_strcmp0(jft->hash, g_checksum_get_string(jft->md5))) {
+ scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: File corrupt (%s)",
+ jft->name);
+ } else {
+ scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: transfer finished (%s)"
+ " and verified", jft->name);
+ }
+ } else {
+ scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: transfer finished (%s)"
+ " but not verified", jft->name);
+ }
+}
+
+static gchar *_convert_size(guint64 size)
+{
+ gchar *strsize;
+
+ if (size < 1024)
+ strsize = g_strdup_printf("%" G_GUINT64_FORMAT " B", size);
+ else if (size < 1048576)
+ strsize = g_strdup_printf("%.2lf KiB", size/1024.0);
+ else if (size < 1073741824)
+ strsize = g_strdup_printf("%.2lf MiB", size/1048576.0);
+ else if (size < 1099511627776)
+ strsize = g_strdup_printf("%.2lf GiB", size/1073741824.0);
+
+ return strsize;
+}
+
+static gchar* info(gconstpointer data)
+{
+ JingleFT *jft = (JingleFT *)data;
+ gchar *info, *strsize = _convert_size(jft->size);
+ info = g_strdup_printf("JFT: Receive %s (%s)", jft->name, strsize);
+
+ g_free(strsize);
+
+ return info;
+}
+
+static void jingle_ft_init(void)
+{
+ jingle_register_app(NS_JINGLE_APP_FT, &funcs, JINGLE_TRANSPORT_STREAMING);
+ xmpp_add_feature(NS_JINGLE_APP_FT);
+ jft_cid = compl_new_category();
+ if (jft_cid) {
+ compl_add_category_word(jft_cid, "send");
+ //compl_add_category_word(jft_cid, "request");
+ compl_add_category_word(jft_cid, "info");
+ compl_add_category_word(jft_cid, "flush");
+ }
+ /* Add command */
+ cmd_add("jft", "Manage file transfer", jft_cid, 0, do_sendfile, NULL);
+}
+
+static void jingle_ft_uninit(void)
+{
+ g_slist_free(info_list);
+ xmpp_del_feature(NS_JINGLE_APP_FT);
+ jingle_unregister_app(NS_JINGLE_APP_FT);
+ cmd_del("file");
+ if (jft_cid)
+ compl_del_category(jft_cid);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jingle-ft/filetransfer.h Mon Aug 16 23:37:31 2010 +0200
@@ -0,0 +1,126 @@
+#ifndef __JINGLEFT_H__
+#define __JINGLEFT_H__ 1
+
+/**
+ * \file filetransfer.h
+ * \brief The main file of app-module to the file transfer XEP 234
+ * \author Nicolas Cornu
+ * \version 0.1
+ */
+
+#define NS_JINGLE_APP_FT "urn:xmpp:jingle:apps:file-transfer:1"
+#define NS_JINGLE_APP_FT_INFO "urn:xmpp:jingle:apps:file-transfer:info:1"
+#define NS_SI_FT "http://jabber.org/protocol/si/profile/file-transfer"
+#define JINGLE_FT_SIZE_READ 2048
+
+/**
+ * \enum JingleFTType
+ * \brief type of the content
+ */
+typedef enum {
+ JINGLE_FT_OFFER,
+ JINGLE_FT_REQUEST,
+} JingleFTType;
+
+/**
+ * \enum JingleFTDirection
+ * \brief Direction of the file
+ */
+typedef enum {
+ JINGLE_FT_INCOMING, /*!< We download the file */
+ JINGLE_FT_OUTGOING /*!< We upload the file */
+} JingleFTDirection;
+
+/**
+ * \enum JingleFTState
+ * \brief state of the file
+ */
+typedef enum {
+ JINGLE_FT_PENDING, /*!< The file isn't yet accepted by the both parts */
+ JINGLE_FT_STARTING, /*!< The file is transfering */
+ JINGLE_FT_ENDING, /*!< The file has been transfered */
+ JINGLE_FT_REJECT, /*!< The transfer has been refused */
+ JINGLE_FT_ERROR /*!< And error occured during the transfer */
+} JingleFTState;
+
+/**
+ * \struct JingleFT
+ * \brief represent the file transfer himself
+ */
+typedef struct {
+ /**
+ * the last modification of the file, optional
+ */
+ time_t date;
+
+ /**
+ * MD5 hash of the file, optional
+ */
+ gchar *hash;
+
+ /**
+ * the name of the file that the sender wishes to send
+ */
+ gchar *name;
+
+ /**
+ * the size, in bytes, of the data to be sent
+ */
+ guint64 size;
+
+ /**
+ * Data already send/receive
+ */
+ guint64 transmit;
+
+ /**
+ * descriptor to the output file
+ */
+ GIOChannel *outfile;
+
+ /**
+ * Is it an offer or a request ?
+ */
+ JingleFTType type;
+
+ /**
+ * Is it if the file is incoming or outgoing
+ */
+ JingleFTDirection dir;
+
+ /**
+ * The state of the file (PENDING, STARTING, ...)
+ */
+ JingleFTState state;
+
+ /**
+ * A little description of the transfer
+ */
+ gchar *desc;
+
+ /**
+ * Where we compute the hash
+ */
+ GChecksum *md5;
+
+} JingleFT;
+
+/**
+ * \struct JingleFTInfo
+ * \brief contain only a link to the gconstpointer
+ *
+ * We keep localy a list of all the files transfered to print infos
+ *
+ */
+typedef struct {
+ /**
+ * An index. Not use now, but will in the futur to cancel a transfer.
+ */
+ int index;
+
+ /**
+ * The link to the JingleFT in the session.
+ */
+ JingleFT *jft;
+} JingleFTInfo;
+#endif
--- a/jingle-ibb/CMakeLists.txt Mon Aug 16 23:36:49 2010 +0200
+++ b/jingle-ibb/CMakeLists.txt Mon Aug 16 23:37:31 2010 +0200
@@ -1,4 +1,4 @@
-add_library(jingle-inbandbytestream MODULE ibb.c ibb.h)
-set_target_properties(jingle-inbandbytestream PROPERTIES COMPILE_FLAGS "-Wall -O0 -g")
+add_library(jingle-ibb MODULE ibb.c ibb.h)
+set_target_properties(jingle-ibb PROPERTIES COMPILE_FLAGS "-Wall -O2 -g")
include_directories(${CMAKE_SOURCE_DIR})
-install(TARGETS jingle-inbandbytestream DESTINATION lib/mcabber)
+install(TARGETS jingle-ibb DESTINATION lib/mcabber)
--- a/jingle-socks5/CMakeLists.txt Mon Aug 16 23:36:49 2010 +0200
+++ b/jingle-socks5/CMakeLists.txt Mon Aug 16 23:37:31 2010 +0200
@@ -1,4 +1,4 @@
add_library(jingle-socks5 MODULE socks5.c socks5.h)
-set_target_properties(jingle-socks5 PROPERTIES COMPILE_FLAGS "-Wall -O0 -g")
+set_target_properties(jingle-socks5 PROPERTIES COMPILE_FLAGS "-Wall -O2 -g")
include_directories(${CMAKE_SOURCE_DIR})
install(TARGETS jingle-socks5 DESTINATION lib/mcabber)
--- a/jingle/CMakeLists.txt Mon Aug 16 23:36:49 2010 +0200
+++ b/jingle/CMakeLists.txt Mon Aug 16 23:37:31 2010 +0200
@@ -1,5 +1,5 @@
add_library(jingle MODULE jingle.c jingle.h check.c check.h action-handlers.c action-handlers.c register.c register.h sessions.c sessions.h send.c send.h)
-set_target_properties(jingle PROPERTIES COMPILE_FLAGS "-O0 -g")
+set_target_properties(jingle PROPERTIES COMPILE_FLAGS "-Wall -O2 -g")
include_directories(${LM_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR})
target_link_libraries(jingle ${LM_LIBRARIES})
install(TARGETS jingle DESTINATION lib/mcabber)
--- a/jingle/check.c Mon Aug 16 23:36:49 2010 +0200
+++ b/jingle/check.c Mon Aug 16 23:37:31 2010 +0200
@@ -104,7 +104,6 @@
JingleContent *cn = g_new0(JingleContent, 1);
const gchar *creatorstr, *sendersstr;
gint tmp, tmp2;
- LmMessageNode *tmpnode = NULL;
creatorstr = lm_message_node_get_attribute(node, "creator");
cn->disposition = lm_message_node_get_attribute(node, "disposition");
--- a/jingle/jingle.c Mon Aug 16 23:36:49 2010 +0200
+++ b/jingle/jingle.c Mon Aug 16 23:37:31 2010 +0200
@@ -161,9 +161,8 @@
{
GSList *el, *prev;
time_t now = time(NULL);
- JingleAckHandle *ah;
- el = ack_handlers;
+ el = prev = ack_handlers;
while(el) {
JingleAckHandle *ah = el->data;