2 * empathy-ft-handler.c - Source for EmpathyFTHandler
3 * Copyright © 2009, 2012 Collabora Ltd.
4 * Copyright © 2009 Frédéric Péters
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 * Author: Cosimo Cecchi <cosimo.cecchi@collabora.co.uk>
23 /* empathy-ft-handler.c */
26 #include <glib/gi18n.h>
27 #include <telepathy-glib/account-channel-request.h>
28 #include <telepathy-glib/util.h>
29 #include <telepathy-glib/dbus.h>
30 #include <telepathy-glib/interfaces.h>
32 #include <tubes/file-transfer-helper.h>
37 * SECTION:empathy-ft-handler
38 * @title: EmpathyFTHandler
39 * @short_description: an object representing a File Transfer
40 * @include: libempathy/empathy-ft-handler
42 * #EmpathyFTHandler is the object which represents a File Transfer with all
44 * The creation of an #EmpathyFTHandler is done with
45 * empathy_ft_handler_new_outgoing() or empathy_ft_handler_new_incoming(),
46 * even though clients should not need to call them directly, as
47 * #EmpathyFTFactory does it for them. Remember that for the file transfer
48 * to work with an incoming handler,
49 * empathy_ft_handler_incoming_set_destination() should be called after
50 * empathy_ft_handler_new_incoming(). #EmpathyFTFactory does this
52 * It's important to note that, as the creation of the handlers is async, once
53 * an handler is created, it already has all the interesting properties set,
54 * like filename, total bytes, content type and so on, making it useful
55 * to be displayed in an UI.
56 * The transfer API works like a state machine; it has three signals,
57 * ::transfer-started, ::transfer-progress, ::transfer-done, which will be
58 * emitted in the relevant phases.
59 * In addition, if the handler is created with checksumming enabled,
60 * other three signals (::hashing-started, ::hashing-progress, ::hashing-done)
61 * will be emitted before or after the transfer, depending on the direction
62 * (respectively outgoing and incoming) of the handler.
63 * At any time between the call to empathy_ft_handler_start_transfer() and
64 * the last signal, a ::transfer-error can be emitted, indicating that an
65 * error has happened in the operation. The message of the error is localized
69 G_DEFINE_TYPE (EmpathyFTHandler
, empathy_ft_handler
, G_TYPE_OBJECT
)
71 #define BUFFER_SIZE 4096
81 PROP_MODIFICATION_TIME
,
83 PROP_TRANSFERRED_BYTES
,
100 GError
*error
/* comment to make the style checker happy */;
105 EmpathyFTHandler
*handler
;
109 EmpathyFTHandlerReadyCallback callback
;
111 EmpathyFTHandler
*handler
;
115 struct _EmpathyFTHandlerPriv
{
116 gboolean dispose_run
;
119 TpFileTransferChannel
*channel
;
120 GCancellable
*cancellable
;
123 /* request for the new transfer */
126 /* transfer properties */
133 guint64 transferred_bytes
;
136 TpFileHashType content_hash_type
;
139 gint64 user_action_time
;
143 guint remaining_time
;
144 gint64 last_update_time
;
146 gboolean is_completed
;
149 static guint signals
[LAST_SIGNAL
] = { 0 };
151 static gboolean
do_hash_job_incoming (GIOSchedulerJob
*job
,
152 GCancellable
*cancellable
, gpointer user_data
);
154 /* GObject implementations */
156 do_get_property (GObject
*object
,
161 EmpathyFTHandler
*self
= EMPATHY_FT_HANDLER (object
);
162 EmpathyFTHandlerPriv
*priv
= self
->priv
;
167 g_value_set_object (value
, priv
->account
);
170 g_value_set_object (value
, priv
->contact
);
172 case PROP_CONTENT_TYPE
:
173 g_value_set_string (value
, priv
->content_type
);
175 case PROP_DESCRIPTION
:
176 g_value_set_string (value
, priv
->description
);
179 g_value_set_string (value
, priv
->filename
);
181 case PROP_MODIFICATION_TIME
:
182 g_value_set_uint64 (value
, priv
->mtime
);
184 case PROP_TOTAL_BYTES
:
185 g_value_set_uint64 (value
, priv
->total_bytes
);
187 case PROP_TRANSFERRED_BYTES
:
188 g_value_set_uint64 (value
, priv
->transferred_bytes
);
191 g_value_set_object (value
, priv
->gfile
);
194 g_value_set_object (value
, priv
->channel
);
196 case PROP_USER_ACTION_TIME
:
197 g_value_set_int64 (value
, priv
->user_action_time
);
200 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
205 do_set_property (GObject
*object
,
210 EmpathyFTHandler
*self
= EMPATHY_FT_HANDLER (object
);
211 EmpathyFTHandlerPriv
*priv
= self
->priv
;
216 priv
->account
= g_value_dup_object (value
);
219 priv
->contact
= g_value_dup_object (value
);
221 case PROP_CONTENT_TYPE
:
222 priv
->content_type
= g_value_dup_string (value
);
224 case PROP_DESCRIPTION
:
225 priv
->description
= g_value_dup_string (value
);
228 priv
->filename
= g_value_dup_string (value
);
230 case PROP_MODIFICATION_TIME
:
231 priv
->mtime
= g_value_get_uint64 (value
);
233 case PROP_TOTAL_BYTES
:
234 priv
->total_bytes
= g_value_get_uint64 (value
);
236 case PROP_TRANSFERRED_BYTES
:
237 priv
->transferred_bytes
= g_value_get_uint64 (value
);
240 priv
->gfile
= g_value_dup_object (value
);
243 priv
->channel
= g_value_dup_object (value
);
245 case PROP_USER_ACTION_TIME
:
246 priv
->user_action_time
= g_value_get_int64 (value
);
249 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
254 do_dispose (GObject
*object
)
256 EmpathyFTHandler
*self
= EMPATHY_FT_HANDLER (object
);
257 EmpathyFTHandlerPriv
*priv
= self
->priv
;
259 if (priv
->dispose_run
)
262 priv
->dispose_run
= TRUE
;
264 if (priv
->account
!= NULL
) {
265 g_object_unref (priv
->account
);
266 priv
->account
= NULL
;
269 if (priv
->contact
!= NULL
) {
270 g_object_unref (priv
->contact
);
271 priv
->contact
= NULL
;
274 if (priv
->gfile
!= NULL
) {
275 g_object_unref (priv
->gfile
);
279 if (priv
->channel
!= NULL
) {
280 tp_channel_close_async (TP_CHANNEL (priv
->channel
), NULL
, NULL
);
281 g_object_unref (priv
->channel
);
282 priv
->channel
= NULL
;
285 if (priv
->cancellable
!= NULL
) {
286 g_object_unref (priv
->cancellable
);
287 priv
->cancellable
= NULL
;
290 if (priv
->request
!= NULL
)
292 g_hash_table_unref (priv
->request
);
293 priv
->request
= NULL
;
296 G_OBJECT_CLASS (empathy_ft_handler_parent_class
)->dispose (object
);
300 do_finalize (GObject
*object
)
302 EmpathyFTHandler
*self
= EMPATHY_FT_HANDLER (object
);
303 EmpathyFTHandlerPriv
*priv
= self
->priv
;
305 DEBUG ("%p", object
);
307 g_free (priv
->content_type
);
308 priv
->content_type
= NULL
;
310 g_free (priv
->filename
);
311 priv
->filename
= NULL
;
313 g_free (priv
->description
);
314 priv
->description
= NULL
;
316 g_free (priv
->content_hash
);
317 priv
->content_hash
= NULL
;
319 g_free (priv
->service_name
);
320 priv
->service_name
= NULL
;
322 G_OBJECT_CLASS (empathy_ft_handler_parent_class
)->finalize (object
);
326 empathy_ft_handler_class_init (EmpathyFTHandlerClass
*klass
)
328 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
329 GParamSpec
*param_spec
;
331 g_type_class_add_private (klass
, sizeof (EmpathyFTHandlerPriv
));
333 object_class
->get_property
= do_get_property
;
334 object_class
->set_property
= do_set_property
;
335 object_class
->dispose
= do_dispose
;
336 object_class
->finalize
= do_finalize
;
341 * EmpathyFTHandler:account:
343 * The local #TpAccount for the file transfer
345 param_spec
= g_param_spec_object ("account",
346 "account", "The remote account",
348 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
| G_PARAM_CONSTRUCT_ONLY
);
349 g_object_class_install_property (object_class
, PROP_ACCOUNT
, param_spec
);
352 * EmpathyFTHandler:contact:
354 * The remote #TpContact for the transfer
356 param_spec
= g_param_spec_object ("contact",
357 "contact", "The remote contact",
359 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
| G_PARAM_CONSTRUCT_ONLY
);
360 g_object_class_install_property (object_class
, PROP_CONTACT
, param_spec
);
363 * EmpathyFTHandler:content-type:
365 * The content type of the file being transferred
367 param_spec
= g_param_spec_string ("content-type",
368 "content-type", "The content type of the file", NULL
,
369 G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
370 g_object_class_install_property (object_class
,
371 PROP_CONTENT_TYPE
, param_spec
);
374 * EmpathyFTHandler:description:
376 * The description of the file being transferred
378 param_spec
= g_param_spec_string ("description",
379 "description", "The description of the file", NULL
,
380 G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
381 g_object_class_install_property (object_class
,
382 PROP_DESCRIPTION
, param_spec
);
385 * EmpathyFTHandler:filename:
387 * The name of the file being transferred
389 param_spec
= g_param_spec_string ("filename",
390 "filename", "The name of the file", NULL
,
391 G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
392 g_object_class_install_property (object_class
,
393 PROP_FILENAME
, param_spec
);
396 * EmpathyFTHandler:modification-time:
398 * The modification time of the file being transferred
400 param_spec
= g_param_spec_uint64 ("modification-time",
401 "modification-time", "The mtime of the file", 0,
402 G_MAXUINT64
, 0, G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
403 g_object_class_install_property (object_class
,
404 PROP_MODIFICATION_TIME
, param_spec
);
407 * EmpathyFTHandler:total-bytes:
409 * The size (in bytes) of the file being transferred
411 param_spec
= g_param_spec_uint64 ("total-bytes",
412 "total-bytes", "The size of the file", 0,
413 G_MAXUINT64
, 0, G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
414 g_object_class_install_property (object_class
,
415 PROP_TOTAL_BYTES
, param_spec
);
418 * EmpathyFTHandler:transferred-bytes:
420 * The number of the bytes already transferred
422 param_spec
= g_param_spec_uint64 ("transferred-bytes",
423 "transferred-bytes", "The number of bytes already transferred", 0,
424 G_MAXUINT64
, 0, G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
425 g_object_class_install_property (object_class
,
426 PROP_TRANSFERRED_BYTES
, param_spec
);
429 * EmpathyFTHandler:gfile:
431 * The #GFile object where the transfer actually happens
433 param_spec
= g_param_spec_object ("gfile",
434 "gfile", "The GFile we're handling",
436 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
437 g_object_class_install_property (object_class
, PROP_G_FILE
, param_spec
);
440 * EmpathyFTHandler:channel:
442 * The underlying #TpFileTransferChannel managing the transfer
444 param_spec
= g_param_spec_object ("channel",
445 "channel", "The file transfer channel",
446 TP_TYPE_FILE_TRANSFER_CHANNEL
,
447 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
| G_PARAM_CONSTRUCT_ONLY
);
448 g_object_class_install_property (object_class
, PROP_CHANNEL
, param_spec
);
450 param_spec
= g_param_spec_int64 ("user-action-time", "user action time",
453 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
| G_PARAM_CONSTRUCT_ONLY
);
454 g_object_class_install_property (object_class
, PROP_USER_ACTION_TIME
,
460 * EmpathyFTHandler::transfer-started
461 * @handler: the object which has received the signal
462 * @channel: the #TpFileTransferChannel for which the transfer has started
464 * This signal is emitted when the actual transfer starts.
466 signals
[TRANSFER_STARTED
] =
467 g_signal_new ("transfer-started", G_TYPE_FROM_CLASS (klass
),
468 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
469 g_cclosure_marshal_generic
,
471 1, TP_TYPE_FILE_TRANSFER_CHANNEL
);
474 * EmpathyFTHandler::transfer-done
475 * @handler: the object which has received the signal
476 * @channel: the #TpFileTransferChannel for which the transfer has started
478 * This signal will be emitted when the actual transfer is completed
481 signals
[TRANSFER_DONE
] =
482 g_signal_new ("transfer-done", G_TYPE_FROM_CLASS (klass
),
483 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
484 g_cclosure_marshal_generic
,
486 1, TP_TYPE_FILE_TRANSFER_CHANNEL
);
489 * EmpathyFTHandler::transfer-error
490 * @handler: the object which has received the signal
493 * This signal can be emitted anytime between the call to
494 * empathy_ft_handler_start_transfer() and the last expected signal
495 * (::transfer-done or ::hashing-done), and it's guaranteed to be the last
496 * signal coming from the handler, meaning that no other operation will
497 * take place after this signal.
499 signals
[TRANSFER_ERROR
] =
500 g_signal_new ("transfer-error", G_TYPE_FROM_CLASS (klass
),
501 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
502 g_cclosure_marshal_generic
,
507 * EmpathyFTHandler::transfer-progress
508 * @handler: the object which has received the signal
509 * @current_bytes: the bytes currently transferred
510 * @total_bytes: the total bytes of the handler
511 * @remaining_time: the number of seconds remaining for the transfer
513 * @speed: the current speed of the transfer (in KB/s)
515 * This signal is emitted to notify clients of the progress of the
518 signals
[TRANSFER_PROGRESS
] =
519 g_signal_new ("transfer-progress", G_TYPE_FROM_CLASS (klass
),
520 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
521 g_cclosure_marshal_generic
,
523 4, G_TYPE_UINT64
, G_TYPE_UINT64
, G_TYPE_UINT
, G_TYPE_DOUBLE
);
526 * EmpathyFTHandler::hashing-started
527 * @handler: the object which has received the signal
529 * This signal is emitted when the hashing operation of the handler
530 * is started. Note that this might happen or not, depending on the CM
531 * and remote contact capabilities. Clients shoud use
532 * empathy_ft_handler_get_use_hash() before calling
533 * empathy_ft_handler_start_transfer() to know whether they should connect
536 signals
[HASHING_STARTED
] =
537 g_signal_new ("hashing-started", G_TYPE_FROM_CLASS (klass
),
538 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
539 g_cclosure_marshal_generic
,
543 * EmpathyFTHandler::hashing-progress
544 * @handler: the object which has received the signal
545 * @current_bytes: the bytes currently hashed
546 * @total_bytes: the total bytes of the handler
548 * This signal is emitted to notify clients of the progress of the
551 signals
[HASHING_PROGRESS
] =
552 g_signal_new ("hashing-progress", G_TYPE_FROM_CLASS (klass
),
553 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
554 g_cclosure_marshal_generic
,
556 2, G_TYPE_UINT64
, G_TYPE_UINT64
);
559 * EmpathyFTHandler::hashing-done
560 * @handler: the object which has received the signal
562 * This signal is emitted when the hashing operation of the handler
565 signals
[HASHING_DONE
] =
566 g_signal_new ("hashing-done", G_TYPE_FROM_CLASS (klass
),
567 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
568 g_cclosure_marshal_generic
,
573 empathy_ft_handler_init (EmpathyFTHandler
*self
)
575 EmpathyFTHandlerPriv
*priv
= G_TYPE_INSTANCE_GET_PRIVATE (self
,
576 EMPATHY_TYPE_FT_HANDLER
, EmpathyFTHandlerPriv
);
579 priv
->cancellable
= g_cancellable_new ();
582 /* private functions */
585 hash_data_free (HashingData
*data
)
587 g_free (data
->buffer
);
589 if (data
->stream
!= NULL
)
590 g_object_unref (data
->stream
);
592 if (data
->checksum
!= NULL
)
593 g_checksum_free (data
->checksum
);
595 if (data
->error
!= NULL
)
596 g_error_free (data
->error
);
598 if (data
->handler
!= NULL
)
599 g_object_unref (data
->handler
);
601 g_slice_free (HashingData
, data
);
605 tp_file_hash_to_g_checksum (TpFileHashType type
)
607 GChecksumType retval
;
611 case TP_FILE_HASH_TYPE_MD5
:
612 retval
= G_CHECKSUM_MD5
;
614 case TP_FILE_HASH_TYPE_SHA1
:
615 retval
= G_CHECKSUM_SHA1
;
617 case TP_FILE_HASH_TYPE_SHA256
:
618 retval
= G_CHECKSUM_SHA256
;
620 case TP_FILE_HASH_TYPE_NONE
:
622 g_assert_not_reached ();
630 check_hash_incoming (EmpathyFTHandler
*handler
)
632 HashingData
*hash_data
;
633 EmpathyFTHandlerPriv
*priv
= handler
->priv
;
635 if (!tp_str_empty (priv
->content_hash
))
637 hash_data
= g_slice_new0 (HashingData
);
638 hash_data
->total_bytes
= priv
->total_bytes
;
639 hash_data
->handler
= g_object_ref (handler
);
640 hash_data
->checksum
= g_checksum_new
641 (tp_file_hash_to_g_checksum (priv
->content_hash_type
));
643 g_signal_emit (handler
, signals
[HASHING_STARTED
], 0);
645 g_io_scheduler_push_job (do_hash_job_incoming
, hash_data
, NULL
,
646 G_PRIORITY_DEFAULT
, priv
->cancellable
);
651 emit_error_signal (EmpathyFTHandler
*handler
,
654 EmpathyFTHandlerPriv
*priv
= handler
->priv
;
656 DEBUG ("Error in transfer: %s\n", error
->message
);
658 if (!g_cancellable_is_cancelled (priv
->cancellable
))
659 g_cancellable_cancel (priv
->cancellable
);
661 g_signal_emit (handler
, signals
[TRANSFER_ERROR
], 0, error
);
665 time_get_current (void)
670 now
= g_date_time_new_now_utc ();
671 result
= g_date_time_to_unix (now
);
672 g_date_time_unref (now
);
678 update_remaining_time_and_speed (EmpathyFTHandler
*handler
,
679 guint64 transferred_bytes
)
681 EmpathyFTHandlerPriv
*priv
= handler
->priv
;
682 gint64 elapsed_time
, current_time
;
683 guint64 transferred
, last_transferred_bytes
;
687 last_transferred_bytes
= priv
->transferred_bytes
;
688 priv
->transferred_bytes
= transferred_bytes
;
690 current_time
= time_get_current ();
691 elapsed_time
= current_time
- priv
->last_update_time
;
693 if (elapsed_time
>= 1)
695 transferred
= transferred_bytes
- last_transferred_bytes
;
696 speed
= (gdouble
) transferred
/ (gdouble
) elapsed_time
;
697 remaining_time
= (priv
->total_bytes
- priv
->transferred_bytes
) / speed
;
699 priv
->remaining_time
= remaining_time
;
700 priv
->last_update_time
= current_time
;
705 ft_transfer_transferred_bytes_cb (TpFileTransferChannel
*channel
,
707 EmpathyFTHandler
*handler
)
709 EmpathyFTHandlerPriv
*priv
= handler
->priv
;
712 (void)pspec
; /* suppress unused-parameter warning */
714 if (empathy_ft_handler_is_cancelled (handler
))
717 bytes
= tp_file_transfer_channel_get_transferred_bytes (channel
);
719 if (priv
->transferred_bytes
== 0)
721 priv
->last_update_time
= time_get_current ();
722 g_signal_emit (handler
, signals
[TRANSFER_STARTED
], 0, channel
);
725 if (priv
->transferred_bytes
!= bytes
)
727 update_remaining_time_and_speed (handler
, bytes
);
729 g_signal_emit (handler
, signals
[TRANSFER_PROGRESS
], 0,
730 bytes
, priv
->total_bytes
, priv
->remaining_time
,
736 ft_transfer_provide_cb (GObject
*source
,
737 GAsyncResult
*result
,
740 TpFileTransferChannel
*channel
= TP_FILE_TRANSFER_CHANNEL (source
);
741 EmpathyFTHandler
*handler
= user_data
;
742 GError
*error
= NULL
;
744 if (!tp_file_transfer_channel_provide_file_finish (channel
, result
, &error
))
746 emit_error_signal (handler
, error
);
747 g_clear_error (&error
);
752 ft_transfer_accept_cb (GObject
*source
,
753 GAsyncResult
*result
,
756 TpFileTransferChannel
*channel
= TP_FILE_TRANSFER_CHANNEL (source
);
757 EmpathyFTHandler
*handler
= user_data
;
758 GError
*error
= NULL
;
760 if (!tp_file_transfer_channel_accept_file_finish (channel
, result
, &error
))
762 emit_error_signal (handler
, error
);
763 g_clear_error (&error
);
768 error_from_state_change_reason (TpFileTransferStateChangeReason reason
)
771 GError
*retval
= NULL
;
777 case TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE
:
778 string
= _("No reason was specified");
780 case TP_FILE_TRANSFER_STATE_CHANGE_REASON_REQUESTED
:
781 string
= _("The change in state was requested");
783 case TP_FILE_TRANSFER_STATE_CHANGE_REASON_LOCAL_STOPPED
:
784 string
= _("You canceled the file transfer");
786 case TP_FILE_TRANSFER_STATE_CHANGE_REASON_REMOTE_STOPPED
:
787 string
= _("The other participant canceled the file transfer");
789 case TP_FILE_TRANSFER_STATE_CHANGE_REASON_LOCAL_ERROR
:
790 string
= _("Error while trying to transfer the file");
792 case TP_FILE_TRANSFER_STATE_CHANGE_REASON_REMOTE_ERROR
:
793 string
= _("The other participant is unable to transfer the file");
796 string
= _("Unknown reason");
800 retval
= g_error_new_literal (EMPATHY_FT_ERROR_QUARK
,
801 EMPATHY_FT_ERROR_TP_ERROR
, string
);
807 ft_transfer_state_cb (TpFileTransferChannel
*channel
,
809 EmpathyFTHandler
*handler
)
811 EmpathyFTHandlerPriv
*priv
= handler
->priv
;
812 TpFileTransferStateChangeReason reason
;
813 TpFileTransferState state
= tp_file_transfer_channel_get_state (
816 (void)pspec
; /* suppress unused-parameter warning */
818 if (state
== TP_FILE_TRANSFER_STATE_COMPLETED
)
820 priv
->is_completed
= TRUE
;
821 g_signal_emit (handler
, signals
[TRANSFER_DONE
], 0, channel
);
823 tp_channel_close_async (TP_CHANNEL (channel
), NULL
, NULL
);
825 if (empathy_ft_handler_is_incoming (handler
) && priv
->use_hash
)
827 check_hash_incoming (handler
);
830 else if (state
== TP_FILE_TRANSFER_STATE_CANCELLED
)
832 GError
*error
= error_from_state_change_reason (reason
);
833 emit_error_signal (handler
, error
);
834 g_clear_error (&error
);
839 ft_handler_create_channel_cb (GObject
*source
,
840 GAsyncResult
*result
,
843 EmpathyFTHandler
*handler
= user_data
;
844 EmpathyFTHandlerPriv
*priv
= handler
->priv
;
845 GError
*error
= NULL
;
848 DEBUG ("Dispatcher create channel CB");
850 channel
= tp_account_channel_request_create_and_handle_channel_finish (
851 TP_ACCOUNT_CHANNEL_REQUEST (source
), result
, NULL
, &error
);
854 DEBUG ("Failed to request FT channel: %s", error
->message
);
856 g_cancellable_set_error_if_cancelled (priv
->cancellable
, &error
);
860 emit_error_signal (handler
, error
);
862 g_clear_object (&channel
);
863 g_error_free (error
);
867 priv
->channel
= TP_FILE_TRANSFER_CHANNEL (channel
);
869 tp_g_signal_connect_object (priv
->channel
, "notify::state",
870 G_CALLBACK (ft_transfer_state_cb
), handler
, 0);
871 tp_g_signal_connect_object (priv
->channel
, "notify::transferred-bytes",
872 G_CALLBACK (ft_transfer_transferred_bytes_cb
), handler
, 0);
874 tp_file_transfer_channel_provide_file_async (priv
->channel
, priv
->gfile
,
875 ft_transfer_provide_cb
, handler
);
879 ft_handler_push_to_dispatcher (EmpathyFTHandler
*handler
)
881 EmpathyFTHandlerPriv
*priv
= handler
->priv
;
882 TpAccountChannelRequest
*req
;
884 DEBUG ("Pushing request to the dispatcher");
886 req
= tp_account_channel_request_new (priv
->account
, priv
->request
,
887 priv
->user_action_time
);
889 tp_account_channel_request_create_and_handle_channel_async (req
, NULL
,
890 ft_handler_create_channel_cb
, handler
);
892 g_object_unref (req
);
896 ft_handler_populate_outgoing_request (EmpathyFTHandler
*handler
)
898 guint contact_handle
;
899 EmpathyFTHandlerPriv
*priv
= handler
->priv
;
902 contact_handle
= tp_contact_get_handle (priv
->contact
);
903 uri
= g_file_get_uri (priv
->gfile
);
905 priv
->request
= tp_asv_new (
906 TP_PROP_CHANNEL_CHANNEL_TYPE
, G_TYPE_STRING
,
907 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER
,
908 TP_PROP_CHANNEL_TARGET_HANDLE_TYPE
, G_TYPE_UINT
,
909 TP_HANDLE_TYPE_CONTACT
,
910 TP_PROP_CHANNEL_TARGET_HANDLE
, G_TYPE_UINT
,
912 TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_CONTENT_TYPE
, G_TYPE_STRING
,
914 TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_FILENAME
, G_TYPE_STRING
,
916 TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_SIZE
, G_TYPE_UINT64
,
918 TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_DATE
, G_TYPE_UINT64
,
920 TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_URI
, G_TYPE_STRING
, uri
,
923 if (priv
->service_name
!= NULL
)
924 tp_asv_set_string (priv
->request
, TP_PROP_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA_SERVICE_NAME
, priv
->service_name
);
926 if (priv
->description
!= NULL
)
927 tp_asv_set_string (priv
->request
, TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_DESCRIPTION
, priv
->description
);
933 hash_job_done (gpointer user_data
)
935 HashingData
*hash_data
= user_data
;
936 EmpathyFTHandler
*handler
= hash_data
->handler
;
937 EmpathyFTHandlerPriv
*priv
;
938 GError
*error
= NULL
;
940 DEBUG ("Closing stream after hashing.");
942 priv
= handler
->priv
;
944 if (hash_data
->error
!= NULL
)
946 error
= hash_data
->error
;
947 hash_data
->error
= NULL
;
951 DEBUG ("Got file hash %s", g_checksum_get_string (hash_data
->checksum
));
953 if (empathy_ft_handler_is_incoming (handler
))
955 if (g_strcmp0 (g_checksum_get_string (hash_data
->checksum
),
958 DEBUG ("Hash mismatch when checking incoming handler: "
959 "received %s, calculated %s", priv
->content_hash
,
960 g_checksum_get_string (hash_data
->checksum
));
962 error
= g_error_new_literal (EMPATHY_FT_ERROR_QUARK
,
963 EMPATHY_FT_ERROR_HASH_MISMATCH
,
964 _("File transfer completed, but the file was corrupted"));
969 DEBUG ("Hash verification matched, received %s, calculated %s",
971 g_checksum_get_string (hash_data
->checksum
));
976 /* set the checksum in the request...
977 * org.freedesktop.Telepathy.Channel.Type.FileTransfer.ContentHash
979 tp_asv_set_string (priv
->request
,
980 TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_CONTENT_HASH
,
981 g_checksum_get_string (hash_data
->checksum
));
988 emit_error_signal (handler
, error
);
989 g_clear_error (&error
);
993 g_signal_emit (handler
, signals
[HASHING_DONE
], 0);
995 if (!empathy_ft_handler_is_incoming (handler
))
996 /* the request is complete now, push it to the dispatcher */
997 ft_handler_push_to_dispatcher (handler
);
1000 hash_data_free (hash_data
);
1006 emit_hashing_progress (gpointer user_data
)
1008 HashingData
*hash_data
= user_data
;
1010 g_signal_emit (hash_data
->handler
, signals
[HASHING_PROGRESS
], 0,
1011 (guint64
) hash_data
->total_read
, (guint64
) hash_data
->total_bytes
);
1017 do_hash_job (GIOSchedulerJob
*job
,
1018 GCancellable
*cancellable
,
1021 HashingData
*hash_data
= user_data
;
1023 GError
*error
= NULL
;
1026 if (hash_data
->buffer
== NULL
)
1027 hash_data
->buffer
= g_malloc0 (BUFFER_SIZE
);
1029 bytes_read
= g_input_stream_read (hash_data
->stream
, hash_data
->buffer
,
1030 BUFFER_SIZE
, cancellable
, &error
);
1034 hash_data
->total_read
+= bytes_read
;
1036 /* we now have the chunk */
1039 g_checksum_update (hash_data
->checksum
, hash_data
->buffer
, bytes_read
);
1040 g_io_scheduler_job_send_to_mainloop_async (job
, emit_hashing_progress
,
1043 g_free (hash_data
->buffer
);
1044 hash_data
->buffer
= NULL
;
1050 g_input_stream_close (hash_data
->stream
, cancellable
, &error
);
1055 hash_data
->error
= error
;
1057 g_io_scheduler_job_send_to_mainloop_async (job
, hash_job_done
,
1064 do_hash_job_incoming (GIOSchedulerJob
*job
,
1065 GCancellable
*cancellable
,
1068 HashingData
*hash_data
= user_data
;
1069 EmpathyFTHandler
*handler
= hash_data
->handler
;
1070 EmpathyFTHandlerPriv
*priv
= handler
->priv
;
1071 GError
*error
= NULL
;
1073 DEBUG ("checking integrity for incoming handler");
1075 /* need to get the stream first */
1077 G_INPUT_STREAM (g_file_read (priv
->gfile
, cancellable
, &error
));
1081 hash_data
->error
= error
;
1082 g_io_scheduler_job_send_to_mainloop_async (job
, hash_job_done
,
1087 return do_hash_job (job
, cancellable
, user_data
);
1091 ft_handler_read_async_cb (GObject
*source
,
1095 GFileInputStream
*stream
;
1096 GError
*error
= NULL
;
1097 HashingData
*hash_data
;
1098 EmpathyFTHandler
*handler
= user_data
;
1099 EmpathyFTHandlerPriv
*priv
= handler
->priv
;
1101 (void)source
; /* suppress unused-parameter warning */
1103 DEBUG ("GFile read async CB.");
1105 stream
= g_file_read_finish (priv
->gfile
, res
, &error
);
1108 emit_error_signal (handler
, error
);
1109 g_clear_error (&error
);
1114 hash_data
= g_slice_new0 (HashingData
);
1115 hash_data
->stream
= G_INPUT_STREAM (stream
);
1116 hash_data
->total_bytes
= priv
->total_bytes
;
1117 hash_data
->handler
= g_object_ref (handler
);
1118 /* FIXME: MD5 is the only ContentHashType supported right now */
1119 hash_data
->checksum
= g_checksum_new (G_CHECKSUM_MD5
);
1121 tp_asv_set_uint32 (priv
->request
,
1122 TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_CONTENT_HASH_TYPE
,
1123 TP_FILE_HASH_TYPE_MD5
);
1125 g_signal_emit (handler
, signals
[HASHING_STARTED
], 0);
1127 g_io_scheduler_push_job (do_hash_job
, hash_data
, NULL
,
1128 G_PRIORITY_DEFAULT
, priv
->cancellable
);
1132 callbacks_data_free (gpointer user_data
)
1134 CallbacksData
*data
= user_data
;
1136 if (data
->handler
!= NULL
)
1137 g_object_unref (data
->handler
);
1139 g_slice_free (CallbacksData
, data
);
1147 return *(guint
*) a
- *(guint
*) b
;
1151 set_content_hash_type_from_classes (EmpathyFTHandler
*handler
,
1154 GArray
*possible_values
;
1157 EmpathyFTHandlerPriv
*priv
= handler
->priv
;
1158 gboolean support_ft
= FALSE
;
1161 possible_values
= g_array_new (TRUE
, TRUE
, sizeof (guint
));
1163 for (i
= 0; i
< classes
->len
; i
++)
1167 const gchar
*chan_type
;
1169 tp_value_array_unpack (g_ptr_array_index (classes
, i
), 2,
1172 chan_type
= tp_asv_get_string (fixed
, TP_PROP_CHANNEL_CHANNEL_TYPE
);
1174 if (tp_strdiff (chan_type
, TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER
))
1177 if (tp_asv_get_uint32 (fixed
, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE
, NULL
) !=
1178 TP_HANDLE_TYPE_CONTACT
)
1183 value
= tp_asv_get_uint32
1184 (fixed
, TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_CONTENT_HASH_TYPE
,
1188 g_array_append_val (possible_values
, value
);
1193 g_array_unref (possible_values
);
1197 if (possible_values
->len
== 0)
1199 /* there are no channel classes with hash support, disable it. */
1200 priv
->use_hash
= FALSE
;
1201 priv
->content_hash_type
= TP_FILE_HASH_TYPE_NONE
;
1206 priv
->use_hash
= TRUE
;
1208 if (possible_values
->len
== 1)
1210 priv
->content_hash_type
= g_array_index (possible_values
, guint
, 0);
1214 /* order the array and pick the first non zero, so that MD5
1215 * is the preferred value.
1217 g_array_sort (possible_values
, cmp_uint
);
1219 if (g_array_index (possible_values
, guint
, 0) == 0)
1220 priv
->content_hash_type
= g_array_index (possible_values
, guint
, 1);
1222 priv
->content_hash_type
= g_array_index (possible_values
, guint
, 0);
1226 g_array_unref (possible_values
);
1228 DEBUG ("Hash enabled %s; setting content hash type as %u",
1229 priv
->use_hash
? "True" : "False", priv
->content_hash_type
);
1235 check_hashing (CallbacksData
*data
)
1237 EmpathyFTHandler
*handler
= data
->handler
;
1238 EmpathyFTHandlerPriv
*priv
= handler
->priv
;
1239 GError
*myerr
= NULL
;
1240 TpCapabilities
*caps
;
1244 conn
= tp_account_get_connection (priv
->account
);
1246 caps
= tp_connection_get_capabilities (conn
);
1249 data
->callback (handler
, NULL
, data
->user_data
);
1253 classes
= tp_capabilities_get_channel_classes (caps
);
1255 /* set whether we support hash and the type of it */
1256 if (!set_content_hash_type_from_classes (handler
, classes
))
1258 g_set_error_literal (&myerr
, EMPATHY_FT_ERROR_QUARK
,
1259 EMPATHY_FT_ERROR_NOT_SUPPORTED
,
1260 _("File transfer not supported by remote contact"));
1262 if (!g_cancellable_is_cancelled (priv
->cancellable
))
1263 g_cancellable_cancel (priv
->cancellable
);
1265 data
->callback (handler
, myerr
, data
->user_data
);
1266 g_clear_error (&myerr
);
1270 /* get back to the caller now */
1271 data
->callback (handler
, NULL
, data
->user_data
);
1275 callbacks_data_free (data
);
1279 ft_handler_complete_request (EmpathyFTHandler
*handler
)
1281 EmpathyFTHandlerPriv
*priv
= handler
->priv
;
1283 /* populate the request table with all the known properties */
1284 ft_handler_populate_outgoing_request (handler
);
1287 /* start hashing the file */
1288 g_file_read_async (priv
->gfile
, G_PRIORITY_DEFAULT
,
1289 priv
->cancellable
, ft_handler_read_async_cb
, handler
);
1291 /* push directly the handler to the dispatcher */
1292 ft_handler_push_to_dispatcher (handler
);
1296 ft_handler_gfile_ready_cb (GObject
*source
,
1298 CallbacksData
*cb_data
)
1301 GError
*error
= NULL
;
1303 EmpathyFTHandlerPriv
*priv
= cb_data
->handler
->priv
;
1305 (void)source
; /* suppress unused-parameter warning */
1307 DEBUG ("Got GFileInfo.");
1309 info
= g_file_query_info_finish (priv
->gfile
, res
, &error
);
1314 if (g_file_info_get_file_type (info
) != G_FILE_TYPE_REGULAR
)
1316 error
= g_error_new_literal (EMPATHY_FT_ERROR_QUARK
,
1317 EMPATHY_FT_ERROR_INVALID_SOURCE_FILE
,
1318 _("The selected file is not a regular file"));
1322 priv
->total_bytes
= g_file_info_get_size (info
);
1323 if (priv
->total_bytes
== 0)
1325 error
= g_error_new_literal (EMPATHY_FT_ERROR_QUARK
,
1326 EMPATHY_FT_ERROR_EMPTY_SOURCE_FILE
,
1327 _("The selected file is empty"));
1331 priv
->content_type
= g_strdup (g_file_info_get_content_type (info
));
1332 priv
->filename
= g_strdup (g_file_info_get_display_name (info
));
1333 g_file_info_get_modification_time (info
, &mtime
);
1334 priv
->mtime
= mtime
.tv_sec
;
1335 priv
->transferred_bytes
= 0;
1336 priv
->description
= NULL
;
1338 g_object_unref (info
);
1343 if (!g_cancellable_is_cancelled (priv
->cancellable
))
1344 g_cancellable_cancel (priv
->cancellable
);
1346 cb_data
->callback (cb_data
->handler
, error
, cb_data
->user_data
);
1347 g_error_free (error
);
1349 callbacks_data_free (cb_data
);
1353 /* see if FT/hashing are allowed */
1354 check_hashing (cb_data
);
1359 channel_prepared_cb (
1361 GAsyncResult
*result
,
1364 TpFileTransferChannel
*channel
= TP_FILE_TRANSFER_CHANNEL (source
);
1365 CallbacksData
*cb_data
= user_data
;
1366 EmpathyFTHandler
*handler
= cb_data
->handler
;
1367 EmpathyFTHandlerPriv
*priv
= handler
->priv
;
1368 GHashTable
*properties
;
1369 GError
*error
= NULL
;
1371 if (!tp_proxy_prepare_finish (channel
, result
, &error
))
1373 if (!g_cancellable_is_cancelled (priv
->cancellable
))
1374 g_cancellable_cancel (priv
->cancellable
);
1376 cb_data
->callback (handler
, error
, cb_data
->user_data
);
1377 g_clear_error (&error
);
1378 callbacks_data_free (cb_data
);
1382 properties
= tp_channel_borrow_immutable_properties (TP_CHANNEL (channel
));
1384 priv
->content_hash
= g_strdup (
1385 tp_asv_get_string (properties
, "ContentHash"));
1387 priv
->content_hash_type
= tp_asv_get_uint32 (
1388 properties
, "ContentHashType", NULL
);
1390 priv
->contact
= g_object_ref (tp_channel_get_target_contact (TP_CHANNEL (channel
)));
1392 cb_data
->callback (handler
, NULL
, cb_data
->user_data
);
1396 /* public methods */
1399 * empathy_ft_handler_new_outgoing:
1400 * @account: the #TpAccount to send @source to
1401 * @contact: the #TpContact to send @source to
1402 * @source: the #GFile to send
1403 * @callback: callback to be called when the handler has been created
1404 * @user_data: user data to be passed to @callback
1406 * Triggers the creation of a new #EmpathyFTHandler for an outgoing transfer.
1409 empathy_ft_handler_new_outgoing (
1414 EmpathyFTHandlerReadyCallback callback
,
1417 EmpathyFTHandler
*handler
;
1418 CallbacksData
*data
;
1419 EmpathyFTHandlerPriv
*priv
;
1421 DEBUG ("New handler outgoing");
1423 g_return_if_fail (TP_IS_ACCOUNT (account
));
1424 g_return_if_fail (TP_IS_CONTACT (contact
));
1425 g_return_if_fail (G_IS_FILE (source
));
1427 handler
= g_object_new (EMPATHY_TYPE_FT_HANDLER
,
1431 "user-action-time", action_time
,
1434 priv
= handler
->priv
;
1436 data
= g_slice_new0 (CallbacksData
);
1437 data
->callback
= callback
;
1438 data
->user_data
= user_data
;
1439 data
->handler
= g_object_ref (handler
);
1441 /* start collecting info about the file */
1442 g_file_query_info_async (priv
->gfile
,
1443 G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME
","
1444 G_FILE_ATTRIBUTE_STANDARD_SIZE
","
1445 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
","
1446 G_FILE_ATTRIBUTE_STANDARD_TYPE
","
1447 G_FILE_ATTRIBUTE_TIME_MODIFIED
,
1448 G_FILE_QUERY_INFO_NONE
, G_PRIORITY_DEFAULT
,
1449 NULL
, (GAsyncReadyCallback
) ft_handler_gfile_ready_cb
, data
);
1453 empathy_ft_handler_set_service_name (
1454 EmpathyFTHandler
*self
,
1455 const gchar
*service_name
)
1457 g_free (self
->priv
->service_name
);
1458 self
->priv
->service_name
= g_strdup (service_name
);
1462 empathy_ft_handler_set_description (
1463 EmpathyFTHandler
*self
,
1464 const gchar
*description
)
1466 g_free (self
->priv
->description
);
1467 self
->priv
->description
= g_strdup (description
);
1471 * empathy_ft_handler_new_incoming:
1472 * @channel: the #TpFileTransferChannel proxy to the incoming channel
1473 * @callback: callback to be called when the handler has been created
1474 * @user_data: user data to be passed to @callback
1476 * Triggers the creation of a new #EmpathyFTHandler for an incoming transfer.
1477 * Note that for the handler to be useful, you will have to set a destination
1478 * file with empathy_ft_handler_incoming_set_destination() after the handler
1482 empathy_ft_handler_new_incoming (TpFileTransferChannel
*channel
,
1483 EmpathyFTHandlerReadyCallback callback
,
1486 EmpathyFTHandler
*handler
;
1487 CallbacksData
*data
;
1488 EmpathyFTHandlerPriv
*priv
;
1489 GQuark features
[] = { TP_CHANNEL_FEATURE_CONTACTS
, 0 };
1491 g_return_if_fail (TP_IS_FILE_TRANSFER_CHANNEL (channel
));
1493 handler
= g_object_new (EMPATHY_TYPE_FT_HANDLER
,
1494 "channel", channel
, NULL
);
1496 priv
= handler
->priv
;
1498 data
= g_slice_new0 (CallbacksData
);
1499 data
->callback
= callback
;
1500 data
->user_data
= user_data
;
1501 data
->handler
= g_object_ref (handler
);
1503 priv
->total_bytes
= tp_file_transfer_channel_get_size (channel
);
1505 priv
->transferred_bytes
= tp_file_transfer_channel_get_transferred_bytes (
1508 priv
->filename
= g_strdup (tp_file_transfer_channel_get_filename (channel
));
1510 priv
->content_type
= g_strdup (tp_file_transfer_channel_get_mime_type (
1513 priv
->description
= g_strdup (tp_file_transfer_channel_get_description (
1516 tp_proxy_prepare_async (channel
, features
,
1517 channel_prepared_cb
, data
);
1521 * empathy_ft_handler_start_transfer:
1522 * @handler: an #EmpathyFTHandler
1524 * Starts the transfer machinery. After this call, the transfer and hashing
1525 * signals will be emitted by the handler.
1528 empathy_ft_handler_start_transfer (EmpathyFTHandler
*handler
)
1530 EmpathyFTHandlerPriv
*priv
;
1532 g_return_if_fail (EMPATHY_IS_FT_HANDLER (handler
));
1534 priv
= handler
->priv
;
1536 if (priv
->channel
== NULL
)
1538 ft_handler_complete_request (handler
);
1542 /* TODO: add support for resume. */
1543 tp_file_transfer_channel_accept_file_async (priv
->channel
,
1544 priv
->gfile
, 0, ft_transfer_accept_cb
, handler
);
1546 tp_g_signal_connect_object (priv
->channel
, "notify::state",
1547 G_CALLBACK (ft_transfer_state_cb
), handler
, 0);
1548 tp_g_signal_connect_object (priv
->channel
, "notify::transferred-bytes",
1549 G_CALLBACK (ft_transfer_transferred_bytes_cb
), handler
, 0);
1554 * empathy_ft_handler_cancel_transfer:
1555 * @handler: an #EmpathyFTHandler
1557 * Cancels an ongoing handler operation. Note that this doesn't destroy
1558 * the object, which will keep all the properties, altough it won't be able
1559 * to do any more I/O.
1562 empathy_ft_handler_cancel_transfer (EmpathyFTHandler
*handler
)
1564 EmpathyFTHandlerPriv
*priv
;
1566 g_return_if_fail (EMPATHY_IS_FT_HANDLER (handler
));
1568 priv
= handler
->priv
;
1570 /* if we don't have a channel, we are hashing, so
1571 * we can just cancel the GCancellable to stop it.
1573 if (priv
->channel
== NULL
)
1574 g_cancellable_cancel (priv
->cancellable
);
1576 tp_channel_close_async (TP_CHANNEL (priv
->channel
), NULL
, NULL
);
1580 * empathy_ft_handler_incoming_set_destination:
1581 * @handler: an #EmpathyFTHandler
1582 * @destination: the #GFile where the transfer should be saved
1584 * Sets the destination of the incoming handler to be @destination.
1585 * Note that calling this method is mandatory before starting the transfer
1586 * for incoming handlers.
1589 empathy_ft_handler_incoming_set_destination (EmpathyFTHandler
*handler
,
1592 EmpathyFTHandlerPriv
*priv
;
1594 g_return_if_fail (EMPATHY_IS_FT_HANDLER (handler
));
1595 g_return_if_fail (G_IS_FILE (destination
));
1597 priv
= handler
->priv
;
1599 g_object_set (handler
, "gfile", destination
, NULL
);
1601 /* check if hash is supported. if it isn't, set use_hash to FALSE
1602 * anyway, so that clients won't be expecting us to checksum.
1604 if (tp_str_empty (priv
->content_hash
) ||
1605 priv
->content_hash_type
== TP_FILE_HASH_TYPE_NONE
)
1606 priv
->use_hash
= FALSE
;
1608 priv
->use_hash
= TRUE
;
1612 * empathy_ft_handler_get_filename:
1613 * @handler: an #EmpathyFTHandler
1615 * Returns the name of the file being transferred.
1617 * Return value: the name of the file being transferred
1620 empathy_ft_handler_get_filename (EmpathyFTHandler
*handler
)
1622 EmpathyFTHandlerPriv
*priv
;
1624 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), NULL
);
1626 priv
= handler
->priv
;
1628 return priv
->filename
;
1632 empathy_ft_handler_get_description (EmpathyFTHandler
*handler
)
1634 EmpathyFTHandlerPriv
*priv
;
1636 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), NULL
);
1638 priv
= handler
->priv
;
1640 return priv
->description
;
1644 * empathy_ft_handler_get_content_type:
1645 * @handler: an #EmpathyFTHandler
1647 * Returns the content type of the file being transferred.
1649 * Return value: the content type of the file being transferred
1652 empathy_ft_handler_get_content_type (EmpathyFTHandler
*handler
)
1654 EmpathyFTHandlerPriv
*priv
;
1656 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), NULL
);
1658 priv
= handler
->priv
;
1660 return priv
->content_type
;
1664 * empathy_ft_handler_get_contact:
1665 * @handler: an #EmpathyFTHandler
1667 * Returns the remote #TpContact at the other side of the transfer.
1669 * Return value: the remote #TpContact for @handler
1672 empathy_ft_handler_get_contact (EmpathyFTHandler
*handler
)
1674 EmpathyFTHandlerPriv
*priv
;
1676 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), NULL
);
1678 priv
= handler
->priv
;
1680 return priv
->contact
;
1684 * empathy_ft_handler_get_gfile:
1685 * @handler: an #EmpathyFTHandler
1687 * Returns the #GFile where the transfer is being read/saved.
1689 * Return value: the #GFile where the transfer is being read/saved
1692 empathy_ft_handler_get_gfile (EmpathyFTHandler
*handler
)
1694 EmpathyFTHandlerPriv
*priv
;
1696 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), NULL
);
1698 priv
= handler
->priv
;
1704 * empathy_ft_handler_get_use_hash:
1705 * @handler: an #EmpathyFTHandler
1707 * Returns whether @handler has checksumming enabled. This can depend on
1708 * the CM and the remote contact capabilities.
1710 * Return value: %TRUE if the handler has checksumming enabled,
1714 empathy_ft_handler_get_use_hash (EmpathyFTHandler
*handler
)
1716 EmpathyFTHandlerPriv
*priv
;
1718 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), FALSE
);
1720 priv
= handler
->priv
;
1722 return priv
->use_hash
;
1726 * empathy_ft_handler_is_incoming:
1727 * @handler: an #EmpathyFTHandler
1729 * Returns whether @handler is incoming or outgoing.
1731 * Return value: %TRUE if the handler is incoming, %FALSE otherwise.
1734 empathy_ft_handler_is_incoming (EmpathyFTHandler
*handler
)
1736 EmpathyFTHandlerPriv
*priv
;
1738 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), FALSE
);
1740 priv
= handler
->priv
;
1742 if (priv
->channel
== NULL
)
1745 return !tp_channel_get_requested ((TpChannel
*) priv
->channel
);
1749 * empathy_ft_handler_get_transferred_bytes:
1750 * @handler: an #EmpathyFTHandler
1752 * Returns the number of bytes already transferred by the handler.
1754 * Return value: the number of bytes already transferred by the handler.
1757 empathy_ft_handler_get_transferred_bytes (EmpathyFTHandler
*handler
)
1759 EmpathyFTHandlerPriv
*priv
;
1761 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), 0);
1763 priv
= handler
->priv
;
1765 return priv
->transferred_bytes
;
1769 * empathy_ft_handler_get_total_bytes:
1770 * @handler: an #EmpathyFTHandler
1772 * Returns the total size of the file being transferred by the handler.
1774 * Return value: a number of bytes indicating the total size of the file being
1775 * transferred by the handler.
1778 empathy_ft_handler_get_total_bytes (EmpathyFTHandler
*handler
)
1780 EmpathyFTHandlerPriv
*priv
;
1782 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), 0);
1784 priv
= handler
->priv
;
1786 return priv
->total_bytes
;
1790 * empathy_ft_handler_is_completed:
1791 * @handler: an #EmpathyFTHandler
1793 * Returns whether the transfer for @handler has been completed succesfully.
1795 * Return value: %TRUE if the handler has been transferred correctly, %FALSE
1799 empathy_ft_handler_is_completed (EmpathyFTHandler
*handler
)
1801 EmpathyFTHandlerPriv
*priv
;
1803 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), FALSE
);
1805 priv
= handler
->priv
;
1807 return priv
->is_completed
;
1811 * empathy_ft_handler_is_cancelled:
1812 * @handler: an #EmpathyFTHandler
1814 * Returns whether the transfer for @handler has been cancelled or has stopped
1817 * Return value: %TRUE if the transfer for @handler has been cancelled
1818 * or has stopped due to an error, %FALSE otherwise.
1821 empathy_ft_handler_is_cancelled (EmpathyFTHandler
*handler
)
1823 EmpathyFTHandlerPriv
*priv
;
1825 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), FALSE
);
1827 priv
= handler
->priv
;
1829 return g_cancellable_is_cancelled (priv
->cancellable
);