205 // TODO: check if the file already exist or if it was created |
205 // TODO: check if the file already exist or if it was created |
206 // during the call to jingle_ft_check and handle_data |
206 // during the call to jingle_ft_check and handle_data |
207 if (jft->outfile == NULL) { |
207 if (jft->outfile == NULL) { |
208 jft->outfile = g_io_channel_new_file(jft->name, "w", &err); |
208 jft->outfile = g_io_channel_new_file(jft->name, "w", &err); |
209 if (jft->outfile == NULL || err != NULL) { |
209 if (jft->outfile == NULL || err != NULL) { |
210 // propagate the GError ? |
210 scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: %s %s", err->message, |
|
211 jft->name); |
|
212 //TODO: propagate the GError ? |
|
213 g_error_free(err); |
211 return FALSE; |
214 return FALSE; |
212 } |
215 } |
213 g_io_channel_set_encoding(jft->outfile, NULL, NULL); |
216 status = g_io_channel_set_encoding(jft->outfile, NULL, &err); |
|
217 if (status != G_IO_STATUS_NORMAL || err != NULL) { |
|
218 scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: %s %s", err->message, |
|
219 jft->name); |
|
220 g_error_free(err); |
|
221 return FALSE; |
|
222 } |
214 } |
223 } |
215 |
224 |
216 status = g_io_channel_write_chars(jft->outfile, data, (gssize) len, |
225 status = g_io_channel_write_chars(jft->outfile, data, (gssize) len, |
217 &bytes_written, &err); |
226 &bytes_written, &err); |
218 g_io_channel_flush(jft->outfile, NULL); |
|
219 if (status != G_IO_STATUS_NORMAL || err != NULL) { |
227 if (status != G_IO_STATUS_NORMAL || err != NULL) { |
|
228 scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: %s %s", err->message, |
|
229 jft->name); |
|
230 g_error_free(err); |
220 return FALSE; |
231 return FALSE; |
221 } |
232 } |
|
233 status = g_io_channel_flush(jft->outfile, &err); |
|
234 if (status != G_IO_STATUS_NORMAL || err != NULL) { |
|
235 scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: %s %s", err->message, |
|
236 jft->name); |
|
237 g_error_free(err); |
|
238 return FALSE; |
|
239 } |
|
240 |
222 if (bytes_written != len) { |
241 if (bytes_written != len) { |
223 // not supposed to happen if status is normal, unless outfile is non-blocking |
242 // not supposed to happen if status is normal, unless outfile is non-blocking |
224 return FALSE; |
243 return FALSE; |
225 } |
244 } |
226 return TRUE; |
245 return TRUE; |
257 gchar *sid = jingle_generate_sid(); |
276 gchar *sid = jingle_generate_sid(); |
258 gchar *ressource, *recipientjid; |
277 gchar *ressource, *recipientjid; |
259 const gchar *namespaces[] = {NS_JINGLE, NS_JINGLE_APP_FT, NULL}; |
278 const gchar *namespaces[] = {NS_JINGLE, NS_JINGLE_APP_FT, NULL}; |
260 const gchar *myjid = g_strdup(lm_connection_get_jid(lconnection)); |
279 const gchar *myjid = g_strdup(lm_connection_get_jid(lconnection)); |
261 JingleFT *jft = g_new0(JingleFT, 1); |
280 JingleFT *jft = g_new0(JingleFT, 1); |
262 |
281 GError *err = NULL; |
|
282 |
263 if (CURRENT_JID == NULL) { // CURRENT_JID = the jid of the user which has focus |
283 if (CURRENT_JID == NULL) { // CURRENT_JID = the jid of the user which has focus |
264 scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: Please, choose a valid JID in the roster"); |
284 scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: Please, choose a valid JID in the roster"); |
265 return; |
285 return; |
266 } |
286 } |
267 ressource = jingle_find_compatible_res(CURRENT_JID, namespaces); |
287 ressource = jingle_find_compatible_res(CURRENT_JID, namespaces); |
278 jft->desc = g_strdup(args[0]); |
298 jft->desc = g_strdup(args[0]); |
279 jft->type = JINGLE_FT_OFFER; |
299 jft->type = JINGLE_FT_OFFER; |
280 jft->name = g_path_get_basename(filename); |
300 jft->name = g_path_get_basename(filename); |
281 jft->date = fileinfo.st_mtime; |
301 jft->date = fileinfo.st_mtime; |
282 jft->size = fileinfo.st_size; |
302 jft->size = fileinfo.st_size; |
283 jft->outfile = g_io_channel_new_file (filename, "r", NULL); |
303 jft->outfile = g_io_channel_new_file (filename, "r", &err); |
284 if (jft->outfile == NULL) { |
304 if (jft->outfile == NULL || err != NULL) { |
285 scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: Cannot open file %s", args[1]); |
305 scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: %s %s", err->message, |
|
306 args[1]); |
|
307 g_error_free(err); |
286 return; |
308 return; |
287 } |
309 } |
288 |
310 |
289 g_io_channel_set_encoding(jft->outfile, NULL, NULL); |
311 g_io_channel_set_encoding(jft->outfile, NULL, &err); |
|
312 if (jft->outfile == NULL || err != NULL) { |
|
313 scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: %s %s", err->message, |
|
314 args[1]); |
|
315 g_error_free(err); |
|
316 return; |
|
317 } |
290 |
318 |
291 session_add_app(sess, "file", NS_JINGLE_APP_FT, jft); |
319 session_add_app(sess, "file", NS_JINGLE_APP_FT, jft); |
292 |
320 |
293 jingle_handle_app(sess, "file", NS_JINGLE_APP_FT, jft, recipientjid); |
321 jingle_handle_app(sess, "file", NS_JINGLE_APP_FT, jft, recipientjid); |
294 |
322 |
317 |
345 |
318 node2 = lm_message_node_add_child(node2, "file", NULL); |
346 node2 = lm_message_node_add_child(node2, "file", NULL); |
319 |
347 |
320 size = g_strdup_printf("%" G_GUINT64_FORMAT, jft->size); |
348 size = g_strdup_printf("%" G_GUINT64_FORMAT, jft->size); |
321 |
349 |
322 lm_message_node_set_attributes(node2, "xmlns", NS_SI_FT, "name", jft->name, |
350 lm_message_node_set_attributes(node2, "xmlns", NS_SI_FT, |
323 "size", size, NULL); |
351 "name", jft->name, |
|
352 "size", size, |
|
353 NULL); |
324 g_free(size); |
354 g_free(size); |
325 |
355 |
326 if (jft->hash != NULL) |
356 if (jft->hash != NULL) |
327 lm_message_node_set_attribute(node2, "hash", jft->hash); |
357 lm_message_node_set_attribute(node2, "hash", jft->hash); |
328 |
358 |
337 } |
367 } |
338 |
368 |
339 static void send_hash(gchar *sid, gchar *to, gchar *hash) |
369 static void send_hash(gchar *sid, gchar *to, gchar *hash) |
340 { |
370 { |
341 JingleAckHandle *ackhandle; |
371 JingleAckHandle *ackhandle; |
342 |
372 GError *err = NULL; |
343 LmMessage *r = lm_message_new_with_sub_type(to, LM_MESSAGE_TYPE_IQ, LM_MESSAGE_SUB_TYPE_SET); |
373 gboolean ret; |
|
374 |
|
375 LmMessage *r = lm_message_new_with_sub_type(to, LM_MESSAGE_TYPE_IQ, |
|
376 LM_MESSAGE_SUB_TYPE_SET); |
344 LmMessageNode *node = lm_message_get_node(r); |
377 LmMessageNode *node = lm_message_get_node(r); |
345 lm_message_node_add_child(node, "jingle", NULL); |
378 lm_message_node_add_child(node, "jingle", NULL); |
346 node = lm_message_node_get_child(node, "jingle"); |
379 node = lm_message_node_get_child(node, "jingle"); |
347 lm_message_node_set_attributes(node, "xmlns", NS_JINGLE, "sid", sid, "action", "session-info", NULL); |
380 lm_message_node_set_attributes(node, "xmlns", NS_JINGLE, |
|
381 "sid", sid, |
|
382 "action", "session-info", |
|
383 NULL); |
348 lm_message_node_add_child(node, "hash", hash); |
384 lm_message_node_add_child(node, "hash", hash); |
349 node = lm_message_node_get_child(node, "hash"); |
385 node = lm_message_node_get_child(node, "hash"); |
350 lm_message_node_set_attribute(node, "xmlns", NS_JINGLE_APP_FT_INFO); |
386 lm_message_node_set_attribute(node, "xmlns", NS_JINGLE_APP_FT_INFO); |
351 |
387 |
352 ackhandle = g_new0(JingleAckHandle, 1); |
388 ackhandle = g_new0(JingleAckHandle, 1); |
353 ackhandle->callback = NULL; |
389 ackhandle->callback = NULL; |
354 ackhandle->user_data = NULL; |
390 ackhandle->user_data = NULL; |
355 |
391 |
356 lm_connection_send_with_reply(lconnection, r, |
392 ret = lm_connection_send_with_reply(lconnection, r, |
357 jingle_new_ack_handler(ackhandle), NULL); |
393 jingle_new_ack_handler(ackhandle), &err); |
|
394 |
|
395 // It's not really a problem, but we must tell it! |
|
396 if (ret == FALSE || err != NULL) { |
|
397 scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: cannot send hash: %s", |
|
398 err->message); |
|
399 g_error_free(err); |
|
400 } |
|
401 |
358 lm_message_unref(r); |
402 lm_message_unref(r); |
359 } |
403 } |
360 |
404 |
361 static void send(session_content *sc) |
405 static void send(session_content *sc) |
362 { |
406 { |
363 JingleFT *jft; |
407 JingleFT *jft; |
364 gchar buf[JINGLE_FT_SIZE_READ]; |
408 gchar buf[JINGLE_FT_SIZE_READ]; |
365 gsize read; |
409 gsize read; |
366 GIOStatus status; |
410 GIOStatus status; |
367 int count = 0; |
411 int count = 0; |
|
412 GError *err = NULL; |
|
413 |
368 JingleSession *sess = session_find_by_sid(sc->sid, sc->from); |
414 JingleSession *sess = session_find_by_sid(sc->sid, sc->from); |
369 if (sess == NULL) { |
415 if (sess == NULL) { |
370 scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: error before transfer"); |
416 scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: error before transfer"); |
371 // We haven't LmMessage: jingle_send_iq_error(jn->message, "cancel", "item-not-found", "unknown-session"); |
417 // We haven't LmMessage: jingle_send_iq_error(jn->message, "cancel", "item-not-found", "unknown-session"); |
372 return; |
418 return; |
376 |
422 |
377 jft = (JingleFT*)sc2->description; |
423 jft = (JingleFT*)sc2->description; |
378 |
424 |
379 do { |
425 do { |
380 count++; |
426 count++; |
381 status = g_io_channel_read_chars(jft->outfile, (gchar*)buf, JINGLE_FT_SIZE_READ, &read, NULL); |
427 status = g_io_channel_read_chars(jft->outfile, (gchar*)buf, |
|
428 JINGLE_FT_SIZE_READ, &read, &err); |
382 } while (status == G_IO_STATUS_AGAIN && count < 10); |
429 } while (status == G_IO_STATUS_AGAIN && count < 10); |
383 |
430 |
384 if (status == G_IO_STATUS_AGAIN) { |
431 if (status == G_IO_STATUS_AGAIN) { |
385 // TODO: something better |
432 // TODO: something better |
386 scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: file unavailable"); |
433 scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: file unavailable"); |
387 return; |
434 return; |
388 } |
435 } |
389 |
436 |
390 if (status == G_IO_STATUS_ERROR) { |
437 if (status == G_IO_STATUS_ERROR || err != NULL) { |
391 scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: an error occured"); |
438 scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: %s", err->message); |
|
439 g_error_free(err); |
392 return; |
440 return; |
393 } |
441 } |
394 |
442 |
395 if (status == G_IO_STATUS_NORMAL) { |
443 if (status == G_IO_STATUS_NORMAL) { |
396 g_checksum_update(jft->md5, (guchar*)buf, read); |
444 g_checksum_update(jft->md5, (guchar*)buf, read); |
398 handle_app_data(sc->sid, sc->from, sc->name, buf, read); |
446 handle_app_data(sc->sid, sc->from, sc->name, buf, read); |
399 } |
447 } |
400 |
448 |
401 if (status == G_IO_STATUS_EOF) { |
449 if (status == G_IO_STATUS_EOF) { |
402 handle_app_data(sc->sid, sc->from, sc->name, NULL, 0); |
450 handle_app_data(sc->sid, sc->from, sc->name, NULL, 0); |
403 scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: transfer finish (%s)", jft->name); |
451 scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: transfer finish (%s)", |
|
452 jft->name); |
404 jft->hash = g_strdup(g_checksum_get_string(jft->md5)); |
453 jft->hash = g_strdup(g_checksum_get_string(jft->md5)); |
405 // Call a function to say state is ended |
454 // Call a function to say state is ended |
406 session_changestate_sessioncontent(sess, sc2->name, JINGLE_SESSION_STATE_ENDED); |
455 session_changestate_sessioncontent(sess, sc2->name, |
|
456 JINGLE_SESSION_STATE_ENDED); |
407 // Send the hash |
457 // Send the hash |
408 send_hash(sess->sid, sess->to, jft->hash); |
458 send_hash(sess->sid, sess->to, jft->hash); |
409 g_checksum_free(jft->md5); |
459 g_checksum_free(jft->md5); |
410 |
460 |
411 if (!session_remove_sessioncontent(sess, sc2->name)) { |
461 if (!session_remove_sessioncontent(sess, sc2->name)) { |
439 |
489 |
440 // When we got a session-terminate |
490 // When we got a session-terminate |
441 static void stop(gconstpointer data) |
491 static void stop(gconstpointer data) |
442 { |
492 { |
443 JingleFT *jft = (JingleFT*)data; |
493 JingleFT *jft = (JingleFT*)data; |
|
494 GError *err = NULL; |
|
495 GIOStatus status; |
|
496 |
|
497 if (jft->outfile != NULL) { |
|
498 status = g_io_channel_flush(jft->outfile, &err); |
|
499 if (status != G_IO_STATUS_NORMAL || err != NULL) { |
|
500 scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: %s", |
|
501 err->message); |
|
502 g_error_free(err); |
|
503 } |
|
504 g_io_channel_unref(jft->outfile); |
|
505 } |
444 |
506 |
445 if (jft->hash != NULL && jft->md5 != NULL) { |
507 if (jft->hash != NULL && jft->md5 != NULL) { |
446 if (g_strcmp0(jft->hash, g_checksum_get_string(jft->md5))) { |
508 if (g_strcmp0(jft->hash, g_checksum_get_string(jft->md5))) { |
447 scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: File corrupt (%s)", jft->name); |
509 scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: File corrupt (%s)", |
|
510 jft->name); |
448 } else { |
511 } else { |
449 scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: transfer finished (%s) and verified", jft->name); |
512 scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: transfer finished (%s)" |
|
513 " and verified", jft->name); |
450 } |
514 } |
451 } else { |
515 } else { |
452 scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: transfer finished (%s) but not verified", jft->name); |
516 scr_LogPrint(LPRINT_LOGNORM, "Jingle File Transfer: transfer finished (%s)" |
|
517 " but not verified", jft->name); |
453 } |
518 } |
454 |
519 |
455 g_checksum_free(jft->md5); |
520 g_checksum_free(jft->md5); |
456 |
521 |
457 if (jft->outfile != NULL) { |
|
458 g_io_channel_flush(jft->outfile, NULL); |
|
459 |
|
460 g_io_channel_unref(jft->outfile); |
|
461 } |
|
462 } |
522 } |
463 |
523 |
464 static void jingle_ft_init(void) |
524 static void jingle_ft_init(void) |
465 { |
525 { |
466 jingle_register_app(NS_JINGLE_APP_FT, &funcs, JINGLE_TRANSPORT_STREAMING); |
526 jingle_register_app(NS_JINGLE_APP_FT, &funcs, JINGLE_TRANSPORT_STREAMING); |