rename text/{persona,individual}-id as they are not standard
[empathy-mirror.git] / libempathy / empathy-ft-handler.c
blob99b6dc85ffd78eeab5c245c9a998b5accf8923c8
1 /*
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 */
24 #include <glib.h>
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"
40 /**
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
47 * its properties.
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
55 * automatically.
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
70 * to use in an UI.
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
79 enum {
80 PROP_TP_FILE = 1,
81 PROP_G_FILE,
82 PROP_CONTACT,
83 PROP_CONTENT_TYPE,
84 PROP_DESCRIPTION,
85 PROP_FILENAME,
86 PROP_MODIFICATION_TIME,
87 PROP_TOTAL_BYTES,
88 PROP_TRANSFERRED_BYTES
91 enum {
92 HASHING_STARTED,
93 HASHING_PROGRESS,
94 HASHING_DONE,
95 TRANSFER_STARTED,
96 TRANSFER_PROGRESS,
97 TRANSFER_DONE,
98 TRANSFER_ERROR,
99 LAST_SIGNAL
102 typedef struct {
103 GInputStream *stream;
104 GError *error /* comment to make the style checker happy */;
105 guchar *buffer;
106 GChecksum *checksum;
107 gssize total_read;
108 guint64 total_bytes;
109 EmpathyFTHandler *handler;
110 } HashingData;
112 typedef struct {
113 EmpathyFTHandlerReadyCallback callback;
114 gpointer user_data;
115 EmpathyFTHandler *handler;
116 } CallbacksData;
118 /* private data */
119 typedef struct {
120 gboolean dispose_run;
122 GFile *gfile;
123 EmpathyTpFile *tpfile;
124 GCancellable *cancellable;
125 gboolean use_hash;
127 /* request for the new transfer */
128 GHashTable *request;
130 /* transfer properties */
131 EmpathyContact *contact;
132 gchar *content_type;
133 gchar *filename;
134 gchar *description;
135 guint64 total_bytes;
136 guint64 transferred_bytes;
137 guint64 mtime;
138 gchar *content_hash;
139 TpFileHashType content_hash_type;
141 /* time and speed */
142 gdouble speed;
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 */
155 static void
156 do_get_property (GObject *object,
157 guint property_id,
158 GValue *value,
159 GParamSpec *pspec)
161 EmpathyFTHandlerPriv *priv = GET_PRIV (object);
163 switch (property_id)
165 case PROP_CONTACT:
166 g_value_set_object (value, priv->contact);
167 break;
168 case PROP_CONTENT_TYPE:
169 g_value_set_string (value, priv->content_type);
170 break;
171 case PROP_DESCRIPTION:
172 g_value_set_string (value, priv->description);
173 break;
174 case PROP_FILENAME:
175 g_value_set_string (value, priv->filename);
176 break;
177 case PROP_MODIFICATION_TIME:
178 g_value_set_uint64 (value, priv->mtime);
179 break;
180 case PROP_TOTAL_BYTES:
181 g_value_set_uint64 (value, priv->total_bytes);
182 break;
183 case PROP_TRANSFERRED_BYTES:
184 g_value_set_uint64 (value, priv->transferred_bytes);
185 break;
186 case PROP_G_FILE:
187 g_value_set_object (value, priv->gfile);
188 break;
189 case PROP_TP_FILE:
190 g_value_set_object (value, priv->tpfile);
191 break;
192 default:
193 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
197 static void
198 do_set_property (GObject *object,
199 guint property_id,
200 const GValue *value,
201 GParamSpec *pspec)
203 EmpathyFTHandlerPriv *priv = GET_PRIV (object);
205 switch (property_id)
207 case PROP_CONTACT:
208 priv->contact = g_value_dup_object (value);
209 break;
210 case PROP_CONTENT_TYPE:
211 priv->content_type = g_value_dup_string (value);
212 break;
213 case PROP_DESCRIPTION:
214 priv->description = g_value_dup_string (value);
215 break;
216 case PROP_FILENAME:
217 priv->filename = g_value_dup_string (value);
218 break;
219 case PROP_MODIFICATION_TIME:
220 priv->mtime = g_value_get_uint64 (value);
221 break;
222 case PROP_TOTAL_BYTES:
223 priv->total_bytes = g_value_get_uint64 (value);
224 break;
225 case PROP_TRANSFERRED_BYTES:
226 priv->transferred_bytes = g_value_get_uint64 (value);
227 break;
228 case PROP_G_FILE:
229 priv->gfile = g_value_dup_object (value);
230 break;
231 case PROP_TP_FILE:
232 priv->tpfile = g_value_dup_object (value);
233 break;
234 default:
235 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
239 static void
240 do_dispose (GObject *object)
242 EmpathyFTHandlerPriv *priv = GET_PRIV (object);
244 if (priv->dispose_run)
245 return;
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);
256 priv->gfile = NULL;
259 if (priv->tpfile != NULL) {
260 empathy_tp_file_close (priv->tpfile);
261 g_object_unref (priv->tpfile);
262 priv->tpfile = NULL;
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);
279 static void
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);
301 static void
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;
314 /* properties */
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",
400 G_TYPE_FILE,
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);
415 /* signals */
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,
428 G_TYPE_NONE,
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
437 * successfully.
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,
443 G_TYPE_NONE,
444 1, EMPATHY_TYPE_TP_FILE);
447 * EmpathyFTHandler::transfer-error
448 * @handler: the object which has received the signal
449 * @error: a #GError
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,
461 G_TYPE_NONE,
462 1, G_TYPE_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
470 * to be completed
471 * @speed: the current speed of the transfer (in KB/s)
473 * This signal is emitted to notify clients of the progress of the
474 * transfer.
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,
480 G_TYPE_NONE,
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
492 * to this signal.
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,
498 G_TYPE_NONE, 0);
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
507 * hashing operation.
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,
513 G_TYPE_NONE,
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
521 * is completed.
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,
527 G_TYPE_NONE, 0);
530 static void
531 empathy_ft_handler_init (EmpathyFTHandler *self)
533 EmpathyFTHandlerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
534 EMPATHY_TYPE_FT_HANDLER, EmpathyFTHandlerPriv);
536 self->priv = priv;
537 priv->cancellable = g_cancellable_new ();
540 /* private functions */
542 static void
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);
562 static GChecksumType
563 tp_file_hash_to_g_checksum (TpFileHashType type)
565 GChecksumType retval;
567 switch (type)
569 case TP_FILE_HASH_TYPE_MD5:
570 retval = G_CHECKSUM_MD5;
571 break;
572 case TP_FILE_HASH_TYPE_SHA1:
573 retval = G_CHECKSUM_SHA1;
574 break;
575 case TP_FILE_HASH_TYPE_SHA256:
576 retval = G_CHECKSUM_SHA256;
577 break;
578 case TP_FILE_HASH_TYPE_NONE:
579 default:
580 g_assert_not_reached ();
581 break;
584 return retval;
587 static void
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);
608 static void
609 emit_error_signal (EmpathyFTHandler *handler,
610 const GError *error)
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);
622 static void
623 ft_transfer_operation_callback (EmpathyTpFile *tp_file,
624 const GError *error,
625 gpointer user_data)
627 EmpathyFTHandler *handler = user_data;
628 EmpathyFTHandlerPriv *priv = GET_PRIV (handler);
630 DEBUG ("Transfer operation callback, error %p", error);
632 if (error != NULL)
634 emit_error_signal (handler, error);
636 else
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);
650 static void
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;
657 gdouble speed;
658 gint remaining_time;
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;
671 priv->speed = speed;
672 priv->remaining_time = remaining_time;
673 priv->last_update_time = current_time;
677 static void
678 ft_transfer_progress_callback (EmpathyTpFile *tp_file,
679 guint64 transferred_bytes,
680 gpointer user_data)
682 EmpathyFTHandler *handler = user_data;
683 EmpathyFTHandlerPriv *priv = GET_PRIV (handler);
685 if (empathy_ft_handler_is_cancelled (handler))
686 return;
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,
700 priv->speed);
704 static void
705 ft_handler_create_channel_cb (GObject *source,
706 GAsyncResult *result,
707 gpointer user_data)
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);
719 if (channel == NULL)
720 DEBUG ("Failed to request FT channel: %s", error->message);
721 else
722 g_cancellable_set_error_if_cancelled (priv->cancellable, &error);
724 if (error != NULL)
726 emit_error_signal (handler, error);
728 g_error_free (error);
729 goto out;
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);
738 out:
739 tp_clear_object (&channel);
742 static void
743 ft_handler_push_to_dispatcher (EmpathyFTHandler *handler)
745 TpAccount *account;
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);
762 static void
763 ft_handler_populate_outgoing_request (EmpathyFTHandler *handler)
765 guint contact_handle;
766 EmpathyFTHandlerPriv *priv = GET_PRIV (handler);
767 gchar *uri;
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,
778 contact_handle,
779 TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_CONTENT_TYPE, G_TYPE_STRING,
780 priv->content_type,
781 TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_FILENAME, G_TYPE_STRING,
782 priv->filename,
783 TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_SIZE, G_TYPE_UINT64,
784 priv->total_bytes,
785 TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_DATE, G_TYPE_UINT64,
786 priv->mtime,
787 TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_URI, G_TYPE_STRING, uri,
788 NULL);
790 g_free (uri);
793 static gboolean
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;
809 goto cleanup;
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),
817 priv->content_hash))
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"));
826 goto cleanup;
828 else
830 DEBUG ("Hash verification matched, received %s, calculated %s",
831 priv->content_hash,
832 g_checksum_get_string (hash_data->checksum));
835 else
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));
845 cleanup:
847 if (error != NULL)
849 emit_error_signal (handler, error);
850 g_clear_error (&error);
852 else
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);
863 return FALSE;
866 static gboolean
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);
874 return FALSE;
877 static gboolean
878 do_hash_job (GIOSchedulerJob *job,
879 GCancellable *cancellable,
880 gpointer user_data)
882 HashingData *hash_data = user_data;
883 gssize bytes_read;
884 GError *error = NULL;
886 again:
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);
892 if (error != NULL)
893 goto out;
895 hash_data->total_read += bytes_read;
897 /* we now have the chunk */
898 if (bytes_read > 0)
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,
902 hash_data, NULL);
904 g_free (hash_data->buffer);
905 hash_data->buffer = NULL;
907 goto again;
909 else
911 g_input_stream_close (hash_data->stream, cancellable, &error);
914 out:
915 if (error != NULL)
916 hash_data->error = error;
918 g_io_scheduler_job_send_to_mainloop_async (job, hash_job_done,
919 hash_data, NULL);
921 return FALSE;
924 static gboolean
925 do_hash_job_incoming (GIOSchedulerJob *job,
926 GCancellable *cancellable,
927 gpointer user_data)
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 */
937 hash_data->stream =
938 G_INPUT_STREAM (g_file_read (priv->gfile, cancellable, &error));
940 if (error != NULL)
942 hash_data->error = error;
943 g_io_scheduler_job_send_to_mainloop_async (job, hash_job_done,
944 hash_data, NULL);
945 return FALSE;
948 return do_hash_job (job, cancellable, user_data);
951 static void
952 ft_handler_read_async_cb (GObject *source,
953 GAsyncResult *res,
954 gpointer user_data)
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);
965 if (error != NULL)
967 emit_error_signal (handler, error);
968 g_clear_error (&error);
970 return;
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);
990 static void
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);
1001 static gboolean
1002 set_content_hash_type_from_classes (EmpathyFTHandler *handler,
1003 GPtrArray *classes)
1005 GArray *possible_values;
1006 guint value;
1007 gboolean valid;
1008 EmpathyFTHandlerPriv *priv = GET_PRIV (handler);
1009 gboolean support_ft = FALSE;
1010 guint i;
1012 possible_values = g_array_new (TRUE, TRUE, sizeof (guint));
1014 for (i = 0; i < classes->len; i++)
1016 GHashTable *fixed;
1017 GStrv allowed;
1018 const gchar *chan_type;
1020 tp_value_array_unpack (g_ptr_array_index (classes, i), 2,
1021 &fixed, &allowed);
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))
1026 continue;
1028 if (tp_asv_get_uint32 (fixed, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, NULL) !=
1029 TP_HANDLE_TYPE_CONTACT)
1030 continue;
1032 support_ft = TRUE;
1034 value = tp_asv_get_uint32
1035 (fixed, TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_CONTENT_HASH_TYPE,
1036 &valid);
1038 if (valid)
1039 g_array_append_val (possible_values, value);
1042 if (!support_ft)
1044 g_array_free (possible_values, TRUE);
1045 return FALSE;
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;
1054 goto out;
1057 priv->use_hash = TRUE;
1059 if (possible_values->len == 1)
1061 priv->content_hash_type = g_array_index (possible_values, guint, 0);
1063 else
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);
1072 else
1073 priv->content_hash_type = g_array_index (possible_values, guint, 0);
1076 out:
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);
1082 return TRUE;
1085 static void
1086 check_hashing (CallbacksData *data)
1088 EmpathyFTHandler *handler = data->handler;
1089 EmpathyFTHandlerPriv *priv = GET_PRIV (handler);
1090 GError *myerr = NULL;
1091 TpCapabilities *caps;
1092 GPtrArray *classes;
1093 TpConnection *conn;
1095 conn = empathy_contact_get_connection (priv->contact);
1097 caps = tp_connection_get_capabilities (conn);
1098 if (caps == NULL)
1100 data->callback (handler, NULL, data->user_data);
1101 goto out;
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);
1119 else
1121 /* get back to the caller now */
1122 data->callback (handler, NULL, data->user_data);
1125 out:
1126 callbacks_data_free (data);
1129 static void
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);
1137 if (priv->use_hash)
1138 /* start hashing the file */
1139 g_file_read_async (priv->gfile, G_PRIORITY_DEFAULT,
1140 priv->cancellable, ft_handler_read_async_cb, handler);
1141 else
1142 /* push directly the handler to the dispatcher */
1143 ft_handler_push_to_dispatcher (handler);
1146 static void
1147 ft_handler_gfile_ready_cb (GObject *source,
1148 GAsyncResult *res,
1149 CallbacksData *cb_data)
1151 GFileInfo *info;
1152 GError *error = NULL;
1153 GTimeVal mtime;
1154 EmpathyFTHandlerPriv *priv = GET_PRIV (cb_data->handler);
1156 DEBUG ("Got GFileInfo.");
1158 info = g_file_query_info_finish (priv->gfile, res, &error);
1160 if (error != NULL)
1161 goto out;
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"));
1168 goto out;
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"));
1177 goto out;
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);
1189 out:
1190 if (error != NULL)
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);
1200 else
1202 /* see if FT/hashing are allowed */
1203 check_hashing (cb_data);
1207 static void
1208 contact_factory_contact_cb (TpConnection *connection,
1209 EmpathyContact *contact,
1210 const GError *error,
1211 gpointer user_data,
1212 GObject *weak_object)
1214 CallbacksData *cb_data = user_data;
1215 EmpathyFTHandler *handler = EMPATHY_FT_HANDLER (weak_object);
1216 EmpathyFTHandlerPriv *priv = GET_PRIV (handler);
1218 if (error != NULL)
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);
1225 return;
1228 priv->contact = g_object_ref (contact);
1230 cb_data->callback (handler, NULL, cb_data->user_data);
1233 static void
1234 channel_get_all_properties_cb (TpProxy *proxy,
1235 GHashTable *properties,
1236 const GError *error,
1237 gpointer user_data,
1238 GObject *weak_object)
1240 CallbacksData *cb_data = user_data;
1241 EmpathyFTHandler *handler = EMPATHY_FT_HANDLER (weak_object);
1242 EmpathyFTHandlerPriv *priv = GET_PRIV (handler);
1243 TpHandle c_handle;
1245 if (error != NULL)
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);
1253 return;
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.
1295 void
1296 empathy_ft_handler_new_outgoing (EmpathyContact *contact,
1297 GFile *source,
1298 EmpathyFTHandlerReadyCallback callback,
1299 gpointer user_data)
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
1340 * is ready.
1342 void
1343 empathy_ft_handler_new_incoming (EmpathyTpFile *tp_file,
1344 EmpathyFTHandlerReadyCallback callback,
1345 gpointer user_data)
1347 EmpathyFTHandler *handler;
1348 TpChannel *channel;
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.
1375 void
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);
1388 else
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.
1405 void
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);
1419 else
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.
1432 void
1433 empathy_ft_handler_incoming_set_destination (EmpathyFTHandler *handler,
1434 GFile *destination)
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;
1451 else
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
1463 const char *
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
1483 const char *
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
1503 EmpathyContact *
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
1523 GFile *
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);
1532 return priv->gfile;
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,
1543 * %FALSE otherwise.
1545 gboolean
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.
1565 gboolean
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)
1575 return FALSE;
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.
1588 guint64
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.
1609 guint64
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
1628 * otherwise
1630 gboolean
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
1647 * due to an error.
1649 * Return value: %TRUE if the transfer for @handler has been cancelled
1650 * or has stopped due to an error, %FALSE otherwise.
1652 gboolean
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);