1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3
* Copyright (C) 2007 Free Software Foundation
5
* This program is free software; you can redistribute it and/or
6
* modify it under the terms of the GNU Lesser General Public License as
7
* published by the Free Software Foundation; either version 2 of the
8
* License, or (at your option) any later version.
10
* This program 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 program; if not, write to the
17
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18
* Boston, MA 02111-1307, USA.
26
#include <glib/gi18n.h>
28
#include <loudmouth/lm-connection.h>
29
#include <loudmouth/lm-error.h>
32
#include "lm-bs-client.h"
33
#include "lm-bs-transfer.h"
34
#include "lm-bs-receiver.h"
35
#include "lm-bs-private.h"
37
#include "libloudermouth-marshal.h"
39
#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), LM_TYPE_BS_TRANSFER, LmBsTransferPriv))
41
#define XMLNS_BYTESTREAMS "http://jabber.org/protocol/bytestreams"
43
#define FILE_BUFFER_SIZE 1024
45
typedef struct _LmBsTransferPriv LmBsTransferPriv;
47
struct _LmBsTransferPriv {
48
LmBsTransferDirection direction;
49
LmBsTransferStatus status;
51
LmConnection *connection;
54
GHashTable *streamhosts;
62
GIOChannel *file_channel;
63
LmCallback *activate_cb;
66
guint64 bytes_transferred;
70
LmBsTransfer *transfer;
82
static guint signals[LAST_SIGNAL] = { 0 };
84
static void bs_transfer_finalize (GObject *object);
85
static void bs_transfer_free_streamhost_data (gpointer data);
86
static void bs_transfer_client_connected_cb (LmBsClient *client,
87
StreamHostData *sreamhost_data);
88
static void bs_transfer_client_disconnected_cb (LmBsClient *client,
89
StreamHostData *sreamhost_data);
90
static gboolean bs_transfer_channel_open_for_write (LmBsTransfer *transfer,
92
static gboolean bs_transfer_channel_open_for_read (LmBsTransfer *transfer,
94
static gchar * bs_transfer_io_error_to_string (GError *error);
95
static void bs_transfer_initiated (LmBsTransfer *transfer);
96
static void bs_transfer_complete (LmBsTransfer *transfer);
97
static void bs_transfer_progress (LmBsTransfer *transfer);
98
static void bs_transfer_error (LmBsTransfer *transfer,
99
const gchar *error_msg);
100
static gchar * bs_transfer_get_initiator (LmBsTransfer *transfer);
101
static gchar * bs_transfer_get_target (LmBsTransfer *transfer);
103
G_DEFINE_TYPE (LmBsTransfer, lm_bs_transfer, G_TYPE_OBJECT);
106
lm_bs_transfer_class_init (LmBsTransferClass *klass)
108
GObjectClass *object_class;
110
object_class = G_OBJECT_CLASS (klass);
112
object_class->finalize = bs_transfer_finalize;
115
g_signal_new ("initiated",
116
G_TYPE_FROM_CLASS (klass),
120
g_cclosure_marshal_VOID__VOID,
125
g_signal_new ("complete",
126
G_TYPE_FROM_CLASS (klass),
130
g_cclosure_marshal_VOID__VOID,
135
g_signal_new ("progress",
136
G_TYPE_FROM_CLASS (klass),
140
g_cclosure_marshal_VOID__DOUBLE,
145
g_signal_new ("error",
146
G_TYPE_FROM_CLASS (klass),
150
g_cclosure_marshal_VOID__POINTER,
154
g_type_class_add_private (object_class, sizeof (LmBsTransferPriv));
158
lm_bs_transfer_init (LmBsTransfer *transfer)
160
LmBsTransferPriv *priv;
162
priv = GET_PRIV (transfer);
164
priv->streamhosts = g_hash_table_new_full (g_str_hash,
167
(GDestroyNotify) lm_bs_client_unref);
171
bs_transfer_finalize (GObject *object)
173
LmBsTransferPriv *priv;
175
priv = GET_PRIV (object);
177
if (priv->activate_cb) {
178
_lm_utils_free_callback (priv->activate_cb);
181
lm_bs_transfer_close_file (LM_BS_TRANSFER (object));
183
g_free (priv->peer_jid);
184
g_free (priv->location);
186
g_free (priv->iq_id);
188
g_hash_table_destroy (priv->streamhosts);
191
g_object_unref (priv->session);
194
if (priv->connection) {
195
lm_connection_unref (priv->connection);
198
(G_OBJECT_CLASS (lm_bs_transfer_parent_class)->finalize) (object);
202
bs_transfer_free_streamhost_data (gpointer data)
204
StreamHostData *streamhost_data;
206
streamhost_data = data;
208
if (streamhost_data->transfer) {
209
g_object_unref (streamhost_data->transfer);
212
g_free (streamhost_data->jid);
213
g_free (streamhost_data);
217
bs_transfer_client_connected_cb (LmBsClient *client,
218
StreamHostData *streamhost_data)
220
LmBsTransfer *transfer;
221
LmBsTransferPriv *priv;
222
LmBsReceiver *receiver;
224
transfer = streamhost_data->transfer;
226
priv = GET_PRIV (transfer);
228
priv->status = LM_BS_TRANSFER_STATUS_CONNECTED;
230
receiver = lm_bs_receiver_new (client, transfer, streamhost_data->jid);
231
g_hash_table_remove_all (priv->streamhosts);
233
lm_bs_receiver_start_transfer (receiver);
237
bs_transfer_client_disconnected_cb (LmBsClient *client,
238
StreamHostData *streamhost_data)
240
LmBsTransfer *transfer;
241
LmBsTransferPriv *priv;
243
transfer = streamhost_data->transfer;
245
priv = GET_PRIV (transfer);
247
g_hash_table_remove (priv->streamhosts, streamhost_data->jid);
249
if (!g_hash_table_size (priv->streamhosts)) {
250
bs_transfer_error (transfer, _("Unable to connect to the other party"));
255
bs_transfer_channel_open_for_write (LmBsTransfer *transfer,
258
LmBsTransferPriv *priv;
260
priv = GET_PRIV (transfer);
262
if (priv->file_channel) {
266
priv->file_channel = g_io_channel_new_file (priv->location,
270
if (!priv->file_channel) {
274
g_io_channel_set_encoding (priv->file_channel, NULL, NULL);
275
g_io_channel_set_buffered (priv->file_channel, FALSE);
281
bs_transfer_channel_open_for_read (LmBsTransfer *transfer,
284
LmBsTransferPriv *priv;
286
priv = GET_PRIV (transfer);
288
if (priv->file_channel) {
292
priv->file_channel = g_io_channel_new_file (priv->location,
296
if (!priv->file_channel) {
300
g_io_channel_set_encoding (priv->file_channel, NULL, NULL);
301
g_io_channel_set_buffered (priv->file_channel, FALSE);
307
bs_transfer_io_error_to_string (GError *error)
309
g_return_val_if_fail (error != NULL, NULL);
311
if (error->domain == G_FILE_ERROR) {
312
switch(error->code) {
313
case G_FILE_ERROR_EXIST:
314
case G_FILE_ERROR_ACCES:
315
case G_FILE_ERROR_PERM:
316
return _("Permission denied");
317
case G_FILE_ERROR_NAMETOOLONG:
318
return _("File name is too long");
319
case G_FILE_ERROR_NOENT:
320
return _("File doesn't exist");
321
case G_FILE_ERROR_ISDIR:
322
return _("File is a directory");
323
case G_FILE_ERROR_ROFS:
324
return _("Read only file system");
325
case G_FILE_ERROR_TXTBSY:
326
return _("File is busy");
327
case G_FILE_ERROR_FAULT:
328
return _("Bad memory");
329
case G_FILE_ERROR_LOOP:
330
return _("Too many levels of symbolic links");
331
case G_FILE_ERROR_NOSPC:
332
return _("No space is available");
333
case G_FILE_ERROR_NOMEM:
334
return _("Virtual memory exhausted");
335
case G_FILE_ERROR_MFILE:
336
return _("Too many open files");
337
case G_FILE_ERROR_BADF:
338
case G_FILE_ERROR_IO:
339
return _("Input/output error");
340
case G_FILE_ERROR_NODEV:
341
case G_FILE_ERROR_NXIO:
342
return _("No such device");
344
break; /* unknown error */
346
} else if (error->domain == G_IO_CHANNEL_ERROR) {
347
switch(error->code) {
348
case G_IO_CHANNEL_ERROR_FBIG:
349
return _("File is too large");
350
case G_IO_CHANNEL_ERROR_IO:
351
return _("Input/output error");
352
case G_IO_CHANNEL_ERROR_ISDIR:
353
return _("File is a directory");
354
case G_IO_CHANNEL_ERROR_NOSPC:
355
return _("No space is available");
356
case G_IO_CHANNEL_ERROR_NXIO:
357
return _("No such device");
359
break; /* unknown error */
363
return _("Unknown error");
367
bs_transfer_initiated (LmBsTransfer *transfer)
369
LmBsTransferPriv *priv;
371
priv = GET_PRIV (transfer);
373
priv->status = LM_BS_TRANSFER_STATUS_INITIAL;
375
g_signal_emit (transfer, signals[INITIATED], 0);
379
bs_transfer_complete (LmBsTransfer *transfer)
381
LmBsTransferPriv *priv;
383
priv = GET_PRIV (transfer);
385
priv->status = LM_BS_TRANSFER_STATUS_COMPLETE;
387
lm_bs_transfer_close_file (transfer);
389
g_signal_emit (transfer, signals[COMPLETE], 0);
393
bs_transfer_progress (LmBsTransfer *transfer)
395
LmBsTransferPriv *priv;
397
GTimeVal current_time;
398
GTimeVal interval_time;
399
static GTimeVal last_time = { 0, 0 };
401
priv = GET_PRIV (transfer);
405
if (priv->bytes_total > 0) {
406
progress = (gdouble) priv->bytes_transferred / priv->bytes_total;
409
g_get_current_time (¤t_time);
411
/* Only allow signalling every 100th of a second) */
412
interval_time = last_time;
413
g_time_val_add (&interval_time, G_USEC_PER_SEC / 100);
415
/* Throttle the signalling so we don't enter a tight loop */
416
if (current_time.tv_sec - interval_time.tv_sec > 0 ||
417
current_time.tv_usec - interval_time.tv_usec > 0) {
418
g_signal_emit (transfer, signals[PROGRESS], 0, progress);
419
last_time = current_time;
424
bs_transfer_error (LmBsTransfer *transfer,
425
const gchar *error_msg)
429
error = g_error_new (lm_error_quark (),
430
LM_BS_TRANSFER_ERROR_UNABLE_TO_CONNECT,
432
lm_bs_transfer_error (transfer, error);
433
g_error_free (error);
437
bs_transfer_get_initiator (LmBsTransfer *transfer)
439
LmBsTransferPriv *priv;
441
priv = GET_PRIV (transfer);
443
if (priv->direction == LM_BS_TRANSFER_DIRECTION_RECEIVER) {
444
return g_strdup (priv->peer_jid);
447
return lm_connection_get_full_jid (priv->connection);
451
bs_transfer_get_target (LmBsTransfer *transfer)
453
LmBsTransferPriv *priv;
455
priv = GET_PRIV (transfer);
457
if (priv->direction == LM_BS_TRANSFER_DIRECTION_RECEIVER) {
458
return lm_connection_get_full_jid (priv->connection);
460
return g_strdup (priv->peer_jid);
464
lm_bs_transfer_new (LmBsSession *session,
465
LmConnection *connection,
466
LmBsTransferDirection direction,
469
const gchar *peer_jid,
470
const gchar *location,
473
LmBsTransfer *transfer;
474
LmBsTransferPriv *priv;
476
g_return_val_if_fail (connection != NULL, NULL);
477
g_return_val_if_fail (LM_IS_BS_SESSION (session), NULL);
478
g_return_val_if_fail (peer_jid != NULL, NULL);
479
g_return_val_if_fail (location != NULL, NULL);
480
g_return_val_if_fail (sid != NULL, NULL);
482
transfer = g_object_new (LM_TYPE_BS_TRANSFER, NULL);
484
priv = GET_PRIV (transfer);
486
priv->status = LM_BS_TRANSFER_STATUS_INITIAL;
487
priv->direction = direction;
489
priv->connection = lm_connection_ref (connection);
490
priv->session = g_object_ref (session);
492
priv->peer_jid = g_strdup (peer_jid);
493
priv->location = g_strdup (location);
495
priv->sid = g_strdup (sid);
498
priv->file_channel = NULL;
499
priv->activate_cb = NULL;
501
priv->bytes_total = bytes_total;
502
priv->bytes_transferred = 0;
508
lm_bs_transfer_error (LmBsTransfer *transfer,
511
LmBsTransferPriv *priv;
513
g_return_if_fail (LM_IS_BS_TRANSFER (transfer));
515
priv = GET_PRIV (transfer);
517
priv->status = LM_BS_TRANSFER_STATUS_INTERRUPTED;
519
lm_bs_transfer_close_file (transfer);
521
g_signal_emit (transfer, signals[ERROR], 0, error);
525
lm_bs_transfer_append_to_file (LmBsTransfer *transfer,
528
LmBsTransferPriv *priv;
531
const gchar *error_msg;
534
g_return_val_if_fail (LM_IS_BS_TRANSFER (transfer), FALSE);
536
priv = GET_PRIV (transfer);
538
g_return_val_if_fail (priv->direction == LM_BS_TRANSFER_DIRECTION_RECEIVER, FALSE);
542
if (!bs_transfer_channel_open_for_write (transfer, &error)) {
543
error_msg = bs_transfer_io_error_to_string (error);
544
g_error_free (error);
545
bs_transfer_error (transfer, error_msg);
549
io_status = g_io_channel_write_chars (priv->file_channel,
555
if (io_status != G_IO_STATUS_NORMAL) {
556
error_msg = bs_transfer_io_error_to_string (error);
557
g_error_free (error);
558
bs_transfer_error (transfer, error_msg);
562
priv->bytes_transferred += bytes_written;
564
if (priv->bytes_transferred >= priv->bytes_total) {
565
bs_transfer_complete (transfer);
569
bs_transfer_progress (transfer);
575
lm_bs_transfer_get_file_content (LmBsTransfer *transfer,
578
LmBsTransferPriv *priv;
581
const gchar *error_msg;
585
g_return_val_if_fail (LM_IS_BS_TRANSFER (transfer), FALSE);
587
priv = GET_PRIV (transfer);
589
g_return_val_if_fail (priv->direction == LM_BS_TRANSFER_DIRECTION_SENDER, FALSE);
593
if (priv->bytes_transferred >= priv->bytes_total) {
594
bs_transfer_complete (transfer);
598
if (!bs_transfer_channel_open_for_read (transfer, &error)) {
599
error_msg = bs_transfer_io_error_to_string (error);
600
g_error_free (error);
601
bs_transfer_error (transfer, error_msg);
605
buffer = g_malloc (FILE_BUFFER_SIZE);
606
io_status = g_io_channel_read_chars (priv->file_channel,
612
if (io_status != G_IO_STATUS_NORMAL) {
613
error_msg = bs_transfer_io_error_to_string (error);
614
g_error_free (error);
615
bs_transfer_error (transfer, error_msg);
619
*data = g_string_new_len (buffer, bytes_read);
622
priv->bytes_transferred += bytes_read;
623
bs_transfer_progress (transfer);
629
lm_bs_transfer_get_status (LmBsTransfer *transfer)
631
LmBsTransferPriv *priv;
633
g_return_val_if_fail (LM_IS_BS_TRANSFER (transfer), LM_BS_TRANSFER_STATUS_INITIAL);
635
priv = GET_PRIV (transfer);
641
lm_bs_transfer_get_bytes_transferred (LmBsTransfer *transfer)
643
LmBsTransferPriv *priv;
645
g_return_val_if_fail (LM_IS_BS_TRANSFER (transfer), 0);
647
priv = GET_PRIV (transfer);
649
return priv->bytes_transferred;
653
lm_bs_transfer_get_bytes_total (LmBsTransfer *transfer)
655
LmBsTransferPriv *priv;
657
g_return_val_if_fail (LM_IS_BS_TRANSFER (transfer), 0);
659
priv = GET_PRIV (transfer);
661
return priv->bytes_total;
665
lm_bs_transfer_close_file (LmBsTransfer *transfer)
667
LmBsTransferPriv *priv;
669
g_return_if_fail (LM_IS_BS_TRANSFER (transfer));
671
priv = GET_PRIV (transfer);
673
if (!priv->file_channel) {
677
g_io_channel_unref (priv->file_channel);
678
priv->file_channel = NULL;
681
lm_bs_transfer_set_iq_id (LmBsTransfer *transfer,
684
LmBsTransferPriv *priv;
686
g_return_if_fail (LM_IS_BS_TRANSFER (transfer));
688
priv = GET_PRIV (transfer);
690
if (priv->iq_id != NULL) {
691
/* The id is already set. no need to override it,
692
* because it is same for all streamhosts
697
priv->iq_id = g_strdup (iq_id);
701
lm_bs_transfer_add_streamhost (LmBsTransfer *transfer,
706
GMainContext *context;
707
LmBsTransferPriv *priv;
708
LmBsClient *streamhost;
709
LmBsClientFunction func;
710
LmCallback *connected_cb;
711
LmCallback *disconnect_cb;
712
StreamHostData *streamhost_data;
714
g_return_if_fail (LM_IS_BS_TRANSFER (transfer));
716
priv = GET_PRIV (transfer);
718
context = _lm_bs_session_get_context (priv->session);
720
if (priv->direction == LM_BS_TRANSFER_DIRECTION_RECEIVER) {
721
streamhost_data = g_new0 (StreamHostData, 1);
722
streamhost_data->transfer = g_object_ref (transfer);
723
streamhost_data->jid = g_strdup (jid);
725
func = (LmBsClientFunction) bs_transfer_client_connected_cb;
726
connected_cb = _lm_utils_new_callback (func,
730
func = (LmBsClientFunction) bs_transfer_client_disconnected_cb;
731
disconnect_cb = _lm_utils_new_callback (func,
733
(GDestroyNotify) bs_transfer_free_streamhost_data);
735
streamhost = lm_bs_client_new_with_context (port,
743
streamhost = lm_bs_client_new_with_context (port,
752
g_hash_table_insert (priv->streamhosts,
756
if (priv->status == LM_BS_TRANSFER_STATUS_INITIAL &&
757
priv->direction == LM_BS_TRANSFER_DIRECTION_RECEIVER) {
758
bs_transfer_initiated (transfer);
759
lm_bs_client_connect (streamhost);
764
lm_bs_transfer_get_id (LmBsTransfer *transfer)
766
LmBsTransferPriv *priv;
768
g_return_val_if_fail (LM_IS_BS_TRANSFER (transfer), 0);
770
priv = GET_PRIV (transfer);
776
lm_bs_transfer_get_sid (LmBsTransfer *transfer)
778
LmBsTransferPriv *priv;
780
g_return_val_if_fail (LM_IS_BS_TRANSFER (transfer), NULL);
782
priv = GET_PRIV (transfer);
788
lm_bs_transfer_get_auth_sha (LmBsTransfer *transfer)
790
LmBsTransferPriv *priv;
796
g_return_val_if_fail (LM_IS_BS_TRANSFER (transfer), NULL);
798
priv = GET_PRIV (transfer);
800
initiator = bs_transfer_get_initiator (transfer);
801
target = bs_transfer_get_target (transfer);
802
concat = g_strconcat (priv->sid, initiator, target, NULL);
803
sha = lm_sha_hash (concat);
809
return g_strdup (sha);
812
LmBsTransferDirection
813
lm_bs_transfer_get_direction (LmBsTransfer *transfer)
815
LmBsTransferPriv *priv;
817
g_return_val_if_fail (LM_IS_BS_TRANSFER (transfer), LM_BS_TRANSFER_DIRECTION_SENDER);
819
priv = GET_PRIV (transfer);
821
return priv->direction;
825
lm_bs_transfer_get_iq_id (LmBsTransfer *transfer)
827
LmBsTransferPriv *priv;
829
g_return_val_if_fail (LM_IS_BS_TRANSFER (transfer), NULL);
831
priv = GET_PRIV (transfer);
837
lm_bs_transfer_has_streamhost (LmBsTransfer *transfer,
840
LmBsTransferPriv *priv;
842
g_return_val_if_fail (LM_IS_BS_TRANSFER (transfer), FALSE);
844
priv = GET_PRIV (transfer);
846
if (g_hash_table_lookup (priv->streamhosts, jid)) {
854
lm_bs_transfer_set_activate_cb (LmBsTransfer *transfer,
855
LmCallback *activate_cb)
857
LmBsTransferPriv *priv;
859
g_return_if_fail (LM_IS_BS_TRANSFER (transfer));
861
priv = GET_PRIV (transfer);
863
if (priv->activate_cb != NULL) {
864
_lm_utils_free_callback (priv->activate_cb);
867
priv->activate_cb = activate_cb;
871
lm_bs_transfer_send_success_reply (LmBsTransfer *transfer,
874
LmBsTransferPriv *priv;
877
LmMessageNode *node1;
881
g_return_if_fail (LM_IS_BS_TRANSFER (transfer));
883
priv = GET_PRIV (transfer);
885
g_return_if_fail (priv->direction == LM_BS_TRANSFER_DIRECTION_RECEIVER);
887
m = lm_message_new_with_sub_type (priv->peer_jid,
889
LM_MESSAGE_SUB_TYPE_RESULT);
891
lm_message_node_set_attribute (m->node, "id", priv->iq_id);
892
target_jid = bs_transfer_get_target (transfer);
893
lm_message_node_set_attribute (m->node,
897
node = lm_message_node_add_child (m->node, "query", NULL);
898
lm_message_node_set_attribute (node, "xmlns", XMLNS_BYTESTREAMS);
900
node1 = lm_message_node_add_child (node, "streamhost-used", NULL);
901
lm_message_node_set_attribute (node1, "jid", jid);
905
if (!lm_connection_send (priv->connection, m, &error)) {
906
g_printerr ("Failed to send message:'%s'\n",
907
lm_message_node_to_string (m->node));
911
lm_message_unref (m);
915
lm_bs_transfer_activate (LmBsTransfer *transfer,
918
LmBsTransferPriv *priv;
921
g_return_if_fail (LM_IS_BS_TRANSFER (transfer));
922
g_return_if_fail (lm_bs_transfer_has_streamhost (transfer, jid));
924
priv = GET_PRIV (transfer);
926
bs_transfer_initiated (transfer);
928
g_hash_table_remove_all (priv->streamhosts);
930
cb = priv->activate_cb;
931
if (cb && cb->func) {
932
(* ((LmBsClientFunction) cb->func)) (NULL, cb->user_data);