2 * empathy-ft-handler.c - Source for EmpathyFTHandler
3 * Copyright (C) 2009 Collabora Ltd.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 * Author: Cosimo Cecchi <cosimo.cecchi@collabora.co.uk>
22 /* empathy-ft-handler.c */
25 #include <glib/gi18n.h>
26 #include <telepathy-glib/util.h>
27 #include <telepathy-glib/dbus.h>
28 #include <telepathy-glib/interfaces.h>
30 #include "empathy-ft-handler.h"
31 #include "empathy-tp-contact-factory.h"
32 #include "empathy-dispatcher.h"
33 #include "empathy-marshal.h"
34 #include "empathy-time.h"
35 #include "empathy-utils.h"
37 #define DEBUG_FLAG EMPATHY_DEBUG_FT
38 #include "empathy-debug.h"
41 * SECTION:empathy-ft-handler
42 * @title: EmpathyFTHandler
43 * @short_description: an object representing a File Transfer
44 * @include: libempathy/empathy-ft-handler
46 * #EmpathyFTHandler is the object which represents a File Transfer with all
48 * The creation of an #EmpathyFTHandler is done with
49 * empathy_ft_handler_new_outgoing() or empathy_ft_handler_new_incoming(),
50 * even though clients should not need to call them directly, as
51 * #EmpathyFTFactory does it for them. Remember that for the file transfer
52 * to work with an incoming handler,
53 * empathy_ft_handler_incoming_set_destination() should be called after
54 * empathy_ft_handler_new_incoming(). #EmpathyFTFactory does this
56 * It's important to note that, as the creation of the handlers is async, once
57 * an handler is created, it already has all the interesting properties set,
58 * like filename, total bytes, content type and so on, making it useful
59 * to be displayed in an UI.
60 * The transfer API works like a state machine; it has three signals,
61 * ::transfer-started, ::transfer-progress, ::transfer-done, which will be
62 * emitted in the relevant phases.
63 * In addition, if the handler is created with checksumming enabled,
64 * other three signals (::hashing-started, ::hashing-progress, ::hashing-done)
65 * will be emitted before or after the transfer, depending on the direction
66 * (respectively outgoing and incoming) of the handler.
67 * At any time between the call to empathy_ft_handler_start_transfer() and
68 * the last signal, a ::transfer-error can be emitted, indicating that an
69 * error has happened in the operation. The message of the error is localized
73 G_DEFINE_TYPE (EmpathyFTHandler
, empathy_ft_handler
, G_TYPE_OBJECT
)
75 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyFTHandler)
77 #define BUFFER_SIZE 4096
86 PROP_MODIFICATION_TIME
,
88 PROP_TRANSFERRED_BYTES
103 GInputStream
*stream
;
104 GError
*error
/* comment to make the style checker happy */;
109 EmpathyFTHandler
*handler
;
113 EmpathyFTHandlerReadyCallback callback
;
115 EmpathyFTHandler
*handler
;
120 gboolean dispose_run
;
123 EmpathyTpFile
*tpfile
;
124 GCancellable
*cancellable
;
127 EmpathyDispatcher
*dispatcher
;
129 /* request for the new transfer */
132 /* transfer properties */
133 EmpathyContact
*contact
;
138 guint64 transferred_bytes
;
141 TpFileHashType content_hash_type
;
145 guint remaining_time
;
146 time_t last_update_time
;
148 gboolean is_completed
;
149 } EmpathyFTHandlerPriv
;
151 static guint signals
[LAST_SIGNAL
] = { 0 };
153 static gboolean
do_hash_job_incoming (GIOSchedulerJob
*job
,
154 GCancellable
*cancellable
, gpointer user_data
);
156 /* GObject implementations */
158 do_get_property (GObject
*object
,
163 EmpathyFTHandlerPriv
*priv
= GET_PRIV (object
);
168 g_value_set_object (value
, priv
->contact
);
170 case PROP_CONTENT_TYPE
:
171 g_value_set_string (value
, priv
->content_type
);
173 case PROP_DESCRIPTION
:
174 g_value_set_string (value
, priv
->description
);
177 g_value_set_string (value
, priv
->filename
);
179 case PROP_MODIFICATION_TIME
:
180 g_value_set_uint64 (value
, priv
->mtime
);
182 case PROP_TOTAL_BYTES
:
183 g_value_set_uint64 (value
, priv
->total_bytes
);
185 case PROP_TRANSFERRED_BYTES
:
186 g_value_set_uint64 (value
, priv
->transferred_bytes
);
189 g_value_set_object (value
, priv
->gfile
);
192 g_value_set_object (value
, priv
->tpfile
);
195 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
200 do_set_property (GObject
*object
,
205 EmpathyFTHandlerPriv
*priv
= GET_PRIV (object
);
210 priv
->contact
= g_value_dup_object (value
);
212 case PROP_CONTENT_TYPE
:
213 priv
->content_type
= g_value_dup_string (value
);
215 case PROP_DESCRIPTION
:
216 priv
->description
= g_value_dup_string (value
);
219 priv
->filename
= g_value_dup_string (value
);
221 case PROP_MODIFICATION_TIME
:
222 priv
->mtime
= g_value_get_uint64 (value
);
224 case PROP_TOTAL_BYTES
:
225 priv
->total_bytes
= g_value_get_uint64 (value
);
227 case PROP_TRANSFERRED_BYTES
:
228 priv
->transferred_bytes
= g_value_get_uint64 (value
);
231 priv
->gfile
= g_value_dup_object (value
);
234 priv
->tpfile
= g_value_dup_object (value
);
237 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
242 do_dispose (GObject
*object
)
244 EmpathyFTHandlerPriv
*priv
= GET_PRIV (object
);
246 if (priv
->dispose_run
)
249 priv
->dispose_run
= TRUE
;
251 if (priv
->contact
!= NULL
) {
252 g_object_unref (priv
->contact
);
253 priv
->contact
= NULL
;
256 if (priv
->gfile
!= NULL
) {
257 g_object_unref (priv
->gfile
);
261 if (priv
->tpfile
!= NULL
) {
262 empathy_tp_file_close (priv
->tpfile
);
263 g_object_unref (priv
->tpfile
);
267 if (priv
->cancellable
!= NULL
) {
268 g_object_unref (priv
->cancellable
);
269 priv
->cancellable
= NULL
;
272 if (priv
->request
!= NULL
)
274 g_hash_table_unref (priv
->request
);
275 priv
->request
= NULL
;
278 if (priv
->dispatcher
!= NULL
)
280 g_object_unref (priv
->dispatcher
);
281 priv
->dispatcher
= NULL
;
284 G_OBJECT_CLASS (empathy_ft_handler_parent_class
)->dispose (object
);
288 do_finalize (GObject
*object
)
290 EmpathyFTHandlerPriv
*priv
= GET_PRIV (object
);
292 DEBUG ("%p", object
);
294 g_free (priv
->content_type
);
295 priv
->content_type
= NULL
;
297 g_free (priv
->filename
);
298 priv
->filename
= NULL
;
300 g_free (priv
->description
);
301 priv
->description
= NULL
;
303 g_free (priv
->content_hash
);
304 priv
->content_hash
= NULL
;
306 G_OBJECT_CLASS (empathy_ft_handler_parent_class
)->finalize (object
);
310 empathy_ft_handler_class_init (EmpathyFTHandlerClass
*klass
)
312 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
313 GParamSpec
*param_spec
;
315 g_type_class_add_private (klass
, sizeof (EmpathyFTHandlerPriv
));
317 object_class
->get_property
= do_get_property
;
318 object_class
->set_property
= do_set_property
;
319 object_class
->dispose
= do_dispose
;
320 object_class
->finalize
= do_finalize
;
325 * EmpathyFTHandler:contact:
327 * The remote #EmpathyContact for the transfer
329 param_spec
= g_param_spec_object ("contact",
330 "contact", "The remote contact",
331 EMPATHY_TYPE_CONTACT
,
332 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
| G_PARAM_CONSTRUCT_ONLY
);
333 g_object_class_install_property (object_class
, PROP_CONTACT
, param_spec
);
336 * EmpathyFTHandler:content-type:
338 * The content type of the file being transferred
340 param_spec
= g_param_spec_string ("content-type",
341 "content-type", "The content type of the file", NULL
,
342 G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
343 g_object_class_install_property (object_class
,
344 PROP_CONTENT_TYPE
, param_spec
);
347 * EmpathyFTHandler:description:
349 * The description of the file being transferred
351 param_spec
= g_param_spec_string ("description",
352 "description", "The description of the file", NULL
,
353 G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
354 g_object_class_install_property (object_class
,
355 PROP_DESCRIPTION
, param_spec
);
358 * EmpathyFTHandler:filename:
360 * The name of the file being transferred
362 param_spec
= g_param_spec_string ("filename",
363 "filename", "The name of the file", NULL
,
364 G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
365 g_object_class_install_property (object_class
,
366 PROP_FILENAME
, param_spec
);
369 * EmpathyFTHandler:modification-time:
371 * The modification time of the file being transferred
373 param_spec
= g_param_spec_uint64 ("modification-time",
374 "modification-time", "The mtime of the file", 0,
375 G_MAXUINT64
, 0, G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
376 g_object_class_install_property (object_class
,
377 PROP_MODIFICATION_TIME
, param_spec
);
380 * EmpathyFTHandler:total-bytes:
382 * The size (in bytes) of the file being transferred
384 param_spec
= g_param_spec_uint64 ("total-bytes",
385 "total-bytes", "The size of the file", 0,
386 G_MAXUINT64
, 0, G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
387 g_object_class_install_property (object_class
,
388 PROP_TOTAL_BYTES
, param_spec
);
391 * EmpathyFTHandler:transferred-bytes:
393 * The number of the bytes already transferred
395 param_spec
= g_param_spec_uint64 ("transferred-bytes",
396 "transferred-bytes", "The number of bytes already transferred", 0,
397 G_MAXUINT64
, 0, G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
398 g_object_class_install_property (object_class
,
399 PROP_TRANSFERRED_BYTES
, param_spec
);
402 * EmpathyFTHandler:gfile:
404 * The #GFile object where the transfer actually happens
406 param_spec
= g_param_spec_object ("gfile",
407 "gfile", "The GFile we're handling",
409 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
410 g_object_class_install_property (object_class
, PROP_G_FILE
, param_spec
);
413 * EmpathyFTHandler:tp-file:
415 * The underlying #EmpathyTpFile managing the transfer
417 param_spec
= g_param_spec_object ("tp-file",
418 "tp-file", "The file's channel wrapper",
419 EMPATHY_TYPE_TP_FILE
,
420 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
| G_PARAM_CONSTRUCT_ONLY
);
421 g_object_class_install_property (object_class
, PROP_TP_FILE
, param_spec
);
426 * EmpathyFTHandler::transfer-started
427 * @handler: the object which has received the signal
428 * @tp_file: the #EmpathyTpFile for which the transfer has started
430 * This signal is emitted when the actual transfer starts.
432 signals
[TRANSFER_STARTED
] =
433 g_signal_new ("transfer-started", G_TYPE_FROM_CLASS (klass
),
434 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
435 g_cclosure_marshal_VOID__OBJECT
,
437 1, EMPATHY_TYPE_TP_FILE
);
440 * EmpathyFTHandler::transfer-done
441 * @handler: the object which has received the signal
442 * @tp_file: the #EmpathyTpFile for which the transfer has started
444 * This signal will be emitted when the actual transfer is completed
447 signals
[TRANSFER_DONE
] =
448 g_signal_new ("transfer-done", G_TYPE_FROM_CLASS (klass
),
449 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
450 g_cclosure_marshal_VOID__OBJECT
,
452 1, EMPATHY_TYPE_TP_FILE
);
455 * EmpathyFTHandler::transfer-error
456 * @handler: the object which has received the signal
459 * This signal can be emitted anytime between the call to
460 * empathy_ft_handler_start_transfer() and the last expected signal
461 * (::transfer-done or ::hashing-done), and it's guaranteed to be the last
462 * signal coming from the handler, meaning that no other operation will
463 * take place after this signal.
465 signals
[TRANSFER_ERROR
] =
466 g_signal_new ("transfer-error", G_TYPE_FROM_CLASS (klass
),
467 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
468 g_cclosure_marshal_VOID__POINTER
,
473 * EmpathyFTHandler::transfer-progress
474 * @handler: the object which has received the signal
475 * @current_bytes: the bytes currently transferred
476 * @total_bytes: the total bytes of the handler
477 * @remaining_time: the number of seconds remaining for the transfer
479 * @speed: the current speed of the transfer (in KB/s)
481 * This signal is emitted to notify clients of the progress of the
484 signals
[TRANSFER_PROGRESS
] =
485 g_signal_new ("transfer-progress", G_TYPE_FROM_CLASS (klass
),
486 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
487 _empathy_marshal_VOID__UINT64_UINT64_UINT_DOUBLE
,
489 4, G_TYPE_UINT64
, G_TYPE_UINT64
, G_TYPE_UINT
, G_TYPE_DOUBLE
);
492 * EmpathyFTHandler::hashing-started
493 * @handler: the object which has received the signal
495 * This signal is emitted when the hashing operation of the handler
496 * is started. Note that this might happen or not, depending on the CM
497 * and remote contact capabilities. Clients shoud use
498 * empathy_ft_handler_get_use_hash() before calling
499 * empathy_ft_handler_start_transfer() to know whether they should connect
502 signals
[HASHING_STARTED
] =
503 g_signal_new ("hashing-started", G_TYPE_FROM_CLASS (klass
),
504 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
505 g_cclosure_marshal_VOID__VOID
,
509 * EmpathyFTHandler::hashing-progress
510 * @handler: the object which has received the signal
511 * @current_bytes: the bytes currently hashed
512 * @total_bytes: the total bytes of the handler
514 * This signal is emitted to notify clients of the progress of the
517 signals
[HASHING_PROGRESS
] =
518 g_signal_new ("hashing-progress", G_TYPE_FROM_CLASS (klass
),
519 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
520 _empathy_marshal_VOID__UINT64_UINT64
,
522 2, G_TYPE_UINT64
, G_TYPE_UINT64
);
525 * EmpathyFTHandler::hashing-done
526 * @handler: the object which has received the signal
528 * This signal is emitted when the hashing operation of the handler
531 signals
[HASHING_DONE
] =
532 g_signal_new ("hashing-done", G_TYPE_FROM_CLASS (klass
),
533 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
534 g_cclosure_marshal_VOID__VOID
,
539 empathy_ft_handler_init (EmpathyFTHandler
*self
)
541 EmpathyFTHandlerPriv
*priv
= G_TYPE_INSTANCE_GET_PRIVATE (self
,
542 EMPATHY_TYPE_FT_HANDLER
, EmpathyFTHandlerPriv
);
545 priv
->cancellable
= g_cancellable_new ();
546 priv
->dispatcher
= empathy_dispatcher_dup_singleton ();
549 /* private functions */
552 hash_data_free (HashingData
*data
)
554 g_free (data
->buffer
);
556 if (data
->stream
!= NULL
)
557 g_object_unref (data
->stream
);
559 if (data
->checksum
!= NULL
)
560 g_checksum_free (data
->checksum
);
562 if (data
->error
!= NULL
)
563 g_error_free (data
->error
);
565 if (data
->handler
!= NULL
)
566 g_object_unref (data
->handler
);
568 g_slice_free (HashingData
, data
);
572 tp_file_hash_to_g_checksum (TpFileHashType type
)
574 GChecksumType retval
;
578 case TP_FILE_HASH_TYPE_MD5
:
579 retval
= G_CHECKSUM_MD5
;
581 case TP_FILE_HASH_TYPE_SHA1
:
582 retval
= G_CHECKSUM_SHA1
;
584 case TP_FILE_HASH_TYPE_SHA256
:
585 retval
= G_CHECKSUM_SHA256
;
588 g_assert_not_reached ();
596 check_hash_incoming (EmpathyFTHandler
*handler
)
598 HashingData
*hash_data
;
599 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
601 if (!EMP_STR_EMPTY (priv
->content_hash
))
603 hash_data
= g_slice_new0 (HashingData
);
604 hash_data
->total_bytes
= priv
->total_bytes
;
605 hash_data
->handler
= g_object_ref (handler
);
606 hash_data
->checksum
= g_checksum_new
607 (tp_file_hash_to_g_checksum (priv
->content_hash_type
));
609 g_signal_emit (handler
, signals
[HASHING_STARTED
], 0);
611 g_io_scheduler_push_job (do_hash_job_incoming
, hash_data
, NULL
,
612 G_PRIORITY_DEFAULT
, priv
->cancellable
);
617 emit_error_signal (EmpathyFTHandler
*handler
,
620 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
622 if (!g_cancellable_is_cancelled (priv
->cancellable
))
623 g_cancellable_cancel (priv
->cancellable
);
625 g_signal_emit (handler
, signals
[TRANSFER_ERROR
], 0, error
);
629 ft_transfer_operation_callback (EmpathyTpFile
*tp_file
,
633 EmpathyFTHandler
*handler
= user_data
;
634 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
636 DEBUG ("Transfer operation callback, error %p", error
);
640 emit_error_signal (handler
, error
);
644 priv
->is_completed
= TRUE
;
645 g_signal_emit (handler
, signals
[TRANSFER_DONE
], 0, tp_file
);
647 empathy_tp_file_close (tp_file
);
649 if (empathy_ft_handler_is_incoming (handler
) && priv
->use_hash
)
651 check_hash_incoming (handler
);
657 update_remaining_time_and_speed (EmpathyFTHandler
*handler
,
658 guint64 transferred_bytes
)
660 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
661 time_t elapsed_time
, current_time
;
662 guint64 transferred
, last_transferred_bytes
;
666 last_transferred_bytes
= priv
->transferred_bytes
;
667 priv
->transferred_bytes
= transferred_bytes
;
669 current_time
= empathy_time_get_current ();
670 elapsed_time
= current_time
- priv
->last_update_time
;
672 if (elapsed_time
>= 1)
674 transferred
= transferred_bytes
- last_transferred_bytes
;
675 speed
= (gdouble
) transferred
/ (gdouble
) elapsed_time
;
676 remaining_time
= (priv
->total_bytes
- priv
->transferred_bytes
) / speed
;
678 priv
->remaining_time
= remaining_time
;
679 priv
->last_update_time
= current_time
;
684 ft_transfer_progress_callback (EmpathyTpFile
*tp_file
,
685 guint64 transferred_bytes
,
688 EmpathyFTHandler
*handler
= user_data
;
689 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
691 if (empathy_ft_handler_is_cancelled (handler
))
694 if (transferred_bytes
== 0)
696 priv
->last_update_time
= empathy_time_get_current ();
697 g_signal_emit (handler
, signals
[TRANSFER_STARTED
], 0, tp_file
);
700 if (priv
->transferred_bytes
!= transferred_bytes
)
702 update_remaining_time_and_speed (handler
, transferred_bytes
);
704 g_signal_emit (handler
, signals
[TRANSFER_PROGRESS
], 0,
705 transferred_bytes
, priv
->total_bytes
, priv
->remaining_time
,
711 ft_handler_create_channel_cb (EmpathyDispatchOperation
*operation
,
715 EmpathyFTHandler
*handler
= user_data
;
716 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
717 GError
*my_error
= (GError
*) error
;
719 DEBUG ("Dispatcher create channel CB");
721 if (my_error
== NULL
)
723 g_cancellable_set_error_if_cancelled (priv
->cancellable
, &my_error
);
726 if (my_error
!= NULL
)
728 emit_error_signal (handler
, my_error
);
730 if (my_error
!= error
)
731 g_clear_error (&my_error
);
736 priv
->tpfile
= g_object_ref
737 (empathy_dispatch_operation_get_channel_wrapper (operation
));
739 empathy_tp_file_offer (priv
->tpfile
, priv
->gfile
, priv
->cancellable
,
740 ft_transfer_progress_callback
, handler
,
741 ft_transfer_operation_callback
, handler
);
743 empathy_dispatch_operation_claim (operation
);
747 ft_handler_push_to_dispatcher (EmpathyFTHandler
*handler
)
749 TpConnection
*connection
;
750 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
752 DEBUG ("Pushing request to the dispatcher");
754 connection
= empathy_contact_get_connection (priv
->contact
);
756 /* I want to own a reference to the request, and destroy it later */
757 empathy_dispatcher_create_channel (priv
->dispatcher
, connection
,
758 g_hash_table_ref (priv
->request
), ft_handler_create_channel_cb
, handler
);
762 ft_handler_populate_outgoing_request (EmpathyFTHandler
*handler
)
764 guint contact_handle
;
767 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
769 request
= priv
->request
= g_hash_table_new_full (g_str_hash
, g_str_equal
,
770 NULL
, (GDestroyNotify
) tp_g_value_slice_free
);
772 contact_handle
= empathy_contact_get_handle (priv
->contact
);
774 /* org.freedesktop.Telepathy.Channel.ChannelType */
775 value
= tp_g_value_slice_new_string (TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER
);
776 g_hash_table_insert (request
, TP_IFACE_CHANNEL
".ChannelType", value
);
778 /* org.freedesktop.Telepathy.Channel.TargetHandleType */
779 value
= tp_g_value_slice_new_uint (TP_HANDLE_TYPE_CONTACT
);
780 g_hash_table_insert (request
, TP_IFACE_CHANNEL
".TargetHandleType", value
);
782 /* org.freedesktop.Telepathy.Channel.TargetHandle */
783 value
= tp_g_value_slice_new_uint (contact_handle
);
784 g_hash_table_insert (request
, TP_IFACE_CHANNEL
".TargetHandle", value
);
786 /* org.freedesktop.Telepathy.Channel.Type.FileTransfer.ContentType */
787 value
= tp_g_value_slice_new_string (priv
->content_type
);
788 g_hash_table_insert (request
,
789 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER
".ContentType", value
);
791 /* org.freedesktop.Telepathy.Channel.Type.FileTransfer.Filename */
792 value
= tp_g_value_slice_new_string (priv
->filename
);
793 g_hash_table_insert (request
,
794 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER
".Filename", value
);
796 /* org.freedesktop.Telepathy.Channel.Type.FileTransfer.Size */
797 value
= tp_g_value_slice_new_uint64 (priv
->total_bytes
);
798 g_hash_table_insert (request
,
799 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER
".Size", value
);
801 /* org.freedesktop.Telepathy.Channel.Type.FileTransfer.Date */
802 value
= tp_g_value_slice_new_uint64 ((guint64
) priv
->mtime
);
803 g_hash_table_insert (request
,
804 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER
".Date", value
);
808 hash_job_done (gpointer user_data
)
810 HashingData
*hash_data
= user_data
;
811 EmpathyFTHandler
*handler
= hash_data
->handler
;
812 EmpathyFTHandlerPriv
*priv
;
813 GError
*error
= NULL
;
816 DEBUG ("Closing stream after hashing.");
818 priv
= GET_PRIV (handler
);
820 if (hash_data
->error
!= NULL
)
822 error
= hash_data
->error
;
826 DEBUG ("Got file hash %s", g_checksum_get_string (hash_data
->checksum
));
828 if (empathy_ft_handler_is_incoming (handler
))
830 if (g_strcmp0 (g_checksum_get_string (hash_data
->checksum
),
833 DEBUG ("Hash mismatch when checking incoming handler: "
834 "received %s, calculated %s", priv
->content_hash
,
835 g_checksum_get_string (hash_data
->checksum
));
837 error
= g_error_new_literal (EMPATHY_FT_ERROR_QUARK
,
838 EMPATHY_FT_ERROR_HASH_MISMATCH
,
839 _("The hash of the received file and the "
840 "sent one do not match"));
845 DEBUG ("Hash verification matched, received %s, calculated %s",
847 g_checksum_get_string (hash_data
->checksum
));
852 /* set the checksum in the request...
853 * org.freedesktop.Telepathy.Channel.Type.FileTransfer.ContentHash
855 value
= tp_g_value_slice_new_string
856 (g_checksum_get_string (hash_data
->checksum
));
857 g_hash_table_insert (priv
->request
,
858 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER
".ContentHash", value
);
865 emit_error_signal (handler
, error
);
866 g_clear_error (&error
);
870 g_signal_emit (handler
, signals
[HASHING_DONE
], 0);
872 if (!empathy_ft_handler_is_incoming (handler
))
873 /* the request is complete now, push it to the dispatcher */
874 ft_handler_push_to_dispatcher (handler
);
877 hash_data_free (hash_data
);
883 emit_hashing_progress (gpointer user_data
)
885 HashingData
*hash_data
= user_data
;
887 g_signal_emit (hash_data
->handler
, signals
[HASHING_PROGRESS
], 0,
888 (guint64
) hash_data
->total_read
, (guint64
) hash_data
->total_bytes
);
894 do_hash_job (GIOSchedulerJob
*job
,
895 GCancellable
*cancellable
,
898 HashingData
*hash_data
= user_data
;
900 EmpathyFTHandlerPriv
*priv
;
901 GError
*error
= NULL
;
903 priv
= GET_PRIV (hash_data
->handler
);
906 if (hash_data
->buffer
== NULL
)
907 hash_data
->buffer
= g_malloc0 (BUFFER_SIZE
);
909 bytes_read
= g_input_stream_read (hash_data
->stream
, hash_data
->buffer
,
910 BUFFER_SIZE
, cancellable
, &error
);
914 hash_data
->total_read
+= bytes_read
;
916 /* we now have the chunk */
919 g_checksum_update (hash_data
->checksum
, hash_data
->buffer
, bytes_read
);
920 g_io_scheduler_job_send_to_mainloop_async (job
, emit_hashing_progress
,
923 g_free (hash_data
->buffer
);
924 hash_data
->buffer
= NULL
;
930 g_input_stream_close (hash_data
->stream
, cancellable
, &error
);
935 hash_data
->error
= error
;
937 g_io_scheduler_job_send_to_mainloop_async (job
, hash_job_done
,
944 do_hash_job_incoming (GIOSchedulerJob
*job
,
945 GCancellable
*cancellable
,
948 HashingData
*hash_data
= user_data
;
949 EmpathyFTHandler
*handler
= hash_data
->handler
;
950 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
951 GError
*error
= NULL
;
953 DEBUG ("checking integrity for incoming handler");
955 /* need to get the stream first */
957 G_INPUT_STREAM (g_file_read (priv
->gfile
, cancellable
, &error
));
961 hash_data
->error
= error
;
962 g_io_scheduler_job_send_to_mainloop_async (job
, hash_job_done
,
967 return do_hash_job (job
, cancellable
, user_data
);
971 ft_handler_read_async_cb (GObject
*source
,
975 GFileInputStream
*stream
;
976 GError
*error
= NULL
;
977 HashingData
*hash_data
;
979 EmpathyFTHandler
*handler
= user_data
;
980 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
982 DEBUG ("GFile read async CB.");
984 stream
= g_file_read_finish (priv
->gfile
, res
, &error
);
987 emit_error_signal (handler
, error
);
988 g_clear_error (&error
);
993 hash_data
= g_slice_new0 (HashingData
);
994 hash_data
->stream
= G_INPUT_STREAM (stream
);
995 hash_data
->total_bytes
= priv
->total_bytes
;
996 hash_data
->handler
= g_object_ref (handler
);
997 /* FIXME: MD5 is the only ContentHashType supported right now */
998 hash_data
->checksum
= g_checksum_new (G_CHECKSUM_MD5
);
1000 /* org.freedesktop.Telepathy.Channel.Type.FileTransfer.ContentHashType */
1001 value
= tp_g_value_slice_new_uint (TP_FILE_HASH_TYPE_MD5
);
1002 g_hash_table_insert (priv
->request
,
1003 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER
".ContentHashType", value
);
1005 g_signal_emit (handler
, signals
[HASHING_STARTED
], 0);
1007 g_io_scheduler_push_job (do_hash_job
, hash_data
, NULL
,
1008 G_PRIORITY_DEFAULT
, priv
->cancellable
);
1012 callbacks_data_free (gpointer user_data
)
1014 CallbacksData
*data
= user_data
;
1016 if (data
->handler
!= NULL
)
1017 g_object_unref (data
->handler
);
1019 g_slice_free (CallbacksData
, data
);
1023 set_content_hash_type_from_classes (EmpathyFTHandler
*handler
,
1029 GArray
*possible_values
;
1033 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
1035 possible_values
= g_array_new (TRUE
, TRUE
, sizeof (guint
));
1037 for (l
= classes
; l
!= NULL
; l
= l
->next
)
1040 v
= g_value_array_get_nth (class, 0);
1041 fprops
= g_value_get_boxed (v
);
1043 value
= tp_asv_get_uint32
1044 (fprops
, TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER
".ContentHashType",
1048 g_array_append_val (possible_values
, value
);
1051 if (possible_values
->len
== 0)
1053 /* there are no channel classes with hash support, disable it. */
1054 priv
->use_hash
= FALSE
;
1055 priv
->content_hash_type
= TP_FILE_HASH_TYPE_NONE
;
1060 priv
->use_hash
= TRUE
;
1062 if (possible_values
->len
== 1)
1064 priv
->content_hash_type
= g_array_index (possible_values
, guint
, 0);
1068 /* order the array and pick the first non zero, so that MD5
1069 * is the preferred value.
1071 g_array_sort (possible_values
, empathy_uint_compare
);
1073 if (g_array_index (possible_values
, guint
, 0) == 0)
1074 priv
->content_hash_type
= g_array_index (possible_values
, guint
, 1);
1076 priv
->content_hash_type
= g_array_index (possible_values
, guint
, 0);
1080 g_array_free (possible_values
, TRUE
);
1082 DEBUG ("Hash enabled %s; setting content hash type as %u",
1083 priv
->use_hash
? "True" : "False", priv
->content_hash_type
);
1087 find_ft_channel_classes_cb (GList
*channel_classes
,
1090 CallbacksData
*data
= user_data
;
1091 EmpathyFTHandler
*handler
= data
->handler
;
1092 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
1093 GError
*myerr
= NULL
;
1095 if (channel_classes
== NULL
)
1097 g_set_error_literal (&myerr
, EMPATHY_FT_ERROR_QUARK
,
1098 EMPATHY_FT_ERROR_NOT_SUPPORTED
,
1099 _("File transfer not supported by remote contact"));
1101 if (!g_cancellable_is_cancelled (priv
->cancellable
))
1102 g_cancellable_cancel (priv
->cancellable
);
1104 data
->callback (handler
, myerr
, data
->user_data
);
1105 g_clear_error (&myerr
);
1109 /* set whether we support hash and the type of it */
1110 set_content_hash_type_from_classes (handler
, channel_classes
);
1112 /* get back to the caller now */
1113 data
->callback (handler
, NULL
, data
->user_data
);
1116 callbacks_data_free (data
);
1120 ft_handler_complete_request (EmpathyFTHandler
*handler
)
1122 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
1124 /* populate the request table with all the known properties */
1125 ft_handler_populate_outgoing_request (handler
);
1128 /* start hashing the file */
1129 g_file_read_async (priv
->gfile
, G_PRIORITY_DEFAULT
,
1130 priv
->cancellable
, ft_handler_read_async_cb
, handler
);
1132 /* push directly the handler to the dispatcher */
1133 ft_handler_push_to_dispatcher (handler
);
1137 ft_handler_gfile_ready_cb (GObject
*source
,
1139 CallbacksData
*cb_data
)
1142 GError
*error
= NULL
;
1144 EmpathyFTHandlerPriv
*priv
= GET_PRIV (cb_data
->handler
);
1146 DEBUG ("Got GFileInfo.");
1148 info
= g_file_query_info_finish (priv
->gfile
, res
, &error
);
1153 if (g_file_info_get_file_type (info
) != G_FILE_TYPE_REGULAR
)
1155 error
= g_error_new_literal (EMPATHY_FT_ERROR_QUARK
,
1156 EMPATHY_FT_ERROR_INVALID_SOURCE_FILE
,
1157 _("The selected file is not a regular file"));
1161 priv
->total_bytes
= g_file_info_get_size (info
);
1162 if (priv
->total_bytes
== 0)
1164 error
= g_error_new_literal (EMPATHY_FT_ERROR_QUARK
,
1165 EMPATHY_FT_ERROR_EMPTY_SOURCE_FILE
,
1166 _("The selected file is empty"));
1170 priv
->content_type
= g_strdup (g_file_info_get_content_type (info
));
1171 priv
->filename
= g_strdup (g_file_info_get_display_name (info
));
1172 g_file_info_get_modification_time (info
, &mtime
);
1173 priv
->mtime
= mtime
.tv_sec
;
1174 priv
->transferred_bytes
= 0;
1175 priv
->description
= NULL
;
1177 g_object_unref (info
);
1182 if (!g_cancellable_is_cancelled (priv
->cancellable
))
1183 g_cancellable_cancel (priv
->cancellable
);
1185 cb_data
->callback (cb_data
->handler
, error
, cb_data
->user_data
);
1186 g_error_free (error
);
1188 callbacks_data_free (cb_data
);
1192 /* see if FT/hashing are allowed */
1193 empathy_dispatcher_find_requestable_channel_classes_async
1194 (priv
->dispatcher
, empathy_contact_get_connection (priv
->contact
),
1195 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER
, TP_HANDLE_TYPE_CONTACT
,
1196 find_ft_channel_classes_cb
, cb_data
,
1197 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER
".ContentHashType", NULL
);
1202 contact_factory_contact_cb (EmpathyTpContactFactory
*factory
,
1203 EmpathyContact
*contact
,
1204 const GError
*error
,
1206 GObject
*weak_object
)
1208 CallbacksData
*cb_data
= user_data
;
1209 EmpathyFTHandler
*handler
= EMPATHY_FT_HANDLER (weak_object
);
1210 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
1214 if (!g_cancellable_is_cancelled (priv
->cancellable
))
1215 g_cancellable_cancel (priv
->cancellable
);
1217 cb_data
->callback (handler
, (GError
*) error
, cb_data
->user_data
);
1218 callbacks_data_free (cb_data
);
1222 priv
->contact
= g_object_ref (contact
);
1224 cb_data
->callback (handler
, NULL
, cb_data
->user_data
);
1228 channel_get_all_properties_cb (TpProxy
*proxy
,
1229 GHashTable
*properties
,
1230 const GError
*error
,
1232 GObject
*weak_object
)
1234 CallbacksData
*cb_data
= user_data
;
1235 EmpathyFTHandler
*handler
= EMPATHY_FT_HANDLER (weak_object
);
1236 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
1237 EmpathyTpContactFactory
*c_factory
;
1242 if (!g_cancellable_is_cancelled (priv
->cancellable
))
1243 g_cancellable_cancel (priv
->cancellable
);
1245 cb_data
->callback (handler
, (GError
*) error
, cb_data
->user_data
);
1247 callbacks_data_free (cb_data
);
1251 priv
->total_bytes
= g_value_get_uint64 (
1252 g_hash_table_lookup (properties
, "Size"));
1254 priv
->transferred_bytes
= g_value_get_uint64 (
1255 g_hash_table_lookup (properties
, "TransferredBytes"));
1257 priv
->filename
= g_value_dup_string (
1258 g_hash_table_lookup (properties
, "Filename"));
1260 priv
->content_hash
= g_value_dup_string (
1261 g_hash_table_lookup (properties
, "ContentHash"));
1263 priv
->content_hash_type
= g_value_get_uint (
1264 g_hash_table_lookup (properties
, "ContentHashType"));
1266 priv
->content_type
= g_value_dup_string (
1267 g_hash_table_lookup (properties
, "ContentType"));
1269 priv
->description
= g_value_dup_string (
1270 g_hash_table_lookup (properties
, "Description"));
1272 c_factory
= empathy_tp_contact_factory_dup_singleton
1273 (tp_channel_borrow_connection (TP_CHANNEL (proxy
)));
1274 c_handle
= tp_channel_get_handle (TP_CHANNEL (proxy
), NULL
);
1275 empathy_tp_contact_factory_get_from_handle (c_factory
, c_handle
,
1276 contact_factory_contact_cb
, cb_data
, callbacks_data_free
,
1277 G_OBJECT (handler
));
1279 g_object_unref (c_factory
);
1282 /* public methods */
1285 * empathy_ft_handler_new_outgoing:
1286 * @contact: the #EmpathyContact to send @source to
1287 * @source: the #GFile to send
1288 * @callback: callback to be called when the handler has been created
1289 * @user_data: user data to be passed to @callback
1291 * Triggers the creation of a new #EmpathyFTHandler for an outgoing transfer.
1294 empathy_ft_handler_new_outgoing (EmpathyContact
*contact
,
1296 EmpathyFTHandlerReadyCallback callback
,
1299 EmpathyFTHandler
*handler
;
1300 CallbacksData
*data
;
1301 EmpathyFTHandlerPriv
*priv
;
1303 DEBUG ("New handler outgoing");
1305 g_return_if_fail (EMPATHY_IS_CONTACT (contact
));
1306 g_return_if_fail (G_IS_FILE (source
));
1308 handler
= g_object_new (EMPATHY_TYPE_FT_HANDLER
,
1309 "contact", contact
, "gfile", source
, NULL
);
1311 priv
= GET_PRIV (handler
);
1313 data
= g_slice_new0 (CallbacksData
);
1314 data
->callback
= callback
;
1315 data
->user_data
= user_data
;
1316 data
->handler
= g_object_ref (handler
);
1318 /* start collecting info about the file */
1319 g_file_query_info_async (priv
->gfile
,
1320 G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME
","
1321 G_FILE_ATTRIBUTE_STANDARD_SIZE
","
1322 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
","
1323 G_FILE_ATTRIBUTE_STANDARD_TYPE
","
1324 G_FILE_ATTRIBUTE_TIME_MODIFIED
,
1325 G_FILE_QUERY_INFO_NONE
, G_PRIORITY_DEFAULT
,
1326 NULL
, (GAsyncReadyCallback
) ft_handler_gfile_ready_cb
, data
);
1330 * empathy_ft_handler_new_incoming:
1331 * @tp_file: the #EmpathyTpFile wrapping the incoming channel
1332 * @callback: callback to be called when the handler has been created
1333 * @user_data: user data to be passed to @callback
1335 * Triggers the creation of a new #EmpathyFTHandler for an incoming transfer.
1336 * Note that for the handler to be useful, you will have to set a destination
1337 * file with empathy_ft_handler_incoming_set_destination() after the handler
1341 empathy_ft_handler_new_incoming (EmpathyTpFile
*tp_file
,
1342 EmpathyFTHandlerReadyCallback callback
,
1345 EmpathyFTHandler
*handler
;
1347 CallbacksData
*data
;
1349 g_return_if_fail (EMPATHY_IS_TP_FILE (tp_file
));
1351 handler
= g_object_new (EMPATHY_TYPE_FT_HANDLER
,
1352 "tp-file", tp_file
, NULL
);
1354 g_object_get (tp_file
, "channel", &channel
, NULL
);
1356 data
= g_slice_new0 (CallbacksData
);
1357 data
->callback
= callback
;
1358 data
->user_data
= user_data
;
1359 data
->handler
= g_object_ref (handler
);
1361 tp_cli_dbus_properties_call_get_all (channel
,
1362 -1, TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER
,
1363 channel_get_all_properties_cb
, data
, NULL
, G_OBJECT (handler
));
1367 * empathy_ft_handler_start_transfer:
1368 * @handler: an #EmpathyFTHandler
1370 * Starts the transfer machinery. After this call, the transfer and hashing
1371 * signals will be emitted by the handler.
1374 empathy_ft_handler_start_transfer (EmpathyFTHandler
*handler
)
1376 EmpathyFTHandlerPriv
*priv
;
1378 g_return_if_fail (EMPATHY_IS_FT_HANDLER (handler
));
1380 priv
= GET_PRIV (handler
);
1382 if (priv
->tpfile
== NULL
)
1384 ft_handler_complete_request (handler
);
1388 /* TODO: add support for resume. */
1389 empathy_tp_file_accept (priv
->tpfile
, 0, priv
->gfile
, priv
->cancellable
,
1390 ft_transfer_progress_callback
, handler
,
1391 ft_transfer_operation_callback
, handler
);
1396 * empathy_ft_handler_cancel_transfer:
1397 * @handler: an #EmpathyFTHandler
1399 * Cancels an ongoing handler operation. Note that this doesn't destroy
1400 * the object, which will keep all the properties, altough it won't be able
1401 * to do any more I/O.
1404 empathy_ft_handler_cancel_transfer (EmpathyFTHandler
*handler
)
1406 EmpathyFTHandlerPriv
*priv
;
1408 g_return_if_fail (EMPATHY_IS_FT_HANDLER (handler
));
1410 priv
= GET_PRIV (handler
);
1412 /* if we don't have an EmpathyTpFile, we are hashing, so
1413 * we can just cancel the GCancellable to stop it.
1415 if (priv
->tpfile
== NULL
)
1416 g_cancellable_cancel (priv
->cancellable
);
1418 empathy_tp_file_cancel (priv
->tpfile
);
1422 * empathy_ft_handler_incoming_set_destination:
1423 * @handler: an #EmpathyFTHandler
1424 * @destination: the #GFile where the transfer should be saved
1426 * Sets the destination of the incoming handler to be @destination.
1427 * Note that calling this method is mandatory before starting the transfer
1428 * for incoming handlers.
1431 empathy_ft_handler_incoming_set_destination (EmpathyFTHandler
*handler
,
1434 EmpathyFTHandlerPriv
*priv
;
1436 g_return_if_fail (EMPATHY_IS_FT_HANDLER (handler
));
1437 g_return_if_fail (G_IS_FILE (destination
));
1439 priv
= GET_PRIV (handler
);
1441 g_object_set (handler
, "gfile", destination
, NULL
);
1443 /* check if hash is supported. if it isn't, set use_hash to FALSE
1444 * anyway, so that clients won't be expecting us to checksum.
1446 if (EMP_STR_EMPTY (priv
->content_hash
) ||
1447 priv
->content_hash_type
== TP_FILE_HASH_TYPE_NONE
)
1448 priv
->use_hash
= FALSE
;
1450 priv
->use_hash
= TRUE
;
1454 * empathy_ft_handler_get_filename:
1455 * @handler: an #EmpathyFTHandler
1457 * Returns the name of the file being transferred.
1459 * Return value: the name of the file being transferred
1462 empathy_ft_handler_get_filename (EmpathyFTHandler
*handler
)
1464 EmpathyFTHandlerPriv
*priv
;
1466 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), NULL
);
1468 priv
= GET_PRIV (handler
);
1470 return priv
->filename
;
1474 * empathy_ft_handler_get_content_type:
1475 * @handler: an #EmpathyFTHandler
1477 * Returns the content type of the file being transferred.
1479 * Return value: the content type of the file being transferred
1482 empathy_ft_handler_get_content_type (EmpathyFTHandler
*handler
)
1484 EmpathyFTHandlerPriv
*priv
;
1486 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), NULL
);
1488 priv
= GET_PRIV (handler
);
1490 return priv
->content_type
;
1494 * empathy_ft_handler_get_contact:
1495 * @handler: an #EmpathyFTHandler
1497 * Returns the remote #EmpathyContact at the other side of the transfer.
1499 * Return value: the remote #EmpathyContact for @handler
1502 empathy_ft_handler_get_contact (EmpathyFTHandler
*handler
)
1504 EmpathyFTHandlerPriv
*priv
;
1506 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), NULL
);
1508 priv
= GET_PRIV (handler
);
1510 return priv
->contact
;
1514 * empathy_ft_handler_get_gfile:
1515 * @handler: an #EmpathyFTHandler
1517 * Returns the #GFile where the transfer is being read/saved.
1519 * Return value: the #GFile where the transfer is being read/saved
1522 empathy_ft_handler_get_gfile (EmpathyFTHandler
*handler
)
1524 EmpathyFTHandlerPriv
*priv
;
1526 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), NULL
);
1528 priv
= GET_PRIV (handler
);
1534 * empathy_ft_handler_get_use_hash:
1535 * @handler: an #EmpathyFTHandler
1537 * Returns whether @handler has checksumming enabled. This can depend on
1538 * the CM and the remote contact capabilities.
1540 * Return value: %TRUE if the handler has checksumming enabled,
1544 empathy_ft_handler_get_use_hash (EmpathyFTHandler
*handler
)
1546 EmpathyFTHandlerPriv
*priv
;
1548 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), FALSE
);
1550 priv
= GET_PRIV (handler
);
1552 return priv
->use_hash
;
1556 * empathy_ft_handler_is_incoming:
1557 * @handler: an #EmpathyFTHandler
1559 * Returns whether @handler is incoming or outgoing.
1561 * Return value: %TRUE if the handler is incoming, %FALSE otherwise.
1564 empathy_ft_handler_is_incoming (EmpathyFTHandler
*handler
)
1566 EmpathyFTHandlerPriv
*priv
;
1568 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), FALSE
);
1570 priv
= GET_PRIV (handler
);
1572 if (priv
->tpfile
== NULL
)
1575 return empathy_tp_file_is_incoming (priv
->tpfile
);
1579 * empathy_ft_handler_get_transferred_bytes:
1580 * @handler: an #EmpathyFTHandler
1582 * Returns the number of bytes already transferred by the handler.
1584 * Return value: the number of bytes already transferred by the handler.
1587 empathy_ft_handler_get_transferred_bytes (EmpathyFTHandler
*handler
)
1589 EmpathyFTHandlerPriv
*priv
;
1591 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), 0);
1593 priv
= GET_PRIV (handler
);
1595 return priv
->transferred_bytes
;
1599 * empathy_ft_handler_get_total_bytes:
1600 * @handler: an #EmpathyFTHandler
1602 * Returns the total size of the file being transferred by the handler.
1604 * Return value: a number of bytes indicating the total size of the file being
1605 * transferred by the handler.
1608 empathy_ft_handler_get_total_bytes (EmpathyFTHandler
*handler
)
1610 EmpathyFTHandlerPriv
*priv
;
1612 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), 0);
1614 priv
= GET_PRIV (handler
);
1616 return priv
->total_bytes
;
1620 * empathy_ft_handler_is_completed:
1621 * @handler: an #EmpathyFTHandler
1623 * Returns whether the transfer for @handler has been completed succesfully.
1625 * Return value: %TRUE if the handler has been transferred correctly, %FALSE
1629 empathy_ft_handler_is_completed (EmpathyFTHandler
*handler
)
1631 EmpathyFTHandlerPriv
*priv
;
1633 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), FALSE
);
1635 priv
= GET_PRIV (handler
);
1637 return priv
->is_completed
;
1641 * empathy_ft_handler_is_cancelled:
1642 * @handler: an #EmpathyFTHandler
1644 * Returns whether the transfer for @handler has been cancelled or has stopped
1647 * Return value: %TRUE if the transfer for @handler has been cancelled
1648 * or has stopped due to an error, %FALSE otherwise.
1651 empathy_ft_handler_is_cancelled (EmpathyFTHandler
*handler
)
1653 EmpathyFTHandlerPriv
*priv
;
1655 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), FALSE
);
1657 priv
= GET_PRIV (handler
);
1659 return g_cancellable_is_cancelled (priv
->cancellable
);