1 /*
2 * ft-channel.c - Source for GabbleFileTransferChannel
3 * Copyright (C) 2009-2010 Collabora Ltd.
4 * @author: Guillaume Desmottes <guillaume.desmottes@collabora.co.uk>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include "config.h"
22
23 #include <glib/gstdio.h>
24 #include <dbus/dbus-glib.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <sys/types.h>
30
31 #include <gibber/gibber-sockets.h>
32
33 #ifdef HAVE_UNISTD_H
34 # include <unistd.h>
35 #endif
36
37 #define DEBUG_FLAG GABBLE_DEBUG_FT
38 #include "debug.h"
39
40 #include <gibber/gibber-listener.h>
41 #include <gibber/gibber-transport.h>
42 #include <gibber/gibber-unix-transport.h> /* just for the feature-test */
43
44 #include "connection.h"
45 #include "ft-channel.h"
46 #include "gabble-signals-marshal.h"
47 #include "namespaces.h"
48 #include "presence-cache.h"
49 #include "util.h"
50
51 #include <telepathy-glib/telepathy-glib.h>
52 #include <telepathy-glib/telepathy-glib-dbus.h>
53
54 static void file_transfer_iface_init (gpointer g_iface, gpointer iface_data);
55 static void transferred_chunk (GabbleFileTransferChannel *self, guint64 count);
56 static gboolean set_bytestream (GabbleFileTransferChannel *self,
57 GabbleBytestreamIface *bytestream);
58 #ifdef ENABLE_JINGLE_FILE_TRANSFER
59 static gboolean set_gtalk_file_collection (GabbleFileTransferChannel *self,
60 GTalkFileCollection *gtalk_file_collection);
61 #endif
62
63
64 G_DEFINE_TYPE_WITH_CODE (GabbleFileTransferChannel, gabble_file_transfer_channel,
65 TP_TYPE_BASE_CHANNEL,
66 G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_FILE_TRANSFER,
67 file_transfer_iface_init);
68 G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SVC_CHANNEL_TYPE_FILETRANSFER_FUTURE,
69 NULL);
70 G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA,
71 NULL);
72 );
73
74 #define GABBLE_UNDEFINED_FILE_SIZE G_MAXUINT64
75
76 /* properties */
77 enum
78 {
79 /* Channel.Type.FileTransfer D-Bus properties */
80 PROP_STATE = 1,
81 PROP_CONTENT_TYPE,
82 PROP_FILENAME,
83 PROP_SIZE,
84 PROP_CONTENT_HASH_TYPE,
85 PROP_CONTENT_HASH,
86 PROP_DESCRIPTION,
87 PROP_DATE,
88 PROP_AVAILABLE_SOCKET_TYPES,
89 PROP_TRANSFERRED_BYTES,
90 PROP_INITIAL_OFFSET,
91 PROP_RESUME_SUPPORTED,
92 PROP_FILE_COLLECTION,
93 PROP_URI,
94
95 PROP_CONNECTION,
96 PROP_BYTESTREAM,
97
98 #ifdef ENABLE_JINGLE_FILE_TRANSFER
99 /* Chan.Type.FileTransfer.FUTURE */
100 PROP_GTALK_FILE_COLLECTION,
101 #endif
102
103 /* Chan.Iface.FileTransfer.Metadata */
104 PROP_SERVICE_NAME,
105 PROP_METADATA,
106
107 LAST_PROPERTY
108 };
109
110 /* private structure */
111 struct _GabbleFileTransferChannelPrivate {
112 gboolean dispose_has_run;
113 GTimeVal last_transferred_bytes_emitted;
114 guint progress_timer;
115 TpSocketAddressType socket_type;
116 GValue *socket_address;
117 gboolean resume_supported;
118
119 #ifdef ENABLE_JINGLE_FILE_TRANSFER
120 GTalkFileCollection *gtalk_file_collection;
121 #endif
122
123 GabbleBytestreamIface *bytestream;
124 GibberListener *listener;
125 GibberTransport *transport;
126
127 /* properties */
128 TpFileTransferState state;
129 gchar *content_type;
130 gchar *filename;
131 guint64 size;
132 TpFileHashType content_hash_type;
133 gchar *content_hash;
134 gchar *description;
135 GHashTable *available_socket_types;
136 guint64 transferred_bytes;
137 guint64 initial_offset;
138 guint64 date;
139 gchar *file_collection;
140 gchar *uri;
141 gchar *service_name;
142 GHashTable *metadata;
143 gboolean channel_opened;
144 };
145
146 static void gabble_file_transfer_channel_set_state (
147 TpSvcChannelTypeFileTransfer *iface, TpFileTransferState state,
148 TpFileTransferStateChangeReason reason);
149 static void close_session_and_transport (GabbleFileTransferChannel *self);
150
151 static void
gabble_file_transfer_channel_close(TpBaseChannel * base)152 gabble_file_transfer_channel_close (TpBaseChannel *base)
153 {
154 GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (base);
155
156 if (self->priv->state != TP_FILE_TRANSFER_STATE_COMPLETED &&
157 self->priv->state != TP_FILE_TRANSFER_STATE_CANCELLED)
158 {
159 gabble_file_transfer_channel_set_state (
160 TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
161 TP_FILE_TRANSFER_STATE_CANCELLED,
162 TP_FILE_TRANSFER_STATE_CHANGE_REASON_LOCAL_STOPPED);
163
164 close_session_and_transport (self);
165 }
166
167 tp_base_channel_destroyed (base);
168 }
169
170 static void
gabble_file_transfer_channel_init(GabbleFileTransferChannel * obj)171 gabble_file_transfer_channel_init (GabbleFileTransferChannel *obj)
172 {
173 obj->priv = G_TYPE_INSTANCE_GET_PRIVATE (obj,
174 GABBLE_TYPE_FILE_TRANSFER_CHANNEL, GabbleFileTransferChannelPrivate);
175 }
176
177 static void
gabble_file_transfer_channel_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)178 gabble_file_transfer_channel_get_property (GObject *object,
179 guint property_id,
180 GValue *value,
181 GParamSpec *pspec)
182 {
183 GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (object);
184
185 switch (property_id)
186 {
187 case PROP_STATE:
188 g_value_set_uint (value, self->priv->state);
189 break;
190 case PROP_CONTENT_TYPE:
191 g_value_set_string (value, self->priv->content_type);
192 break;
193 case PROP_FILENAME:
194 g_value_set_string (value, self->priv->filename);
195 break;
196 case PROP_SIZE:
197 g_value_set_uint64 (value, self->priv->size);
198 break;
199 case PROP_CONTENT_HASH_TYPE:
200 g_value_set_uint (value, self->priv->content_hash_type);
201 break;
202 case PROP_CONTENT_HASH:
203 g_value_set_string (value, self->priv->content_hash);
204 break;
205 case PROP_DESCRIPTION:
206 g_value_set_string (value, self->priv->description);
207 break;
208 case PROP_AVAILABLE_SOCKET_TYPES:
209 g_value_set_boxed (value, self->priv->available_socket_types);
210 break;
211 case PROP_TRANSFERRED_BYTES:
212 g_value_set_uint64 (value, self->priv->transferred_bytes);
213 break;
214 case PROP_INITIAL_OFFSET:
215 g_value_set_uint64 (value, self->priv->initial_offset);
216 break;
217 case PROP_DATE:
218 g_value_set_uint64 (value, self->priv->date);
219 break;
220 case PROP_FILE_COLLECTION:
221 g_value_set_string (value, self->priv->file_collection);
222 break;
223 case PROP_URI:
224 g_value_set_string (value,
225 self->priv->uri != NULL ? self->priv->uri: "");
226 break;
227 case PROP_RESUME_SUPPORTED:
228 g_value_set_boolean (value, self->priv->resume_supported);
229 break;
230 case PROP_BYTESTREAM:
231 g_value_set_object (value, self->priv->bytestream);
232 break;
233 #ifdef ENABLE_JINGLE_FILE_TRANSFER
234 case PROP_GTALK_FILE_COLLECTION:
235 g_value_set_object (value, self->priv->gtalk_file_collection);
236 break;
237 #endif
238 case PROP_SERVICE_NAME:
239 g_value_set_string (value, self->priv->service_name);
240 break;
241 case PROP_METADATA:
242 {
243 /* We're fine with priv->metadata being NULL but dbus-glib
244 * doesn't like iterating NULL as if it was a hash table. */
245 if (self->priv->metadata == NULL)
246 {
247 g_value_take_boxed (value,
248 g_hash_table_new (g_str_hash, g_str_equal));
249 }
250 else
251 {
252 g_value_set_boxed (value, self->priv->metadata);
253 }
254 }
255 break;
256 default:
257 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
258 break;
259 }
260 }
261
262 static void
gabble_file_transfer_channel_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)263 gabble_file_transfer_channel_set_property (GObject *object,
264 guint property_id,
265 const GValue *value,
266 GParamSpec *pspec)
267 {
268 GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (object);
269
270 switch (property_id)
271 {
272 case PROP_STATE:
273 gabble_file_transfer_channel_set_state (
274 TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (object),
275 g_value_get_uint (value),
276 TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE);
277 break;
278 case PROP_CONTENT_TYPE:
279 g_free (self->priv->content_type);
280 self->priv->content_type = g_value_dup_string (value);
281 break;
282 case PROP_FILENAME:
283 g_free (self->priv->filename);
284 self->priv->filename = g_value_dup_string (value);
285 break;
286 case PROP_SIZE:
287 self->priv->size = g_value_get_uint64 (value);
288 break;
289 case PROP_CONTENT_HASH_TYPE:
290 self->priv->content_hash_type = g_value_get_uint (value);
291 break;
292 case PROP_CONTENT_HASH:
293 g_free (self->priv->content_hash);
294 self->priv->content_hash = g_value_dup_string (value);
295 break;
296 case PROP_DESCRIPTION:
297 g_free (self->priv->description);
298 self->priv->description = g_value_dup_string (value);
299 break;
300 case PROP_DATE:
301 self->priv->date = g_value_get_uint64 (value);
302 break;
303 case PROP_INITIAL_OFFSET:
304 self->priv->initial_offset = g_value_get_uint64 (value);
305 break;
306 case PROP_FILE_COLLECTION:
307 g_free (self->priv->file_collection);
308 self->priv->file_collection = g_value_dup_string (value);
309 break;
310 case PROP_URI:
311 g_assert (self->priv->uri == NULL); /* construct only */
312 self->priv->uri = g_value_dup_string (value);
313 break;
314 case PROP_RESUME_SUPPORTED:
315 self->priv->resume_supported = g_value_get_boolean (value);
316 break;
317 case PROP_BYTESTREAM:
318 set_bytestream (self,
319 GABBLE_BYTESTREAM_IFACE (g_value_get_object (value)));
320 break;
321 #ifdef ENABLE_JINGLE_FILE_TRANSFER
322 case PROP_GTALK_FILE_COLLECTION:
323 set_gtalk_file_collection (self,
324 GTALK_FILE_COLLECTION (g_value_get_object (value)));
325 break;
326 #endif
327 case PROP_SERVICE_NAME:
328 self->priv->service_name = g_value_dup_string (value);
329 break;
330 case PROP_METADATA:
331 self->priv->metadata = g_value_dup_boxed (value);
332 break;
333 default:
334 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
335 break;
336 }
337 }
338
339 static void
free_array(GArray * array)340 free_array (GArray *array)
341 {
342 g_array_unref (array);
343 }
344
345 static void
connection_presences_updated_cb(GabblePresenceCache * cache,GArray * handles,GabbleFileTransferChannel * self)346 connection_presences_updated_cb (GabblePresenceCache *cache,
347 GArray *handles,
348 GabbleFileTransferChannel *self)
349 {
350 TpBaseChannel *base = TP_BASE_CHANNEL (self);
351 TpBaseConnection *base_conn = tp_base_channel_get_connection (base);
352 GabbleConnection *conn = GABBLE_CONNECTION (base_conn);
353 guint i;
354
355 for (i = 0; i < handles->len ; i++)
356 {
357 TpHandle handle;
358
359 handle = g_array_index (handles, TpHandle, i);
360 if (handle == tp_base_channel_get_target_handle (base))
361 {
362 GabblePresence *presence;
363
364 presence = gabble_presence_cache_get (
365 conn->presence_cache, handle);
366
367 if (presence == NULL || presence->status < GABBLE_PRESENCE_XA)
368 {
369 /* Contact is disconnected */
370 if (self->priv->state != TP_FILE_TRANSFER_STATE_COMPLETED &&
371 self->priv->state != TP_FILE_TRANSFER_STATE_CANCELLED)
372 {
373 DEBUG ("peer disconnected. FileTransfer is cancelled");
374
375 gabble_file_transfer_channel_set_state (
376 TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
377 TP_FILE_TRANSFER_STATE_CANCELLED,
378 TP_FILE_TRANSFER_STATE_CHANGE_REASON_REMOTE_STOPPED);
379 }
380 }
381 }
382 }
383 }
384
385 static void
gabble_file_transfer_channel_constructed(GObject * obj)386 gabble_file_transfer_channel_constructed (GObject *obj)
387 {
388 GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (obj);
389 TpBaseChannel *base = TP_BASE_CHANNEL (self);
390 TpBaseConnection *base_conn = tp_base_channel_get_connection (base);
391 GabbleConnection *conn = GABBLE_CONNECTION (base_conn);
392 TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (
393 base_conn, TP_HANDLE_TYPE_CONTACT);
394 GArray *socket_access;
395 TpSocketAccessControl access_control;
396
397 /* Parent constructed chain */
398 void (*chain_up) (GObject *) =
399 ((GObjectClass *) gabble_file_transfer_channel_parent_class)->constructed;
400
401 if (chain_up != NULL)
402 chain_up (obj);
403
404 /* Initialise the available socket types hash table */
405 self->priv->available_socket_types = g_hash_table_new_full (g_direct_hash,
406 g_direct_equal, NULL, (GDestroyNotify) free_array);
407
408 #ifdef GIBBER_TYPE_UNIX_TRANSPORT
409 /* Socket_Address_Type_Unix */
410 socket_access = g_array_sized_new (FALSE, FALSE,
411 sizeof (TpSocketAccessControl), 1);
412 access_control = TP_SOCKET_ACCESS_CONTROL_LOCALHOST;
413 g_array_append_val (socket_access, access_control);
414 g_hash_table_insert (self->priv->available_socket_types,
415 GUINT_TO_POINTER (TP_SOCKET_ADDRESS_TYPE_UNIX), socket_access);
416 #endif
417
418 /* Socket_Address_Type_IPv4 */
419 socket_access = g_array_sized_new (FALSE, FALSE,
420 sizeof (TpSocketAccessControl), 1);
421 access_control = TP_SOCKET_ACCESS_CONTROL_LOCALHOST;
422 g_array_append_val (socket_access, access_control);
423 g_hash_table_insert (self->priv->available_socket_types,
424 GUINT_TO_POINTER (TP_SOCKET_ADDRESS_TYPE_IPV4), socket_access);
425
426 /* Socket_Address_Type_IPv6 */
427 socket_access = g_array_sized_new (FALSE, FALSE,
428 sizeof (TpSocketAccessControl), 1);
429 access_control = TP_SOCKET_ACCESS_CONTROL_LOCALHOST;
430 g_array_append_val (socket_access, access_control);
431 g_hash_table_insert (self->priv->available_socket_types,
432 GUINT_TO_POINTER (TP_SOCKET_ADDRESS_TYPE_IPV6), socket_access);
433
434 gabble_signal_connect_weak (conn->presence_cache,
435 "presences-updated", G_CALLBACK (connection_presences_updated_cb), obj);
436
437 DEBUG ("New FT channel created: %s (contact: %s, initiator: %s, "
438 "file: \"%s\", size: %" G_GUINT64_FORMAT ")",
439 tp_base_channel_get_object_path (base),
440 tp_handle_inspect (contact_repo, tp_base_channel_get_target_handle (base)),
441 tp_handle_inspect (contact_repo,
442 tp_base_channel_get_initiator (base)),
443 self->priv->filename, self->priv->size);
444
445 if (!tp_base_channel_is_requested (base))
446 /* Incoming transfer, URI has to be set by the handler */
447 g_assert (self->priv->uri == NULL);
448 }
449
450 static void gabble_file_transfer_channel_dispose (GObject *object);
451 static void gabble_file_transfer_channel_finalize (GObject *object);
452
453 static gboolean
file_transfer_channel_properties_setter(GObject * object,GQuark interface,GQuark name,const GValue * value,gpointer setter_data,GError ** error)454 file_transfer_channel_properties_setter (GObject *object,
455 GQuark interface,
456 GQuark name,
457 const GValue *value,
458 gpointer setter_data,
459 GError **error)
460 {
461 GabbleFileTransferChannel *self = (GabbleFileTransferChannel *) object;
462 TpBaseChannel *base = TP_BASE_CHANNEL (self);
463
464 g_return_val_if_fail (interface == TP_IFACE_QUARK_CHANNEL_TYPE_FILE_TRANSFER,
465 FALSE);
466
467 /* There is only one property with write access. So TpDBusPropertiesMixin
468 * already checked this. */
469 g_assert (name == g_quark_from_static_string ("URI"));
470
471 /* TpDBusPropertiesMixin already checked this */
472 g_assert (G_VALUE_HOLDS_STRING (value));
473
474 if (self->priv->uri != NULL)
475 {
476 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
477 "URI has already be set");
478 return FALSE;
479 }
480
481 if (tp_base_channel_is_requested (base))
482 {
483 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
484 "Channel is not an incoming transfer");
485 return FALSE;
486 }
487
488 if (self->priv->state != TP_FILE_TRANSFER_STATE_PENDING)
489 {
490 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
491 "State is not pending; cannot set URI");
492 return FALSE;
493 }
494
495 self->priv->uri = g_value_dup_string (value);
496
497 tp_svc_channel_type_file_transfer_emit_uri_defined (self, self->priv->uri);
498
499 return TRUE;
500 }
501
502 static void
gabble_file_transfer_channel_fill_immutable_properties(TpBaseChannel * chan,GHashTable * properties)503 gabble_file_transfer_channel_fill_immutable_properties (TpBaseChannel *chan,
504 GHashTable *properties)
505 {
506 TpBaseChannelClass *cls = TP_BASE_CHANNEL_CLASS (
507 gabble_file_transfer_channel_parent_class);
508
509 cls->fill_immutable_properties (chan, properties);
510
511 tp_dbus_properties_mixin_fill_properties_hash (
512 G_OBJECT (chan), properties,
513 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "State",
514 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "ContentType",
515 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "Filename",
516 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "Size",
517 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "ContentHashType",
518 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "ContentHash",
519 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "Description",
520 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "Date",
521 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "AvailableSocketTypes",
522 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "TransferredBytes",
523 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "InitialOffset",
524 GABBLE_IFACE_CHANNEL_TYPE_FILETRANSFER_FUTURE, "FileCollection",
525 TP_IFACE_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA, "ServiceName",
526 TP_IFACE_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA, "Metadata",
527 NULL);
528
529 /* URI is immutable only for outgoing transfers */
530 if (tp_base_channel_is_requested (chan))
531 {
532 tp_dbus_properties_mixin_fill_properties_hash (G_OBJECT (chan), properties,
533 TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "URI", NULL);
534 }
535 }
536
537 static gchar *
gabble_file_transfer_channel_get_object_path_suffix(TpBaseChannel * chan)538 gabble_file_transfer_channel_get_object_path_suffix (TpBaseChannel *chan)
539 {
540 return g_strdup_printf ("FileTransferChannel/%p", chan);
541 }
542
543 static GPtrArray *
gabble_file_transfer_channel_get_interfaces(TpBaseChannel * base)544 gabble_file_transfer_channel_get_interfaces (TpBaseChannel *base)
545 {
546 GPtrArray *interfaces;
547
548 interfaces = TP_BASE_CHANNEL_CLASS (
549 gabble_file_transfer_channel_parent_class)->get_interfaces (base);
550
551 g_ptr_array_add (interfaces, GABBLE_IFACE_CHANNEL_TYPE_FILETRANSFER_FUTURE);
552 g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA);
553
554 return interfaces;
555 }
556
557 static void
gabble_file_transfer_channel_class_init(GabbleFileTransferChannelClass * gabble_file_transfer_channel_class)558 gabble_file_transfer_channel_class_init (
559 GabbleFileTransferChannelClass *gabble_file_transfer_channel_class)
560 {
561 GObjectClass *object_class = G_OBJECT_CLASS (
562 gabble_file_transfer_channel_class);
563 TpBaseChannelClass *base_class = TP_BASE_CHANNEL_CLASS (
564 gabble_file_transfer_channel_class);
565 GParamSpec *param_spec;
566
567 static TpDBusPropertiesMixinPropImpl file_props[] = {
568 { "State", "state", NULL },
569 { "ContentType", "content-type", NULL },
570 { "Filename", "filename", NULL },
571 { "Size", "size", NULL },
572 { "ContentHashType", "content-hash-type", NULL },
573 { "ContentHash", "content-hash", NULL },
574 { "Description", "description", NULL },
575 { "AvailableSocketTypes", "available-socket-types", NULL },
576 { "TransferredBytes", "transferred-bytes", NULL },
577 { "InitialOffset", "initial-offset", NULL },
578 { "Date", "date", NULL },
579 { "URI", "uri", NULL },
580 { NULL }
581 };
582
583 static TpDBusPropertiesMixinPropImpl file_future_props[] = {
584 { "FileCollection", "file-collection", NULL },
585 { NULL }
586 };
587
588 static TpDBusPropertiesMixinPropImpl file_metadata_props[] = {
589 { "ServiceName", "service-name", NULL },
590 { "Metadata", "metadata", NULL },
591 { NULL }
592 };
593
594 static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = {
595 { TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER,
596 tp_dbus_properties_mixin_getter_gobject_properties,
597 file_transfer_channel_properties_setter,
598 file_props
599 },
600 { GABBLE_IFACE_CHANNEL_TYPE_FILETRANSFER_FUTURE,
601 tp_dbus_properties_mixin_getter_gobject_properties,
602 NULL,
603 file_future_props
604 },
605 { TP_IFACE_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA,
606 tp_dbus_properties_mixin_getter_gobject_properties,
607 NULL,
608 file_metadata_props
609 },
610 { NULL }
611 };
612
613 g_type_class_add_private (gabble_file_transfer_channel_class,
614 sizeof (GabbleFileTransferChannelPrivate));
615
616 object_class->dispose = gabble_file_transfer_channel_dispose;
617 object_class->finalize = gabble_file_transfer_channel_finalize;
618 object_class->constructed = gabble_file_transfer_channel_constructed;
619 object_class->get_property = gabble_file_transfer_channel_get_property;
620 object_class->set_property = gabble_file_transfer_channel_set_property;
621
622 base_class->channel_type = TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER;
623 base_class->target_handle_type = TP_HANDLE_TYPE_CONTACT;
624 base_class->get_interfaces = gabble_file_transfer_channel_get_interfaces;
625 base_class->close = gabble_file_transfer_channel_close;
626 base_class->fill_immutable_properties =
627 gabble_file_transfer_channel_fill_immutable_properties;
628 base_class->get_object_path_suffix =
629 gabble_file_transfer_channel_get_object_path_suffix;
630
631 param_spec = g_param_spec_uint (
632 "state",
633 "TpFileTransferState state",
634 "State of the file transfer in this channel",
635 0,
636 NUM_TP_FILE_TRANSFER_STATES,
637 TP_FILE_TRANSFER_STATE_NONE,
638 G_PARAM_CONSTRUCT_ONLY |
639 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
640 g_object_class_install_property (object_class, PROP_STATE, param_spec);
641
642 param_spec = g_param_spec_string (
643 "content-type",
644 "gchar *content-type",
645 "ContentType of the file",
646 "application/octet-stream",
647 G_PARAM_CONSTRUCT_ONLY |
648 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
649 g_object_class_install_property (object_class, PROP_CONTENT_TYPE,
650 param_spec);
651
652 param_spec = g_param_spec_string (
653 "filename",
654 "gchar *filename",
655 "Name of the file",
656 "",
657 G_PARAM_CONSTRUCT_ONLY |
658 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
659 g_object_class_install_property (object_class, PROP_FILENAME, param_spec);
660
661 param_spec = g_param_spec_uint64 (
662 "size",
663 "guint size",
664 "Size of the file in bytes",
665 0,
666 G_MAXUINT64,
667 GABBLE_UNDEFINED_FILE_SIZE,
668 G_PARAM_CONSTRUCT_ONLY |
669 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
670 g_object_class_install_property (object_class, PROP_SIZE, param_spec);
671
672 param_spec = g_param_spec_uint (
673 "content-hash-type",
674 "TpFileHashType content-hash-type",
675 "Hash type",
676 0,
677 NUM_TP_FILE_HASH_TYPES,
678 TP_FILE_HASH_TYPE_NONE,
679 G_PARAM_CONSTRUCT_ONLY |
680 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
681 g_object_class_install_property (object_class, PROP_CONTENT_HASH_TYPE,
682 param_spec);
683
684 param_spec = g_param_spec_string (
685 "content-hash",
686 "gchar *content-hash",
687 "Hash of the file contents",
688 "",
689 G_PARAM_CONSTRUCT_ONLY |
690 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
691 g_object_class_install_property (object_class, PROP_CONTENT_HASH,
692 param_spec);
693
694 param_spec = g_param_spec_string (
695 "description",
696 "gchar *description",
697 "Description of the file",
698 "",
699 G_PARAM_CONSTRUCT_ONLY |
700 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
701 g_object_class_install_property (object_class, PROP_DESCRIPTION, param_spec);
702
703 param_spec = g_param_spec_boxed (
704 "available-socket-types",
705 "GabbleSupportedSocketMap available-socket-types",
706 "Available socket types",
707 TP_HASH_TYPE_SUPPORTED_SOCKET_MAP,
708 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
709 g_object_class_install_property (object_class, PROP_AVAILABLE_SOCKET_TYPES,
710 param_spec);
711
712 param_spec = g_param_spec_uint64 (
713 "transferred-bytes",
714 "guint64 transferred-bytes",
715 "Bytes transferred",
716 0,
717 G_MAXUINT64,
718 0,
719 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
720 g_object_class_install_property (object_class, PROP_TRANSFERRED_BYTES,
721 param_spec);
722
723 param_spec = g_param_spec_uint64 (
724 "initial-offset",
725 "guint64 initial_offset",
726 "Offset set at the beginning of the transfer",
727 0,
728 G_MAXUINT64,
729 0,
730 G_PARAM_CONSTRUCT_ONLY |
731 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
732 g_object_class_install_property (object_class, PROP_INITIAL_OFFSET,
733 param_spec);
734
735 param_spec = g_param_spec_uint64 (
736 "date",
737 "Epoch time",
738 "the last modification time of the file being transferred",
739 0,
740 G_MAXUINT64,
741 0,
742 G_PARAM_CONSTRUCT_ONLY |
743 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
744 g_object_class_install_property (object_class, PROP_DATE,
745 param_spec);
746
747 param_spec = g_param_spec_object (
748 "bytestream",
749 "Object implementing the GabbleBytestreamIface interface",
750 "Bytestream object used to send the file",
751 G_TYPE_OBJECT,
752 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
753 g_object_class_install_property (object_class, PROP_BYTESTREAM,
754 param_spec);
755
756 #ifdef ENABLE_JINGLE_FILE_TRANSFER
757 param_spec = g_param_spec_object (
758 "gtalk-file-collection",
759 "GTalkFileCollection object for gtalk-compatible file transfer",
760 "GTalk compatible file transfer collection",
761 G_TYPE_OBJECT,
762 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
763 g_object_class_install_property (object_class, PROP_GTALK_FILE_COLLECTION,
764 param_spec);
765 #endif
766
767 param_spec = g_param_spec_boolean (
768 "resume-supported",
769 "resume is supported",
770 "TRUE if resume is supported on this file transfer channel",
771 FALSE,
772 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
773 g_object_class_install_property (object_class, PROP_RESUME_SUPPORTED,
774 param_spec);
775
776 param_spec = g_param_spec_string (
777 "file-collection",
778 "gchar *file_colletion",
779 "Token identifying a collection of files",
780 "",
781 G_PARAM_CONSTRUCT_ONLY |
782 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
783 g_object_class_install_property (object_class, PROP_FILE_COLLECTION,
784 param_spec);
785
786 param_spec = g_param_spec_string (
787 "uri", "URI",
788 "URI of the file being transferred",
789 NULL,
790 G_PARAM_CONSTRUCT_ONLY |
791 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
792 g_object_class_install_property (object_class, PROP_URI,
793 param_spec);
794
795 param_spec = g_param_spec_string ("service-name",
796 "ServiceName",
797 "The Metadata.ServiceName property of this channel",
798 "",
799 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
800 g_object_class_install_property (object_class, PROP_SERVICE_NAME,
801 param_spec);
802
803 param_spec = g_param_spec_boxed ("metadata",
804 "Metadata",
805 "The Metadata.Metadata property of this channel",
806 TP_HASH_TYPE_METADATA,
807 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
808 g_object_class_install_property (object_class, PROP_METADATA,
809 param_spec);
810
811 gabble_file_transfer_channel_class->dbus_props_class.interfaces =
812 prop_interfaces;
813 tp_dbus_properties_mixin_class_init (object_class,
814 G_STRUCT_OFFSET (GabbleFileTransferChannelClass, dbus_props_class));
815 }
816
817 void
gabble_file_transfer_channel_dispose(GObject * object)818 gabble_file_transfer_channel_dispose (GObject *object)
819 {
820 GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (object);
821
822 if (self->priv->dispose_has_run)
823 return;
824
825 DEBUG ("dispose called");
826 self->priv->dispose_has_run = TRUE;
827
828 if (self->priv->progress_timer != 0)
829 {
830 g_source_remove (self->priv->progress_timer);
831 self->priv->progress_timer = 0;
832 }
833
834 close_session_and_transport (self);
835
836 /* release any references held by the object here */
837
838 if (G_OBJECT_CLASS (gabble_file_transfer_channel_parent_class)->dispose)
839 G_OBJECT_CLASS (gabble_file_transfer_channel_parent_class)->dispose (object);
840 }
841
842 static void
erase_socket(GabbleFileTransferChannel * self)843 erase_socket (GabbleFileTransferChannel *self)
844 {
845 GArray *array;
846
847 if (self->priv->socket_type != TP_SOCKET_ADDRESS_TYPE_UNIX)
848 /* only UNIX sockets have to be erased */
849 return;
850
851 if (self->priv->socket_address == NULL)
852 return;
853
854 array = g_value_get_boxed (self->priv->socket_address);
855 if (g_unlink (array->data) != 0)
856 {
857 DEBUG ("unlink failed: %s", g_strerror (errno));
858 }
859 }
860
861 static void
gabble_file_transfer_channel_finalize(GObject * object)862 gabble_file_transfer_channel_finalize (GObject *object)
863 {
864 GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (object);
865
866 /* free any data held directly by the object here */
867 erase_socket (self);
868 g_free (self->priv->filename);
869 if (self->priv->socket_address != NULL)
870 tp_g_value_slice_free (self->priv->socket_address);
871 g_free (self->priv->content_type);
872 g_free (self->priv->content_hash);
873 g_free (self->priv->description);
874 g_hash_table_unref (self->priv->available_socket_types);
875 g_free (self->priv->file_collection);
876 g_free (self->priv->uri);
877 g_free (self->priv->service_name);
878 if (self->priv->metadata != NULL)
879 g_hash_table_unref (self->priv->metadata);
880
881 G_OBJECT_CLASS (gabble_file_transfer_channel_parent_class)->finalize (object);
882 }
883
884 static void
close_session_and_transport(GabbleFileTransferChannel * self)885 close_session_and_transport (GabbleFileTransferChannel *self)
886 {
887
888 DEBUG ("Closing session and transport");
889
890 #ifdef ENABLE_JINGLE_FILE_TRANSFER
891 if (self->priv->gtalk_file_collection != NULL)
892 gtalk_file_collection_terminate (self->priv->gtalk_file_collection, self);
893
894 tp_clear_object (&self->priv->gtalk_file_collection);
895 #endif
896
897 if (self->priv->bytestream != NULL)
898 gabble_bytestream_iface_close (self->priv->bytestream, NULL);
899
900 tp_clear_object (&self->priv->bytestream);
901 tp_clear_object (&self->priv->listener);
902 tp_clear_object (&self->priv->transport);
903 }
904
905 static gboolean setup_local_socket (GabbleFileTransferChannel *self,
906 TpSocketAddressType address_type, TpSocketAccessControl access_control,
907 const GValue *access_control_param);
908
909 static void
gabble_file_transfer_channel_set_state(TpSvcChannelTypeFileTransfer * iface,TpFileTransferState state,TpFileTransferStateChangeReason reason)910 gabble_file_transfer_channel_set_state (
911 TpSvcChannelTypeFileTransfer *iface,
912 TpFileTransferState state,
913 TpFileTransferStateChangeReason reason)
914 {
915 GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (iface);
916
917 if (self->priv->state == state)
918 return;
919
920 self->priv->state = state;
921 tp_svc_channel_type_file_transfer_emit_file_transfer_state_changed (iface,
922 state, reason);
923 }
924
925 static gboolean
check_address_and_access_control(GabbleFileTransferChannel * self,TpSocketAddressType address_type,TpSocketAccessControl access_control,const GValue * access_control_param,GError ** error)926 check_address_and_access_control (GabbleFileTransferChannel *self,
927 TpSocketAddressType address_type,
928 TpSocketAccessControl access_control,
929 const GValue *access_control_param,
930 GError **error)
931 {
932 GArray *access_arr;
933 guint i;
934
935 /* Do we support this AddressType? */
936 access_arr = g_hash_table_lookup (self->priv->available_socket_types,
937 GUINT_TO_POINTER (address_type));
938 if (access_arr == NULL)
939 {
940 g_set_error (error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED,
941 "AddressType %u is not implemented", address_type);
942 return FALSE;
943 }
944
945 /* Do we support this AccessControl? */
946 for (i = 0; i < access_arr->len; i++)
947 {
948 TpSocketAccessControl control;
949
950 control = g_array_index (access_arr, TpSocketAccessControl, i);
951 if (control == access_control)
952 return TRUE;
953 }
954
955 g_set_error (error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED,
956 "AccesControl %u is not implemented with AddressType %u",
957 access_control, address_type);
958
959 return FALSE;
960 }
961
962 static void
channel_open(GabbleFileTransferChannel * self)963 channel_open (GabbleFileTransferChannel *self)
964 {
965 DEBUG ("Channel open");
966
967 /* This is needed in case the ProvideFile wasn't called yet, to know if we
968 should go into OPEN state when ProvideFile gets called. */
969 self->priv->channel_opened = TRUE;
970
971 if (self->priv->socket_address != NULL)
972 {
973 /* ProvideFile has already been called. Channel is Open */
974 tp_svc_channel_type_file_transfer_emit_initial_offset_defined (self,
975 self->priv->initial_offset);
976
977 gabble_file_transfer_channel_set_state (
978 TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
979 TP_FILE_TRANSFER_STATE_OPEN,
980 TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE);
981
982 if (self->priv->transport != NULL)
983 gibber_transport_block_receiving (self->priv->transport, FALSE);
984 }
985 else
986 {
987 /* Client has to call ProvideFile to open the channel */
988 gabble_file_transfer_channel_set_state (
989 TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
990 TP_FILE_TRANSFER_STATE_ACCEPTED,
991 TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE);
992 }
993 }
994
995 static void
bytestream_closed(GabbleFileTransferChannel * self)996 bytestream_closed (GabbleFileTransferChannel *self)
997 {
998 if (self->priv->state != TP_FILE_TRANSFER_STATE_COMPLETED &&
999 self->priv->state != TP_FILE_TRANSFER_STATE_CANCELLED)
1000 {
1001 gboolean receiver = !tp_base_channel_is_requested (
1002 TP_BASE_CHANNEL (self));
1003
1004 /* Something did wrong */
1005 gabble_file_transfer_channel_set_state (
1006 TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
1007 TP_FILE_TRANSFER_STATE_CANCELLED,
1008 receiver ?
1009 TP_FILE_TRANSFER_STATE_CHANGE_REASON_LOCAL_ERROR :
1010 TP_FILE_TRANSFER_STATE_CHANGE_REASON_REMOTE_ERROR);
1011 }
1012 }
1013
1014
1015 static void
bytestream_state_changed_cb(GabbleBytestreamIface * bytestream,GabbleBytestreamState state,gpointer user_data)1016 bytestream_state_changed_cb (GabbleBytestreamIface *bytestream,
1017 GabbleBytestreamState state,
1018 gpointer user_data)
1019 {
1020 GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (user_data);
1021
1022 if (state == GABBLE_BYTESTREAM_STATE_OPEN)
1023 {
1024 channel_open (self);
1025 }
1026 else if (state == GABBLE_BYTESTREAM_STATE_CLOSED)
1027 {
1028 bytestream_closed (self);
1029 }
1030 }
1031
1032 static void bytestream_write_blocked_cb (GabbleBytestreamIface *bytestream,
1033 gboolean blocked,
1034 GabbleFileTransferChannel *self);
1035 static gboolean
set_bytestream(GabbleFileTransferChannel * self,GabbleBytestreamIface * bytestream)1036 set_bytestream (GabbleFileTransferChannel *self,
1037 GabbleBytestreamIface *bytestream)
1038
1039 {
1040 if (bytestream == NULL)
1041 return FALSE;
1042
1043 g_return_val_if_fail (self->priv->bytestream == NULL, FALSE);
1044 #ifdef ENABLE_JINGLE_FILE_TRANSFER
1045 g_return_val_if_fail (self->priv->gtalk_file_collection == NULL, FALSE);
1046 #endif
1047
1048 DEBUG ("Setting bytestream to %p", bytestream);
1049
1050 self->priv->bytestream = g_object_ref (bytestream);
1051
1052 gabble_signal_connect_weak (bytestream, "state-changed",
1053 G_CALLBACK (bytestream_state_changed_cb), G_OBJECT (self));
1054 gabble_signal_connect_weak (bytestream, "write-blocked",
1055 G_CALLBACK (bytestream_write_blocked_cb), G_OBJECT (self));
1056
1057 return TRUE;
1058 }
1059
1060 #ifdef ENABLE_JINGLE_FILE_TRANSFER
1061 static gboolean
set_gtalk_file_collection(GabbleFileTransferChannel * self,GTalkFileCollection * gtalk_file_collection)1062 set_gtalk_file_collection (
1063 GabbleFileTransferChannel *self, GTalkFileCollection *gtalk_file_collection)
1064 {
1065 if (gtalk_file_collection == NULL)
1066 return FALSE;
1067
1068 g_return_val_if_fail (self->priv->bytestream == NULL, FALSE);
1069 g_return_val_if_fail (self->priv->gtalk_file_collection == NULL, FALSE);
1070
1071 self->priv->gtalk_file_collection = g_object_ref (gtalk_file_collection);
1072
1073 /* No need to listen to any signals, the GTalkFileCollection will call our callbacks
1074 on his own */
1075
1076 return TRUE;
1077 }
1078 #endif
1079
1080 static void
bytestream_negotiate_cb(GabbleBytestreamIface * bytestream,WockyStanza * msg,GObject * object,gpointer user_data)1081 bytestream_negotiate_cb (GabbleBytestreamIface *bytestream,
1082 WockyStanza *msg,
1083 GObject *object,
1084 gpointer user_data)
1085 {
1086 GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (user_data);
1087 WockyNode *si;
1088 WockyNode *file = NULL;
1089
1090 if (bytestream == NULL)
1091 {
1092 DEBUG ("receiver refused file offer");
1093 gabble_file_transfer_channel_set_state (
1094 TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
1095 TP_FILE_TRANSFER_STATE_CANCELLED,
1096 TP_FILE_TRANSFER_STATE_CHANGE_REASON_REMOTE_STOPPED);
1097 return;
1098 }
1099
1100 si = wocky_node_get_child_ns (wocky_stanza_get_top_node (msg), "si", NS_SI);
1101 if (si != NULL)
1102 file = wocky_node_get_child_ns (si, "file", NULL);
1103
1104 if (file != NULL)
1105 {
1106 WockyNode *range;
1107
1108 range = wocky_node_get_child (file, "range");
1109 if (range != NULL)
1110 {
1111 const gchar *offset_str;
1112
1113 offset_str = wocky_node_get_attribute (range, "offset");
1114 if (offset_str != NULL)
1115 {
1116 self->priv->initial_offset = g_ascii_strtoull (offset_str, NULL,
1117 0);
1118 }
1119 }
1120 }
1121
1122 DEBUG ("receiver accepted file offer (offset: %" G_GUINT64_FORMAT ")",
1123 self->priv->initial_offset);
1124
1125 set_bytestream (self, bytestream);
1126
1127 }
1128
1129 static void
add_metadata_forms(GabbleFileTransferChannel * self,WockyNode * file)1130 add_metadata_forms (GabbleFileTransferChannel *self,
1131 WockyNode *file)
1132 {
1133 if (!tp_str_empty (self->priv->service_name))
1134 {
1135 wocky_node_add_build (file,
1136 '(', "x",
1137 ':', NS_X_DATA,
1138 '@', "type", "result",
1139 '(', "field",
1140 '@', "var", "FORM_TYPE",
1141 '@', "type", "hidden",
1142 '(', "value",
1143 '$', NS_TP_FT_METADATA_SERVICE,
1144 ')',
1145 ')',
1146 '(', "field",
1147 '@', "var", "ServiceName",
1148 '(', "value",
1149 '$', self->priv->service_name,
1150 ')',
1151 ')',
1152 ')',
1153 NULL);
1154 }
1155
1156 if (self->priv->metadata != NULL
1157 && g_hash_table_size (self->priv->metadata) > 0)
1158 {
1159 WockyNode *x;
1160 GHashTableIter iter;
1161 gpointer key, val;
1162
1163 wocky_node_add_build (file,
1164 '(', "x",
1165 ':', NS_X_DATA,
1166 '*', &x,
1167 '@', "type", "result",
1168 '(', "field",
1169 '@', "var", "FORM_TYPE",
1170 '@', "type", "hidden",
1171 '(', "value",
1172 '$', NS_TP_FT_METADATA,
1173 ')',
1174 ')',
1175 ')',
1176 NULL);
1177
1178 g_hash_table_iter_init (&iter, self->priv->metadata);
1179 while (g_hash_table_iter_next (&iter, &key, &val))
1180 {
1181 const gchar * const *values = val;
1182
1183 WockyNode *field = wocky_node_add_child (x, "field");
1184 wocky_node_set_attribute (field, "var", key);
1185
1186 for (; values != NULL && *values != NULL; values++)
1187 {
1188 wocky_node_add_child_with_content (field, "value", *values);
1189 }
1190 }
1191 }
1192 }
1193
1194 static void
offer_bytestream(GabbleFileTransferChannel * self,const gchar * jid,const gchar * resource)1195 offer_bytestream (GabbleFileTransferChannel *self, const gchar *jid,
1196 const gchar *resource)
1197 {
1198 GabbleConnection *conn = GABBLE_CONNECTION (tp_base_channel_get_connection (
1199 TP_BASE_CHANNEL (self)));
1200 WockyStanza *msg;
1201 WockyNode *si_node, *file_node;
1202 gchar *stream_id, *size_str, *full_jid;
1203
1204 if (resource)
1205 full_jid = g_strdup_printf ("%s/%s", jid, resource);
1206 else
1207 full_jid = g_strdup (jid);
1208
1209 DEBUG ("Offering SI Bytestream file transfer to %s", full_jid);
1210
1211 /* Outgoing FT , we'll need SOCK5 proxies */
1212 gabble_bytestream_factory_query_socks5_proxies (
1213 conn->bytestream_factory);
1214
1215
1216 stream_id = gabble_bytestream_factory_generate_stream_id ();
1217
1218 msg = gabble_bytestream_factory_make_stream_init_iq (full_jid,
1219 stream_id, NS_FILE_TRANSFER);
1220
1221 si_node = wocky_node_get_child_ns (
1222 wocky_stanza_get_top_node (msg), "si", NS_SI);
1223 g_assert (si_node != NULL);
1224
1225 size_str = g_strdup_printf ("%" G_GUINT64_FORMAT, self->priv->size);
1226
1227 file_node = wocky_node_add_child_ns (si_node, "file", NS_FILE_TRANSFER);
1228 wocky_node_set_attributes (file_node,
1229 "name", self->priv->filename,
1230 "size", size_str,
1231 "mime-type", self->priv->content_type,
1232 NULL);
1233
1234 add_metadata_forms (self, file_node);
1235
1236 if (self->priv->content_hash != NULL)
1237 wocky_node_set_attribute (file_node, "hash", self->priv->content_hash);
1238
1239 if (self->priv->date != 0)
1240 {
1241 time_t t;
1242 struct tm *tm;
1243 char date_str[21];
1244
1245 t = (time_t) self->priv->date;
1246 tm = gmtime (&t);
1247
1248 #ifdef G_OS_WIN32
1249 strftime (date_str, sizeof (date_str), "%Y-%m-%dT%H:%M:%SZ", tm);
1250 #else
1251 strftime (date_str, sizeof (date_str), "%FT%H:%M:%SZ", tm);
1252 #endif
1253
1254 wocky_node_set_attribute (file_node, "date", date_str);
1255 }
1256
1257 wocky_node_add_child_with_content (file_node, "desc", self->priv->description);
1258
1259 /* we support resume */
1260 wocky_node_add_child (file_node, "range");
1261
1262 gabble_bytestream_factory_negotiate_stream (
1263 conn->bytestream_factory, msg, stream_id,
1264 bytestream_negotiate_cb, self, G_OBJECT (self));
1265
1266 g_object_unref (msg);
1267 g_free (stream_id);
1268 g_free (size_str);
1269 g_free (full_jid);
1270 }
1271
1272 #ifdef ENABLE_JINGLE_FILE_TRANSFER
1273 void
gabble_file_transfer_channel_gtalk_file_collection_state_changed(GabbleFileTransferChannel * self,GTalkFileCollectionState state,gboolean local_terminator)1274 gabble_file_transfer_channel_gtalk_file_collection_state_changed (
1275 GabbleFileTransferChannel *self,
1276 GTalkFileCollectionState state, gboolean local_terminator)
1277 {
1278 DEBUG ("gtalk ft state changed to %d", state);
1279 switch (state)
1280 {
1281 case GTALK_FILE_COLLECTION_STATE_PENDING:
1282 gabble_file_transfer_channel_set_state (
1283 TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
1284 TP_FILE_TRANSFER_STATE_PENDING,
1285 TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE);
1286 break;
1287 case GTALK_FILE_COLLECTION_STATE_ACCEPTED:
1288 if (self->priv->state == TP_FILE_TRANSFER_STATE_PENDING)
1289 {
1290 gabble_file_transfer_channel_set_state (
1291 TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
1292 TP_FILE_TRANSFER_STATE_ACCEPTED,
1293 TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE);
1294 }
1295 break;
1296 case GTALK_FILE_COLLECTION_STATE_OPEN:
1297 channel_open (self);
1298 break;
1299 case GTALK_FILE_COLLECTION_STATE_TERMINATED:
1300 if (self->priv->state != TP_FILE_TRANSFER_STATE_COMPLETED &&
1301 self->priv->state != TP_FILE_TRANSFER_STATE_CANCELLED)
1302 {
1303 gabble_file_transfer_channel_set_state (
1304 TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
1305 TP_FILE_TRANSFER_STATE_CANCELLED,
1306 local_terminator ?
1307 TP_FILE_TRANSFER_STATE_CHANGE_REASON_LOCAL_STOPPED:
1308 TP_FILE_TRANSFER_STATE_CHANGE_REASON_REMOTE_STOPPED);
1309 }
1310 close_session_and_transport (self);
1311 break;
1312 case GTALK_FILE_COLLECTION_STATE_ERROR:
1313 case GTALK_FILE_COLLECTION_STATE_CONNECTION_FAILED:
1314 gabble_file_transfer_channel_set_state (
1315 TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
1316 TP_FILE_TRANSFER_STATE_CANCELLED,
1317 TP_FILE_TRANSFER_STATE_CHANGE_REASON_LOCAL_ERROR);
1318
1319 close_session_and_transport (self);
1320 break;
1321 case GTALK_FILE_COLLECTION_STATE_COMPLETED:
1322 gabble_file_transfer_channel_set_state (
1323 TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
1324 TP_FILE_TRANSFER_STATE_COMPLETED,
1325 TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE);
1326
1327 if (self->priv->transport &&
1328 gibber_transport_buffer_is_empty (self->priv->transport))
1329 gibber_transport_disconnect (self->priv->transport);
1330 break;
1331 }
1332 }
1333
1334 static gboolean
offer_gtalk_file_transfer(GabbleFileTransferChannel * self,const gchar * full_jid,GError ** error)1335 offer_gtalk_file_transfer (GabbleFileTransferChannel *self,
1336 const gchar *full_jid, GError **error)
1337 {
1338 TpBaseChannel *base = TP_BASE_CHANNEL (self);
1339 GabbleConnection *conn = GABBLE_CONNECTION (
1340 tp_base_channel_get_connection (base));
1341 WockyJingleFactory *jf;
1342 GTalkFileCollection *gtalk_file_collection;
1343
1344 DEBUG ("Offering Gtalk file transfer to %s", full_jid);
1345
1346 jf = gabble_jingle_mint_get_factory (conn->jingle_mint);
1347 g_return_val_if_fail (jf != NULL, FALSE);
1348
1349 gtalk_file_collection = gtalk_file_collection_new (self,
1350 jf,
1351 tp_base_channel_get_target_handle (base), full_jid);
1352
1353 g_return_val_if_fail (gtalk_file_collection != NULL, FALSE);
1354
1355 set_gtalk_file_collection (self, gtalk_file_collection);
1356
1357 gtalk_file_collection_initiate (self->priv->gtalk_file_collection, self);
1358
1359 /* We would have gotten a set_gtalk_file_collection so we already hold an
1360 additional reference to the object, so we can drop the reference we got
1361 from the gtalk_file_collection_new. If we didn't get our
1362 set_gtalk_file_collection called, then the ft manager doesn't handle us,
1363 so it's best to just destroy it anyways */
1364 g_object_unref (gtalk_file_collection);
1365
1366 return TRUE;
1367 }
1368 #endif
1369
1370 gboolean
gabble_file_transfer_channel_offer_file(GabbleFileTransferChannel * self,GError ** error)1371 gabble_file_transfer_channel_offer_file (GabbleFileTransferChannel *self,
1372 GError **error)
1373 {
1374 TpBaseChannel *base = TP_BASE_CHANNEL (self);
1375 TpBaseConnection *base_conn = tp_base_channel_get_connection (base);
1376 GabbleConnection *conn = GABBLE_CONNECTION (base_conn);
1377 GabblePresence *presence;
1378 gboolean result;
1379 TpHandleRepoIface *contact_repo, *room_repo;
1380 const gchar *jid;
1381 gboolean si = FALSE;
1382 gboolean use_si = FALSE;
1383 const gchar *si_resource = NULL;
1384 #ifdef ENABLE_JINGLE_FILE_TRANSFER
1385 gboolean jingle_share = FALSE;
1386 const gchar *share_resource = NULL;
1387 #endif
1388
1389 g_assert (!tp_str_empty (self->priv->filename));
1390 g_assert (self->priv->size != GABBLE_UNDEFINED_FILE_SIZE);
1391 g_return_val_if_fail (self->priv->bytestream == NULL, FALSE);
1392 #ifdef ENABLE_JINGLE_FILE_TRANSFER
1393 g_return_val_if_fail (self->priv->gtalk_file_collection == NULL, FALSE);
1394 #endif
1395
1396 presence = gabble_presence_cache_get (conn->presence_cache,
1397 tp_base_channel_get_target_handle (base));
1398
1399 if (presence == NULL)
1400 {
1401 DEBUG ("can't find contact's presence");
1402 g_set_error (error, TP_ERROR, TP_ERROR_OFFLINE,
1403 "can't find contact's presence");
1404
1405 return FALSE;
1406 }
1407
1408 if (self->priv->service_name != NULL || self->priv->metadata != NULL)
1409 {
1410 if (!gabble_presence_has_cap (presence, NS_TP_FT_METADATA))
1411 {
1412 DEBUG ("trying to use Metadata properties on a contact "
1413 "who doesn't support it");
1414 g_set_error (error, TP_ERROR, TP_ERROR_NOT_CAPABLE,
1415 "The specified contact does not support the "
1416 "Metadata extension; you should ensure both ServiceName and "
1417 "Metadata properties are not present in the channel "
1418 "request");
1419 return FALSE;
1420 }
1421 }
1422
1423 contact_repo = tp_base_connection_get_handles (base_conn,
1424 TP_HANDLE_TYPE_CONTACT);
1425 room_repo = tp_base_connection_get_handles (base_conn,
1426 TP_HANDLE_TYPE_ROOM);
1427
1428 jid = tp_handle_inspect (contact_repo,
1429 tp_base_channel_get_target_handle (base));
1430 if (gabble_get_room_handle_from_jid (room_repo, jid) == 0)
1431 {
1432 /* Not a MUC jid, need to get a resource */
1433
1434 /* FIXME: should we check for SI, bytestreams and/or IBB too?
1435 * http://bugs.freedesktop.org/show_bug.cgi?id=23777 */
1436 si_resource = gabble_presence_pick_resource_by_caps (presence, 0,
1437 gabble_capability_set_predicate_has, NS_FILE_TRANSFER);
1438 si = (si_resource != NULL);
1439
1440 #ifdef ENABLE_JINGLE_FILE_TRANSFER
1441 share_resource = gabble_presence_pick_resource_by_caps (presence, 0,
1442 gabble_capability_set_predicate_has, NS_GOOGLE_FEAT_SHARE);
1443 jingle_share = (share_resource != NULL);
1444 #endif
1445 }
1446 else
1447 {
1448 /* MUC jid, we already have the full jid */
1449 si = gabble_presence_has_cap (presence, NS_FILE_TRANSFER);
1450 #ifdef ENABLE_JINGLE_FILE_TRANSFER
1451 jingle_share = gabble_presence_has_cap (presence, NS_GOOGLE_FEAT_SHARE);
1452 #endif
1453 }
1454
1455 /* Use bytestream if we have SI, but no jingle-share or if we have SI and
1456 jingle-share but we have no google relay token */
1457 #ifdef ENABLE_JINGLE_FILE_TRANSFER
1458 use_si = si &&
1459 (!jingle_share ||
1460 wocky_jingle_info_get_google_relay_token (
1461 gabble_jingle_mint_get_info (conn->jingle_mint)) == NULL);
1462 #else
1463 use_si = si;
1464 #endif
1465
1466 if (use_si)
1467 {
1468 offer_bytestream (self, jid, si_resource);
1469 result = TRUE;
1470 }
1471 #ifdef ENABLE_JINGLE_FILE_TRANSFER
1472 else if (jingle_share)
1473 {
1474 gchar *full_jid = gabble_peer_to_jid (conn,
1475 tp_base_channel_get_target_handle (base), share_resource);
1476 result = offer_gtalk_file_transfer (self, full_jid, error);
1477 g_free (full_jid);
1478 }
1479 #endif
1480 else
1481 {
1482 DEBUG ("contact doesn't have file transfer capabilities");
1483 g_set_error (error, TP_ERROR, TP_ERROR_NOT_CAPABLE,
1484 "contact doesn't have file transfer capabilities");
1485 result = FALSE;
1486 }
1487
1488 return result;
1489 }
1490
1491 static void
emit_progress_update(GabbleFileTransferChannel * self)1492 emit_progress_update (GabbleFileTransferChannel *self)
1493 {
1494 TpSvcChannelTypeFileTransfer *iface =
1495 TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self);
1496
1497 g_get_current_time (&self->priv->last_transferred_bytes_emitted);
1498
1499 tp_svc_channel_type_file_transfer_emit_transferred_bytes_changed (
1500 iface, self->priv->transferred_bytes);
1501
1502 if (self->priv->progress_timer != 0)
1503 {
1504 g_source_remove (self->priv->progress_timer);
1505 self->priv->progress_timer = 0;
1506 }
1507 }
1508
1509 static gboolean
emit_progress_update_cb(gpointer user_data)1510 emit_progress_update_cb (gpointer user_data)
1511 {
1512 GabbleFileTransferChannel *self =
1513 GABBLE_FILE_TRANSFER_CHANNEL (user_data);
1514
1515 emit_progress_update (self);
1516
1517 return FALSE;
1518 }
1519
1520 static void
transferred_chunk(GabbleFileTransferChannel * self,guint64 count)1521 transferred_chunk (GabbleFileTransferChannel *self,
1522 guint64 count)
1523 {
1524 GTimeVal timeval;
1525 gint interval;
1526
1527 self->priv->transferred_bytes += count;
1528
1529 if (self->priv->transferred_bytes + self->priv->initial_offset >=
1530 self->priv->size)
1531 {
1532 /* If the transfer has finished send an update right away */
1533 emit_progress_update (self);
1534 return;
1535 }
1536
1537 if (self->priv->progress_timer != 0)
1538 {
1539 /* A progress update signal is already scheduled */
1540 return;
1541 }
1542
1543 /* Only emit the TransferredBytes signal if it has been one second since its
1544 * last emission.
1545 */
1546 g_get_current_time (&timeval);
1547 interval = timeval.tv_sec -
1548 self->priv->last_transferred_bytes_emitted.tv_sec;
1549
1550 if (interval > 1)
1551 {
1552 /* At least more then a second apart, emit right away */
1553 emit_progress_update (self);
1554 return;
1555 }
1556
1557 /* Convert interval to milliseconds and calculate it more precisely */
1558 interval *= 1000;
1559
1560 interval += (timeval.tv_usec -
1561 self->priv->last_transferred_bytes_emitted.tv_usec)/1000;
1562
1563 /* Protect against clock skew, if the interval is negative the worst thing
1564 * that can happen is that we wait an extra second before emitting the signal
1565 */
1566 interval = ABS (interval);
1567
1568 if (interval > 1000)
1569 emit_progress_update (self);
1570 else
1571 self->priv->progress_timer = g_timeout_add (1000 - interval,
1572 emit_progress_update_cb, self);
1573 }
1574
1575 static void
data_received_cb(GabbleFileTransferChannel * self,const guint8 * data,guint len)1576 data_received_cb (GabbleFileTransferChannel *self, const guint8 *data, guint len)
1577 {
1578 GError *error = NULL;
1579
1580 g_assert (self->priv->transport != NULL);
1581
1582 if (!gibber_transport_send (self->priv->transport, data, len, &error))
1583 {
1584 DEBUG ("sending to transport failed: %s", error->message);
1585 g_error_free (error);
1586
1587 gabble_file_transfer_channel_set_state (
1588 TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
1589 TP_FILE_TRANSFER_STATE_CANCELLED,
1590 TP_FILE_TRANSFER_STATE_CHANGE_REASON_LOCAL_ERROR);
1591 return;
1592 }
1593
1594 transferred_chunk (self, (guint64) len);
1595
1596 if (self->priv->bytestream != NULL &&
1597 self->priv->transferred_bytes + self->priv->initial_offset >=
1598 self->priv->size)
1599 {
1600 DEBUG ("Received all the file. Transfer is complete");
1601 gabble_file_transfer_channel_set_state (
1602 TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
1603 TP_FILE_TRANSFER_STATE_COMPLETED,
1604 TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE);
1605
1606 if (gibber_transport_buffer_is_empty (self->priv->transport))
1607 gibber_transport_disconnect (self->priv->transport);
1608
1609 return;
1610 }
1611
1612 if (!gibber_transport_buffer_is_empty (self->priv->transport))
1613 {
1614 /* We don't want to send more data while the buffer isn't empty */
1615 if (self->priv->bytestream != NULL)
1616 gabble_bytestream_iface_block_reading (self->priv->bytestream, TRUE);
1617 #ifdef ENABLE_JINGLE_FILE_TRANSFER
1618 else if (self->priv->gtalk_file_collection != NULL)
1619 gtalk_file_collection_block_reading (self->priv->gtalk_file_collection,
1620 self, TRUE);
1621 #endif
1622 }
1623 }
1624
1625 #ifdef ENABLE_JINGLE_FILE_TRANSFER
1626 void
gabble_file_transfer_channel_gtalk_file_collection_data_received(GabbleFileTransferChannel * self,const gchar * data,guint len)1627 gabble_file_transfer_channel_gtalk_file_collection_data_received (
1628 GabbleFileTransferChannel *self, const gchar *data, guint len)
1629 {
1630 data_received_cb (self, (const guint8 *) data, len);
1631 }
1632 #endif
1633
1634 static void
bytestream_data_received_cb(GabbleBytestreamIface * stream,TpHandle sender,GString * data,gpointer user_data)1635 bytestream_data_received_cb (GabbleBytestreamIface *stream,
1636 TpHandle sender,
1637 GString *data,
1638 gpointer user_data)
1639 {
1640 GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (user_data);
1641 data_received_cb (self, (const guint8 *) data->str, data->len);
1642 }
1643
1644 static void
augment_si_reply(WockyNode * si,gpointer user_data)1645 augment_si_reply (WockyNode *si,
1646 gpointer user_data)
1647 {
1648 GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (user_data);
1649 WockyNode *file;
1650
1651 file = wocky_node_add_child_ns (si, "file", NS_FILE_TRANSFER);
1652
1653 if (self->priv->initial_offset != 0)
1654 {
1655 WockyNode *range;
1656 gchar *offset_str;
1657
1658 range = wocky_node_add_child (file, "range");
1659 offset_str = g_strdup_printf ("%" G_GUINT64_FORMAT,
1660 self->priv->initial_offset);
1661 wocky_node_set_attribute (range, "offset", offset_str);
1662
1663 /* Don't set "length" attribute as the default is the length of the file
1664 * from offset to the end which is what we want when resuming a FT. */
1665
1666 g_free (offset_str);
1667 }
1668 }
1669
1670 /**
1671 * gabble_file_transfer_channel_accept_file
1672 *
1673 * Implements D-Bus method AcceptFile
1674 * on interface org.freedesktop.Telepathy.Channel.Type.FileTransfer
1675 */
1676 static void
gabble_file_transfer_channel_accept_file(TpSvcChannelTypeFileTransfer * iface,guint address_type,guint access_control,const GValue * access_control_param,guint64 offset,DBusGMethodInvocation * context)1677 gabble_file_transfer_channel_accept_file (TpSvcChannelTypeFileTransfer *iface,
1678 guint address_type,
1679 guint access_control,
1680 const GValue *access_control_param,
1681 guint64 offset,
1682 DBusGMethodInvocation *context)
1683 {
1684 GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (iface);
1685 TpBaseChannel *base = TP_BASE_CHANNEL (self);
1686 GError *error = NULL;
1687
1688 if (tp_base_channel_is_requested (base))
1689 {
1690 g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
1691 "Channel is not an incoming transfer");
1692 dbus_g_method_return_error (context, error);
1693 g_error_free (error);
1694 return;
1695 }
1696
1697 if (self->priv->state != TP_FILE_TRANSFER_STATE_PENDING)
1698 {
1699 g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
1700 "State is not pending; cannot accept file");
1701 dbus_g_method_return_error (context, error);
1702 g_error_free (error);
1703 return;
1704 }
1705
1706 if (!check_address_and_access_control (self, address_type, access_control,
1707 access_control_param, &error))
1708 {
1709 dbus_g_method_return_error (context, error);
1710 g_error_free (error);
1711 return;
1712 }
1713
1714 if (!setup_local_socket (self, address_type, access_control,
1715 access_control_param))
1716 {
1717 DEBUG ("Could not set up local socket");
1718 g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
1719 "Could not set up local socket");
1720 dbus_g_method_return_error (context, error);
1721 g_error_free (error);
1722 return;
1723 }
1724
1725 gabble_file_transfer_channel_set_state (iface,
1726 TP_FILE_TRANSFER_STATE_ACCEPTED,
1727 TP_FILE_TRANSFER_STATE_CHANGE_REASON_REQUESTED);
1728
1729 tp_svc_channel_type_file_transfer_return_from_accept_file (context,
1730 self->priv->socket_address);
1731
1732 if (self->priv->resume_supported)
1733 {
1734 self->priv->initial_offset = offset;
1735 }
1736 else
1737 {
1738 DEBUG ("Resume is not supported on this file transfer");
1739 self->priv->initial_offset = 0;
1740 }
1741
1742 if (self->priv->bytestream != NULL)
1743 {
1744 gabble_signal_connect_weak (self->priv->bytestream, "data-received",
1745 G_CALLBACK (bytestream_data_received_cb), G_OBJECT (self));
1746
1747
1748 /* Block the bytestream while the user is not connected to the socket */
1749 gabble_bytestream_iface_block_reading (self->priv->bytestream, TRUE);
1750
1751 /* channel state will change to open once the bytestream is open */
1752 gabble_bytestream_iface_accept (self->priv->bytestream, augment_si_reply,
1753 self);
1754 }
1755 #ifdef ENABLE_JINGLE_FILE_TRANSFER
1756 else if (self->priv->gtalk_file_collection != NULL)
1757 {
1758 /* Block the gtalk ft stream while the user is not connected
1759 to the socket */
1760 gtalk_file_collection_block_reading (self->priv->gtalk_file_collection,
1761 self, TRUE);
1762 gtalk_file_collection_accept (self->priv->gtalk_file_collection, self);
1763 }
1764 #endif
1765 else
1766 {
1767 g_assert_not_reached ();
1768 }
1769 }
1770
1771 /**
1772 * gabble_file_transfer_channel_provide_file
1773 *
1774 * Implements D-Bus method ProvideFile
1775 * on interface org.freedesktop.Telepathy.Channel.Type.FileTransfer
1776 */
1777 static void
gabble_file_transfer_channel_provide_file(TpSvcChannelTypeFileTransfer * iface,guint address_type,guint access_control,const GValue * access_control_param,DBusGMethodInvocation * context)1778 gabble_file_transfer_channel_provide_file (
1779 TpSvcChannelTypeFileTransfer *iface,
1780 guint address_type,
1781 guint access_control,
1782 const GValue *access_control_param,
1783 DBusGMethodInvocation *context)
1784 {
1785 GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (iface);
1786 GError *error = NULL;
1787
1788 if (!tp_base_channel_is_requested (TP_BASE_CHANNEL (self)))
1789 {
1790 g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
1791 "Channel is not an outgoing transfer");
1792 dbus_g_method_return_error (context, error);
1793 g_error_free (error);
1794 return;
1795 }
1796
1797 if (self->priv->state != TP_FILE_TRANSFER_STATE_PENDING &&
1798 self->priv->state != TP_FILE_TRANSFER_STATE_ACCEPTED)
1799 {
1800 g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
1801 "State is not pending or accepted; cannot provide file");
1802 dbus_g_method_return_error (context, error);
1803 g_error_free (error);
1804 return;
1805 }
1806
1807 if (self->priv->socket_address != NULL)
1808 {
1809 g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
1810 "ProvideFile has already been called for this channel");
1811 dbus_g_method_return_error (context, error);
1812 g_error_free (error);
1813 return;
1814 }
1815
1816 if (!check_address_and_access_control (self, address_type, access_control,
1817 access_control_param, &error))
1818 {
1819 dbus_g_method_return_error (context, error);
1820 g_error_free (error);
1821 return;
1822 }
1823
1824 if (!setup_local_socket (self, address_type, access_control,
1825 access_control_param))
1826 {
1827 DEBUG ("Could not set up local socket");
1828 g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
1829 "Could not set up local socket");
1830 dbus_g_method_return_error (context, error);
1831 g_error_free (error);
1832 return;
1833 }
1834
1835 if (self->priv->channel_opened)
1836 {
1837 /* Remote already accepted the file. Channel is Open.
1838 * If not channel stay Pending. */
1839 tp_svc_channel_type_file_transfer_emit_initial_offset_defined (self,
1840 self->priv->initial_offset);
1841
1842 gabble_file_transfer_channel_set_state (iface,
1843 TP_FILE_TRANSFER_STATE_OPEN,
1844 TP_FILE_TRANSFER_STATE_CHANGE_REASON_REQUESTED);
1845 }
1846
1847 tp_svc_channel_type_file_transfer_return_from_provide_file (context,
1848 self->priv->socket_address);
1849 }
1850
1851 static void
file_transfer_iface_init(gpointer g_iface,gpointer iface_data)1852 file_transfer_iface_init (gpointer g_iface,
1853 gpointer iface_data)
1854 {
1855 TpSvcChannelTypeFileTransferClass *klass =
1856 (TpSvcChannelTypeFileTransferClass *) g_iface;
1857
1858 #define IMPLEMENT(x) tp_svc_channel_type_file_transfer_implement_##x (\
1859 klass, gabble_file_transfer_channel_##x)
1860 IMPLEMENT (accept_file);
1861 IMPLEMENT (provide_file);
1862 #undef IMPLEMENT
1863 }
1864
1865 #ifdef GIBBER_TYPE_UNIX_TRANSPORT
1866 static gchar *
get_local_unix_socket_path(GabbleFileTransferChannel * self)1867 get_local_unix_socket_path (GabbleFileTransferChannel *self)
1868 {
1869 TpBaseConnection *base_conn = tp_base_channel_get_connection (
1870 TP_BASE_CHANNEL (self));
1871 GabbleConnection *conn = GABBLE_CONNECTION (base_conn);
1872 const gchar *tmp_dir;
1873 gchar *path = NULL;
1874 gchar *name;
1875 struct stat buf;
1876
1877 tmp_dir = gabble_ft_manager_get_tmp_dir (conn->ft_manager);
1878 if (tmp_dir == NULL)
1879 return NULL;
1880
1881 name = g_strdup_printf ("ft-channel-%p", self);
1882 path = g_build_filename (tmp_dir, name, NULL);
1883 g_free (name);
1884
1885 if (g_stat (path, &buf) == 0)
1886 {
1887 /* The file is not supposed to exist */
1888 DEBUG ("file %s already exists", path);
1889 g_assert_not_reached ();
1890 }
1891
1892 return path;
1893 }
1894 #endif
1895
1896 /*
1897 * Data is available from the channel so we can send it.
1898 */
1899 static void
transport_handler(GibberTransport * transport,GibberBuffer * data,gpointer user_data)1900 transport_handler (GibberTransport *transport,
1901 GibberBuffer *data,
1902 gpointer user_data)
1903 {
1904 GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (user_data);
1905
1906 if (self->priv->bytestream != NULL)
1907 {
1908 if (!gabble_bytestream_iface_send (self->priv->bytestream, data->length,
1909 (const gchar *) data->data))
1910 {
1911 DEBUG ("Sending failed. Closing the bytestream");
1912 close_session_and_transport (self);
1913 return;
1914 }
1915 }
1916 #ifdef ENABLE_JINGLE_FILE_TRANSFER
1917 else if (self->priv->gtalk_file_collection != NULL)
1918 {
1919 if (!gtalk_file_collection_send_data (self->priv->gtalk_file_collection,
1920 self, (const gchar *) data->data, data->length))
1921 {
1922 DEBUG ("Sending failed. Closing the jingle session");
1923 close_session_and_transport (self);
1924 return;
1925 }
1926 }
1927 #endif
1928
1929 transferred_chunk (self, (guint64) data->length);
1930
1931 if (self->priv->transferred_bytes + self->priv->initial_offset >=
1932 self->priv->size)
1933 {
1934 if (self->priv->bytestream != NULL)
1935 {
1936 DEBUG ("All the file has been sent. Closing the bytestream");
1937 gabble_file_transfer_channel_set_state (
1938 TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
1939 TP_FILE_TRANSFER_STATE_COMPLETED,
1940 TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE);
1941 gabble_bytestream_iface_close (self->priv->bytestream, NULL);
1942 }
1943 #ifdef ENABLE_JINGLE_FILE_TRANSFER
1944 else if (self->priv->gtalk_file_collection != NULL)
1945 {
1946 DEBUG ("All the file has been sent.");
1947 gtalk_file_collection_completed (self->priv->gtalk_file_collection,
1948 self);
1949 }
1950 #endif
1951 }
1952 }
1953
1954 static void
bytestream_write_blocked_cb(GabbleBytestreamIface * bytestream,gboolean blocked,GabbleFileTransferChannel * self)1955 bytestream_write_blocked_cb (GabbleBytestreamIface *bytestream,
1956 gboolean blocked,
1957 GabbleFileTransferChannel *self)
1958 {
1959 if (self->priv->transport != NULL)
1960 gibber_transport_block_receiving (self->priv->transport, blocked);
1961 }
1962
1963 #ifdef ENABLE_JINGLE_FILE_TRANSFER
1964 void
gabble_file_transfer_channel_gtalk_file_collection_write_blocked(GabbleFileTransferChannel * self,gboolean blocked)1965 gabble_file_transfer_channel_gtalk_file_collection_write_blocked (
1966 GabbleFileTransferChannel *self, gboolean blocked)
1967 {
1968 if (self->priv->transport != NULL)
1969 gibber_transport_block_receiving (self->priv->transport, blocked);
1970 }
1971 #endif
1972
1973 static void
file_transfer_send(GabbleFileTransferChannel * self)1974 file_transfer_send (GabbleFileTransferChannel *self)
1975 {
1976 /* We shouldn't receive data if the bytestream isn't open otherwise it
1977 will error out */
1978 if (self->priv->state == TP_FILE_TRANSFER_STATE_OPEN)
1979 gibber_transport_block_receiving (self->priv->transport, FALSE);
1980 else
1981 gibber_transport_block_receiving (self->priv->transport, TRUE);
1982
1983 gibber_transport_set_handler (self->priv->transport, transport_handler,
1984 self);
1985 }
1986
1987 static void
file_transfer_receive(GabbleFileTransferChannel * self)1988 file_transfer_receive (GabbleFileTransferChannel *self)
1989 {
1990 /* Client is connected, we can now receive data. Unblock the bytestream */
1991 if (self->priv->bytestream != NULL)
1992 gabble_bytestream_iface_block_reading (self->priv->bytestream, FALSE);
1993 #ifdef ENABLE_JINGLE_FILE_TRANSFER
1994 else if (self->priv->gtalk_file_collection != NULL)
1995 gtalk_file_collection_block_reading (self->priv->gtalk_file_collection,
1996 self, FALSE);
1997 #endif
1998 }
1999
2000 static void
transport_disconnected_cb(GibberTransport * transport,GabbleFileTransferChannel * self)2001 transport_disconnected_cb (GibberTransport *transport,
2002 GabbleFileTransferChannel *self)
2003 {
2004 TpBaseChannel *base = TP_BASE_CHANNEL (self);
2005 gboolean requested = tp_base_channel_is_requested (base);
2006
2007 DEBUG ("transport to local socket has been disconnected");
2008
2009 /* If we are sending the file, we can expect the transport to be closed as
2010 soon as we received all the data. Otherwise, it should only get closed once
2011 the channel has gone to state COMPLETED.
2012 This allows to make sure we detect an error if the channel is closed while
2013 receiving a gtalk-ft folder where the size is an approximation of the real
2014 size to be received */
2015 if ((requested &&
2016 self->priv->transferred_bytes + self->priv->initial_offset <
2017 self->priv->size) ||
2018 (!requested && self->priv->state != TP_FILE_TRANSFER_STATE_COMPLETED))
2019 {
2020
2021 gabble_file_transfer_channel_set_state (
2022 TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
2023 TP_FILE_TRANSFER_STATE_CANCELLED,
2024 TP_FILE_TRANSFER_STATE_CHANGE_REASON_LOCAL_ERROR);
2025
2026 close_session_and_transport (self);
2027 }
2028 }
2029
2030 static void
transport_buffer_empty_cb(GibberTransport * transport,GabbleFileTransferChannel * self)2031 transport_buffer_empty_cb (GibberTransport *transport,
2032 GabbleFileTransferChannel *self)
2033 {
2034 /* Buffer is empty so we can unblock the buffer if it was blocked */
2035 if (self->priv->bytestream != NULL)
2036 gabble_bytestream_iface_block_reading (self->priv->bytestream, FALSE);
2037
2038 #ifdef ENABLE_JINGLE_FILE_TRANSFER
2039 if (self->priv->gtalk_file_collection != NULL)
2040 gtalk_file_collection_block_reading (self->priv->gtalk_file_collection,
2041 self, FALSE);
2042 #endif
2043
2044 if (self->priv->state > TP_FILE_TRANSFER_STATE_OPEN)
2045 gibber_transport_disconnect (transport);
2046 }
2047
2048 /*
2049 * Some client is connecting to the Unix socket.
2050 */
2051 static void
new_connection_cb(GibberListener * listener,GibberTransport * transport,struct sockaddr_storage * addr,guint size,gpointer user_data)2052 new_connection_cb (GibberListener *listener,
2053 GibberTransport *transport,
2054 struct sockaddr_storage *addr,
2055 guint size,
2056 gpointer user_data)
2057 {
2058 GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (user_data);
2059 TpBaseChannel *base = TP_BASE_CHANNEL (self);
2060
2061 DEBUG ("Client connected to local socket");
2062
2063 self->priv->transport = g_object_ref (transport);
2064 gabble_signal_connect_weak (transport, "disconnected",
2065 G_CALLBACK (transport_disconnected_cb), G_OBJECT (self));
2066 gabble_signal_connect_weak (transport, "buffer-empty",
2067 G_CALLBACK (transport_buffer_empty_cb), G_OBJECT (self));
2068
2069 if (!tp_base_channel_is_requested (base))
2070 /* Incoming file transfer */
2071 file_transfer_receive (self);
2072 else
2073 /* Outgoing file transfer */
2074 file_transfer_send (self);
2075
2076 /* stop listening on local socket */
2077 tp_clear_object (&self->priv->listener);
2078 }
2079
2080 static gboolean
setup_local_socket(GabbleFileTransferChannel * self,TpSocketAddressType address_type,TpSocketAccessControl access_control,const GValue * access_control_param)2081 setup_local_socket (GabbleFileTransferChannel *self,
2082 TpSocketAddressType address_type,
2083 TpSocketAccessControl access_control,
2084 const GValue *access_control_param)
2085 {
2086 GError *error = NULL;
2087
2088 self->priv->listener = gibber_listener_new ();
2089
2090 /* Add this stage the address_type and access_control have been checked and
2091 * are supposed to be valid */
2092 #ifdef GIBBER_TYPE_UNIX_TRANSPORT
2093 if (address_type == TP_SOCKET_ADDRESS_TYPE_UNIX)
2094 {
2095 gchar *path;
2096 GArray *array;
2097
2098 g_assert (access_control == TP_SOCKET_ACCESS_CONTROL_LOCALHOST);
2099
2100 path = get_local_unix_socket_path (self);
2101 if (path == NULL)
2102 return FALSE;
2103
2104 if (!gibber_listener_listen_socket (self->priv->listener, (gchar *) path,
2105 FALSE, &error))
2106 {
2107 DEBUG ("listen_socket failed: %s", error->message);
2108 g_error_free (error);
2109 tp_clear_object (&self->priv->listener);
2110 return FALSE;
2111 }
2112
2113 array = g_array_sized_new (TRUE, FALSE, sizeof (gchar), strlen (path));
2114 g_array_insert_vals (array, 0, path, strlen (path));
2115
2116 self->priv->socket_address = tp_g_value_slice_new (
2117 DBUS_TYPE_G_UCHAR_ARRAY);
2118 g_value_set_boxed (self->priv->socket_address, array);
2119
2120 DEBUG ("local socket %s", path);
2121 g_free (path);
2122 g_array_unref (array);
2123 }
2124 else
2125 #endif
2126 if (address_type == TP_SOCKET_ADDRESS_TYPE_IPV4)
2127 {
2128 int ret;
2129
2130 g_assert (access_control == TP_SOCKET_ACCESS_CONTROL_LOCALHOST);
2131
2132 ret = gibber_listener_listen_tcp_loopback_af (self->priv->listener, 0,
2133 GIBBER_AF_IPV4, &error);
2134 if (!ret)
2135 {
2136 DEBUG ("Error listening on ipv4 socket: %s", error->message);
2137 g_error_free (error);
2138 return FALSE;
2139 }
2140
2141 self->priv->socket_address = tp_g_value_slice_new (
2142 TP_STRUCT_TYPE_SOCKET_ADDRESS_IPV4);
2143 g_value_take_boxed (self->priv->socket_address,
2144 dbus_g_type_specialized_construct (
2145 TP_STRUCT_TYPE_SOCKET_ADDRESS_IPV4));
2146
2147 dbus_g_type_struct_set (self->priv->socket_address,
2148 0, "127.0.0.1",
2149 1, gibber_listener_get_port (self->priv->listener),
2150 G_MAXUINT);
2151 }
2152 else if (address_type == TP_SOCKET_ADDRESS_TYPE_IPV6)
2153 {
2154 int ret;
2155
2156 g_assert (access_control == TP_SOCKET_ACCESS_CONTROL_LOCALHOST);
2157
2158 ret = gibber_listener_listen_tcp_loopback_af (self->priv->listener, 0,
2159 GIBBER_AF_IPV6, &error);
2160 if (!ret)
2161 {
2162 DEBUG ("Error listening on ipv6 socket: %s", error->message);
2163 g_error_free (error);
2164 return FALSE;
2165 }
2166
2167 self->priv->socket_address = tp_g_value_slice_new (
2168 TP_STRUCT_TYPE_SOCKET_ADDRESS_IPV6);
2169 g_value_take_boxed (self->priv->socket_address,
2170 dbus_g_type_specialized_construct (
2171 TP_STRUCT_TYPE_SOCKET_ADDRESS_IPV6));
2172
2173 dbus_g_type_struct_set (self->priv->socket_address,
2174 0, "::1",
2175 1, gibber_listener_get_port (self->priv->listener),
2176 G_MAXUINT);
2177 }
2178 else
2179 {
2180 g_assert_not_reached ();
2181 }
2182
2183 self->priv->socket_type = address_type;
2184
2185 gabble_signal_connect_weak (self->priv->listener, "new-connection",
2186 G_CALLBACK (new_connection_cb), G_OBJECT (self));
2187
2188 return TRUE;
2189 }
2190
2191 GabbleFileTransferChannel *
gabble_file_transfer_channel_new(GabbleConnection * conn,TpHandle handle,TpHandle initiator_handle,TpFileTransferState state,const gchar * content_type,const gchar * filename,guint64 size,TpFileHashType content_hash_type,const gchar * content_hash,const gchar * description,guint64 date,guint64 initial_offset,gboolean resume_supported,GabbleBytestreamIface * bytestream,GTalkFileCollection * gtalk_file_collection,const gchar * file_collection,const gchar * uri,const gchar * service_name,const GHashTable * metadata)2192 gabble_file_transfer_channel_new (GabbleConnection *conn,
2193 TpHandle handle,
2194 TpHandle initiator_handle,
2195 TpFileTransferState state,
2196 const gchar *content_type,
2197 const gchar *filename,
2198 guint64 size,
2199 TpFileHashType content_hash_type,
2200 const gchar *content_hash,
2201 const gchar *description,
2202 guint64 date,
2203 guint64 initial_offset,
2204 gboolean resume_supported,
2205 GabbleBytestreamIface *bytestream,
2206 #ifdef ENABLE_JINGLE_FILE_TRANSFER
2207 GTalkFileCollection *gtalk_file_collection,
2208 #else
2209 gpointer gtalk_file_collection_dummy,
2210 #endif
2211 const gchar *file_collection,
2212 const gchar *uri,
2213 const gchar *service_name,
2214 const GHashTable *metadata)
2215
2216 {
2217 #ifndef ENABLE_JINGLE_FILE_TRANSFER
2218 g_assert (gtalk_file_collection_dummy == NULL);
2219 #endif
2220
2221 return g_object_new (GABBLE_TYPE_FILE_TRANSFER_CHANNEL,
2222 "connection", conn,
2223 "handle", handle,
2224 "initiator-handle", initiator_handle,
2225 "requested", (initiator_handle != handle),
2226 "state", state,
2227 "content-type", content_type,
2228 "filename", filename,
2229 "size", size,
2230 "content-hash-type", content_hash_type,
2231 "content-hash", content_hash,
2232 "description", description,
2233 "date", date,
2234 "initial-offset", initial_offset,
2235 "resume-supported", resume_supported,
2236 "file-collection", file_collection,
2237 "bytestream", bytestream,
2238 #ifdef ENABLE_JINGLE_FILE_TRANSFER
2239 "gtalk-file-collection", gtalk_file_collection,
2240 #endif
2241 "uri", uri,
2242 "service-name", service_name,
2243 "metadata", metadata,
2244 NULL);
2245 }
2246