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