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-marshal.h"
34 #include "empathy-time.h"
35 #include "empathy-utils.h"
37 #define DEBUG_FLAG EMPATHY_DEBUG_FT
38 #include "empathy-debug.h"
41 * SECTION:empathy-ft-handler
42 * @title: EmpathyFTHandler
43 * @short_description: an object representing a File Transfer
44 * @include: libempathy/empathy-ft-handler
46 * #EmpathyFTHandler is the object which represents a File Transfer with all
48 * The creation of an #EmpathyFTHandler is done with
49 * empathy_ft_handler_new_outgoing() or empathy_ft_handler_new_incoming(),
50 * even though clients should not need to call them directly, as
51 * #EmpathyFTFactory does it for them. Remember that for the file transfer
52 * to work with an incoming handler,
53 * empathy_ft_handler_incoming_set_destination() should be called after
54 * empathy_ft_handler_new_incoming(). #EmpathyFTFactory does this
56 * It's important to note that, as the creation of the handlers is async, once
57 * an handler is created, it already has all the interesting properties set,
58 * like filename, total bytes, content type and so on, making it useful
59 * to be displayed in an UI.
60 * The transfer API works like a state machine; it has three signals,
61 * ::transfer-started, ::transfer-progress, ::transfer-done, which will be
62 * emitted in the relevant phases.
63 * In addition, if the handler is created with checksumming enabled,
64 * other three signals (::hashing-started, ::hashing-progress, ::hashing-done)
65 * will be emitted before or after the transfer, depending on the direction
66 * (respectively outgoing and incoming) of the handler.
67 * At any time between the call to empathy_ft_handler_start_transfer() and
68 * the last signal, a ::transfer-error can be emitted, indicating that an
69 * error has happened in the operation. The message of the error is localized
73 G_DEFINE_TYPE (EmpathyFTHandler
, empathy_ft_handler
, G_TYPE_OBJECT
)
75 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyFTHandler)
77 #define BUFFER_SIZE 4096
86 PROP_MODIFICATION_TIME
,
88 PROP_TRANSFERRED_BYTES
103 GInputStream
*stream
;
104 GError
*error
/* comment to make the style checker happy */;
109 EmpathyFTHandler
*handler
;
113 EmpathyFTHandlerReadyCallback callback
;
115 EmpathyFTHandler
*handler
;
120 gboolean dispose_run
;
123 EmpathyTpFile
*tpfile
;
124 GCancellable
*cancellable
;
127 /* request for the new transfer */
130 /* transfer properties */
131 EmpathyContact
*contact
;
136 guint64 transferred_bytes
;
139 TpFileHashType content_hash_type
;
143 guint remaining_time
;
144 gint64 last_update_time
;
146 gboolean is_completed
;
147 } EmpathyFTHandlerPriv
;
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 EmpathyFTHandlerPriv
*priv
= GET_PRIV (object
);
166 g_value_set_object (value
, priv
->contact
);
168 case PROP_CONTENT_TYPE
:
169 g_value_set_string (value
, priv
->content_type
);
171 case PROP_DESCRIPTION
:
172 g_value_set_string (value
, priv
->description
);
175 g_value_set_string (value
, priv
->filename
);
177 case PROP_MODIFICATION_TIME
:
178 g_value_set_uint64 (value
, priv
->mtime
);
180 case PROP_TOTAL_BYTES
:
181 g_value_set_uint64 (value
, priv
->total_bytes
);
183 case PROP_TRANSFERRED_BYTES
:
184 g_value_set_uint64 (value
, priv
->transferred_bytes
);
187 g_value_set_object (value
, priv
->gfile
);
190 g_value_set_object (value
, priv
->tpfile
);
193 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
198 do_set_property (GObject
*object
,
203 EmpathyFTHandlerPriv
*priv
= GET_PRIV (object
);
208 priv
->contact
= g_value_dup_object (value
);
210 case PROP_CONTENT_TYPE
:
211 priv
->content_type
= g_value_dup_string (value
);
213 case PROP_DESCRIPTION
:
214 priv
->description
= g_value_dup_string (value
);
217 priv
->filename
= g_value_dup_string (value
);
219 case PROP_MODIFICATION_TIME
:
220 priv
->mtime
= g_value_get_uint64 (value
);
222 case PROP_TOTAL_BYTES
:
223 priv
->total_bytes
= g_value_get_uint64 (value
);
225 case PROP_TRANSFERRED_BYTES
:
226 priv
->transferred_bytes
= g_value_get_uint64 (value
);
229 priv
->gfile
= g_value_dup_object (value
);
232 priv
->tpfile
= g_value_dup_object (value
);
235 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
240 do_dispose (GObject
*object
)
242 EmpathyFTHandlerPriv
*priv
= GET_PRIV (object
);
244 if (priv
->dispose_run
)
247 priv
->dispose_run
= TRUE
;
249 if (priv
->contact
!= NULL
) {
250 g_object_unref (priv
->contact
);
251 priv
->contact
= NULL
;
254 if (priv
->gfile
!= NULL
) {
255 g_object_unref (priv
->gfile
);
259 if (priv
->tpfile
!= NULL
) {
260 empathy_tp_file_close (priv
->tpfile
);
261 g_object_unref (priv
->tpfile
);
265 if (priv
->cancellable
!= NULL
) {
266 g_object_unref (priv
->cancellable
);
267 priv
->cancellable
= NULL
;
270 if (priv
->request
!= NULL
)
272 g_hash_table_unref (priv
->request
);
273 priv
->request
= NULL
;
276 G_OBJECT_CLASS (empathy_ft_handler_parent_class
)->dispose (object
);
280 do_finalize (GObject
*object
)
282 EmpathyFTHandlerPriv
*priv
= GET_PRIV (object
);
284 DEBUG ("%p", object
);
286 g_free (priv
->content_type
);
287 priv
->content_type
= NULL
;
289 g_free (priv
->filename
);
290 priv
->filename
= NULL
;
292 g_free (priv
->description
);
293 priv
->description
= NULL
;
295 g_free (priv
->content_hash
);
296 priv
->content_hash
= NULL
;
298 G_OBJECT_CLASS (empathy_ft_handler_parent_class
)->finalize (object
);
302 empathy_ft_handler_class_init (EmpathyFTHandlerClass
*klass
)
304 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
305 GParamSpec
*param_spec
;
307 g_type_class_add_private (klass
, sizeof (EmpathyFTHandlerPriv
));
309 object_class
->get_property
= do_get_property
;
310 object_class
->set_property
= do_set_property
;
311 object_class
->dispose
= do_dispose
;
312 object_class
->finalize
= do_finalize
;
317 * EmpathyFTHandler:contact:
319 * The remote #EmpathyContact for the transfer
321 param_spec
= g_param_spec_object ("contact",
322 "contact", "The remote contact",
323 EMPATHY_TYPE_CONTACT
,
324 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
| G_PARAM_CONSTRUCT_ONLY
);
325 g_object_class_install_property (object_class
, PROP_CONTACT
, param_spec
);
328 * EmpathyFTHandler:content-type:
330 * The content type of the file being transferred
332 param_spec
= g_param_spec_string ("content-type",
333 "content-type", "The content type of the file", NULL
,
334 G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
335 g_object_class_install_property (object_class
,
336 PROP_CONTENT_TYPE
, param_spec
);
339 * EmpathyFTHandler:description:
341 * The description of the file being transferred
343 param_spec
= g_param_spec_string ("description",
344 "description", "The description of the file", NULL
,
345 G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
346 g_object_class_install_property (object_class
,
347 PROP_DESCRIPTION
, param_spec
);
350 * EmpathyFTHandler:filename:
352 * The name of the file being transferred
354 param_spec
= g_param_spec_string ("filename",
355 "filename", "The name of the file", NULL
,
356 G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
357 g_object_class_install_property (object_class
,
358 PROP_FILENAME
, param_spec
);
361 * EmpathyFTHandler:modification-time:
363 * The modification time of the file being transferred
365 param_spec
= g_param_spec_uint64 ("modification-time",
366 "modification-time", "The mtime of the file", 0,
367 G_MAXUINT64
, 0, G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
368 g_object_class_install_property (object_class
,
369 PROP_MODIFICATION_TIME
, param_spec
);
372 * EmpathyFTHandler:total-bytes:
374 * The size (in bytes) of the file being transferred
376 param_spec
= g_param_spec_uint64 ("total-bytes",
377 "total-bytes", "The size of the file", 0,
378 G_MAXUINT64
, 0, G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
379 g_object_class_install_property (object_class
,
380 PROP_TOTAL_BYTES
, param_spec
);
383 * EmpathyFTHandler:transferred-bytes:
385 * The number of the bytes already transferred
387 param_spec
= g_param_spec_uint64 ("transferred-bytes",
388 "transferred-bytes", "The number of bytes already transferred", 0,
389 G_MAXUINT64
, 0, G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
390 g_object_class_install_property (object_class
,
391 PROP_TRANSFERRED_BYTES
, param_spec
);
394 * EmpathyFTHandler:gfile:
396 * The #GFile object where the transfer actually happens
398 param_spec
= g_param_spec_object ("gfile",
399 "gfile", "The GFile we're handling",
401 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
402 g_object_class_install_property (object_class
, PROP_G_FILE
, param_spec
);
405 * EmpathyFTHandler:tp-file:
407 * The underlying #EmpathyTpFile managing the transfer
409 param_spec
= g_param_spec_object ("tp-file",
410 "tp-file", "The file's channel wrapper",
411 EMPATHY_TYPE_TP_FILE
,
412 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
| G_PARAM_CONSTRUCT_ONLY
);
413 g_object_class_install_property (object_class
, PROP_TP_FILE
, param_spec
);
418 * EmpathyFTHandler::transfer-started
419 * @handler: the object which has received the signal
420 * @tp_file: the #EmpathyTpFile for which the transfer has started
422 * This signal is emitted when the actual transfer starts.
424 signals
[TRANSFER_STARTED
] =
425 g_signal_new ("transfer-started", G_TYPE_FROM_CLASS (klass
),
426 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
427 g_cclosure_marshal_VOID__OBJECT
,
429 1, EMPATHY_TYPE_TP_FILE
);
432 * EmpathyFTHandler::transfer-done
433 * @handler: the object which has received the signal
434 * @tp_file: the #EmpathyTpFile for which the transfer has started
436 * This signal will be emitted when the actual transfer is completed
439 signals
[TRANSFER_DONE
] =
440 g_signal_new ("transfer-done", G_TYPE_FROM_CLASS (klass
),
441 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
442 g_cclosure_marshal_VOID__OBJECT
,
444 1, EMPATHY_TYPE_TP_FILE
);
447 * EmpathyFTHandler::transfer-error
448 * @handler: the object which has received the signal
451 * This signal can be emitted anytime between the call to
452 * empathy_ft_handler_start_transfer() and the last expected signal
453 * (::transfer-done or ::hashing-done), and it's guaranteed to be the last
454 * signal coming from the handler, meaning that no other operation will
455 * take place after this signal.
457 signals
[TRANSFER_ERROR
] =
458 g_signal_new ("transfer-error", G_TYPE_FROM_CLASS (klass
),
459 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
460 g_cclosure_marshal_VOID__POINTER
,
465 * EmpathyFTHandler::transfer-progress
466 * @handler: the object which has received the signal
467 * @current_bytes: the bytes currently transferred
468 * @total_bytes: the total bytes of the handler
469 * @remaining_time: the number of seconds remaining for the transfer
471 * @speed: the current speed of the transfer (in KB/s)
473 * This signal is emitted to notify clients of the progress of the
476 signals
[TRANSFER_PROGRESS
] =
477 g_signal_new ("transfer-progress", G_TYPE_FROM_CLASS (klass
),
478 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
479 _empathy_marshal_VOID__UINT64_UINT64_UINT_DOUBLE
,
481 4, G_TYPE_UINT64
, G_TYPE_UINT64
, G_TYPE_UINT
, G_TYPE_DOUBLE
);
484 * EmpathyFTHandler::hashing-started
485 * @handler: the object which has received the signal
487 * This signal is emitted when the hashing operation of the handler
488 * is started. Note that this might happen or not, depending on the CM
489 * and remote contact capabilities. Clients shoud use
490 * empathy_ft_handler_get_use_hash() before calling
491 * empathy_ft_handler_start_transfer() to know whether they should connect
494 signals
[HASHING_STARTED
] =
495 g_signal_new ("hashing-started", G_TYPE_FROM_CLASS (klass
),
496 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
497 g_cclosure_marshal_VOID__VOID
,
501 * EmpathyFTHandler::hashing-progress
502 * @handler: the object which has received the signal
503 * @current_bytes: the bytes currently hashed
504 * @total_bytes: the total bytes of the handler
506 * This signal is emitted to notify clients of the progress of the
509 signals
[HASHING_PROGRESS
] =
510 g_signal_new ("hashing-progress", G_TYPE_FROM_CLASS (klass
),
511 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
512 _empathy_marshal_VOID__UINT64_UINT64
,
514 2, G_TYPE_UINT64
, G_TYPE_UINT64
);
517 * EmpathyFTHandler::hashing-done
518 * @handler: the object which has received the signal
520 * This signal is emitted when the hashing operation of the handler
523 signals
[HASHING_DONE
] =
524 g_signal_new ("hashing-done", G_TYPE_FROM_CLASS (klass
),
525 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
526 g_cclosure_marshal_VOID__VOID
,
531 empathy_ft_handler_init (EmpathyFTHandler
*self
)
533 EmpathyFTHandlerPriv
*priv
= G_TYPE_INSTANCE_GET_PRIVATE (self
,
534 EMPATHY_TYPE_FT_HANDLER
, EmpathyFTHandlerPriv
);
537 priv
->cancellable
= g_cancellable_new ();
540 /* private functions */
543 hash_data_free (HashingData
*data
)
545 g_free (data
->buffer
);
547 if (data
->stream
!= NULL
)
548 g_object_unref (data
->stream
);
550 if (data
->checksum
!= NULL
)
551 g_checksum_free (data
->checksum
);
553 if (data
->error
!= NULL
)
554 g_error_free (data
->error
);
556 if (data
->handler
!= NULL
)
557 g_object_unref (data
->handler
);
559 g_slice_free (HashingData
, data
);
563 tp_file_hash_to_g_checksum (TpFileHashType type
)
565 GChecksumType retval
;
569 case TP_FILE_HASH_TYPE_MD5
:
570 retval
= G_CHECKSUM_MD5
;
572 case TP_FILE_HASH_TYPE_SHA1
:
573 retval
= G_CHECKSUM_SHA1
;
575 case TP_FILE_HASH_TYPE_SHA256
:
576 retval
= G_CHECKSUM_SHA256
;
578 case TP_FILE_HASH_TYPE_NONE
:
580 g_assert_not_reached ();
588 check_hash_incoming (EmpathyFTHandler
*handler
)
590 HashingData
*hash_data
;
591 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
593 if (!EMP_STR_EMPTY (priv
->content_hash
))
595 hash_data
= g_slice_new0 (HashingData
);
596 hash_data
->total_bytes
= priv
->total_bytes
;
597 hash_data
->handler
= g_object_ref (handler
);
598 hash_data
->checksum
= g_checksum_new
599 (tp_file_hash_to_g_checksum (priv
->content_hash_type
));
601 g_signal_emit (handler
, signals
[HASHING_STARTED
], 0);
603 g_io_scheduler_push_job (do_hash_job_incoming
, hash_data
, NULL
,
604 G_PRIORITY_DEFAULT
, priv
->cancellable
);
609 emit_error_signal (EmpathyFTHandler
*handler
,
612 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
614 DEBUG ("Error in transfer: %s\n", error
->message
);
616 if (!g_cancellable_is_cancelled (priv
->cancellable
))
617 g_cancellable_cancel (priv
->cancellable
);
619 g_signal_emit (handler
, signals
[TRANSFER_ERROR
], 0, error
);
623 ft_transfer_operation_callback (EmpathyTpFile
*tp_file
,
627 EmpathyFTHandler
*handler
= user_data
;
628 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
630 DEBUG ("Transfer operation callback, error %p", error
);
634 emit_error_signal (handler
, error
);
638 priv
->is_completed
= TRUE
;
639 g_signal_emit (handler
, signals
[TRANSFER_DONE
], 0, tp_file
);
641 empathy_tp_file_close (tp_file
);
643 if (empathy_ft_handler_is_incoming (handler
) && priv
->use_hash
)
645 check_hash_incoming (handler
);
651 update_remaining_time_and_speed (EmpathyFTHandler
*handler
,
652 guint64 transferred_bytes
)
654 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
655 gint64 elapsed_time
, current_time
;
656 guint64 transferred
, last_transferred_bytes
;
660 last_transferred_bytes
= priv
->transferred_bytes
;
661 priv
->transferred_bytes
= transferred_bytes
;
663 current_time
= empathy_time_get_current ();
664 elapsed_time
= current_time
- priv
->last_update_time
;
666 if (elapsed_time
>= 1)
668 transferred
= transferred_bytes
- last_transferred_bytes
;
669 speed
= (gdouble
) transferred
/ (gdouble
) elapsed_time
;
670 remaining_time
= (priv
->total_bytes
- priv
->transferred_bytes
) / speed
;
672 priv
->remaining_time
= remaining_time
;
673 priv
->last_update_time
= current_time
;
678 ft_transfer_progress_callback (EmpathyTpFile
*tp_file
,
679 guint64 transferred_bytes
,
682 EmpathyFTHandler
*handler
= user_data
;
683 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
685 if (empathy_ft_handler_is_cancelled (handler
))
688 if (transferred_bytes
== 0)
690 priv
->last_update_time
= empathy_time_get_current ();
691 g_signal_emit (handler
, signals
[TRANSFER_STARTED
], 0, tp_file
);
694 if (priv
->transferred_bytes
!= transferred_bytes
)
696 update_remaining_time_and_speed (handler
, transferred_bytes
);
698 g_signal_emit (handler
, signals
[TRANSFER_PROGRESS
], 0,
699 transferred_bytes
, priv
->total_bytes
, priv
->remaining_time
,
705 ft_handler_create_channel_cb (GObject
*source
,
706 GAsyncResult
*result
,
709 EmpathyFTHandler
*handler
= user_data
;
710 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
711 GError
*error
= NULL
;
712 TpChannel
*channel
= NULL
;
714 DEBUG ("Dispatcher create channel CB");
716 channel
= tp_account_channel_request_create_and_handle_channel_finish (
717 TP_ACCOUNT_CHANNEL_REQUEST (source
), result
, NULL
, &error
);
720 DEBUG ("Failed to request FT channel: %s", error
->message
);
722 g_cancellable_set_error_if_cancelled (priv
->cancellable
, &error
);
726 emit_error_signal (handler
, error
);
728 g_error_free (error
);
732 priv
->tpfile
= empathy_tp_file_new (channel
);
734 empathy_tp_file_offer (priv
->tpfile
, priv
->gfile
, priv
->cancellable
,
735 ft_transfer_progress_callback
, handler
,
736 ft_transfer_operation_callback
, handler
);
739 tp_clear_object (&channel
);
743 ft_handler_push_to_dispatcher (EmpathyFTHandler
*handler
)
746 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
747 TpAccountChannelRequest
*req
;
749 DEBUG ("Pushing request to the dispatcher");
751 account
= empathy_contact_get_account (priv
->contact
);
753 req
= tp_account_channel_request_new (account
, priv
->request
,
754 TP_USER_ACTION_TIME_NOT_USER_ACTION
);
756 tp_account_channel_request_create_and_handle_channel_async (req
, NULL
,
757 ft_handler_create_channel_cb
, handler
);
759 g_object_unref (req
);
763 ft_handler_populate_outgoing_request (EmpathyFTHandler
*handler
)
765 guint contact_handle
;
766 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
769 contact_handle
= empathy_contact_get_handle (priv
->contact
);
770 uri
= g_file_get_uri (priv
->gfile
);
772 priv
->request
= tp_asv_new (
773 TP_PROP_CHANNEL_CHANNEL_TYPE
, G_TYPE_STRING
,
774 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER
,
775 TP_PROP_CHANNEL_TARGET_HANDLE_TYPE
, G_TYPE_UINT
,
776 TP_HANDLE_TYPE_CONTACT
,
777 TP_PROP_CHANNEL_TARGET_HANDLE
, G_TYPE_UINT
,
779 TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_CONTENT_TYPE
, G_TYPE_STRING
,
781 TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_FILENAME
, G_TYPE_STRING
,
783 TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_SIZE
, G_TYPE_UINT64
,
785 TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_DATE
, G_TYPE_UINT64
,
787 TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_URI
, G_TYPE_STRING
, uri
,
794 hash_job_done (gpointer user_data
)
796 HashingData
*hash_data
= user_data
;
797 EmpathyFTHandler
*handler
= hash_data
->handler
;
798 EmpathyFTHandlerPriv
*priv
;
799 GError
*error
= NULL
;
801 DEBUG ("Closing stream after hashing.");
803 priv
= GET_PRIV (handler
);
805 if (hash_data
->error
!= NULL
)
807 error
= hash_data
->error
;
808 hash_data
->error
= NULL
;
812 DEBUG ("Got file hash %s", g_checksum_get_string (hash_data
->checksum
));
814 if (empathy_ft_handler_is_incoming (handler
))
816 if (g_strcmp0 (g_checksum_get_string (hash_data
->checksum
),
819 DEBUG ("Hash mismatch when checking incoming handler: "
820 "received %s, calculated %s", priv
->content_hash
,
821 g_checksum_get_string (hash_data
->checksum
));
823 error
= g_error_new_literal (EMPATHY_FT_ERROR_QUARK
,
824 EMPATHY_FT_ERROR_HASH_MISMATCH
,
825 _("File transfer completed, but the file was corrupted"));
830 DEBUG ("Hash verification matched, received %s, calculated %s",
832 g_checksum_get_string (hash_data
->checksum
));
837 /* set the checksum in the request...
838 * org.freedesktop.Telepathy.Channel.Type.FileTransfer.ContentHash
840 tp_asv_set_string (priv
->request
,
841 TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_CONTENT_HASH
,
842 g_checksum_get_string (hash_data
->checksum
));
849 emit_error_signal (handler
, error
);
850 g_clear_error (&error
);
854 g_signal_emit (handler
, signals
[HASHING_DONE
], 0);
856 if (!empathy_ft_handler_is_incoming (handler
))
857 /* the request is complete now, push it to the dispatcher */
858 ft_handler_push_to_dispatcher (handler
);
861 hash_data_free (hash_data
);
867 emit_hashing_progress (gpointer user_data
)
869 HashingData
*hash_data
= user_data
;
871 g_signal_emit (hash_data
->handler
, signals
[HASHING_PROGRESS
], 0,
872 (guint64
) hash_data
->total_read
, (guint64
) hash_data
->total_bytes
);
878 do_hash_job (GIOSchedulerJob
*job
,
879 GCancellable
*cancellable
,
882 HashingData
*hash_data
= user_data
;
884 GError
*error
= NULL
;
887 if (hash_data
->buffer
== NULL
)
888 hash_data
->buffer
= g_malloc0 (BUFFER_SIZE
);
890 bytes_read
= g_input_stream_read (hash_data
->stream
, hash_data
->buffer
,
891 BUFFER_SIZE
, cancellable
, &error
);
895 hash_data
->total_read
+= bytes_read
;
897 /* we now have the chunk */
900 g_checksum_update (hash_data
->checksum
, hash_data
->buffer
, bytes_read
);
901 g_io_scheduler_job_send_to_mainloop_async (job
, emit_hashing_progress
,
904 g_free (hash_data
->buffer
);
905 hash_data
->buffer
= NULL
;
911 g_input_stream_close (hash_data
->stream
, cancellable
, &error
);
916 hash_data
->error
= error
;
918 g_io_scheduler_job_send_to_mainloop_async (job
, hash_job_done
,
925 do_hash_job_incoming (GIOSchedulerJob
*job
,
926 GCancellable
*cancellable
,
929 HashingData
*hash_data
= user_data
;
930 EmpathyFTHandler
*handler
= hash_data
->handler
;
931 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
932 GError
*error
= NULL
;
934 DEBUG ("checking integrity for incoming handler");
936 /* need to get the stream first */
938 G_INPUT_STREAM (g_file_read (priv
->gfile
, cancellable
, &error
));
942 hash_data
->error
= error
;
943 g_io_scheduler_job_send_to_mainloop_async (job
, hash_job_done
,
948 return do_hash_job (job
, cancellable
, user_data
);
952 ft_handler_read_async_cb (GObject
*source
,
956 GFileInputStream
*stream
;
957 GError
*error
= NULL
;
958 HashingData
*hash_data
;
959 EmpathyFTHandler
*handler
= user_data
;
960 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
962 DEBUG ("GFile read async CB.");
964 stream
= g_file_read_finish (priv
->gfile
, res
, &error
);
967 emit_error_signal (handler
, error
);
968 g_clear_error (&error
);
973 hash_data
= g_slice_new0 (HashingData
);
974 hash_data
->stream
= G_INPUT_STREAM (stream
);
975 hash_data
->total_bytes
= priv
->total_bytes
;
976 hash_data
->handler
= g_object_ref (handler
);
977 /* FIXME: MD5 is the only ContentHashType supported right now */
978 hash_data
->checksum
= g_checksum_new (G_CHECKSUM_MD5
);
980 tp_asv_set_uint32 (priv
->request
,
981 TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_CONTENT_HASH_TYPE
,
982 TP_FILE_HASH_TYPE_MD5
);
984 g_signal_emit (handler
, signals
[HASHING_STARTED
], 0);
986 g_io_scheduler_push_job (do_hash_job
, hash_data
, NULL
,
987 G_PRIORITY_DEFAULT
, priv
->cancellable
);
991 callbacks_data_free (gpointer user_data
)
993 CallbacksData
*data
= user_data
;
995 if (data
->handler
!= NULL
)
996 g_object_unref (data
->handler
);
998 g_slice_free (CallbacksData
, data
);
1002 set_content_hash_type_from_classes (EmpathyFTHandler
*handler
,
1005 GArray
*possible_values
;
1008 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
1009 gboolean support_ft
= FALSE
;
1012 possible_values
= g_array_new (TRUE
, TRUE
, sizeof (guint
));
1014 for (i
= 0; i
< classes
->len
; i
++)
1018 const gchar
*chan_type
;
1020 tp_value_array_unpack (g_ptr_array_index (classes
, i
), 2,
1023 chan_type
= tp_asv_get_string (fixed
, TP_PROP_CHANNEL_CHANNEL_TYPE
);
1025 if (tp_strdiff (chan_type
, TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER
))
1028 if (tp_asv_get_uint32 (fixed
, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE
, NULL
) !=
1029 TP_HANDLE_TYPE_CONTACT
)
1034 value
= tp_asv_get_uint32
1035 (fixed
, TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_CONTENT_HASH_TYPE
,
1039 g_array_append_val (possible_values
, value
);
1044 g_array_free (possible_values
, TRUE
);
1048 if (possible_values
->len
== 0)
1050 /* there are no channel classes with hash support, disable it. */
1051 priv
->use_hash
= FALSE
;
1052 priv
->content_hash_type
= TP_FILE_HASH_TYPE_NONE
;
1057 priv
->use_hash
= TRUE
;
1059 if (possible_values
->len
== 1)
1061 priv
->content_hash_type
= g_array_index (possible_values
, guint
, 0);
1065 /* order the array and pick the first non zero, so that MD5
1066 * is the preferred value.
1068 g_array_sort (possible_values
, empathy_uint_compare
);
1070 if (g_array_index (possible_values
, guint
, 0) == 0)
1071 priv
->content_hash_type
= g_array_index (possible_values
, guint
, 1);
1073 priv
->content_hash_type
= g_array_index (possible_values
, guint
, 0);
1077 g_array_free (possible_values
, TRUE
);
1079 DEBUG ("Hash enabled %s; setting content hash type as %u",
1080 priv
->use_hash
? "True" : "False", priv
->content_hash_type
);
1086 check_hashing (CallbacksData
*data
)
1088 EmpathyFTHandler
*handler
= data
->handler
;
1089 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
1090 GError
*myerr
= NULL
;
1091 TpCapabilities
*caps
;
1095 conn
= empathy_contact_get_connection (priv
->contact
);
1097 caps
= tp_connection_get_capabilities (conn
);
1100 data
->callback (handler
, NULL
, data
->user_data
);
1104 classes
= tp_capabilities_get_channel_classes (caps
);
1106 /* set whether we support hash and the type of it */
1107 if (!set_content_hash_type_from_classes (handler
, classes
))
1109 g_set_error_literal (&myerr
, EMPATHY_FT_ERROR_QUARK
,
1110 EMPATHY_FT_ERROR_NOT_SUPPORTED
,
1111 _("File transfer not supported by remote contact"));
1113 if (!g_cancellable_is_cancelled (priv
->cancellable
))
1114 g_cancellable_cancel (priv
->cancellable
);
1116 data
->callback (handler
, myerr
, data
->user_data
);
1117 g_clear_error (&myerr
);
1121 /* get back to the caller now */
1122 data
->callback (handler
, NULL
, data
->user_data
);
1126 callbacks_data_free (data
);
1130 ft_handler_complete_request (EmpathyFTHandler
*handler
)
1132 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
1134 /* populate the request table with all the known properties */
1135 ft_handler_populate_outgoing_request (handler
);
1138 /* start hashing the file */
1139 g_file_read_async (priv
->gfile
, G_PRIORITY_DEFAULT
,
1140 priv
->cancellable
, ft_handler_read_async_cb
, handler
);
1142 /* push directly the handler to the dispatcher */
1143 ft_handler_push_to_dispatcher (handler
);
1147 ft_handler_gfile_ready_cb (GObject
*source
,
1149 CallbacksData
*cb_data
)
1152 GError
*error
= NULL
;
1154 EmpathyFTHandlerPriv
*priv
= GET_PRIV (cb_data
->handler
);
1156 DEBUG ("Got GFileInfo.");
1158 info
= g_file_query_info_finish (priv
->gfile
, res
, &error
);
1163 if (g_file_info_get_file_type (info
) != G_FILE_TYPE_REGULAR
)
1165 error
= g_error_new_literal (EMPATHY_FT_ERROR_QUARK
,
1166 EMPATHY_FT_ERROR_INVALID_SOURCE_FILE
,
1167 _("The selected file is not a regular file"));
1171 priv
->total_bytes
= g_file_info_get_size (info
);
1172 if (priv
->total_bytes
== 0)
1174 error
= g_error_new_literal (EMPATHY_FT_ERROR_QUARK
,
1175 EMPATHY_FT_ERROR_EMPTY_SOURCE_FILE
,
1176 _("The selected file is empty"));
1180 priv
->content_type
= g_strdup (g_file_info_get_content_type (info
));
1181 priv
->filename
= g_strdup (g_file_info_get_display_name (info
));
1182 g_file_info_get_modification_time (info
, &mtime
);
1183 priv
->mtime
= mtime
.tv_sec
;
1184 priv
->transferred_bytes
= 0;
1185 priv
->description
= NULL
;
1187 g_object_unref (info
);
1192 if (!g_cancellable_is_cancelled (priv
->cancellable
))
1193 g_cancellable_cancel (priv
->cancellable
);
1195 cb_data
->callback (cb_data
->handler
, error
, cb_data
->user_data
);
1196 g_error_free (error
);
1198 callbacks_data_free (cb_data
);
1202 /* see if FT/hashing are allowed */
1203 check_hashing (cb_data
);
1208 contact_factory_contact_cb (TpConnection
*connection
,
1209 EmpathyContact
*contact
,
1210 const GError
*error
,
1212 GObject
*weak_object
)
1214 CallbacksData
*cb_data
= user_data
;
1215 EmpathyFTHandler
*handler
= EMPATHY_FT_HANDLER (weak_object
);
1216 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
1220 if (!g_cancellable_is_cancelled (priv
->cancellable
))
1221 g_cancellable_cancel (priv
->cancellable
);
1223 cb_data
->callback (handler
, (GError
*) error
, cb_data
->user_data
);
1224 callbacks_data_free (cb_data
);
1228 priv
->contact
= g_object_ref (contact
);
1230 cb_data
->callback (handler
, NULL
, cb_data
->user_data
);
1234 channel_get_all_properties_cb (TpProxy
*proxy
,
1235 GHashTable
*properties
,
1236 const GError
*error
,
1238 GObject
*weak_object
)
1240 CallbacksData
*cb_data
= user_data
;
1241 EmpathyFTHandler
*handler
= EMPATHY_FT_HANDLER (weak_object
);
1242 EmpathyFTHandlerPriv
*priv
= GET_PRIV (handler
);
1247 if (!g_cancellable_is_cancelled (priv
->cancellable
))
1248 g_cancellable_cancel (priv
->cancellable
);
1250 cb_data
->callback (handler
, (GError
*) error
, cb_data
->user_data
);
1252 callbacks_data_free (cb_data
);
1256 priv
->total_bytes
= g_value_get_uint64 (
1257 g_hash_table_lookup (properties
, "Size"));
1259 priv
->transferred_bytes
= g_value_get_uint64 (
1260 g_hash_table_lookup (properties
, "TransferredBytes"));
1262 priv
->filename
= g_value_dup_string (
1263 g_hash_table_lookup (properties
, "Filename"));
1265 priv
->content_hash
= g_value_dup_string (
1266 g_hash_table_lookup (properties
, "ContentHash"));
1268 priv
->content_hash_type
= g_value_get_uint (
1269 g_hash_table_lookup (properties
, "ContentHashType"));
1271 priv
->content_type
= g_value_dup_string (
1272 g_hash_table_lookup (properties
, "ContentType"));
1274 priv
->description
= g_value_dup_string (
1275 g_hash_table_lookup (properties
, "Description"));
1277 c_handle
= tp_channel_get_handle (TP_CHANNEL (proxy
), NULL
);
1278 empathy_tp_contact_factory_get_from_handle (
1279 tp_channel_borrow_connection (TP_CHANNEL (proxy
)), c_handle
,
1280 contact_factory_contact_cb
, cb_data
, callbacks_data_free
,
1281 G_OBJECT (handler
));
1284 /* public methods */
1287 * empathy_ft_handler_new_outgoing:
1288 * @contact: the #EmpathyContact to send @source to
1289 * @source: the #GFile to send
1290 * @callback: callback to be called when the handler has been created
1291 * @user_data: user data to be passed to @callback
1293 * Triggers the creation of a new #EmpathyFTHandler for an outgoing transfer.
1296 empathy_ft_handler_new_outgoing (EmpathyContact
*contact
,
1298 EmpathyFTHandlerReadyCallback callback
,
1301 EmpathyFTHandler
*handler
;
1302 CallbacksData
*data
;
1303 EmpathyFTHandlerPriv
*priv
;
1305 DEBUG ("New handler outgoing");
1307 g_return_if_fail (EMPATHY_IS_CONTACT (contact
));
1308 g_return_if_fail (G_IS_FILE (source
));
1310 handler
= g_object_new (EMPATHY_TYPE_FT_HANDLER
,
1311 "contact", contact
, "gfile", source
, NULL
);
1313 priv
= GET_PRIV (handler
);
1315 data
= g_slice_new0 (CallbacksData
);
1316 data
->callback
= callback
;
1317 data
->user_data
= user_data
;
1318 data
->handler
= g_object_ref (handler
);
1320 /* start collecting info about the file */
1321 g_file_query_info_async (priv
->gfile
,
1322 G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME
","
1323 G_FILE_ATTRIBUTE_STANDARD_SIZE
","
1324 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
","
1325 G_FILE_ATTRIBUTE_STANDARD_TYPE
","
1326 G_FILE_ATTRIBUTE_TIME_MODIFIED
,
1327 G_FILE_QUERY_INFO_NONE
, G_PRIORITY_DEFAULT
,
1328 NULL
, (GAsyncReadyCallback
) ft_handler_gfile_ready_cb
, data
);
1332 * empathy_ft_handler_new_incoming:
1333 * @tp_file: the #EmpathyTpFile wrapping the incoming channel
1334 * @callback: callback to be called when the handler has been created
1335 * @user_data: user data to be passed to @callback
1337 * Triggers the creation of a new #EmpathyFTHandler for an incoming transfer.
1338 * Note that for the handler to be useful, you will have to set a destination
1339 * file with empathy_ft_handler_incoming_set_destination() after the handler
1343 empathy_ft_handler_new_incoming (EmpathyTpFile
*tp_file
,
1344 EmpathyFTHandlerReadyCallback callback
,
1347 EmpathyFTHandler
*handler
;
1349 CallbacksData
*data
;
1351 g_return_if_fail (EMPATHY_IS_TP_FILE (tp_file
));
1353 handler
= g_object_new (EMPATHY_TYPE_FT_HANDLER
,
1354 "tp-file", tp_file
, NULL
);
1356 g_object_get (tp_file
, "channel", &channel
, NULL
);
1358 data
= g_slice_new0 (CallbacksData
);
1359 data
->callback
= callback
;
1360 data
->user_data
= user_data
;
1361 data
->handler
= g_object_ref (handler
);
1363 tp_cli_dbus_properties_call_get_all (channel
,
1364 -1, TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER
,
1365 channel_get_all_properties_cb
, data
, NULL
, G_OBJECT (handler
));
1369 * empathy_ft_handler_start_transfer:
1370 * @handler: an #EmpathyFTHandler
1372 * Starts the transfer machinery. After this call, the transfer and hashing
1373 * signals will be emitted by the handler.
1376 empathy_ft_handler_start_transfer (EmpathyFTHandler
*handler
)
1378 EmpathyFTHandlerPriv
*priv
;
1380 g_return_if_fail (EMPATHY_IS_FT_HANDLER (handler
));
1382 priv
= GET_PRIV (handler
);
1384 if (priv
->tpfile
== NULL
)
1386 ft_handler_complete_request (handler
);
1390 /* TODO: add support for resume. */
1391 empathy_tp_file_accept (priv
->tpfile
, 0, priv
->gfile
, priv
->cancellable
,
1392 ft_transfer_progress_callback
, handler
,
1393 ft_transfer_operation_callback
, handler
);
1398 * empathy_ft_handler_cancel_transfer:
1399 * @handler: an #EmpathyFTHandler
1401 * Cancels an ongoing handler operation. Note that this doesn't destroy
1402 * the object, which will keep all the properties, altough it won't be able
1403 * to do any more I/O.
1406 empathy_ft_handler_cancel_transfer (EmpathyFTHandler
*handler
)
1408 EmpathyFTHandlerPriv
*priv
;
1410 g_return_if_fail (EMPATHY_IS_FT_HANDLER (handler
));
1412 priv
= GET_PRIV (handler
);
1414 /* if we don't have an EmpathyTpFile, we are hashing, so
1415 * we can just cancel the GCancellable to stop it.
1417 if (priv
->tpfile
== NULL
)
1418 g_cancellable_cancel (priv
->cancellable
);
1420 empathy_tp_file_cancel (priv
->tpfile
);
1424 * empathy_ft_handler_incoming_set_destination:
1425 * @handler: an #EmpathyFTHandler
1426 * @destination: the #GFile where the transfer should be saved
1428 * Sets the destination of the incoming handler to be @destination.
1429 * Note that calling this method is mandatory before starting the transfer
1430 * for incoming handlers.
1433 empathy_ft_handler_incoming_set_destination (EmpathyFTHandler
*handler
,
1436 EmpathyFTHandlerPriv
*priv
;
1438 g_return_if_fail (EMPATHY_IS_FT_HANDLER (handler
));
1439 g_return_if_fail (G_IS_FILE (destination
));
1441 priv
= GET_PRIV (handler
);
1443 g_object_set (handler
, "gfile", destination
, NULL
);
1445 /* check if hash is supported. if it isn't, set use_hash to FALSE
1446 * anyway, so that clients won't be expecting us to checksum.
1448 if (EMP_STR_EMPTY (priv
->content_hash
) ||
1449 priv
->content_hash_type
== TP_FILE_HASH_TYPE_NONE
)
1450 priv
->use_hash
= FALSE
;
1452 priv
->use_hash
= TRUE
;
1456 * empathy_ft_handler_get_filename:
1457 * @handler: an #EmpathyFTHandler
1459 * Returns the name of the file being transferred.
1461 * Return value: the name of the file being transferred
1464 empathy_ft_handler_get_filename (EmpathyFTHandler
*handler
)
1466 EmpathyFTHandlerPriv
*priv
;
1468 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), NULL
);
1470 priv
= GET_PRIV (handler
);
1472 return priv
->filename
;
1476 * empathy_ft_handler_get_content_type:
1477 * @handler: an #EmpathyFTHandler
1479 * Returns the content type of the file being transferred.
1481 * Return value: the content type of the file being transferred
1484 empathy_ft_handler_get_content_type (EmpathyFTHandler
*handler
)
1486 EmpathyFTHandlerPriv
*priv
;
1488 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), NULL
);
1490 priv
= GET_PRIV (handler
);
1492 return priv
->content_type
;
1496 * empathy_ft_handler_get_contact:
1497 * @handler: an #EmpathyFTHandler
1499 * Returns the remote #EmpathyContact at the other side of the transfer.
1501 * Return value: the remote #EmpathyContact for @handler
1504 empathy_ft_handler_get_contact (EmpathyFTHandler
*handler
)
1506 EmpathyFTHandlerPriv
*priv
;
1508 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), NULL
);
1510 priv
= GET_PRIV (handler
);
1512 return priv
->contact
;
1516 * empathy_ft_handler_get_gfile:
1517 * @handler: an #EmpathyFTHandler
1519 * Returns the #GFile where the transfer is being read/saved.
1521 * Return value: the #GFile where the transfer is being read/saved
1524 empathy_ft_handler_get_gfile (EmpathyFTHandler
*handler
)
1526 EmpathyFTHandlerPriv
*priv
;
1528 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), NULL
);
1530 priv
= GET_PRIV (handler
);
1536 * empathy_ft_handler_get_use_hash:
1537 * @handler: an #EmpathyFTHandler
1539 * Returns whether @handler has checksumming enabled. This can depend on
1540 * the CM and the remote contact capabilities.
1542 * Return value: %TRUE if the handler has checksumming enabled,
1546 empathy_ft_handler_get_use_hash (EmpathyFTHandler
*handler
)
1548 EmpathyFTHandlerPriv
*priv
;
1550 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), FALSE
);
1552 priv
= GET_PRIV (handler
);
1554 return priv
->use_hash
;
1558 * empathy_ft_handler_is_incoming:
1559 * @handler: an #EmpathyFTHandler
1561 * Returns whether @handler is incoming or outgoing.
1563 * Return value: %TRUE if the handler is incoming, %FALSE otherwise.
1566 empathy_ft_handler_is_incoming (EmpathyFTHandler
*handler
)
1568 EmpathyFTHandlerPriv
*priv
;
1570 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), FALSE
);
1572 priv
= GET_PRIV (handler
);
1574 if (priv
->tpfile
== NULL
)
1577 return empathy_tp_file_is_incoming (priv
->tpfile
);
1581 * empathy_ft_handler_get_transferred_bytes:
1582 * @handler: an #EmpathyFTHandler
1584 * Returns the number of bytes already transferred by the handler.
1586 * Return value: the number of bytes already transferred by the handler.
1589 empathy_ft_handler_get_transferred_bytes (EmpathyFTHandler
*handler
)
1591 EmpathyFTHandlerPriv
*priv
;
1593 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), 0);
1595 priv
= GET_PRIV (handler
);
1597 return priv
->transferred_bytes
;
1601 * empathy_ft_handler_get_total_bytes:
1602 * @handler: an #EmpathyFTHandler
1604 * Returns the total size of the file being transferred by the handler.
1606 * Return value: a number of bytes indicating the total size of the file being
1607 * transferred by the handler.
1610 empathy_ft_handler_get_total_bytes (EmpathyFTHandler
*handler
)
1612 EmpathyFTHandlerPriv
*priv
;
1614 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), 0);
1616 priv
= GET_PRIV (handler
);
1618 return priv
->total_bytes
;
1622 * empathy_ft_handler_is_completed:
1623 * @handler: an #EmpathyFTHandler
1625 * Returns whether the transfer for @handler has been completed succesfully.
1627 * Return value: %TRUE if the handler has been transferred correctly, %FALSE
1631 empathy_ft_handler_is_completed (EmpathyFTHandler
*handler
)
1633 EmpathyFTHandlerPriv
*priv
;
1635 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), FALSE
);
1637 priv
= GET_PRIV (handler
);
1639 return priv
->is_completed
;
1643 * empathy_ft_handler_is_cancelled:
1644 * @handler: an #EmpathyFTHandler
1646 * Returns whether the transfer for @handler has been cancelled or has stopped
1649 * Return value: %TRUE if the transfer for @handler has been cancelled
1650 * or has stopped due to an error, %FALSE otherwise.
1653 empathy_ft_handler_is_cancelled (EmpathyFTHandler
*handler
)
1655 EmpathyFTHandlerPriv
*priv
;
1657 g_return_val_if_fail (EMPATHY_IS_FT_HANDLER (handler
), FALSE
);
1659 priv
= GET_PRIV (handler
);
1661 return g_cancellable_is_cancelled (priv
->cancellable
);