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 should 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 #pragma GCC diagnostic push
646 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
647 g_io_scheduler_push_job (do_hash_job_incoming
, hash_data
, NULL
,
648 G_PRIORITY_DEFAULT
, priv
->cancellable
);
649 #pragma GCC diagnostic pop
654 emit_error_signal (EmpathyFTHandler
*handler
,
657 EmpathyFTHandlerPriv
*priv
= handler
->priv
;
659 DEBUG ("Error in transfer: %s\n", error
->message
);
661 if (!g_cancellable_is_cancelled (priv
->cancellable
))
662 g_cancellable_cancel (priv
->cancellable
);
664 g_signal_emit (handler
, signals
[TRANSFER_ERROR
], 0, error
);
668 time_get_current (void)
673 now
= g_date_time_new_now_utc ();
674 result
= g_date_time_to_unix (now
);
675 g_date_time_unref (now
);
681 update_remaining_time_and_speed (EmpathyFTHandler
*handler
,
682 guint64 transferred_bytes
)
684 EmpathyFTHandlerPriv
*priv
= handler
->priv
;
685 gint64 elapsed_time
, current_time
;
686 guint64 transferred
, last_transferred_bytes
;
690 last_transferred_bytes
= priv
->transferred_bytes
;
691 priv
->transferred_bytes
= transferred_bytes
;
693 current_time
= time_get_current ();
694 elapsed_time
= current_time
- priv
->last_update_time
;
696 if (elapsed_time
>= 1)
698 transferred
= transferred_bytes
- last_transferred_bytes
;
699 speed
= (gdouble
) transferred
/ (gdouble
) elapsed_time
;
700 remaining_time
= (priv
->total_bytes
- priv
->transferred_bytes
) / speed
;
702 priv
->remaining_time
= remaining_time
;
703 priv
->last_update_time
= current_time
;
708 ft_transfer_transferred_bytes_cb (TpFileTransferChannel
*channel
,
710 EmpathyFTHandler
*handler
)
712 EmpathyFTHandlerPriv
*priv
= handler
->priv
;
715 (void)pspec
; /* suppress unused-parameter warning */
717 if (empathy_ft_handler_is_cancelled (handler
))
720 bytes
= tp_file_transfer_channel_get_transferred_bytes (channel
);
722 if (priv
->transferred_bytes
== 0)
724 priv
->last_update_time
= time_get_current ();
725 g_signal_emit (handler
, signals
[TRANSFER_STARTED
], 0, channel
);
728 if (priv
->transferred_bytes
!= bytes
)
730 update_remaining_time_and_speed (handler
, bytes
);
732 g_signal_emit (handler
, signals
[TRANSFER_PROGRESS
], 0,
733 bytes
, priv
->total_bytes
, priv
->remaining_time
,
739 ft_transfer_provide_cb (GObject
*source
,
740 GAsyncResult
*result
,
743 TpFileTransferChannel
*channel
= TP_FILE_TRANSFER_CHANNEL (source
);
744 EmpathyFTHandler
*handler
= user_data
;
745 GError
*error
= NULL
;
747 if (!tp_file_transfer_channel_provide_file_finish (channel
, result
, &error
))
749 emit_error_signal (handler
, error
);
750 g_clear_error (&error
);
755 ft_transfer_accept_cb (GObject
*source
,
756 GAsyncResult
*result
,
759 TpFileTransferChannel
*channel
= TP_FILE_TRANSFER_CHANNEL (source
);
760 EmpathyFTHandler
*handler
= user_data
;
761 GError
*error
= NULL
;
763 if (!tp_file_transfer_channel_accept_file_finish (channel
, result
, &error
))
765 emit_error_signal (handler
, error
);
766 g_clear_error (&error
);
771 error_from_state_change_reason (TpFileTransferStateChangeReason reason
)
774 GError
*retval
= NULL
;
780 case TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE
:
781 string
= _("No reason was specified");
783 case TP_FILE_TRANSFER_STATE_CHANGE_REASON_REQUESTED
:
784 string
= _("The change in state was requested");
786 case TP_FILE_TRANSFER_STATE_CHANGE_REASON_LOCAL_STOPPED
:
787 string
= _("You canceled the file transfer");
789 case TP_FILE_TRANSFER_STATE_CHANGE_REASON_REMOTE_STOPPED
:
790 string
= _("The other participant canceled the file transfer");
792 case TP_FILE_TRANSFER_STATE_CHANGE_REASON_LOCAL_ERROR
:
793 string
= _("Error while trying to transfer the file");
795 case TP_FILE_TRANSFER_STATE_CHANGE_REASON_REMOTE_ERROR
:
796 string
= _("The other participant is unable to transfer the file");
799 string
= _("Unknown reason");
803 retval
= g_error_new_literal (EMPATHY_FT_ERROR_QUARK
,
804 EMPATHY_FT_ERROR_TP_ERROR
, string
);
810 ft_transfer_state_cb (TpFileTransferChannel
*channel
,
812 EmpathyFTHandler
*handler
)
814 EmpathyFTHandlerPriv
*priv
= handler
->priv
;
815 TpFileTransferStateChangeReason reason
;
816 TpFileTransferState state
= tp_file_transfer_channel_get_state (
819 (void)pspec
; /* suppress unused-parameter warning */
821 if (state
== TP_FILE_TRANSFER_STATE_COMPLETED
)
823 priv
->is_completed
= TRUE
;
824 g_signal_emit (handler
, signals
[TRANSFER_DONE
], 0, channel
);
826 tp_channel_close_async (TP_CHANNEL (channel
), NULL
, NULL
);
828 if (empathy_ft_handler_is_incoming (handler
) && priv
->use_hash
)
830 check_hash_incoming (handler
);
833 else if (state
== TP_FILE_TRANSFER_STATE_CANCELLED
)
835 GError
*error
= error_from_state_change_reason (reason
);
836 emit_error_signal (handler
, error
);
837 g_clear_error (&error
);
842 ft_handler_create_channel_cb (GObject
*source
,
843 GAsyncResult
*result
,
846 EmpathyFTHandler
*handler
= user_data
;
847 EmpathyFTHandlerPriv
*priv
= handler
->priv
;
848 GError
*error
= NULL
;
851 DEBUG ("Dispatcher create channel CB");
853 channel
= tp_account_channel_request_create_and_handle_channel_finish (
854 TP_ACCOUNT_CHANNEL_REQUEST (source
), result
, NULL
, &error
);
857 DEBUG ("Failed to request FT channel: %s", error
->message
);
859 g_cancellable_set_error_if_cancelled (priv
->cancellable
, &error
);
863 emit_error_signal (handler
, error
);
865 g_clear_object (&channel
);
866 g_error_free (error
);
870 priv
->channel
= TP_FILE_TRANSFER_CHANNEL (channel
);
872 tp_g_signal_connect_object (priv
->channel
, "notify::state",
873 G_CALLBACK (ft_transfer_state_cb
), handler
, 0);
874 tp_g_signal_connect_object (priv
->channel
, "notify::transferred-bytes",
875 G_CALLBACK (ft_transfer_transferred_bytes_cb
), handler
, 0);
877 tp_file_transfer_channel_provide_file_async (priv
->channel
, priv
->gfile
,
878 ft_transfer_provide_cb
, handler
);
882 ft_handler_push_to_dispatcher (EmpathyFTHandler
*handler
)
884 EmpathyFTHandlerPriv
*priv
= handler
->priv
;
885 TpAccountChannelRequest
*req
;
887 DEBUG ("Pushing request to the dispatcher");
889 req
= tp_account_channel_request_new (priv
->account
, priv
->request
,
890 priv
->user_action_time
);
892 tp_account_channel_request_create_and_handle_channel_async (req
, NULL
,
893 ft_handler_create_channel_cb
, handler
);
895 g_object_unref (req
);
899 ft_handler_populate_outgoing_request (EmpathyFTHandler
*handler
)
901 guint contact_handle
;
902 EmpathyFTHandlerPriv
*priv
= handler
->priv
;
905 contact_handle
= tp_contact_get_handle (priv
->contact
);
906 uri
= g_file_get_uri (priv
->gfile
);
908 priv
->request
= tp_asv_new (
909 TP_PROP_CHANNEL_CHANNEL_TYPE
, G_TYPE_STRING
,
910 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER
,
911 TP_PROP_CHANNEL_TARGET_HANDLE_TYPE
, G_TYPE_UINT
,
912 TP_HANDLE_TYPE_CONTACT
,
913 TP_PROP_CHANNEL_TARGET_HANDLE
, G_TYPE_UINT
,
915 TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_CONTENT_TYPE
, G_TYPE_STRING
,
917 TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_FILENAME
, G_TYPE_STRING
,
919 TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_SIZE
, G_TYPE_UINT64
,
921 TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_DATE
, G_TYPE_UINT64
,
923 TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_URI
, G_TYPE_STRING
, uri
,
926 if (priv
->service_name
!= NULL
)
927 tp_asv_set_string (priv
->request
, TP_PROP_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA_SERVICE_NAME
, priv
->service_name
);
929 if (priv
->description
!= NULL
)
930 tp_asv_set_string (priv
->request
, TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_DESCRIPTION
, priv
->description
);
936 hash_job_done (gpointer user_data
)
938 HashingData
*hash_data
= user_data
;
939 EmpathyFTHandler
*handler
= hash_data
->handler
;
940 EmpathyFTHandlerPriv
*priv
;
941 GError
*error
= NULL
;
943 DEBUG ("Closing stream after hashing.");
945 priv
= handler
->priv
;
947 if (hash_data
->error
!= NULL
)
949 error
= hash_data
->error
;
950 hash_data
->error
= NULL
;
954 DEBUG ("Got file hash %s", g_checksum_get_string (hash_data
->checksum
));
956 if (empathy_ft_handler_is_incoming (handler
))
958 if (g_strcmp0 (g_checksum_get_string (hash_data
->checksum
),
961 DEBUG ("Hash mismatch when checking incoming handler: "
962 "received %s, calculated %s", priv
->content_hash
,
963 g_checksum_get_string (hash_data
->checksum
));
965 error
= g_error_new_literal (EMPATHY_FT_ERROR_QUARK
,
966 EMPATHY_FT_ERROR_HASH_MISMATCH
,
967 _("File transfer completed, but the file was corrupted"));
972 DEBUG ("Hash verification matched, received %s, calculated %s",
974 g_checksum_get_string (hash_data
->checksum
));
979 /* set the checksum in the request...
980 * org.freedesktop.Telepathy.Channel.Type.FileTransfer.ContentHash
982 tp_asv_set_string (priv
->request
,
983 TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_CONTENT_HASH
,
984 g_checksum_get_string (hash_data
->checksum
));
991 emit_error_signal (handler
, error
);
992 g_clear_error (&error
);
996 g_signal_emit (handler
, signals
[HASHING_DONE
], 0);
998 if (!empathy_ft_handler_is_incoming (handler
))
999 /* the request is complete now, push it to the dispatcher */
1000 ft_handler_push_to_dispatcher (handler
);
1003 hash_data_free (hash_data
);
1009 emit_hashing_progress (gpointer user_data
)
1011 HashingData
*hash_data
= user_data
;
1013 g_signal_emit (hash_data
->handler
, signals
[HASHING_PROGRESS
], 0,
1014 (guint64
) hash_data
->total_read
, (guint64
) hash_data
->total_bytes
);
1020 do_hash_job (GIOSchedulerJob
*job
,
1021 GCancellable
*cancellable
,
1024 HashingData
*hash_data
= user_data
;
1026 GError
*error
= NULL
;
1029 if (hash_data
->buffer
== NULL
)
1030 hash_data
->buffer
= g_malloc0 (BUFFER_SIZE
);
1032 bytes_read
= g_input_stream_read (hash_data
->stream
, hash_data
->buffer
,
1033 BUFFER_SIZE
, cancellable
, &error
);
1037 hash_data
->total_read
+= bytes_read
;
1039 /* we now have the chunk */
1042 g_checksum_update (hash_data
->checksum
, hash_data
->buffer
, bytes_read
);
1043 #pragma GCC diagnostic push
1044 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1045 g_io_scheduler_job_send_to_mainloop_async (job
, emit_hashing_progress
,
1047 #pragma GCC diagnostic pop
1048 g_free (hash_data
->buffer
);
1049 hash_data
->buffer
= NULL
;
1055 g_input_stream_close (hash_data
->stream
, cancellable
, &error
);
1060 hash_data
->error
= error
;
1062 #pragma GCC diagnostic push
1063 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1064 g_io_scheduler_job_send_to_mainloop_async (job
, hash_job_done
,
1066 #pragma GCC diagnostic pop
1072 do_hash_job_incoming (GIOSchedulerJob
*job
,
1073 GCancellable
*cancellable
,
1076 HashingData
*hash_data
= user_data
;
1077 EmpathyFTHandler
*handler
= hash_data
->handler
;
1078 EmpathyFTHandlerPriv
*priv
= handler
->priv
;
1079 GError
*error
= NULL
;
1081 DEBUG ("checking integrity for incoming handler");
1083 /* need to get the stream first */
1085 G_INPUT_STREAM (g_file_read (priv
->gfile
, cancellable
, &error
));
1089 hash_data
->error
= error
;
1090 #pragma GCC diagnostic push
1091 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1092 g_io_scheduler_job_send_to_mainloop_async (job
, hash_job_done
,
1094 #pragma GCC diagnostic pop
1098 return do_hash_job (job
, cancellable
, user_data
);
1102 ft_handler_read_async_cb (GObject
*source
,
1106 GFileInputStream
*stream
;
1107 GError
*error
= NULL
;
1108 HashingData
*hash_data
;
1109 EmpathyFTHandler
*handler
= user_data
;
1110 EmpathyFTHandlerPriv
*priv
= handler
->priv
;
1112 (void)source
; /* suppress unused-parameter warning */
1114 DEBUG ("GFile read async CB.");
1116 stream
= g_file_read_finish (priv
->gfile
, res
, &error
);
1119 emit_error_signal (handler
, error
);
1120 g_clear_error (&error
);
1125 hash_data
= g_slice_new0 (HashingData
);
1126 hash_data
->stream
= G_INPUT_STREAM (stream
);
1127 hash_data
->total_bytes
= priv
->total_bytes
;
1128 hash_data
->handler
= g_object_ref (handler
);
1129 /* FIXME: MD5 is the only ContentHashType supported right now */
1130 hash_data
->checksum
= g_checksum_new (G_CHECKSUM_MD5
);
1132 tp_asv_set_uint32 (priv
->request
,
1133 TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_CONTENT_HASH_TYPE
,
1134 TP_FILE_HASH_TYPE_MD5
);
1136 g_signal_emit (handler
, signals
[HASHING_STARTED
], 0);
1138 #pragma GCC diagnostic push
1139 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1140 g_io_scheduler_push_job (do_hash_job
, hash_data
, NULL
,
1141 G_PRIORITY_DEFAULT
, priv
->cancellable
);
1142 #pragma GCC diagnostic pop
1146 callbacks_data_free (gpointer user_data
)
1148 CallbacksData
*data
= user_data
;
1150 if (data
->handler
!= NULL
)
1151 g_object_unref (data
->handler
);
1153 g_slice_free (CallbacksData
, data
);
1161 return *(guint
*) a
- *(guint
*) b
;
1165 set_content_hash_type_from_classes (EmpathyFTHandler
*handler
,
1168 GArray
*possible_values
;
1171 EmpathyFTHandlerPriv
*priv
= handler
->priv
;
1172 gboolean support_ft
= FALSE
;
1175 possible_values
= g_array_new (TRUE
, TRUE
, sizeof (guint
));
1177 for (i
= 0; i
< classes
->len
; i
++)
1181 const gchar
*chan_type
;
1183 tp_value_array_unpack (g_ptr_array_index (classes
, i
), 2,
1186 chan_type
= tp_asv_get_string (fixed
, TP_PROP_CHANNEL_CHANNEL_TYPE
);
1188 if (tp_strdiff (chan_type
, TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER
))
1191 if (tp_asv_get_uint32 (fixed
, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE
, NULL
) !=
1192 TP_HANDLE_TYPE_CONTACT
)
1197 value
= tp_asv_get_uint32
1198 (fixed
, TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_CONTENT_HASH_TYPE
,
1202 g_array_append_val (possible_values
, value
);
1207 g_array_unref (possible_values
);
1211 if (possible_values
->len
== 0)
1213 /* there are no channel classes with hash support, disable it. */
1214 priv
->use_hash
= FALSE
;
1215 priv
->content_hash_type
= TP_FILE_HASH_TYPE_NONE
;
1220 priv
->use_hash
= TRUE
;
1222 if (possible_values
->len
== 1)
1224 priv
->content_hash_type
= g_array_index (possible_values
, guint
, 0);
1228 /* order the array and pick the first non zero, so that MD5
1229 * is the preferred value.
1231 g_array_sort (possible_values
, cmp_uint
);
1233 if (g_array_index (possible_values
, guint
, 0) == 0)
1234 priv
->content_hash_type
= g_array_index (possible_values
, guint
, 1);
1236 priv
->content_hash_type
= g_array_index (possible_values
, guint
, 0);
1240 g_array_unref (possible_values
);
1242 DEBUG ("Hash enabled %s; setting content hash type as %u",
1243 priv
->use_hash
? "True" : "False", priv
->content_hash_type
);
1249 check_hashing (CallbacksData
*data
)
1251 EmpathyFTHandler
*handler
= data
->handler
;
1252 EmpathyFTHandlerPriv
*priv
= handler
->priv
;
1253 GError
*myerr
= NULL
;
1254 TpCapabilities
*caps
;
1258 conn
= tp_account_get_connection (priv
->account
);
1260 caps
= tp_connection_get_capabilities (conn
);
1263 data
->callback (handler
, NULL
, data
->user_data
);
1267 classes
= tp_capabilities_get_channel_classes (caps
);
1269 /* set whether we support hash and the type of it */
1270 if (!set_content_hash_type_from_classes (handler
, classes
))
1272 g_set_error_literal (&myerr
, EMPATHY_FT_ERROR_QUARK
,
1273 EMPATHY_FT_ERROR_NOT_SUPPORTED
,
1274 _("File transfer not supported by remote contact"));
1276 if (!g_cancellable_is_cancelled (priv
->cancellable
))
1277 g_cancellable_cancel (priv
->cancellable
);
1279 data
->callback (handler
, myerr
, data
->user_data
);
1280 g_clear_error (&myerr
);
1284 /* get back to the caller now */
1285 data
->callback (handler
, NULL
, data
->user_data
);
1289 callbacks_data_free (data
);
1293 ft_handler_complete_request (EmpathyFTHandler
*handler
)
1295 EmpathyFTHandlerPriv
*priv
= handler
->priv
;
1297 /* populate the request table with all the known properties */
1298 ft_handler_populate_outgoing_request (handler
);
1301 /* start hashing the file */
1302 g_file_read_async (priv
->gfile
, G_PRIORITY_DEFAULT
,
1303 priv
->cancellable
, ft_handler_read_async_cb
, handler
);
1305 /* push directly the handler to the dispatcher */
1306 ft_handler_push_to_dispatcher (handler
);
1310 ft_handler_gfile_ready_cb (GObject
*source
,
1312 CallbacksData
*cb_data
)
1315 GError
*error
= NULL
;
1317 EmpathyFTHandlerPriv
*priv
= cb_data
->handler
->priv
;
1319 (void)source
; /* suppress unused-parameter warning */
1321 DEBUG ("Got GFileInfo.");
1323 info
= g_file_query_info_finish (priv
->gfile
, res
, &error
);
1328 if (g_file_info_get_file_type (info
) != G_FILE_TYPE_REGULAR
)
1330 error
= g_error_new_literal (EMPATHY_FT_ERROR_QUARK
,
1331 EMPATHY_FT_ERROR_INVALID_SOURCE_FILE
,
1332 _("The selected file is not a regular file"));
1336 priv
->total_bytes
= g_file_info_get_size (info
);
1337 if (priv
->total_bytes
== 0)
1339 error
= g_error_new_literal (EMPATHY_FT_ERROR_QUARK
,
1340 EMPATHY_FT_ERROR_EMPTY_SOURCE_FILE
,
1341 _("The selected file is empty"));
1345 priv
->content_type
= g_strdup (g_file_info_get_content_type (info
));
1346 priv
->filename
= g_strdup (g_file_info_get_display_name (info
));
1347 g_file_info_get_modification_time (info
, &mtime
);
1348 priv
->mtime
= mtime
.tv_sec
;
1349 priv
->transferred_bytes
= 0;
1350 priv
->description
= NULL
;
1352 g_object_unref (info
);
1357 if (!g_cancellable_is_cancelled (priv
->cancellable
))
1358 g_cancellable_cancel (priv
->cancellable
);
1360 cb_data
->callback (cb_data
->handler
, error
, cb_data
->user_data
);
1361 g_error_free (error
);
1363 callbacks_data_free (cb_data
);
1367 /* see if FT/hashing are allowed */
1368 check_hashing (cb_data
);
1373 channel_prepared_cb (
1375 GAsyncResult
*result
,
1378 TpFileTransferChannel
*channel
= TP_FILE_TRANSFER_CHANNEL (source
);
1379 CallbacksData
*cb_data
= user_data
;
1380 EmpathyFTHandler
*handler
= cb_data
->handler
;
1381 EmpathyFTHandlerPriv
*priv
= handler
->priv
;
1382 GHashTable
*properties
;
1383 GError
*error
= NULL
;
1385 if (!tp_proxy_prepare_finish (channel
, result
, &error
))
1387 if (!g_cancellable_is_cancelled (priv
->cancellable
))
1388 g_cancellable_cancel (priv
->cancellable
);
1390 cb_data
->callback (handler
, error
, cb_data
->user_data
);
1391 g_clear_error (&error
);
1392 callbacks_data_free (cb_data
);
1395 #pragma GCC diagnostic push
1396 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1397 properties
= tp_channel_borrow_immutable_properties (TP_CHANNEL (channel
));
1398 #pragma GCC diagnostic pop
1400 priv
->content_hash
= g_strdup (
1401 tp_asv_get_string (properties
, "ContentHash"));
1403 priv
->content_hash_type
= tp_asv_get_uint32 (
1404 properties
, "ContentHashType", NULL
);
1406 priv
->contact
= g_object_ref (tp_channel_get_target_contact (TP_CHANNEL (channel
)));
1408 cb_data
->callback (handler
, NULL
, cb_data
->user_data
);
1412 /* public methods */
1415 * empathy_ft_handler_new_outgoing:
1416 * @account: the #TpAccount to send @source to
1417 * @contact: the #TpContact to send @source to
1418 * @source: the #GFile to send
1419 * @callback: callback to be called when the handler has been created
1420 * @user_data: user data to be passed to @callback
1422 * Triggers the creation of a new #EmpathyFTHandler for an outgoing transfer.
1425 empathy_ft_handler_new_outgoing (
1430 EmpathyFTHandlerReadyCallback callback
,
1433 EmpathyFTHandler
*handler
;
1434 CallbacksData
*data
;
1435 EmpathyFTHandlerPriv
*priv
;
1437 DEBUG ("New handler outgoing");
1439 g_return_if_fail (TP_IS_ACCOUNT (account
));
1440 g_return_if_fail (TP_IS_CONTACT (contact
));
1441 g_return_if_fail (G_IS_FILE (source
));
1443 handler
= g_object_new (EMPATHY_TYPE_FT_HANDLER
,
1447 "user-action-time", action_time
,
1450 priv
= handler
->priv
;
1452 data
= g_slice_new0 (CallbacksData
);
1453 data
->callback
= callback
;
1454 data
->user_data
= user_data
;
1455 data
->handler
= g_object_ref (handler
);
1457 /* start collecting info about the file */
1458 g_file_query_info_async (priv
->gfile
,
1459 G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME
","
1460 G_FILE_ATTRIBUTE_STANDARD_SIZE
","
1461 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
","
1462 G_FILE_ATTRIBUTE_STANDARD_TYPE
","
1463 G_FILE_ATTRIBUTE_TIME_MODIFIED
,
1464 G_FILE_QUERY_INFO_NONE
, G_PRIORITY_DEFAULT
,
1465 NULL
, (GAsyncReadyCallback
) ft_handler_gfile_ready_cb
, data
);
1469 empathy_ft_handler_set_service_name (
1470 EmpathyFTHandler
*self
,
1471 const gchar
*service_name
)
1473 g_free (self
->priv
->service_name
);
1474 self
->priv
->service_name
= g_strdup (service_name
);
1478 empathy_ft_handler_set_description (
1479 EmpathyFTHandler
*self
,
1480 const gchar
*description
)
1482 g_free (self
->priv
->description
);
1483 self
->priv
->description
= g_strdup (description
);
1487 * empathy_ft_handler_new_incoming:
1488 * @channel: the #TpFileTransferChannel proxy to the incoming channel
1489 * @callback: callback to be called when the handler has been created
1490 * @user_data: user data to be passed to @callback
1492 * Triggers the creation of a new #EmpathyFTHandler for an incoming transfer.
1493 * Note that for the handler to be useful, you will have to set a destination
1494 * file with empathy_ft_handler_incoming_set_destination() after the handler
1498 empathy_ft_handler_new_incoming (TpFileTransferChannel
*channel
,
1499 EmpathyFTHandlerReadyCallback callback
,
1502 EmpathyFTHandler
*handler
;
1503 CallbacksData
*data
;
1504 EmpathyFTHandlerPriv
*priv
;
1505 GQuark features
[] = { TP_CHANNEL_FEATURE_CONTACTS
, 0 };
1507 g_return_if_fail (TP_IS_FILE_TRANSFER_CHANNEL (channel
));
1509 handler
= g_object_new (EMPATHY_TYPE_FT_HANDLER
,
1510 "channel", channel
, NULL
);
1512 priv
= handler
->priv
;
1514 data
= g_slice_new0 (CallbacksData
);
1515 data
->callback
= callback
;
1516 data
->user_data
= user_data
;
1517 data
->handler
= g_object_ref (handler
);
1519 priv
->total_bytes
= tp_file_transfer_channel_get_size (channel
);
1521 priv
->transferred_bytes
= tp_file_transfer_channel_get_transferred_bytes (
1524 priv
->filename
= g_strdup (tp_file_transfer_channel_get_filename (channel
));
1526 priv
->content_type
= g_strdup (tp_file_transfer_channel_get_mime_type (
1529 priv
->description
= g_strdup (tp_file_transfer_channel_get_description (
1532 tp_proxy_prepare_async (channel
, features
,
1533 channel_prepared_cb
, data
);
1537 * empathy_ft_handler_start_transfer:
1538 * @handler: an #EmpathyFTHandler
1540 * Starts the transfer machinery. After this call, the transfer and hashing
1541 * signals will be emitted by the handler.
1544 empathy_ft_handler_start_transfer (EmpathyFTHandler
*handler
)
1546 EmpathyFTHandlerPriv
*priv
;
1548 g_return_if_fail (EMPATHY_IS_FT_HANDLER (handler
));
1550 priv
= handler
->priv
;
1552 if (priv
->channel
== NULL
)
1554 ft_handler_complete_request (handler
);
1558 /* TODO: add support for resume. */
1559 tp_file_transfer_channel_accept_file_async (priv
->channel
,
1560 priv
->gfile
, 0, ft_transfer_accept_cb
, handler
);
1562 tp_g_signal_connect_object (priv
->channel
, "notify::state",
1563 G_CALLBACK (ft_transfer_state_cb
), handler
, 0);
1564 tp_g_signal_connect_object (priv
->channel
, "notify::transferred-bytes",
1565 G_CALLBACK (ft_transfer_transferred_bytes_cb
), handler
, 0);
1570 * empathy_ft_handler_cancel_transfer:
1571 * @handler: an #EmpathyFTHandler
1573 * Cancels an ongoing handler operation. Note that this doesn't destroy
1574 * the object, which will keep all the properties, altough it won't be able
1575 * to do any more I/O.
1578 empathy_ft_handler_cancel_transfer (EmpathyFTHandler
*handler
)
1580 EmpathyFTHandlerPriv
*priv
;
1582 g_return_if_fail (EMPATHY_IS_FT_HANDLER (handler
));
1584 priv
= handler
->priv
;
1586 /* if we don't have a channel, we are hashing, so
1587 * we can just cancel the GCancellable to stop it.
1589 if (priv
->channel
== NULL
)
1590 g_cancellable_cancel (priv
->cancellable
);
1592 tp_channel_close_async (TP_CHANNEL (priv
->channel
), NULL
, NULL
);
1596 * empathy_ft_handler_incoming_set_destination:
1597 * @handler: an #EmpathyFTHandler
1598 * @destination: the #GFile where the transfer should be saved
1600 * Sets the destination of the incoming handler to be @destination.
1601 * Note that calling this method is mandatory before starting the transfer
1602 * for incoming handlers.
1605 empathy_ft_handler_incoming_set_destination (EmpathyFTHandler
*handler
,
1608 EmpathyFTHandlerPriv
*priv
;
1610 g_return_if_fail (EMPATHY_IS_FT_HANDLER (handler
));
1611 g_return_if_fail (G_IS_FILE (destination
));
1613 priv
= handler
->priv
;
1615 g_object_set (handler
, "gfile", destination
, NULL
);
1617 /* check if hash is supported. if it isn't, set use_hash to FALSE
1618 * anyway, so that clients won't be expecting us to checksum.
1620 if (tp_str_empty (priv
->content_hash
) ||
1621 priv
->content_hash_type
== TP_FILE_HASH_TYPE_NONE
)
1622 priv
->use_hash
= FALSE
;
1624 priv
->use_hash
= TRUE
;
1628 * empathy_ft_handler_get_filename:
1629 * @handler: an #EmpathyFTHandler
1631 * Returns the name of the file being transferred.
1633 * Return value: the name of the file being transferred
1636 empathy_ft_handler_get_filename (EmpathyFTHandler
*handler
)
1638 EmpathyFTHandlerPriv
*priv
;
1640 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), NULL
);
1642 priv
= handler
->priv
;
1644 return priv
->filename
;
1648 empathy_ft_handler_get_description (EmpathyFTHandler
*handler
)
1650 EmpathyFTHandlerPriv
*priv
;
1652 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), NULL
);
1654 priv
= handler
->priv
;
1656 return priv
->description
;
1660 * empathy_ft_handler_get_content_type:
1661 * @handler: an #EmpathyFTHandler
1663 * Returns the content type of the file being transferred.
1665 * Return value: the content type of the file being transferred
1668 empathy_ft_handler_get_content_type (EmpathyFTHandler
*handler
)
1670 EmpathyFTHandlerPriv
*priv
;
1672 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), NULL
);
1674 priv
= handler
->priv
;
1676 return priv
->content_type
;
1680 * empathy_ft_handler_get_contact:
1681 * @handler: an #EmpathyFTHandler
1683 * Returns the remote #TpContact at the other side of the transfer.
1685 * Return value: the remote #TpContact for @handler
1688 empathy_ft_handler_get_contact (EmpathyFTHandler
*handler
)
1690 EmpathyFTHandlerPriv
*priv
;
1692 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), NULL
);
1694 priv
= handler
->priv
;
1696 return priv
->contact
;
1700 * empathy_ft_handler_get_gfile:
1701 * @handler: an #EmpathyFTHandler
1703 * Returns the #GFile where the transfer is being read/saved.
1705 * Return value: the #GFile where the transfer is being read/saved
1708 empathy_ft_handler_get_gfile (EmpathyFTHandler
*handler
)
1710 EmpathyFTHandlerPriv
*priv
;
1712 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), NULL
);
1714 priv
= handler
->priv
;
1720 * empathy_ft_handler_get_use_hash:
1721 * @handler: an #EmpathyFTHandler
1723 * Returns whether @handler has checksumming enabled. This can depend on
1724 * the CM and the remote contact capabilities.
1726 * Return value: %TRUE if the handler has checksumming enabled,
1730 empathy_ft_handler_get_use_hash (EmpathyFTHandler
*handler
)
1732 EmpathyFTHandlerPriv
*priv
;
1734 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), FALSE
);
1736 priv
= handler
->priv
;
1738 return priv
->use_hash
;
1742 * empathy_ft_handler_is_incoming:
1743 * @handler: an #EmpathyFTHandler
1745 * Returns whether @handler is incoming or outgoing.
1747 * Return value: %TRUE if the handler is incoming, %FALSE otherwise.
1750 empathy_ft_handler_is_incoming (EmpathyFTHandler
*handler
)
1752 EmpathyFTHandlerPriv
*priv
;
1754 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), FALSE
);
1756 priv
= handler
->priv
;
1758 if (priv
->channel
== NULL
)
1761 return !tp_channel_get_requested ((TpChannel
*) priv
->channel
);
1765 * empathy_ft_handler_get_transferred_bytes:
1766 * @handler: an #EmpathyFTHandler
1768 * Returns the number of bytes already transferred by the handler.
1770 * Return value: the number of bytes already transferred by the handler.
1773 empathy_ft_handler_get_transferred_bytes (EmpathyFTHandler
*handler
)
1775 EmpathyFTHandlerPriv
*priv
;
1777 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), 0);
1779 priv
= handler
->priv
;
1781 return priv
->transferred_bytes
;
1785 * empathy_ft_handler_get_total_bytes:
1786 * @handler: an #EmpathyFTHandler
1788 * Returns the total size of the file being transferred by the handler.
1790 * Return value: a number of bytes indicating the total size of the file being
1791 * transferred by the handler.
1794 empathy_ft_handler_get_total_bytes (EmpathyFTHandler
*handler
)
1796 EmpathyFTHandlerPriv
*priv
;
1798 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), 0);
1800 priv
= handler
->priv
;
1802 return priv
->total_bytes
;
1806 * empathy_ft_handler_is_completed:
1807 * @handler: an #EmpathyFTHandler
1809 * Returns whether the transfer for @handler has been completed successfully.
1811 * Return value: %TRUE if the handler has been transferred correctly, %FALSE
1815 empathy_ft_handler_is_completed (EmpathyFTHandler
*handler
)
1817 EmpathyFTHandlerPriv
*priv
;
1819 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), FALSE
);
1821 priv
= handler
->priv
;
1823 return priv
->is_completed
;
1827 * empathy_ft_handler_is_cancelled:
1828 * @handler: an #EmpathyFTHandler
1830 * Returns whether the transfer for @handler has been cancelled or has stopped
1833 * Return value: %TRUE if the transfer for @handler has been cancelled
1834 * or has stopped due to an error, %FALSE otherwise.
1837 empathy_ft_handler_is_cancelled (EmpathyFTHandler
*handler
)
1839 EmpathyFTHandlerPriv
*priv
;
1841 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), FALSE
);
1843 priv
= handler
->priv
;
1845 return g_cancellable_is_cancelled (priv
->cancellable
);