jingle-ft/filetransfer.c
changeset 139 459b2503c1a3
child 140 bbc50e69f5ad
equal deleted inserted replaced
138:dde8eaf7ff2c 139:459b2503c1a3
       
     1 /*
       
     2  * filetransfer.c
       
     3  *
       
     4  * Copyrigth (C) 2010 Nicolas Cornu <nicolas.cornu@ensi-bourges.fr>
       
     5  *
       
     6  * This program is free software; you can redistribute it and/or modify
       
     7  * it under the terms of the GNU General Public License as published by
       
     8  * the Free Software Foundation; either version 2 of the License, or (at
       
     9  * your option) any later version.
       
    10  *
       
    11  * This program is distributed in the hope that it will be useful, but
       
    12  * WITHOUT ANY WARRANTY; without even the implied warranty of
       
    13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    14  * General Public License for more details.
       
    15  *
       
    16  * You should have received a copy of the GNU General Public License
       
    17  * along with this program; if not, write to the Free Software
       
    18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
       
    19  * USA
       
    20  */
       
    21 #include "config.h"
       
    22 
       
    23 #include <glib.h>
       
    24 #include <glib/gstdio.h>
       
    25 #include <string.h>
       
    26 
       
    27 #include <mcabber/modules.h>
       
    28 #include <mcabber/utils.h>
       
    29 #include <mcabber/xmpp_helper.h>
       
    30 #include <mcabber/settings.h>
       
    31 #include <mcabber/logprint.h>
       
    32 #include <mcabber/compl.h>
       
    33 #include <mcabber/commands.h>
       
    34 #include <mcabber/roster.h>
       
    35 #include <mcabber/utils.h>
       
    36 
       
    37 #include <jingle/jingle.h>
       
    38 #include <jingle/check.h>
       
    39 #include <jingle/register.h>
       
    40 #include <jingle/sessions.h>
       
    41 #include <jingle/send.h>
       
    42 
       
    43 #include "filetransfer.h"
       
    44 
       
    45 
       
    46 static gconstpointer check(JingleContent *cn, GError **err);
       
    47 static gboolean handle(JingleAction action, gconstpointer data, LmMessageNode *node);
       
    48 static void tomessage(gconstpointer data, LmMessageNode *node);
       
    49 static gboolean handle_data(gconstpointer data, const gchar *data2, guint len);
       
    50 static void start(session_content *sc);
       
    51 static void send(session_content *sc);
       
    52 static void stop(gconstpointer data);
       
    53 static gchar* info(gconstpointer data);
       
    54 
       
    55 static gboolean is_md5_hash(const gchar *hash);
       
    56 
       
    57 static void jingle_ft_init(void);
       
    58 static void jingle_ft_uninit(void);
       
    59 // Return must be free
       
    60 static gchar *_convert_size(guint64 size);
       
    61 static int _next_index(void);
       
    62 static void _free(JingleFT *jft);
       
    63 
       
    64 const gchar *deps[] = { "jingle", NULL };
       
    65 
       
    66 static GSList *info_list = NULL;
       
    67 static guint jft_cid = 0;
       
    68 
       
    69 const gchar* strstate[] = {
       
    70   "PENDING",
       
    71   "STARTING",
       
    72   "ENDING",
       
    73   "REJECT",
       
    74   "ERROR"
       
    75 };
       
    76 
       
    77 static JingleAppFuncs funcs = {
       
    78   .check        = check,
       
    79   .handle       = handle,
       
    80   .tomessage    = tomessage,
       
    81   .handle_data  = handle_data,
       
    82   .start        = start,
       
    83   .send         = send,
       
    84   .stop         = stop,
       
    85   .info         = info
       
    86 };
       
    87 
       
    88 module_info_t info_jingle_filetransfer = {
       
    89   .branch          = MCABBER_BRANCH,
       
    90   .api             = MCABBER_API_VERSION,
       
    91   .version         = PROJECT_VERSION,
       
    92   .description     = "Jingle File Transfer (XEP-0234)\n",
       
    93   .requires        = deps,
       
    94   .init            = jingle_ft_init,
       
    95   .uninit          = jingle_ft_uninit,
       
    96   .next            = NULL,
       
    97 };
       
    98 
       
    99 
       
   100 /**
       
   101  * \fn static gconstpointer check(JingleContent *cn, GError **err)
       
   102  * \brief Check if a node description with xmlns of JFT is correct 
       
   103  *
       
   104  * \param cn the jinglecontent a node description
       
   105  * \param err contain an error of the domain JINGLE_CHECK_ERROR
       
   106  * \return a gconstpointer, which is a new allocated JingleFT
       
   107  */
       
   108 static gconstpointer check(JingleContent *cn, GError **err)
       
   109 {
       
   110   JingleFT *ft = NULL;
       
   111   LmMessageNode *node;
       
   112   gint64 tmpsize;
       
   113   const gchar *datestr, *sizestr;
       
   114 
       
   115   node = lm_message_node_get_child(cn->description, "offer");
       
   116  
       
   117   if (!node) {
       
   118     g_set_error(err, JINGLE_CHECK_ERROR, JINGLE_CHECK_ERROR_MISSING,
       
   119                 "the offer element is missing");
       
   120     return NULL;
       
   121   }
       
   122 
       
   123   node = lm_message_node_get_child(node, "file");
       
   124   if (!node) {
       
   125     g_set_error(err, JINGLE_CHECK_ERROR, JINGLE_CHECK_ERROR_MISSING,
       
   126                 "the file element is missing");
       
   127     return NULL;
       
   128   }
       
   129 
       
   130   if (g_strcmp0(lm_message_node_get_attribute(node, "xmlns"), NS_SI_FT)) {
       
   131     g_set_error(err, JINGLE_CHECK_ERROR, JINGLE_CHECK_ERROR_MISSING,
       
   132                 "the file transfer offer has an invalid/unsupported namespace");
       
   133     return NULL;
       
   134   }
       
   135 
       
   136   ft = g_new0(JingleFT, 1);
       
   137   datestr  = lm_message_node_get_attribute(node, "date");
       
   138   ft->hash = (gchar *) lm_message_node_get_attribute(node, "hash");
       
   139   ft->name = (gchar *) lm_message_node_get_attribute(node, "name");
       
   140   sizestr  = lm_message_node_get_attribute(node, "size");
       
   141   ft->transmit = 0;
       
   142   ft->dir = JINGLE_FT_INCOMING;
       
   143   
       
   144   if (!ft->name || !sizestr) {
       
   145     g_set_error(err, JINGLE_CHECK_ERROR, JINGLE_CHECK_ERROR_MISSING,
       
   146                 "an attribute of the file element is missing");
       
   147     g_free(ft);
       
   148     return NULL;
       
   149   }
       
   150 
       
   151   ft->date = from_iso8601(datestr, 1);
       
   152   tmpsize = g_ascii_strtoll(sizestr, NULL, 10);
       
   153 
       
   154   // the size attribute is a xs:integer an therefore can be negative.
       
   155   if (tmpsize < 0) {
       
   156     g_set_error(err, JINGLE_CHECK_ERROR, JINGLE_CHECK_ERROR_BADVALUE,
       
   157                 "the offered file has a negative size");
       
   158     g_free(ft);
       
   159     return NULL;
       
   160   }
       
   161   ft->size = tmpsize;
       
   162 
       
   163   ft->name = g_path_get_basename(ft->name);
       
   164   
       
   165   if (settings_opt_get("jingle_ft_dir") != NULL)
       
   166     ft->name = g_build_filename(settings_opt_get("jingle_ft_dir"), ft->name, NULL);
       
   167   else
       
   168     ft->name = g_build_filename("/tmp", ft->name, NULL);
       
   169 
       
   170   if (!g_strcmp0(ft->name, ".")) {
       
   171     g_set_error(err, JINGLE_CHECK_ERROR, JINGLE_CHECK_ERROR_BADVALUE,
       
   172                 "the offered file has an invalid filename");
       
   173     g_free(ft->name);
       
   174     g_free(ft);
       
   175     return NULL;
       
   176   }
       
   177 
       
   178   // check if the md5 hash is valid ([a-fA-F0-9){32})
       
   179   if (ft->hash != NULL && (strlen(ft->hash) != 32 || !is_md5_hash(ft->hash))) {
       
   180     g_set_error(err, JINGLE_CHECK_ERROR, JINGLE_CHECK_ERROR_BADVALUE,
       
   181                 "the offered file has an invalid hash");
       
   182     g_free(ft->name);
       
   183     g_free(ft);
       
   184     return NULL;
       
   185   }
       
   186   ft->hash = g_strndup(ft->hash, 32);
       
   187 
       
   188   {
       
   189     JingleFTInfo *jfti = g_new0(JingleFTInfo, 1);
       
   190     jfti->index = _next_index();
       
   191     jfti->jft = ft;
       
   192     info_list = g_slist_append(info_list, jfti);
       
   193   }
       
   194 
       
   195   return (gconstpointer) ft;
       
   196 }
       
   197 
       
   198 /**
       
   199  * \fn static gboolean handle(JingleAction action, gconstpointer data, LmMessageNode *node)
       
   200  * \brief handle a function to handle action which are not "current"
       
   201  *
       
   202  * \param action the action which have been received
       
   203  * \param data contain the JingleFT of the content concerned
       
   204  * \param node the node himself
       
   205  * \return a gconstpointer, which is a new allocated JingleFT
       
   206  */
       
   207 static gboolean handle(JingleAction action, gconstpointer data,
       
   208                           LmMessageNode *node)
       
   209 {
       
   210   if (action == JINGLE_SESSION_INFO) {
       
   211     if (!g_strcmp0(lm_message_node_get_attribute(node, "xmlns"),
       
   212                    NS_JINGLE_APP_FT_INFO)
       
   213         && !g_strcmp0(node->name, "hash")) {
       
   214       ((JingleFT *)data)->hash = g_strdup(lm_message_node_get_value(node));
       
   215       return TRUE;
       
   216 	}
       
   217 	return FALSE;
       
   218   }
       
   219   return FALSE;
       
   220 }
       
   221 
       
   222 static gboolean is_md5_hash(const gchar *hash)
       
   223 {
       
   224   int i = 0;
       
   225   for (i = 0; i < 32 && hash[i]; i++)
       
   226     if (!g_ascii_isxdigit(hash[i])) break;
       
   227 
       
   228   if (i == 32)
       
   229     return TRUE;
       
   230   else
       
   231     return FALSE;
       
   232 }
       
   233 
       
   234 static gboolean handle_data(gconstpointer jingleft, const gchar *data, guint len)
       
   235 {
       
   236   JingleFT *jft = (JingleFT *) jingleft;
       
   237   GError *err = NULL;
       
   238   GIOStatus status;
       
   239   gsize bytes_written = 0;
       
   240 
       
   241   if (jft->dir != JINGLE_FT_INCOMING)
       
   242     return FALSE;
       
   243 
       
   244   if (jft->md5 == NULL) {
       
   245     jft->md5 = g_checksum_new(G_CHECKSUM_MD5);
       
   246   }
       
   247   
       
   248   g_checksum_update(jft->md5, (guchar*)data, (gsize)len);
       
   249     
       
   250   // TODO: check if the file already exist or if it was created
       
   251   // during the call to jingle_ft_check and handle_data
       
   252   if (jft->outfile == NULL) {
       
   253     jft->outfile = g_io_channel_new_file(jft->name, "w", &err);
       
   254     if (jft->outfile == NULL || err != NULL) {
       
   255       scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: %s %s", err->message,
       
   256                    jft->name);
       
   257     //TODO: propagate the GError ?
       
   258       g_error_free(err);
       
   259       return FALSE;
       
   260 	}
       
   261 	jft->state = JINGLE_FT_STARTING;
       
   262 	status = g_io_channel_set_encoding(jft->outfile, NULL, &err);
       
   263 	if (status != G_IO_STATUS_NORMAL || err != NULL) {
       
   264      scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: %s %s", err->message,
       
   265                   jft->name);
       
   266      g_error_free(err);
       
   267      return FALSE;
       
   268    }
       
   269   }
       
   270   
       
   271   jft->state = JINGLE_FT_STARTING;
       
   272 	
       
   273   status = g_io_channel_write_chars(jft->outfile, data, (gssize) len,
       
   274                                     &bytes_written, &err);
       
   275   if (status != G_IO_STATUS_NORMAL || err != NULL) {
       
   276      scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: %s %s", err->message,
       
   277                   jft->name);
       
   278     g_error_free(err);
       
   279     return FALSE;
       
   280   }
       
   281   status = g_io_channel_flush(jft->outfile, &err);
       
   282   if (status != G_IO_STATUS_NORMAL || err != NULL) {
       
   283     scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: %s %s", err->message,
       
   284                  jft->name);
       
   285     g_error_free(err);
       
   286     return FALSE;
       
   287   }
       
   288 
       
   289   if (bytes_written != len) {
       
   290     // not supposed to happen if status is normal, unless outfile is non-blocking
       
   291     return FALSE;
       
   292   }
       
   293   
       
   294   jft->transmit += len;
       
   295   return TRUE;
       
   296 }
       
   297 
       
   298 
       
   299 static int _next_index(void)
       
   300 {
       
   301   static int a = 0;
       
   302   return a++;
       
   303 }
       
   304 
       
   305 static void do_sendfile(char *arg)
       
   306 {
       
   307   char **args = split_arg(arg, 3, 0);
       
   308   gchar *filename;
       
   309   struct stat fileinfo;
       
   310 
       
   311   if (!g_strcmp0(args[0], "send")) {
       
   312 	  if (!args[1]) {
       
   313 		 scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: give me a name!");
       
   314 		 free_arg_lst(args);
       
   315 		 return;
       
   316 	  }
       
   317 	  
       
   318 	  filename = expand_filename(args[1]); // expand ~ to HOME
       
   319 	  
       
   320 	  if (g_stat(filename, &fileinfo) != 0) {
       
   321 		 scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: unable to stat %s",
       
   322 		              args[1]);
       
   323 		 free_arg_lst(args);
       
   324 		 return;
       
   325 	  }
       
   326 	  
       
   327 	  if (!S_ISREG(fileinfo.st_mode) && !S_ISLNK(fileinfo.st_mode)) {
       
   328 		 scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: File doesn't exist!");
       
   329 		 free_arg_lst(args);
       
   330 		 return;
       
   331 	  }
       
   332 	  
       
   333 	  scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: Trying to send %s",
       
   334 		            args[1]);
       
   335 
       
   336 	  {
       
   337 		 JingleSession *sess;
       
   338 		 gchar *sid = jingle_generate_sid();
       
   339 		 gchar *ressource, *recipientjid;
       
   340 		 const gchar *namespaces[] = {NS_JINGLE, NS_JINGLE_APP_FT, NULL};
       
   341 		 const gchar *myjid = g_strdup(lm_connection_get_jid(lconnection));
       
   342 		 JingleFT *jft = g_new0(JingleFT, 1);
       
   343 		 GError *err = NULL;
       
   344 		 
       
   345 		 if (CURRENT_JID == NULL) { // CURRENT_JID = the jid of the user which has focus
       
   346 		   scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: Please, choose a valid JID in the roster");
       
   347 		   free_arg_lst(args);
       
   348 		   return;
       
   349 		 }
       
   350 		 ressource = jingle_find_compatible_res(CURRENT_JID, namespaces);
       
   351 		 if (ressource == NULL) {
       
   352 		   scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: Cannot send file, because there is no ressource available");
       
   353 		   free_arg_lst(args);
       
   354 		   return;
       
   355 		 }
       
   356 		 
       
   357 		 recipientjid = g_strdup_printf("%s/%s", CURRENT_JID, ressource);
       
   358 
       
   359 		 sess = session_new(sid, myjid, recipientjid, JINGLE_SESSION_OUTGOING);
       
   360 		 session_add_content(sess, "file", JINGLE_SESSION_STATE_PENDING);
       
   361 
       
   362 		 jft->desc = g_strdup(args[1]);
       
   363 		 jft->type = JINGLE_FT_OFFER;
       
   364 		 jft->name = g_path_get_basename(filename);
       
   365 		 jft->date = fileinfo.st_mtime;
       
   366 		 jft->size = fileinfo.st_size;
       
   367 		 jft->transmit = 0;
       
   368 		 jft->state = JINGLE_FT_PENDING;
       
   369 		 jft->dir = JINGLE_FT_OUTGOING;
       
   370 		 jft->outfile = g_io_channel_new_file (filename, "r", &err);
       
   371 		 if (jft->outfile == NULL || err != NULL) {
       
   372 		   scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: %s %s", err->message,
       
   373 		                args[1]);
       
   374 		   g_error_free(err);
       
   375 		   free_arg_lst(args);
       
   376 		   return;
       
   377 		 }
       
   378 		 
       
   379 		 g_io_channel_set_encoding(jft->outfile, NULL, &err);
       
   380 		 if (jft->outfile == NULL || err != NULL) {
       
   381 		   scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: %s %s", err->message,
       
   382 		                args[1]);
       
   383 		   g_error_free(err);
       
   384 		   free_arg_lst(args);
       
   385 		   return;
       
   386 		 }
       
   387 		 
       
   388 		 session_add_app(sess, "file", NS_JINGLE_APP_FT, jft);
       
   389 
       
   390 		 {
       
   391 		   JingleFTInfo *jfti = g_new0(JingleFTInfo, 1);
       
   392 		   jfti->index = _next_index();
       
   393 		   jfti->jft = jft;
       
   394 		   info_list = g_slist_append(info_list, jfti);
       
   395 		 }
       
   396 		 
       
   397 		 jingle_handle_app(sess, "file", NS_JINGLE_APP_FT, jft, recipientjid);
       
   398 
       
   399 		 g_free(ressource);
       
   400 		 g_free(sid);
       
   401 	  }
       
   402   } else if (!g_strcmp0(args[0], "info")) {
       
   403     GSList *el = info_list;
       
   404     
       
   405     if (!info_list)
       
   406       scr_LogPrint(LPRINT_LOGNORM, "JFT: no file");
       
   407 
       
   408     for (el = info_list; el; el = el->next) {
       
   409       JingleFTInfo *jftio = el->data;
       
   410       gchar *strsize = _convert_size(jftio->jft->size);
       
   411       scr_LogPrint(LPRINT_LOGNORM, "[%i]%s %s %s %.2f%%: %s %s", jftio->index, 
       
   412                    (jftio->jft->dir == JINGLE_FT_INCOMING)?"<==":"-->",
       
   413                    jftio->jft->name, strsize,
       
   414                    (gfloat)jftio->jft->transmit/(gfloat)jftio->jft->size * 100,
       
   415                    jftio->jft->desc?jftio->jft->desc:"",
       
   416                    strstate[jftio->jft->state]);
       
   417       g_free(strsize);
       
   418     }
       
   419   } else if (!g_strcmp0(args[0], "flush")) {
       
   420     GSList *el, *el2 = info_list;
       
   421     int count = 0;
       
   422     el = info_list;
       
   423     while (el) {
       
   424       JingleFTInfo *jftinf;
       
   425       jftinf = el->data;
       
   426       if (jftinf->jft->state == JINGLE_FT_ERROR ||
       
   427           jftinf->jft->state == JINGLE_FT_REJECT ||
       
   428           jftinf->jft->state == JINGLE_FT_ENDING) {
       
   429         count++;
       
   430         _free(jftinf->jft);
       
   431         info_list = g_slist_delete_link(info_list, el);
       
   432         if (info_list == NULL)
       
   433           break;
       
   434         el = el2;
       
   435         continue;
       
   436       }
       
   437       el2 = el;
       
   438       el = el->next;
       
   439     }
       
   440     scr_LogPrint(LPRINT_LOGNORM, "JFT: %i file%s removed", count, (count>1) ? "s" : "");
       
   441   } else {
       
   442     scr_LogPrint(LPRINT_LOGNORM, "/jft: %s is not a correct option.", args[1]);
       
   443   }
       
   444   
       
   445   free_arg_lst(args);
       
   446 }
       
   447 
       
   448 static void _free(JingleFT *jft)
       
   449 {
       
   450   g_free(jft->hash);
       
   451   g_free(jft->name);
       
   452   g_free(jft->desc);
       
   453   g_io_channel_unref(jft->outfile);
       
   454   if (jft->dir == JINGLE_FT_INCOMING)
       
   455     g_checksum_free(jft->md5);
       
   456   g_free(jft);
       
   457 }
       
   458 
       
   459 static void tomessage(gconstpointer data, LmMessageNode *node)
       
   460 {
       
   461   JingleFT *jft = (JingleFT*) data;
       
   462   gchar *size = NULL;
       
   463   gchar date[19];
       
   464   
       
   465   if (lm_message_node_get_child(node, "description") != NULL)
       
   466     return;
       
   467 
       
   468   LmMessageNode *node2 = lm_message_node_add_child(node, "description", NULL);
       
   469   lm_message_node_set_attribute(node2, "xmlns", NS_JINGLE_APP_FT);
       
   470   if (jft->type == JINGLE_FT_OFFER)
       
   471     node2 = lm_message_node_add_child(node2, "offer", NULL);
       
   472   else
       
   473     node2 = lm_message_node_add_child(node2, "request", NULL);
       
   474 
       
   475   node2 = lm_message_node_add_child(node2, "file", NULL);
       
   476 
       
   477   size = g_strdup_printf("%" G_GUINT64_FORMAT, jft->size);
       
   478   
       
   479   lm_message_node_set_attributes(node2, "xmlns", NS_SI_FT,
       
   480                                  "name", jft->name,
       
   481                                  "size", size,
       
   482                                  NULL);
       
   483   g_free(size);
       
   484   
       
   485   if (jft->hash != NULL)
       
   486     lm_message_node_set_attribute(node2, "hash", jft->hash);
       
   487 
       
   488   if (jft->date)
       
   489     if (!to_iso8601(date, jft->date))
       
   490       lm_message_node_set_attribute(node2, "date", date);
       
   491 
       
   492   if (jft->desc != NULL)
       
   493     lm_message_node_add_child(node2, "desc", jft->desc);
       
   494 
       
   495   //if (jft->data != 0)
       
   496 }
       
   497 
       
   498 static void send_hash(const gchar *sid, const gchar *to, const gchar *hash)
       
   499 {
       
   500   JingleAckHandle *ackhandle;
       
   501   GError *err = NULL;
       
   502   gboolean ret;
       
   503   
       
   504   LmMessage *r = lm_message_new_with_sub_type(to, LM_MESSAGE_TYPE_IQ,
       
   505                                               LM_MESSAGE_SUB_TYPE_SET);
       
   506   LmMessageNode *node = lm_message_get_node(r);
       
   507   lm_message_node_add_child(node, "jingle", NULL);
       
   508   node = lm_message_node_get_child(node, "jingle");
       
   509   lm_message_node_set_attributes(node, "xmlns", NS_JINGLE,
       
   510                                  "sid", sid,
       
   511                                  "action", "session-info",
       
   512                                  NULL);
       
   513   lm_message_node_add_child(node, "hash", hash);
       
   514   node = lm_message_node_get_child(node, "hash");
       
   515   lm_message_node_set_attribute(node, "xmlns", NS_JINGLE_APP_FT_INFO);
       
   516   
       
   517   ackhandle = g_new0(JingleAckHandle, 1);
       
   518   ackhandle->callback = NULL;
       
   519   ackhandle->user_data = NULL;
       
   520   
       
   521   ret = lm_connection_send_with_reply(lconnection, r,
       
   522                                       jingle_new_ack_handler(ackhandle), &err);
       
   523   
       
   524   // It's not really a problem, but we must tell it!
       
   525   if (ret == FALSE || err != NULL) {
       
   526     scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: cannot send hash: %s",
       
   527                  err->message);
       
   528     g_error_free(err);
       
   529   }
       
   530   
       
   531   lm_message_unref(r);
       
   532 }
       
   533 
       
   534 static void send(session_content *sc)
       
   535 {
       
   536   JingleFT *jft;
       
   537   gchar buf[JINGLE_FT_SIZE_READ];
       
   538   gsize read;
       
   539   GIOStatus status;
       
   540   int count = 0;
       
   541   GError *err = NULL;
       
   542 
       
   543   JingleSession *sess = session_find_by_sid(sc->sid, sc->from);
       
   544   if (sess == NULL) {
       
   545     scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: error before transfer");
       
   546     // We haven't LmMessage: jingle_send_iq_error(jn->message, "cancel", "item-not-found", "unknown-session");
       
   547     return;
       
   548   }
       
   549 
       
   550   SessionContent *sc2 = session_find_sessioncontent(sess, sc->name);
       
   551 
       
   552   jft = (JingleFT*)sc2->description;
       
   553 
       
   554   if (jft->dir != JINGLE_FT_OUTGOING)
       
   555     return;
       
   556 
       
   557   do {
       
   558     count++;
       
   559     status = g_io_channel_read_chars(jft->outfile, (gchar*)buf,
       
   560                                      JINGLE_FT_SIZE_READ, &read, &err);
       
   561   } while (status == G_IO_STATUS_AGAIN && count < 10);
       
   562 
       
   563   if (status == G_IO_STATUS_AGAIN) {
       
   564     // TODO: something better
       
   565     scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: file unavailable");
       
   566     return;
       
   567   }
       
   568 
       
   569   if (status == G_IO_STATUS_ERROR || err != NULL) {
       
   570     scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: %s", err->message);
       
   571     g_error_free(err);
       
   572     return;
       
   573   }
       
   574   
       
   575   if (status == G_IO_STATUS_NORMAL) {
       
   576     jft->transmit += read;
       
   577     g_checksum_update(jft->md5, (guchar*)buf, read);
       
   578     // Call a handle in jingle who will call the trans
       
   579     handle_app_data(sc->sid, sc->from, sc->name, buf, read);
       
   580   }
       
   581   
       
   582   if (status == G_IO_STATUS_EOF) {
       
   583     handle_app_data(sc->sid, sc->from, sc->name, NULL, 0);
       
   584     scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: transfer finish (%s)",
       
   585                  jft->name);
       
   586     jft->hash = g_strdup(g_checksum_get_string(jft->md5));
       
   587     jft->state = JINGLE_FT_ENDING;
       
   588     // Call a function to say state is ended
       
   589     session_changestate_sessioncontent(sess, sc2->name, 
       
   590                                        JINGLE_SESSION_STATE_ENDED);
       
   591     // Send the hash
       
   592     send_hash(sess->sid, sess->to, jft->hash);
       
   593     g_checksum_free(jft->md5);
       
   594     
       
   595     if (!session_remove_sessioncontent(sess, sc2->name)) {
       
   596       jingle_send_session_terminate(sess, "success");
       
   597       session_delete(sess);
       
   598     }
       
   599   }
       
   600 }
       
   601 
       
   602 static void start(session_content *sc)
       
   603 {
       
   604   JingleFT *jft;
       
   605   
       
   606   JingleSession *sess = session_find_by_sid(sc->sid, sc->from);
       
   607   if (sess == NULL) {
       
   608     scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: error before transfer");
       
   609     return;
       
   610   }
       
   611   
       
   612   SessionContent *sc2 = session_find_sessioncontent(sess, sc->name);
       
   613 
       
   614   jft = (JingleFT*)sc2->description;
       
   615   jft->state = JINGLE_FT_STARTING;
       
   616   jft->md5 = g_checksum_new(G_CHECKSUM_MD5);
       
   617   
       
   618   scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: Transfer start (%s)",
       
   619                jft->name);
       
   620 
       
   621   sc2->appfuncs->send(sc);
       
   622 }
       
   623 
       
   624 // When we got a session-terminate
       
   625 static void stop(gconstpointer data)
       
   626 {
       
   627   JingleFT *jft = (JingleFT*)data;
       
   628   GError *err = NULL;
       
   629   GIOStatus status;
       
   630   
       
   631   jft->state = JINGLE_FT_ENDING;
       
   632   if (jft->outfile != NULL) {
       
   633     status = g_io_channel_shutdown(jft->outfile, TRUE, &err);
       
   634     if (status != G_IO_STATUS_NORMAL || err != NULL) {
       
   635       scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: %s",
       
   636                    err->message);
       
   637       g_error_free(err);
       
   638     }
       
   639   }
       
   640 
       
   641   if (jft->hash != NULL && jft->md5 != NULL) {
       
   642     if (g_strcmp0(jft->hash, g_checksum_get_string(jft->md5))) {
       
   643       scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: File corrupt (%s)",
       
   644                    jft->name);
       
   645     } else {
       
   646       scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: transfer finished (%s)"
       
   647                    " and verified", jft->name);
       
   648     }
       
   649   } else {
       
   650     scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: transfer finished (%s)"
       
   651                  " but not verified", jft->name);
       
   652   }
       
   653 }
       
   654 
       
   655 static gchar *_convert_size(guint64 size)
       
   656 {
       
   657   gchar *strsize;
       
   658  
       
   659   if (size < 1024)
       
   660     strsize = g_strdup_printf("%" G_GUINT64_FORMAT " B", size);
       
   661   else if (size < 1048576)
       
   662     strsize = g_strdup_printf("%.2lf KiB", size/1024.0);
       
   663   else if (size < 1073741824)
       
   664     strsize = g_strdup_printf("%.2lf MiB", size/1048576.0);
       
   665   else if (size < 1099511627776)
       
   666     strsize = g_strdup_printf("%.2lf GiB", size/1073741824.0);
       
   667 
       
   668   return strsize;
       
   669 }
       
   670 
       
   671 static gchar* info(gconstpointer data)
       
   672 {
       
   673   JingleFT *jft = (JingleFT *)data;
       
   674   gchar *info, *strsize = _convert_size(jft->size);
       
   675   info = g_strdup_printf("JFT: Receive %s (%s)", jft->name, strsize);
       
   676 
       
   677   g_free(strsize);
       
   678 
       
   679   return info;
       
   680 }
       
   681 
       
   682 static void jingle_ft_init(void)
       
   683 {
       
   684   jingle_register_app(NS_JINGLE_APP_FT, &funcs, JINGLE_TRANSPORT_STREAMING);
       
   685   xmpp_add_feature(NS_JINGLE_APP_FT);
       
   686   jft_cid = compl_new_category();
       
   687   if (jft_cid) {
       
   688     compl_add_category_word(jft_cid, "send");
       
   689     //compl_add_category_word(jft_cid, "request");
       
   690     compl_add_category_word(jft_cid, "info");
       
   691     compl_add_category_word(jft_cid, "flush");
       
   692   }
       
   693   /* Add command */
       
   694   cmd_add("jft", "Manage file transfer", jft_cid, 0, do_sendfile, NULL);
       
   695 }
       
   696 
       
   697 static void jingle_ft_uninit(void)
       
   698 {
       
   699   g_slist_free(info_list);
       
   700   xmpp_del_feature(NS_JINGLE_APP_FT);
       
   701   jingle_unregister_app(NS_JINGLE_APP_FT);
       
   702   cmd_del("file");
       
   703   if (jft_cid)
       
   704     compl_del_category(jft_cid);
       
   705 }