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/account-channel-request.h>
27 #include <telepathy-glib/util.h>
28 #include <telepathy-glib/dbus.h>
29 #include <telepathy-glib/interfaces.h>
31 #include "empathy-ft-handler.h"
32 #include "empathy-tp-contact-factory.h"
33 #include "empathy-time.h"
34 #include "empathy-utils.h"
36 #define DEBUG_FLAG EMPATHY_DEBUG_FT
37 #include "empathy-debug.h"
40 * SECTION:empathy-ft-handler
41 * @title: EmpathyFTHandler
42 * @short_description: an object representing a File Transfer
43 * @include: libempathy/empathy-ft-handler
45 * #EmpathyFTHandler is the object which represents a File Transfer with all
47 * The creation of an #EmpathyFTHandler is done with
48 * empathy_ft_handler_new_outgoing() or empathy_ft_handler_new_incoming(),
49 * even though clients should not need to call them directly, as
50 * #EmpathyFTFactory does it for them. Remember that for the file transfer
51 * to work with an incoming handler,
52 * empathy_ft_handler_incoming_set_destination() should be called after
53 * empathy_ft_handler_new_incoming(). #EmpathyFTFactory does this
55 * It's important to note that, as the creation of the handlers is async, once
56 * an handler is created, it already has all the interesting properties set,
57 * like filename, total bytes, content type and so on, making it useful
58 * to be displayed in an UI.
59 * The transfer API works like a state machine; it has three signals,
60 * ::transfer-started, ::transfer-progress, ::transfer-done, which will be
61 * emitted in the relevant phases.
62 * In addition, if the handler is created with checksumming enabled,
63 * other three signals (::hashing-started, ::hashing-progress, ::hashing-done)
64 * will be emitted before or after the transfer, depending on the direction
65 * (respectively outgoing and incoming) of the handler.
66 * At any time between the call to empathy_ft_handler_start_transfer() and
67 * the last signal, a ::transfer-error can be emitted, indicating that an
68 * error has happened in the operation. The message of the error is localized
72 G_DEFINE_TYPE (EmpathyFTHandler
, empathy_ft_handler
, G_TYPE_OBJECT
)
74 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyFTHandler)
76 #define BUFFER_SIZE 4096
85 PROP_MODIFICATION_TIME
,
87 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 TpFileTransferChannel
*channel
;
124 GCancellable
*cancellable
;
127 /* request for the new transfer */
130 /* transfer properties */
131 EmpathyContact
*contact
;
136 guint64 transferred_bytes
;
139 TpFileHashType content_hash_type
;
141 gint64 user_action_time
;
145 guint remaining_time
;
146 gint64 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
->channel
);
194 case PROP_USER_ACTION_TIME
:
195 g_value_set_int64 (value
, priv
->user_action_time
);
198 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
203 do_set_property (GObject
*object
,
208 EmpathyFTHandlerPriv
*priv
= GET_PRIV (object
);
213 priv
->contact
= g_value_dup_object (value
);
215 case PROP_CONTENT_TYPE
:
216 priv
->content_type
= g_value_dup_string (value
);
218 case PROP_DESCRIPTION
:
219 priv
->description
= g_value_dup_string (value
);
222 priv
->filename
= g_value_dup_string (value
);
224 case PROP_MODIFICATION_TIME
:
225 priv
->mtime
= g_value_get_uint64 (value
);
227 case PROP_TOTAL_BYTES
:
228 priv
->total_bytes
= g_value_get_uint64 (value
);
230 case PROP_TRANSFERRED_BYTES
:
231 priv
->transferred_bytes
= g_value_get_uint64 (value
);
234 priv
->gfile
= g_value_dup_object (value
);
237 priv
->channel
= g_value_dup_object (value
);
239 case PROP_USER_ACTION_TIME
:
240 priv
->user_action_time
= g_value_get_int64 (value
);
243 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
248 do_dispose (GObject
*object
)
250 EmpathyFTHandlerPriv
*priv
= GET_PRIV (object
);
252 if (priv
->dispose_run
)
255 priv
->dispose_run
= TRUE
;
257 if (priv
->contact
!= NULL
) {
258 g_object_unref (priv
->contact
);
259 priv
->contact
= NULL
;
262 if (priv
->gfile
!= NULL
) {
263 g_object_unref (priv
->gfile
);
267 if (priv
->channel
!= NULL
) {
268 tp_channel_close_async (TP_CHANNEL (priv
->channel
), NULL
, NULL
);
269 g_object_unref (priv
->channel
);
270 priv
->channel
= NULL
;
273 if (priv
->cancellable
!= NULL
) {
274 g_object_unref (priv
->cancellable
);
275 priv
->cancellable
= NULL
;
278 if (priv
->request
!= NULL
)
280 g_hash_table_unref (priv
->request
);
281 priv
->request
= 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:channel:
415 * The underlying #TpFileTransferChannel managing the transfer
417 param_spec
= g_param_spec_object ("channel",
418 "channel", "The file transfer channel",
419 TP_TYPE_FILE_TRANSFER_CHANNEL
,
420 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
| G_PARAM_CONSTRUCT_ONLY
);
421 g_object_class_install_property (object_class
, PROP_CHANNEL
, param_spec
);
423 param_spec
= g_param_spec_int64 ("user-action-time", "user action time",
426 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
| G_PARAM_CONSTRUCT_ONLY
);
427 g_object_class_install_property (object_class
, PROP_USER_ACTION_TIME
,
433 * EmpathyFTHandler::transfer-started
434 * @handler: the object which has received the signal
435 * @channel: the #TpFileTransferChannel for which the transfer has started
437 * This signal is emitted when the actual transfer starts.
439 signals
[TRANSFER_STARTED
] =
440 g_signal_new ("transfer-started", G_TYPE_FROM_CLASS (klass
),
441 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
442 g_cclosure_marshal_generic
,
444 1, TP_TYPE_FILE_TRANSFER_CHANNEL
);
447 * EmpathyFTHandler::transfer-done
448 * @handler: the object which has received the signal
449 * @channel: the #TpFileTransferChannel for which the transfer has started
451 * This signal will be emitted when the actual transfer is completed
454 signals
[TRANSFER_DONE
] =
455 g_signal_new ("transfer-done", G_TYPE_FROM_CLASS (klass
),
456 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
457 g_cclosure_marshal_generic
,
459 1, TP_TYPE_FILE_TRANSFER_CHANNEL
);
462 * EmpathyFTHandler::transfer-error
463 * @handler: the object which has received the signal
466 * This signal can be emitted anytime between the call to
467 * empathy_ft_handler_start_transfer() and the last expected signal
468 * (::transfer-done or ::hashing-done), and it's guaranteed to be the last
469 * signal coming from the handler, meaning that no other operation will
470 * take place after this signal.
472 signals
[TRANSFER_ERROR
] =
473 g_signal_new ("transfer-error", G_TYPE_FROM_CLASS (klass
),
474 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
475 g_cclosure_marshal_generic
,
480 * EmpathyFTHandler::transfer-progress
481 * @handler: the object which has received the signal
482 * @current_bytes: the bytes currently transferred
483 * @total_bytes: the total bytes of the handler
484 * @remaining_time: the number of seconds remaining for the transfer
486 * @speed: the current speed of the transfer (in KB/s)
488 * This signal is emitted to notify clients of the progress of the
491 signals
[TRANSFER_PROGRESS
] =
492 g_signal_new ("transfer-progress", G_TYPE_FROM_CLASS (klass
),
493 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
494 g_cclosure_marshal_generic
,
496 4, G_TYPE_UINT64
, G_TYPE_UINT64
, G_TYPE_UINT
, G_TYPE_DOUBLE
);
499 * EmpathyFTHandler::hashing-started
500 * @handler: the object which has received the signal
502 * This signal is emitted when the hashing operation of the handler
503 * is started. Note that this might happen or not, depending on the CM
504 * and remote contact capabilities. Clients shoud use
505 * empathy_ft_handler_get_use_hash() before calling
506 * empathy_ft_handler_start_transfer() to know whether they should connect
509 signals
[HASHING_STARTED
] =
510 g_signal_new ("hashing-started", G_TYPE_FROM_CLASS (klass
),
511 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
512 g_cclosure_marshal_generic
,
516 * EmpathyFTHandler::hashing-progress
517 * @handler: the object which has received the signal
518 * @current_bytes: the bytes currently hashed
519 * @total_bytes: the total bytes of the handler
521 * This signal is emitted to notify clients of the progress of the
524 signals
[HASHING_PROGRESS
] =
525 g_signal_new ("hashing-progress", G_TYPE_FROM_CLASS (klass
),
526 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
527 g_cclosure_marshal_generic
,
529 2, G_TYPE_UINT64
, G_TYPE_UINT64
);
532 * EmpathyFTHandler::hashing-done
533 * @handler: the object which has received the signal
535 * This signal is emitted when the hashing operation of the handler
538 signals
[HASHING_DONE
] =
539 g_signal_new ("hashing-done", G_TYPE_FROM_CLASS (klass
),
540 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
541 g_cclosure_marshal_generic
,
546 empathy_ft_handler_init (EmpathyFTHandler
*self
)
548 EmpathyFTHandlerPriv
*priv
= G_TYPE_INSTANCE_GET_PRIVATE (self
,
549 EMPATHY_TYPE_FT_HANDLER
, EmpathyFTHandlerPriv
);
552 priv
->cancellable
= g_cancellable_new ();
555 /* private functions */
558 hash_data_free (HashingData
*data
)
560 g_free (data
->buffer
);
562 if (data
->stream
!= NULL
)
563 g_object_unref (data
->stream
);
565 if (data
->checksum
!= NULL
)
566 g_checksum_free (data
->checksum
);
568 if (data
->error
!= NULL
)
569 g_error_free (data
->error
);
571 if (data
->handler
!= NULL
)
572 g_object_unref (data
->handler
);
574 g_slice_free (HashingData
, data
);
578 tp_file_hash_to_g_checksum (TpFileHashType type
)
580 GChecksumType retval
;
584 case TP_FILE_HASH_TYPE_MD5
:
585 retval
= G_CHECKSUM_MD5
;
587 case TP_FILE_HASH_TYPE_SHA1
:
588 retval
= G_CHECKSUM_SHA1
;
590 case TP_FILE_HASH_TYPE_SHA256
:
591 retval
= G_CHECKSUM_SHA256
;
593 case TP_FILE_HASH_TYPE_NONE
:
595 g_assert_not_reached ();
603 check_hash_incoming (EmpathyFTHandler
*handler
)
605 HashingData
*hash_data
;
606 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
608 if (!EMP_STR_EMPTY (priv
->content_hash
))
610 hash_data
= g_slice_new0 (HashingData
);
611 hash_data
->total_bytes
= priv
->total_bytes
;
612 hash_data
->handler
= g_object_ref (handler
);
613 hash_data
->checksum
= g_checksum_new
614 (tp_file_hash_to_g_checksum (priv
->content_hash_type
));
616 g_signal_emit (handler
, signals
[HASHING_STARTED
], 0);
618 g_io_scheduler_push_job (do_hash_job_incoming
, hash_data
, NULL
,
619 G_PRIORITY_DEFAULT
, priv
->cancellable
);
624 emit_error_signal (EmpathyFTHandler
*handler
,
627 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
629 DEBUG ("Error in transfer: %s\n", error
->message
);
631 if (!g_cancellable_is_cancelled (priv
->cancellable
))
632 g_cancellable_cancel (priv
->cancellable
);
634 g_signal_emit (handler
, signals
[TRANSFER_ERROR
], 0, error
);
638 update_remaining_time_and_speed (EmpathyFTHandler
*handler
,
639 guint64 transferred_bytes
)
641 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
642 gint64 elapsed_time
, current_time
;
643 guint64 transferred
, last_transferred_bytes
;
647 last_transferred_bytes
= priv
->transferred_bytes
;
648 priv
->transferred_bytes
= transferred_bytes
;
650 current_time
= empathy_time_get_current ();
651 elapsed_time
= current_time
- priv
->last_update_time
;
653 if (elapsed_time
>= 1)
655 transferred
= transferred_bytes
- last_transferred_bytes
;
656 speed
= (gdouble
) transferred
/ (gdouble
) elapsed_time
;
657 remaining_time
= (priv
->total_bytes
- priv
->transferred_bytes
) / speed
;
659 priv
->remaining_time
= remaining_time
;
660 priv
->last_update_time
= current_time
;
665 ft_transfer_transferred_bytes_cb (TpFileTransferChannel
*channel
,
667 EmpathyFTHandler
*handler
)
669 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
672 if (empathy_ft_handler_is_cancelled (handler
))
675 bytes
= tp_file_transfer_channel_get_transferred_bytes (channel
);
677 if (priv
->transferred_bytes
== 0)
679 priv
->last_update_time
= empathy_time_get_current ();
680 g_signal_emit (handler
, signals
[TRANSFER_STARTED
], 0, channel
);
683 if (priv
->transferred_bytes
!= bytes
)
685 update_remaining_time_and_speed (handler
, bytes
);
687 g_signal_emit (handler
, signals
[TRANSFER_PROGRESS
], 0,
688 bytes
, priv
->total_bytes
, priv
->remaining_time
,
694 ft_transfer_provide_cb (GObject
*source
,
695 GAsyncResult
*result
,
698 TpFileTransferChannel
*channel
= TP_FILE_TRANSFER_CHANNEL (source
);
699 EmpathyFTHandler
*handler
= user_data
;
700 GError
*error
= NULL
;
702 if (!tp_file_transfer_channel_provide_file_finish (channel
, result
, &error
))
704 emit_error_signal (handler
, error
);
705 g_clear_error (&error
);
710 ft_transfer_accept_cb (GObject
*source
,
711 GAsyncResult
*result
,
714 TpFileTransferChannel
*channel
= TP_FILE_TRANSFER_CHANNEL (source
);
715 EmpathyFTHandler
*handler
= user_data
;
716 GError
*error
= NULL
;
718 if (!tp_file_transfer_channel_accept_file_finish (channel
, result
, &error
))
720 emit_error_signal (handler
, error
);
721 g_clear_error (&error
);
726 error_from_state_change_reason (TpFileTransferStateChangeReason reason
)
729 GError
*retval
= NULL
;
735 case TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE
:
736 string
= _("No reason was specified");
738 case TP_FILE_TRANSFER_STATE_CHANGE_REASON_REQUESTED
:
739 string
= _("The change in state was requested");
741 case TP_FILE_TRANSFER_STATE_CHANGE_REASON_LOCAL_STOPPED
:
742 string
= _("You canceled the file transfer");
744 case TP_FILE_TRANSFER_STATE_CHANGE_REASON_REMOTE_STOPPED
:
745 string
= _("The other participant canceled the file transfer");
747 case TP_FILE_TRANSFER_STATE_CHANGE_REASON_LOCAL_ERROR
:
748 string
= _("Error while trying to transfer the file");
750 case TP_FILE_TRANSFER_STATE_CHANGE_REASON_REMOTE_ERROR
:
751 string
= _("The other participant is unable to transfer the file");
754 string
= _("Unknown reason");
758 retval
= g_error_new_literal (EMPATHY_FT_ERROR_QUARK
,
759 EMPATHY_FT_ERROR_TP_ERROR
, string
);
765 ft_transfer_state_cb (TpFileTransferChannel
*channel
,
767 EmpathyFTHandler
*handler
)
769 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
770 TpFileTransferStateChangeReason reason
;
771 TpFileTransferState state
= tp_file_transfer_channel_get_state (
774 if (state
== TP_FILE_TRANSFER_STATE_COMPLETED
)
776 priv
->is_completed
= TRUE
;
777 g_signal_emit (handler
, signals
[TRANSFER_DONE
], 0, channel
);
779 tp_channel_close_async (TP_CHANNEL (channel
), NULL
, NULL
);
781 if (empathy_ft_handler_is_incoming (handler
) && priv
->use_hash
)
783 check_hash_incoming (handler
);
786 else if (state
== TP_FILE_TRANSFER_STATE_CANCELLED
)
788 GError
*error
= error_from_state_change_reason (reason
);
789 emit_error_signal (handler
, error
);
790 g_clear_error (&error
);
795 ft_handler_create_channel_cb (GObject
*source
,
796 GAsyncResult
*result
,
799 EmpathyFTHandler
*handler
= user_data
;
800 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
801 GError
*error
= NULL
;
804 DEBUG ("Dispatcher create channel CB");
806 channel
= tp_account_channel_request_create_and_handle_channel_finish (
807 TP_ACCOUNT_CHANNEL_REQUEST (source
), result
, NULL
, &error
);
810 DEBUG ("Failed to request FT channel: %s", error
->message
);
812 g_cancellable_set_error_if_cancelled (priv
->cancellable
, &error
);
816 emit_error_signal (handler
, error
);
818 g_clear_object (&channel
);
819 g_error_free (error
);
823 priv
->channel
= TP_FILE_TRANSFER_CHANNEL (channel
);
825 tp_g_signal_connect_object (priv
->channel
, "notify::state",
826 G_CALLBACK (ft_transfer_state_cb
), handler
, 0);
827 tp_g_signal_connect_object (priv
->channel
, "notify::transferred-bytes",
828 G_CALLBACK (ft_transfer_transferred_bytes_cb
), handler
, 0);
830 tp_file_transfer_channel_provide_file_async (priv
->channel
, priv
->gfile
,
831 ft_transfer_provide_cb
, handler
);
835 ft_handler_push_to_dispatcher (EmpathyFTHandler
*handler
)
838 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
839 TpAccountChannelRequest
*req
;
841 DEBUG ("Pushing request to the dispatcher");
843 account
= empathy_contact_get_account (priv
->contact
);
845 req
= tp_account_channel_request_new (account
, priv
->request
,
846 priv
->user_action_time
);
848 tp_account_channel_request_create_and_handle_channel_async (req
, NULL
,
849 ft_handler_create_channel_cb
, handler
);
851 g_object_unref (req
);
855 ft_handler_populate_outgoing_request (EmpathyFTHandler
*handler
)
857 guint contact_handle
;
858 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
861 contact_handle
= empathy_contact_get_handle (priv
->contact
);
862 uri
= g_file_get_uri (priv
->gfile
);
864 priv
->request
= tp_asv_new (
865 TP_PROP_CHANNEL_CHANNEL_TYPE
, G_TYPE_STRING
,
866 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER
,
867 TP_PROP_CHANNEL_TARGET_HANDLE_TYPE
, G_TYPE_UINT
,
868 TP_HANDLE_TYPE_CONTACT
,
869 TP_PROP_CHANNEL_TARGET_HANDLE
, G_TYPE_UINT
,
871 TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_CONTENT_TYPE
, G_TYPE_STRING
,
873 TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_FILENAME
, G_TYPE_STRING
,
875 TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_SIZE
, G_TYPE_UINT64
,
877 TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_DATE
, G_TYPE_UINT64
,
879 TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_URI
, G_TYPE_STRING
, uri
,
886 hash_job_done (gpointer user_data
)
888 HashingData
*hash_data
= user_data
;
889 EmpathyFTHandler
*handler
= hash_data
->handler
;
890 EmpathyFTHandlerPriv
*priv
;
891 GError
*error
= NULL
;
893 DEBUG ("Closing stream after hashing.");
895 priv
= GET_PRIV (handler
);
897 if (hash_data
->error
!= NULL
)
899 error
= hash_data
->error
;
900 hash_data
->error
= NULL
;
904 DEBUG ("Got file hash %s", g_checksum_get_string (hash_data
->checksum
));
906 if (empathy_ft_handler_is_incoming (handler
))
908 if (g_strcmp0 (g_checksum_get_string (hash_data
->checksum
),
911 DEBUG ("Hash mismatch when checking incoming handler: "
912 "received %s, calculated %s", priv
->content_hash
,
913 g_checksum_get_string (hash_data
->checksum
));
915 error
= g_error_new_literal (EMPATHY_FT_ERROR_QUARK
,
916 EMPATHY_FT_ERROR_HASH_MISMATCH
,
917 _("File transfer completed, but the file was corrupted"));
922 DEBUG ("Hash verification matched, received %s, calculated %s",
924 g_checksum_get_string (hash_data
->checksum
));
929 /* set the checksum in the request...
930 * org.freedesktop.Telepathy.Channel.Type.FileTransfer.ContentHash
932 tp_asv_set_string (priv
->request
,
933 TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_CONTENT_HASH
,
934 g_checksum_get_string (hash_data
->checksum
));
941 emit_error_signal (handler
, error
);
942 g_clear_error (&error
);
946 g_signal_emit (handler
, signals
[HASHING_DONE
], 0);
948 if (!empathy_ft_handler_is_incoming (handler
))
949 /* the request is complete now, push it to the dispatcher */
950 ft_handler_push_to_dispatcher (handler
);
953 hash_data_free (hash_data
);
959 emit_hashing_progress (gpointer user_data
)
961 HashingData
*hash_data
= user_data
;
963 g_signal_emit (hash_data
->handler
, signals
[HASHING_PROGRESS
], 0,
964 (guint64
) hash_data
->total_read
, (guint64
) hash_data
->total_bytes
);
970 do_hash_job (GIOSchedulerJob
*job
,
971 GCancellable
*cancellable
,
974 HashingData
*hash_data
= user_data
;
976 GError
*error
= NULL
;
979 if (hash_data
->buffer
== NULL
)
980 hash_data
->buffer
= g_malloc0 (BUFFER_SIZE
);
982 bytes_read
= g_input_stream_read (hash_data
->stream
, hash_data
->buffer
,
983 BUFFER_SIZE
, cancellable
, &error
);
987 hash_data
->total_read
+= bytes_read
;
989 /* we now have the chunk */
992 g_checksum_update (hash_data
->checksum
, hash_data
->buffer
, bytes_read
);
993 g_io_scheduler_job_send_to_mainloop_async (job
, emit_hashing_progress
,
996 g_free (hash_data
->buffer
);
997 hash_data
->buffer
= NULL
;
1003 g_input_stream_close (hash_data
->stream
, cancellable
, &error
);
1008 hash_data
->error
= error
;
1010 g_io_scheduler_job_send_to_mainloop_async (job
, hash_job_done
,
1017 do_hash_job_incoming (GIOSchedulerJob
*job
,
1018 GCancellable
*cancellable
,
1021 HashingData
*hash_data
= user_data
;
1022 EmpathyFTHandler
*handler
= hash_data
->handler
;
1023 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
1024 GError
*error
= NULL
;
1026 DEBUG ("checking integrity for incoming handler");
1028 /* need to get the stream first */
1030 G_INPUT_STREAM (g_file_read (priv
->gfile
, cancellable
, &error
));
1034 hash_data
->error
= error
;
1035 g_io_scheduler_job_send_to_mainloop_async (job
, hash_job_done
,
1040 return do_hash_job (job
, cancellable
, user_data
);
1044 ft_handler_read_async_cb (GObject
*source
,
1048 GFileInputStream
*stream
;
1049 GError
*error
= NULL
;
1050 HashingData
*hash_data
;
1051 EmpathyFTHandler
*handler
= user_data
;
1052 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
1054 DEBUG ("GFile read async CB.");
1056 stream
= g_file_read_finish (priv
->gfile
, res
, &error
);
1059 emit_error_signal (handler
, error
);
1060 g_clear_error (&error
);
1065 hash_data
= g_slice_new0 (HashingData
);
1066 hash_data
->stream
= G_INPUT_STREAM (stream
);
1067 hash_data
->total_bytes
= priv
->total_bytes
;
1068 hash_data
->handler
= g_object_ref (handler
);
1069 /* FIXME: MD5 is the only ContentHashType supported right now */
1070 hash_data
->checksum
= g_checksum_new (G_CHECKSUM_MD5
);
1072 tp_asv_set_uint32 (priv
->request
,
1073 TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_CONTENT_HASH_TYPE
,
1074 TP_FILE_HASH_TYPE_MD5
);
1076 g_signal_emit (handler
, signals
[HASHING_STARTED
], 0);
1078 g_io_scheduler_push_job (do_hash_job
, hash_data
, NULL
,
1079 G_PRIORITY_DEFAULT
, priv
->cancellable
);
1083 callbacks_data_free (gpointer user_data
)
1085 CallbacksData
*data
= user_data
;
1087 if (data
->handler
!= NULL
)
1088 g_object_unref (data
->handler
);
1090 g_slice_free (CallbacksData
, data
);
1094 set_content_hash_type_from_classes (EmpathyFTHandler
*handler
,
1097 GArray
*possible_values
;
1100 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
1101 gboolean support_ft
= FALSE
;
1104 possible_values
= g_array_new (TRUE
, TRUE
, sizeof (guint
));
1106 for (i
= 0; i
< classes
->len
; i
++)
1110 const gchar
*chan_type
;
1112 tp_value_array_unpack (g_ptr_array_index (classes
, i
), 2,
1115 chan_type
= tp_asv_get_string (fixed
, TP_PROP_CHANNEL_CHANNEL_TYPE
);
1117 if (tp_strdiff (chan_type
, TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER
))
1120 if (tp_asv_get_uint32 (fixed
, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE
, NULL
) !=
1121 TP_HANDLE_TYPE_CONTACT
)
1126 value
= tp_asv_get_uint32
1127 (fixed
, TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_CONTENT_HASH_TYPE
,
1131 g_array_append_val (possible_values
, value
);
1136 g_array_unref (possible_values
);
1140 if (possible_values
->len
== 0)
1142 /* there are no channel classes with hash support, disable it. */
1143 priv
->use_hash
= FALSE
;
1144 priv
->content_hash_type
= TP_FILE_HASH_TYPE_NONE
;
1149 priv
->use_hash
= TRUE
;
1151 if (possible_values
->len
== 1)
1153 priv
->content_hash_type
= g_array_index (possible_values
, guint
, 0);
1157 /* order the array and pick the first non zero, so that MD5
1158 * is the preferred value.
1160 g_array_sort (possible_values
, empathy_uint_compare
);
1162 if (g_array_index (possible_values
, guint
, 0) == 0)
1163 priv
->content_hash_type
= g_array_index (possible_values
, guint
, 1);
1165 priv
->content_hash_type
= g_array_index (possible_values
, guint
, 0);
1169 g_array_unref (possible_values
);
1171 DEBUG ("Hash enabled %s; setting content hash type as %u",
1172 priv
->use_hash
? "True" : "False", priv
->content_hash_type
);
1178 check_hashing (CallbacksData
*data
)
1180 EmpathyFTHandler
*handler
= data
->handler
;
1181 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
1182 GError
*myerr
= NULL
;
1183 TpCapabilities
*caps
;
1187 conn
= empathy_contact_get_connection (priv
->contact
);
1189 caps
= tp_connection_get_capabilities (conn
);
1192 data
->callback (handler
, NULL
, data
->user_data
);
1196 classes
= tp_capabilities_get_channel_classes (caps
);
1198 /* set whether we support hash and the type of it */
1199 if (!set_content_hash_type_from_classes (handler
, classes
))
1201 g_set_error_literal (&myerr
, EMPATHY_FT_ERROR_QUARK
,
1202 EMPATHY_FT_ERROR_NOT_SUPPORTED
,
1203 _("File transfer not supported by remote contact"));
1205 if (!g_cancellable_is_cancelled (priv
->cancellable
))
1206 g_cancellable_cancel (priv
->cancellable
);
1208 data
->callback (handler
, myerr
, data
->user_data
);
1209 g_clear_error (&myerr
);
1213 /* get back to the caller now */
1214 data
->callback (handler
, NULL
, data
->user_data
);
1218 callbacks_data_free (data
);
1222 ft_handler_complete_request (EmpathyFTHandler
*handler
)
1224 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
1226 /* populate the request table with all the known properties */
1227 ft_handler_populate_outgoing_request (handler
);
1230 /* start hashing the file */
1231 g_file_read_async (priv
->gfile
, G_PRIORITY_DEFAULT
,
1232 priv
->cancellable
, ft_handler_read_async_cb
, handler
);
1234 /* push directly the handler to the dispatcher */
1235 ft_handler_push_to_dispatcher (handler
);
1239 ft_handler_gfile_ready_cb (GObject
*source
,
1241 CallbacksData
*cb_data
)
1244 GError
*error
= NULL
;
1246 EmpathyFTHandlerPriv
*priv
= GET_PRIV (cb_data
->handler
);
1248 DEBUG ("Got GFileInfo.");
1250 info
= g_file_query_info_finish (priv
->gfile
, res
, &error
);
1255 if (g_file_info_get_file_type (info
) != G_FILE_TYPE_REGULAR
)
1257 error
= g_error_new_literal (EMPATHY_FT_ERROR_QUARK
,
1258 EMPATHY_FT_ERROR_INVALID_SOURCE_FILE
,
1259 _("The selected file is not a regular file"));
1263 priv
->total_bytes
= g_file_info_get_size (info
);
1264 if (priv
->total_bytes
== 0)
1266 error
= g_error_new_literal (EMPATHY_FT_ERROR_QUARK
,
1267 EMPATHY_FT_ERROR_EMPTY_SOURCE_FILE
,
1268 _("The selected file is empty"));
1272 priv
->content_type
= g_strdup (g_file_info_get_content_type (info
));
1273 priv
->filename
= g_strdup (g_file_info_get_display_name (info
));
1274 g_file_info_get_modification_time (info
, &mtime
);
1275 priv
->mtime
= mtime
.tv_sec
;
1276 priv
->transferred_bytes
= 0;
1277 priv
->description
= NULL
;
1279 g_object_unref (info
);
1284 if (!g_cancellable_is_cancelled (priv
->cancellable
))
1285 g_cancellable_cancel (priv
->cancellable
);
1287 cb_data
->callback (cb_data
->handler
, error
, cb_data
->user_data
);
1288 g_error_free (error
);
1290 callbacks_data_free (cb_data
);
1294 /* see if FT/hashing are allowed */
1295 check_hashing (cb_data
);
1300 contact_factory_contact_cb (TpConnection
*connection
,
1301 EmpathyContact
*contact
,
1302 const GError
*error
,
1304 GObject
*weak_object
)
1306 CallbacksData
*cb_data
= user_data
;
1307 EmpathyFTHandler
*handler
= EMPATHY_FT_HANDLER (weak_object
);
1308 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
1312 if (!g_cancellable_is_cancelled (priv
->cancellable
))
1313 g_cancellable_cancel (priv
->cancellable
);
1315 cb_data
->callback (handler
, (GError
*) error
, cb_data
->user_data
);
1316 callbacks_data_free (cb_data
);
1320 priv
->contact
= g_object_ref (contact
);
1322 cb_data
->callback (handler
, NULL
, cb_data
->user_data
);
1326 channel_get_all_properties_cb (TpProxy
*proxy
,
1327 GHashTable
*properties
,
1328 const GError
*error
,
1330 GObject
*weak_object
)
1332 CallbacksData
*cb_data
= user_data
;
1333 EmpathyFTHandler
*handler
= EMPATHY_FT_HANDLER (weak_object
);
1334 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
1339 if (!g_cancellable_is_cancelled (priv
->cancellable
))
1340 g_cancellable_cancel (priv
->cancellable
);
1342 cb_data
->callback (handler
, (GError
*) error
, cb_data
->user_data
);
1344 callbacks_data_free (cb_data
);
1348 priv
->content_hash
= g_value_dup_string (
1349 g_hash_table_lookup (properties
, "ContentHash"));
1351 priv
->content_hash_type
= g_value_get_uint (
1352 g_hash_table_lookup (properties
, "ContentHashType"));
1354 c_handle
= tp_channel_get_handle (TP_CHANNEL (proxy
), NULL
);
1355 empathy_tp_contact_factory_get_from_handle (
1356 tp_channel_borrow_connection (TP_CHANNEL (proxy
)), c_handle
,
1357 contact_factory_contact_cb
, cb_data
, callbacks_data_free
,
1358 G_OBJECT (handler
));
1361 /* public methods */
1364 * empathy_ft_handler_new_outgoing:
1365 * @contact: the #EmpathyContact to send @source to
1366 * @source: the #GFile to send
1367 * @callback: callback to be called when the handler has been created
1368 * @user_data: user data to be passed to @callback
1370 * Triggers the creation of a new #EmpathyFTHandler for an outgoing transfer.
1373 empathy_ft_handler_new_outgoing (EmpathyContact
*contact
,
1376 EmpathyFTHandlerReadyCallback callback
,
1379 EmpathyFTHandler
*handler
;
1380 CallbacksData
*data
;
1381 EmpathyFTHandlerPriv
*priv
;
1383 DEBUG ("New handler outgoing");
1385 g_return_if_fail (EMPATHY_IS_CONTACT (contact
));
1386 g_return_if_fail (G_IS_FILE (source
));
1388 handler
= g_object_new (EMPATHY_TYPE_FT_HANDLER
,
1391 "user-action-time", action_time
,
1394 priv
= GET_PRIV (handler
);
1396 data
= g_slice_new0 (CallbacksData
);
1397 data
->callback
= callback
;
1398 data
->user_data
= user_data
;
1399 data
->handler
= g_object_ref (handler
);
1401 /* start collecting info about the file */
1402 g_file_query_info_async (priv
->gfile
,
1403 G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME
","
1404 G_FILE_ATTRIBUTE_STANDARD_SIZE
","
1405 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
","
1406 G_FILE_ATTRIBUTE_STANDARD_TYPE
","
1407 G_FILE_ATTRIBUTE_TIME_MODIFIED
,
1408 G_FILE_QUERY_INFO_NONE
, G_PRIORITY_DEFAULT
,
1409 NULL
, (GAsyncReadyCallback
) ft_handler_gfile_ready_cb
, data
);
1413 * empathy_ft_handler_new_incoming:
1414 * @channel: the #TpFileTransferChannel proxy to the incoming channel
1415 * @callback: callback to be called when the handler has been created
1416 * @user_data: user data to be passed to @callback
1418 * Triggers the creation of a new #EmpathyFTHandler for an incoming transfer.
1419 * Note that for the handler to be useful, you will have to set a destination
1420 * file with empathy_ft_handler_incoming_set_destination() after the handler
1424 empathy_ft_handler_new_incoming (TpFileTransferChannel
*channel
,
1425 EmpathyFTHandlerReadyCallback callback
,
1428 EmpathyFTHandler
*handler
;
1429 CallbacksData
*data
;
1430 EmpathyFTHandlerPriv
*priv
;
1432 g_return_if_fail (TP_IS_FILE_TRANSFER_CHANNEL (channel
));
1434 handler
= g_object_new (EMPATHY_TYPE_FT_HANDLER
,
1435 "channel", channel
, NULL
);
1437 priv
= GET_PRIV (handler
);
1439 data
= g_slice_new0 (CallbacksData
);
1440 data
->callback
= callback
;
1441 data
->user_data
= user_data
;
1442 data
->handler
= g_object_ref (handler
);
1444 priv
->total_bytes
= tp_file_transfer_channel_get_size (channel
);
1446 priv
->transferred_bytes
= tp_file_transfer_channel_get_transferred_bytes (
1449 priv
->filename
= g_strdup (tp_file_transfer_channel_get_filename (channel
));
1451 priv
->content_type
= g_strdup (tp_file_transfer_channel_get_mime_type (
1454 priv
->description
= g_strdup (tp_file_transfer_channel_get_description (
1457 tp_cli_dbus_properties_call_get_all (channel
,
1458 -1, TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER
,
1459 channel_get_all_properties_cb
, data
, NULL
, G_OBJECT (handler
));
1463 * empathy_ft_handler_start_transfer:
1464 * @handler: an #EmpathyFTHandler
1466 * Starts the transfer machinery. After this call, the transfer and hashing
1467 * signals will be emitted by the handler.
1470 empathy_ft_handler_start_transfer (EmpathyFTHandler
*handler
)
1472 EmpathyFTHandlerPriv
*priv
;
1474 g_return_if_fail (EMPATHY_IS_FT_HANDLER (handler
));
1476 priv
= GET_PRIV (handler
);
1478 if (priv
->channel
== NULL
)
1480 ft_handler_complete_request (handler
);
1484 /* TODO: add support for resume. */
1485 tp_file_transfer_channel_accept_file_async (priv
->channel
,
1486 priv
->gfile
, 0, ft_transfer_accept_cb
, handler
);
1488 tp_g_signal_connect_object (priv
->channel
, "notify::state",
1489 G_CALLBACK (ft_transfer_state_cb
), handler
, 0);
1490 tp_g_signal_connect_object (priv
->channel
, "notify::transferred-bytes",
1491 G_CALLBACK (ft_transfer_transferred_bytes_cb
), handler
, 0);
1496 * empathy_ft_handler_cancel_transfer:
1497 * @handler: an #EmpathyFTHandler
1499 * Cancels an ongoing handler operation. Note that this doesn't destroy
1500 * the object, which will keep all the properties, altough it won't be able
1501 * to do any more I/O.
1504 empathy_ft_handler_cancel_transfer (EmpathyFTHandler
*handler
)
1506 EmpathyFTHandlerPriv
*priv
;
1508 g_return_if_fail (EMPATHY_IS_FT_HANDLER (handler
));
1510 priv
= GET_PRIV (handler
);
1512 /* if we don't have a channel, we are hashing, so
1513 * we can just cancel the GCancellable to stop it.
1515 if (priv
->channel
== NULL
)
1516 g_cancellable_cancel (priv
->cancellable
);
1518 tp_channel_close_async (TP_CHANNEL (priv
->channel
), NULL
, NULL
);
1522 * empathy_ft_handler_incoming_set_destination:
1523 * @handler: an #EmpathyFTHandler
1524 * @destination: the #GFile where the transfer should be saved
1526 * Sets the destination of the incoming handler to be @destination.
1527 * Note that calling this method is mandatory before starting the transfer
1528 * for incoming handlers.
1531 empathy_ft_handler_incoming_set_destination (EmpathyFTHandler
*handler
,
1534 EmpathyFTHandlerPriv
*priv
;
1536 g_return_if_fail (EMPATHY_IS_FT_HANDLER (handler
));
1537 g_return_if_fail (G_IS_FILE (destination
));
1539 priv
= GET_PRIV (handler
);
1541 g_object_set (handler
, "gfile", destination
, NULL
);
1543 /* check if hash is supported. if it isn't, set use_hash to FALSE
1544 * anyway, so that clients won't be expecting us to checksum.
1546 if (EMP_STR_EMPTY (priv
->content_hash
) ||
1547 priv
->content_hash_type
== TP_FILE_HASH_TYPE_NONE
)
1548 priv
->use_hash
= FALSE
;
1550 priv
->use_hash
= TRUE
;
1554 * empathy_ft_handler_get_filename:
1555 * @handler: an #EmpathyFTHandler
1557 * Returns the name of the file being transferred.
1559 * Return value: the name of the file being transferred
1562 empathy_ft_handler_get_filename (EmpathyFTHandler
*handler
)
1564 EmpathyFTHandlerPriv
*priv
;
1566 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), NULL
);
1568 priv
= GET_PRIV (handler
);
1570 return priv
->filename
;
1574 * empathy_ft_handler_get_content_type:
1575 * @handler: an #EmpathyFTHandler
1577 * Returns the content type of the file being transferred.
1579 * Return value: the content type of the file being transferred
1582 empathy_ft_handler_get_content_type (EmpathyFTHandler
*handler
)
1584 EmpathyFTHandlerPriv
*priv
;
1586 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), NULL
);
1588 priv
= GET_PRIV (handler
);
1590 return priv
->content_type
;
1594 * empathy_ft_handler_get_contact:
1595 * @handler: an #EmpathyFTHandler
1597 * Returns the remote #EmpathyContact at the other side of the transfer.
1599 * Return value: the remote #EmpathyContact for @handler
1602 empathy_ft_handler_get_contact (EmpathyFTHandler
*handler
)
1604 EmpathyFTHandlerPriv
*priv
;
1606 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), NULL
);
1608 priv
= GET_PRIV (handler
);
1610 return priv
->contact
;
1614 * empathy_ft_handler_get_gfile:
1615 * @handler: an #EmpathyFTHandler
1617 * Returns the #GFile where the transfer is being read/saved.
1619 * Return value: the #GFile where the transfer is being read/saved
1622 empathy_ft_handler_get_gfile (EmpathyFTHandler
*handler
)
1624 EmpathyFTHandlerPriv
*priv
;
1626 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), NULL
);
1628 priv
= GET_PRIV (handler
);
1634 * empathy_ft_handler_get_use_hash:
1635 * @handler: an #EmpathyFTHandler
1637 * Returns whether @handler has checksumming enabled. This can depend on
1638 * the CM and the remote contact capabilities.
1640 * Return value: %TRUE if the handler has checksumming enabled,
1644 empathy_ft_handler_get_use_hash (EmpathyFTHandler
*handler
)
1646 EmpathyFTHandlerPriv
*priv
;
1648 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), FALSE
);
1650 priv
= GET_PRIV (handler
);
1652 return priv
->use_hash
;
1656 * empathy_ft_handler_is_incoming:
1657 * @handler: an #EmpathyFTHandler
1659 * Returns whether @handler is incoming or outgoing.
1661 * Return value: %TRUE if the handler is incoming, %FALSE otherwise.
1664 empathy_ft_handler_is_incoming (EmpathyFTHandler
*handler
)
1666 EmpathyFTHandlerPriv
*priv
;
1668 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), FALSE
);
1670 priv
= GET_PRIV (handler
);
1672 if (priv
->channel
== NULL
)
1675 return !tp_channel_get_requested ((TpChannel
*) priv
->channel
);
1679 * empathy_ft_handler_get_transferred_bytes:
1680 * @handler: an #EmpathyFTHandler
1682 * Returns the number of bytes already transferred by the handler.
1684 * Return value: the number of bytes already transferred by the handler.
1687 empathy_ft_handler_get_transferred_bytes (EmpathyFTHandler
*handler
)
1689 EmpathyFTHandlerPriv
*priv
;
1691 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), 0);
1693 priv
= GET_PRIV (handler
);
1695 return priv
->transferred_bytes
;
1699 * empathy_ft_handler_get_total_bytes:
1700 * @handler: an #EmpathyFTHandler
1702 * Returns the total size of the file being transferred by the handler.
1704 * Return value: a number of bytes indicating the total size of the file being
1705 * transferred by the handler.
1708 empathy_ft_handler_get_total_bytes (EmpathyFTHandler
*handler
)
1710 EmpathyFTHandlerPriv
*priv
;
1712 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), 0);
1714 priv
= GET_PRIV (handler
);
1716 return priv
->total_bytes
;
1720 * empathy_ft_handler_is_completed:
1721 * @handler: an #EmpathyFTHandler
1723 * Returns whether the transfer for @handler has been completed succesfully.
1725 * Return value: %TRUE if the handler has been transferred correctly, %FALSE
1729 empathy_ft_handler_is_completed (EmpathyFTHandler
*handler
)
1731 EmpathyFTHandlerPriv
*priv
;
1733 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), FALSE
);
1735 priv
= GET_PRIV (handler
);
1737 return priv
->is_completed
;
1741 * empathy_ft_handler_is_cancelled:
1742 * @handler: an #EmpathyFTHandler
1744 * Returns whether the transfer for @handler has been cancelled or has stopped
1747 * Return value: %TRUE if the transfer for @handler has been cancelled
1748 * or has stopped due to an error, %FALSE otherwise.
1751 empathy_ft_handler_is_cancelled (EmpathyFTHandler
*handler
)
1753 EmpathyFTHandlerPriv
*priv
;
1755 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), FALSE
);
1757 priv
= GET_PRIV (handler
);
1759 return g_cancellable_is_cancelled (priv
->cancellable
);