Rename some stuff, create a README file, enable warnings in ./jingle
authorNicolas Cornu <nicolas.cornu@ensi-bourges.fr>
Mon, 16 Aug 2010 23:37:31 +0200
changeset 139 459b2503c1a3
parent 138 dde8eaf7ff2c
child 140 bbc50e69f5ad
Rename some stuff, create a README file, enable warnings in ./jingle
CMakeLists.txt
README
jingle-filetransfer/CMakeLists.txt
jingle-filetransfer/filetransfer.c
jingle-filetransfer/filetransfer.h
jingle-ft/CMakeLists.txt
jingle-ft/filetransfer.c
jingle-ft/filetransfer.h
jingle-ibb/CMakeLists.txt
jingle-socks5/CMakeLists.txt
jingle/CMakeLists.txt
jingle/check.c
jingle/jingle.c
--- 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;