1 /*
2  * file-transfer-channel.c - Source for SalutFileTransferChannel
3  * Copyright (C) 2007 Marco Barisione <marco@barisione.org>
4  * Copyright (C) 2005, 2007, 2008 Collabora Ltd.
5  *   @author: Sjoerd Simons <sjoerd@luon.net>
6  *   @author: Jonny Lamb <jonny.lamb@collabora.co.uk>
7  *   @author: Guillaume Desmottes <guillaume.desmottes@collabora.co.uk>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22  */
23 
24 #include <glib/gstdio.h>
25 #include <dbus/dbus-glib.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 
31 #ifdef G_OS_WIN32
32 #include <windows.h>
33 #undef interface
34 #else
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <sys/un.h>
38 #include <gio/gunixsocketaddress.h>
39 #endif
40 
41 #define DEBUG_FLAG DEBUG_FT
42 #include "debug.h"
43 
44 #include "file-transfer-channel.h"
45 #include "signals-marshal.h"
46 
47 #include "connection.h"
48 #include "im-manager.h"
49 #include "contact.h"
50 #include "namespaces.h"
51 
52 #include <wocky/wocky.h>
53 #include <gibber/gibber-file-transfer.h>
54 #include <gibber/gibber-oob-file-transfer.h>
55 
56 #include <telepathy-glib/channel-iface.h>
57 #include <telepathy-glib/interfaces.h>
58 #include <telepathy-glib/dbus.h>
59 #include <telepathy-glib/svc-generic.h>
60 #include <telepathy-glib/gtypes.h>
61 #include <telepathy-glib/gnio-util.h>
62 
63 static void
64 file_transfer_iface_init (gpointer g_iface, gpointer iface_data);
65 
66 G_DEFINE_TYPE_WITH_CODE (SalutFileTransferChannel, salut_file_transfer_channel,
67     TP_TYPE_BASE_CHANNEL,
68     G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_FILE_TRANSFER,
69                            file_transfer_iface_init);
70     G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA,
71                            NULL);
72 );
73 
74 #define CHECK_STR_EMPTY(x) ((x) == NULL || (x)[0] == '\0')
75 
76 #define SALUT_UNDEFINED_FILE_SIZE G_MAXUINT64
77 
78 static const char *salut_file_transfer_channel_interfaces[] = { NULL };
79 
80 /* properties */
81 enum
82 {
83   PROP_STATE = 1,
84   PROP_CONTENT_TYPE,
85   PROP_FILENAME,
86   PROP_SIZE,
87   PROP_CONTENT_HASH_TYPE,
88   PROP_CONTENT_HASH,
89   PROP_DESCRIPTION,
90   PROP_DATE,
91   PROP_AVAILABLE_SOCKET_TYPES,
92   PROP_TRANSFERRED_BYTES,
93   PROP_INITIAL_OFFSET,
94   PROP_URI,
95 
96   /* Chan.I.FileTransfer.Metadata */
97   PROP_SERVICE_NAME,
98   PROP_METADATA,
99 
100   PROP_CONTACT,
101   PROP_CONNECTION,
102   LAST_PROPERTY
103 };
104 
105 /* private structure */
106 struct _SalutFileTransferChannelPrivate {
107   gboolean dispose_has_run;
108   SalutContact *contact;
109   GibberFileTransfer *ft;
110   GTimeVal last_transferred_bytes_emitted;
111   guint progress_timer;
112   GSocket *socket;
113   gboolean remote_accepted;
114   GIOChannel *channel;
115 
116   /* properties */
117   TpFileTransferState state;
118   gchar *content_type;
119   gchar *filename;
120   guint64 size;
121   TpFileHashType content_hash_type;
122   gchar *content_hash;
123   gchar *description;
124   GHashTable *available_socket_types;
125   guint64 transferred_bytes;
126   guint64 initial_offset;
127   guint64 date;
128   gchar *uri;
129   gchar *service_name;
130   GHashTable *metadata;
131 };
132 
133 static void salut_file_transfer_channel_set_state (
134     TpSvcChannelTypeFileTransfer *iface, TpFileTransferState state,
135     TpFileTransferStateChangeReason reason);
136 
137 static void
salut_file_transfer_channel_close(TpBaseChannel * base)138 salut_file_transfer_channel_close (TpBaseChannel *base)
139 {
140   SalutFileTransferChannel *self = SALUT_FILE_TRANSFER_CHANNEL (base);
141 
142   if (self->priv->state != TP_FILE_TRANSFER_STATE_COMPLETED &&
143       self->priv->state != TP_FILE_TRANSFER_STATE_CANCELLED)
144     {
145       gibber_file_transfer_cancel (self->priv->ft, 406);
146       salut_file_transfer_channel_set_state (
147           TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
148           TP_FILE_TRANSFER_STATE_CANCELLED,
149           TP_FILE_TRANSFER_STATE_CHANGE_REASON_LOCAL_STOPPED);
150     }
151 
152   tp_base_channel_destroyed (base);
153 }
154 
155 static void
salut_file_transfer_channel_init(SalutFileTransferChannel * obj)156 salut_file_transfer_channel_init (SalutFileTransferChannel *obj)
157 {
158   obj->priv = G_TYPE_INSTANCE_GET_PRIVATE (obj,
159       SALUT_TYPE_FILE_TRANSFER_CHANNEL, SalutFileTransferChannelPrivate);
160 
161   /* allocate any data required by the object here */
162   obj->priv->contact = NULL;
163 }
164 
165 static void
contact_lost_cb(SalutContact * contact,SalutFileTransferChannel * self)166 contact_lost_cb (SalutContact *contact,
167                  SalutFileTransferChannel *self)
168 {
169   g_assert (contact == self->priv->contact);
170 
171   if (self->priv->state != TP_FILE_TRANSFER_STATE_PENDING)
172     {
173       DEBUG ("%s was disconnected. Ignoring as there is still a chance to"
174          " be able to complete the transfer", contact->name);
175       return;
176     }
177 
178   DEBUG ("%s was disconnected. Cancel file tranfer.", contact->name);
179   salut_file_transfer_channel_set_state (
180       TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
181       TP_FILE_TRANSFER_STATE_CANCELLED,
182       TP_FILE_TRANSFER_STATE_CHANGE_REASON_REMOTE_STOPPED);
183 }
184 
185 static void
salut_file_transfer_channel_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)186 salut_file_transfer_channel_get_property (GObject *object,
187                                           guint property_id,
188                                           GValue *value,
189                                           GParamSpec *pspec)
190 {
191   SalutFileTransferChannel *self = SALUT_FILE_TRANSFER_CHANNEL (object);
192 
193   switch (property_id)
194     {
195       case PROP_CONTACT:
196         g_value_set_object (value, self->priv->contact);
197         break;
198       case PROP_STATE:
199         g_value_set_uint (value, self->priv->state);
200         break;
201       case PROP_CONTENT_TYPE:
202         g_value_set_string (value, self->priv->content_type);
203         break;
204       case PROP_FILENAME:
205         g_value_set_string (value, self->priv->filename);
206         break;
207       case PROP_SIZE:
208         g_value_set_uint64 (value, self->priv->size);
209         break;
210       case PROP_CONTENT_HASH_TYPE:
211         g_value_set_uint (value, self->priv->content_hash_type);
212         break;
213       case PROP_CONTENT_HASH:
214         g_value_set_string (value, self->priv->content_hash);
215         break;
216       case PROP_DESCRIPTION:
217         g_value_set_string (value, self->priv->description);
218         break;
219       case PROP_AVAILABLE_SOCKET_TYPES:
220         g_value_set_boxed (value, self->priv->available_socket_types);
221         break;
222       case PROP_TRANSFERRED_BYTES:
223         g_value_set_uint64 (value, self->priv->transferred_bytes);
224         break;
225       case PROP_INITIAL_OFFSET:
226         g_value_set_uint64 (value, self->priv->initial_offset);
227         break;
228       case PROP_DATE:
229         g_value_set_uint64 (value, self->priv->date);
230         break;
231       case PROP_URI:
232         g_value_set_string (value,
233             self->priv->uri != NULL ? self->priv->uri : "");
234         break;
235       case PROP_SERVICE_NAME:
236         g_value_set_string (value,
237             self->priv->service_name != NULL ? self->priv->service_name : "");
238         break;
239       case PROP_METADATA:
240         {
241           /* We're fine with priv->metadata being NULL but dbus-glib
242            * doesn't like iterating NULL as if it was a hash table. */
243           if (self->priv->metadata == NULL)
244             {
245               g_value_take_boxed (value,
246                   g_hash_table_new (g_str_hash, g_str_equal));
247             }
248           else
249             {
250               g_value_set_boxed (value, self->priv->metadata);
251             }
252         }
253         break;
254       default:
255         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
256         break;
257     }
258 }
259 
260 static void
salut_file_transfer_channel_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)261 salut_file_transfer_channel_set_property (GObject *object,
262                                           guint property_id,
263                                           const GValue *value,
264                                           GParamSpec *pspec)
265 {
266   SalutFileTransferChannel *self = SALUT_FILE_TRANSFER_CHANNEL (object);
267 
268   switch (property_id)
269     {
270       case PROP_CONTACT:
271         self->priv->contact = g_value_dup_object (value);
272         g_signal_connect (self->priv->contact, "lost",
273             G_CALLBACK (contact_lost_cb), self);
274         break;
275       case PROP_STATE:
276         salut_file_transfer_channel_set_state (
277             TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (object),
278             g_value_get_uint (value),
279             TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE);
280         break;
281       case PROP_CONTENT_TYPE:
282         g_free (self->priv->content_type);
283         self->priv->content_type = g_value_dup_string (value);
284         break;
285       case PROP_FILENAME:
286         g_free (self->priv->filename);
287         self->priv->filename = g_value_dup_string (value);
288         break;
289       case PROP_SIZE:
290         self->priv->size = g_value_get_uint64 (value);
291         break;
292       case PROP_CONTENT_HASH_TYPE:
293         self->priv->content_hash_type = g_value_get_uint (value);
294         break;
295       case PROP_CONTENT_HASH:
296         g_free (self->priv->content_hash);
297         self->priv->content_hash = g_value_dup_string (value);
298         break;
299       case PROP_DESCRIPTION:
300         g_free (self->priv->description);
301         self->priv->description = g_value_dup_string (value);
302         break;
303       case PROP_DATE:
304         self->priv->date = g_value_get_uint64 (value);
305         break;
306       case PROP_INITIAL_OFFSET:
307         self->priv->initial_offset = g_value_get_uint64 (value);
308         break;
309       case PROP_URI:
310         g_assert (self->priv->uri == NULL); /* construct only */
311         self->priv->uri = g_value_dup_string (value);
312         break;
313       case PROP_SERVICE_NAME:
314         g_assert (self->priv->service_name == NULL); /* construct only */
315         self->priv->service_name = g_value_dup_string (value);
316         break;
317       case PROP_METADATA:
318         self->priv->metadata = g_value_dup_boxed (value);
319         break;
320       default:
321         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
322         break;
323     }
324 }
325 
326 static void
free_array(GArray * array)327 free_array (GArray *array)
328 {
329   g_array_unref (array);
330 }
331 
332 static void
salut_file_transfer_channel_constructed(GObject * obj)333 salut_file_transfer_channel_constructed (GObject *obj)
334 {
335   SalutFileTransferChannel *self = SALUT_FILE_TRANSFER_CHANNEL (obj);
336   TpBaseChannel *base = TP_BASE_CHANNEL (obj);
337   TpBaseConnection *base_conn = tp_base_channel_get_connection (base);
338   TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (
339       base_conn, TP_HANDLE_TYPE_CONTACT);
340   SalutConnection *conn = SALUT_CONNECTION (base_conn);
341   GArray *unix_access;
342   GArray *ip_access;
343   TpSocketAccessControl access_control;
344 
345   /* Parent constructed chain */
346   void (*chain_up) (GObject *) =
347     ((GObjectClass *) salut_file_transfer_channel_parent_class)->constructed;
348 
349   if (chain_up != NULL)
350     chain_up (obj);
351 
352   /* ref our porter */
353   wocky_meta_porter_hold (WOCKY_META_PORTER (conn->porter),
354       WOCKY_CONTACT (self->priv->contact));
355 
356   /* Initialise the available socket types hash table */
357   self->priv->available_socket_types = g_hash_table_new_full (g_direct_hash,
358       g_direct_equal, NULL, (GDestroyNotify) free_array);
359 
360   /* Socket_Address_Type_Unix */
361   unix_access = g_array_sized_new (FALSE, FALSE, sizeof (TpSocketAccessControl),
362       1);
363   access_control = TP_SOCKET_ACCESS_CONTROL_LOCALHOST;
364   g_array_append_val (unix_access, access_control);
365   g_hash_table_insert (self->priv->available_socket_types,
366       GUINT_TO_POINTER (TP_SOCKET_ADDRESS_TYPE_UNIX), unix_access);
367 
368   /* Socket_Address_Type_IPv4 */
369   ip_access = g_array_sized_new (FALSE, FALSE, sizeof (TpSocketAccessControl),
370       1);
371   g_array_append_val (ip_access, access_control);
372   g_hash_table_insert (self->priv->available_socket_types,
373       GUINT_TO_POINTER (TP_SOCKET_ADDRESS_TYPE_IPV4), ip_access);
374 
375   g_hash_table_insert (self->priv->available_socket_types,
376       GUINT_TO_POINTER (TP_SOCKET_ADDRESS_TYPE_IPV6), ip_access);
377 
378   DEBUG ("New FT channel created: %s (contact: %s, initiator: %s, "
379       "file: \"%s\", size: %" G_GUINT64_FORMAT ")",
380       tp_base_channel_get_object_path (base),
381       tp_handle_inspect (contact_repo, tp_base_channel_get_target_handle (base)),
382       tp_handle_inspect (contact_repo, tp_base_channel_get_initiator (base)),
383       self->priv->filename, self->priv->size);
384 
385   if (!tp_base_channel_is_requested (base))
386     /* Incoming transfer, URI has to be set by the handler */
387     g_assert (self->priv->uri == NULL);
388 }
389 
390 static void
391 salut_file_transfer_channel_dispose (GObject *object);
392 static void
393 salut_file_transfer_channel_finalize (GObject *object);
394 
395 static gboolean
file_transfer_channel_properties_setter(GObject * object,GQuark interface,GQuark name,const GValue * value,gpointer setter_data,GError ** error)396 file_transfer_channel_properties_setter (GObject *object,
397     GQuark interface,
398     GQuark name,
399     const GValue *value,
400     gpointer setter_data,
401     GError **error)
402 {
403   SalutFileTransferChannel *self = (SalutFileTransferChannel *) object;
404 
405   g_return_val_if_fail (interface == TP_IFACE_QUARK_CHANNEL_TYPE_FILE_TRANSFER,
406       FALSE);
407 
408   /* There is only one property with write access. So TpDBusPropertiesMixin
409    * already checked this. */
410   g_assert (name == g_quark_from_static_string ("URI"));
411 
412   /* TpDBusPropertiesMixin already checked this */
413   g_assert (G_VALUE_HOLDS_STRING (value));
414 
415   if (self->priv->uri != NULL)
416     {
417       g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
418           "URI has already be set");
419       return FALSE;
420     }
421 
422   if (tp_base_channel_is_requested (TP_BASE_CHANNEL (self)))
423     {
424       g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
425           "Channel is not an incoming transfer");
426       return FALSE;
427     }
428 
429   if (self->priv->state != TP_FILE_TRANSFER_STATE_PENDING)
430     {
431       g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
432         "State is not pending; cannot set URI");
433       return FALSE;
434     }
435 
436   self->priv->uri = g_value_dup_string (value);
437 
438   tp_svc_channel_type_file_transfer_emit_uri_defined (self, self->priv->uri);
439 
440   return TRUE;
441 }
442 
443 static void
salut_file_transfer_channel_fill_immutable_properties(TpBaseChannel * chan,GHashTable * properties)444 salut_file_transfer_channel_fill_immutable_properties (TpBaseChannel *chan,
445     GHashTable *properties)
446 {
447   TpBaseChannelClass *cls = TP_BASE_CHANNEL_CLASS (
448       salut_file_transfer_channel_parent_class);
449 
450   cls->fill_immutable_properties (chan, properties);
451 
452   tp_dbus_properties_mixin_fill_properties_hash (
453       G_OBJECT (chan), properties,
454       TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "State",
455       TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "ContentType",
456       TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "Filename",
457       TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "Size",
458       TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "ContentHashType",
459       TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "ContentHash",
460       TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "Description",
461       TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "Date",
462       TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "AvailableSocketTypes",
463       TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "TransferredBytes",
464       TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "InitialOffset",
465       TP_IFACE_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA, "ServiceName",
466       TP_IFACE_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA, "Metadata",
467       NULL);
468 
469   /* URI is immutable only for outgoing transfers */
470   if (tp_base_channel_is_requested (chan))
471     {
472       tp_dbus_properties_mixin_fill_properties_hash (
473           G_OBJECT (chan), properties,
474           TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "URI", NULL);
475     }
476 }
477 
478 static gchar *
salut_file_transfer_channel_get_object_path_suffix(TpBaseChannel * chan)479 salut_file_transfer_channel_get_object_path_suffix (TpBaseChannel *chan)
480 {
481   return g_strdup_printf ("FileTransferChannel/%p", chan);
482 }
483 
484 static void
salut_file_transfer_channel_class_init(SalutFileTransferChannelClass * salut_file_transfer_channel_class)485 salut_file_transfer_channel_class_init (
486     SalutFileTransferChannelClass *salut_file_transfer_channel_class)
487 {
488   GObjectClass *object_class = G_OBJECT_CLASS (
489       salut_file_transfer_channel_class);
490   TpBaseChannelClass *base_class = TP_BASE_CHANNEL_CLASS (
491       salut_file_transfer_channel_class);
492   GParamSpec *param_spec;
493 
494   static TpDBusPropertiesMixinPropImpl file_props[] = {
495     { "State", "state", NULL },
496     { "ContentType", "content-type", "content-type" },
497     { "Filename", "filename", "filename" },
498     { "Size", "size", "size" },
499     { "ContentHashType", "content-hash-type", "content-hash-type" },
500     { "ContentHash", "content-hash", "content-hash" },
501     { "Description", "description", "description" },
502     { "AvailableSocketTypes", "available-socket-types", NULL },
503     { "TransferredBytes", "transferred-bytes", NULL },
504     { "InitialOffset", "initial-offset", NULL },
505     { "Date", "date", "date" },
506     { "URI", "uri", NULL },
507     { NULL }
508   };
509 
510   static TpDBusPropertiesMixinPropImpl file_metadata_props[] = {
511     { "ServiceName", "service-name", NULL },
512     { "Metadata", "metadata", NULL },
513     { NULL }
514   };
515 
516   static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = {
517     { TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER,
518       tp_dbus_properties_mixin_getter_gobject_properties,
519       file_transfer_channel_properties_setter,
520       file_props
521     },
522     { TP_IFACE_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA,
523       tp_dbus_properties_mixin_getter_gobject_properties,
524       NULL,
525       file_metadata_props
526     },    { NULL }
527   };
528 
529   g_type_class_add_private (salut_file_transfer_channel_class,
530       sizeof (SalutFileTransferChannelPrivate));
531 
532   object_class->dispose = salut_file_transfer_channel_dispose;
533   object_class->finalize = salut_file_transfer_channel_finalize;
534   object_class->constructed = salut_file_transfer_channel_constructed;
535   object_class->get_property = salut_file_transfer_channel_get_property;
536   object_class->set_property = salut_file_transfer_channel_set_property;
537 
538   base_class->channel_type = TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER;
539   base_class->interfaces = salut_file_transfer_channel_interfaces;
540   base_class->target_handle_type = TP_HANDLE_TYPE_CONTACT;
541   base_class->close = salut_file_transfer_channel_close;
542   base_class->fill_immutable_properties =
543     salut_file_transfer_channel_fill_immutable_properties;
544   base_class->get_object_path_suffix =
545     salut_file_transfer_channel_get_object_path_suffix;
546 
547   param_spec = g_param_spec_object ("contact",
548       "SalutContact object",
549       "Salut Contact to which this channel is dedicated",
550       SALUT_TYPE_CONTACT,
551       G_PARAM_CONSTRUCT_ONLY |
552       G_PARAM_READWRITE |
553       G_PARAM_STATIC_NICK |
554       G_PARAM_STATIC_BLURB);
555   g_object_class_install_property (object_class, PROP_CONTACT, param_spec);
556 
557   param_spec = g_param_spec_uint (
558       "state",
559       "TpFileTransferState state",
560       "State of the file transfer in this channel",
561       0,
562       G_MAXUINT,
563       0,
564       G_PARAM_CONSTRUCT_ONLY |
565       G_PARAM_READWRITE |
566       G_PARAM_STATIC_NICK |
567       G_PARAM_STATIC_BLURB);
568   g_object_class_install_property (object_class, PROP_STATE, param_spec);
569 
570   param_spec = g_param_spec_string (
571       "content-type",
572       "gchar *content-type",
573       "ContentType of the file",
574       "application/octet-stream",
575       G_PARAM_CONSTRUCT_ONLY |
576       G_PARAM_READWRITE |
577       G_PARAM_STATIC_NICK |
578       G_PARAM_STATIC_BLURB);
579   g_object_class_install_property (object_class, PROP_CONTENT_TYPE,
580       param_spec);
581 
582   param_spec = g_param_spec_string (
583       "filename",
584       "gchar *filename",
585       "Name of the file",
586       "",
587       G_PARAM_CONSTRUCT_ONLY |
588       G_PARAM_READWRITE |
589       G_PARAM_STATIC_NICK |
590       G_PARAM_STATIC_BLURB);
591   g_object_class_install_property (object_class, PROP_FILENAME, param_spec);
592 
593   param_spec = g_param_spec_uint64 (
594       "size",
595       "guint size",
596       "Size of the file in bytes",
597       0,
598       G_MAXUINT64,
599       SALUT_UNDEFINED_FILE_SIZE,
600       G_PARAM_CONSTRUCT_ONLY |
601       G_PARAM_READWRITE |
602       G_PARAM_STATIC_NICK |
603       G_PARAM_STATIC_BLURB);
604   g_object_class_install_property (object_class, PROP_SIZE, param_spec);
605 
606   param_spec = g_param_spec_uint (
607       "content-hash-type",
608       "SalutFileHashType content-hash-type",
609       "Hash type",
610       0,
611       G_MAXUINT,
612       TP_FILE_HASH_TYPE_NONE,
613       G_PARAM_READWRITE |
614       G_PARAM_CONSTRUCT_ONLY |
615       G_PARAM_STATIC_NICK |
616       G_PARAM_STATIC_BLURB);
617   g_object_class_install_property (object_class, PROP_CONTENT_HASH_TYPE,
618       param_spec);
619 
620   param_spec = g_param_spec_string (
621       "content-hash",
622       "gchar *content-hash",
623       "Hash of the file contents",
624       "",
625       G_PARAM_CONSTRUCT_ONLY |
626       G_PARAM_READWRITE |
627       G_PARAM_STATIC_NICK |
628       G_PARAM_STATIC_BLURB);
629   g_object_class_install_property (object_class, PROP_CONTENT_HASH,
630       param_spec);
631 
632   param_spec = g_param_spec_string (
633       "description",
634       "gchar *description",
635       "Description of the file",
636       "",
637       G_PARAM_CONSTRUCT_ONLY |
638       G_PARAM_READWRITE |
639       G_PARAM_STATIC_NICK |
640       G_PARAM_STATIC_BLURB);
641   g_object_class_install_property (object_class, PROP_DESCRIPTION, param_spec);
642 
643   param_spec = g_param_spec_boxed (
644       "available-socket-types",
645       "SalutSupportedSocketMap available-socket-types",
646       "Available socket types",
647       dbus_g_type_get_map ("GHashTable", G_TYPE_UINT, DBUS_TYPE_G_UINT_ARRAY),
648       G_PARAM_READABLE |
649       G_PARAM_STATIC_NICK |
650       G_PARAM_STATIC_BLURB);
651   g_object_class_install_property (object_class, PROP_AVAILABLE_SOCKET_TYPES,
652       param_spec);
653 
654   param_spec = g_param_spec_uint64 (
655       "transferred-bytes",
656       "guint64 transferred-bytes",
657       "Bytes transferred",
658       0,
659       G_MAXUINT64,
660       0,
661       G_PARAM_READABLE |
662       G_PARAM_STATIC_NICK |
663       G_PARAM_STATIC_BLURB);
664   g_object_class_install_property (object_class, PROP_TRANSFERRED_BYTES,
665       param_spec);
666 
667   param_spec = g_param_spec_uint64 (
668       "initial-offset",
669       "guint64 initial_offset",
670       "Offset set at the beginning of the transfer",
671       0,
672       G_MAXUINT64,
673       0,
674       G_PARAM_READWRITE |
675       G_PARAM_STATIC_NICK |
676       G_PARAM_STATIC_BLURB);
677   g_object_class_install_property (object_class, PROP_INITIAL_OFFSET,
678       param_spec);
679 
680   param_spec = g_param_spec_uint64 (
681       "date",
682       "Epoch time",
683       "the last modification time of the file being transferred",
684       0,
685       G_MAXUINT64,
686       0,
687       G_PARAM_CONSTRUCT_ONLY |
688       G_PARAM_READWRITE |
689       G_PARAM_STATIC_NICK |
690       G_PARAM_STATIC_BLURB);
691   g_object_class_install_property (object_class, PROP_DATE,
692       param_spec);
693 
694   param_spec = g_param_spec_string (
695       "uri", "URI",
696       "URI of the file being transferred",
697       NULL,
698       G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
699   g_object_class_install_property (object_class, PROP_URI,
700       param_spec);
701 
702   param_spec = g_param_spec_string ("service-name",
703       "ServiceName",
704       "The Metadata.ServiceName property of this channel",
705       "",
706       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
707   g_object_class_install_property (object_class, PROP_SERVICE_NAME,
708       param_spec);
709 
710   param_spec = g_param_spec_boxed ("metadata",
711       "Metadata",
712       "The Metadata.Metadata property of this channel",
713       TP_HASH_TYPE_METADATA,
714       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
715   g_object_class_install_property (object_class, PROP_METADATA,
716       param_spec);
717 
718   salut_file_transfer_channel_class->dbus_props_class.interfaces = \
719       prop_interfaces;
720   tp_dbus_properties_mixin_class_init (object_class,
721       G_STRUCT_OFFSET (SalutFileTransferChannelClass, dbus_props_class));
722 }
723 
724 void
salut_file_transfer_channel_dispose(GObject * object)725 salut_file_transfer_channel_dispose (GObject *object)
726 {
727   SalutFileTransferChannel *self = SALUT_FILE_TRANSFER_CHANNEL (object);
728 
729   if (self->priv->dispose_has_run)
730     return;
731 
732   self->priv->dispose_has_run = TRUE;
733 
734   if (self->priv->progress_timer != 0)
735     {
736       g_source_remove (self->priv->progress_timer);
737       self->priv->progress_timer = 0;
738     }
739 
740   if (self->priv->contact)
741     {
742       g_signal_handlers_disconnect_by_func (self->priv->contact,
743           contact_lost_cb, self);
744       g_object_unref (self->priv->contact);
745       self->priv->contact = NULL;
746     }
747 
748   if (self->priv->ft != NULL)
749     {
750       g_object_unref (self->priv->ft);
751       self->priv->ft = NULL;
752     }
753 
754   if (self->priv->channel != NULL)
755     {
756       g_io_channel_unref (self->priv->channel);
757       self->priv->channel = NULL;
758     }
759 
760   /* release any references held by the object here */
761 
762   if (G_OBJECT_CLASS (salut_file_transfer_channel_parent_class)->dispose)
763     G_OBJECT_CLASS (salut_file_transfer_channel_parent_class)->dispose (object);
764 }
765 
766 static void
salut_file_transfer_channel_finalize(GObject * object)767 salut_file_transfer_channel_finalize (GObject *object)
768 {
769 #ifdef G_OS_UNIX
770   GSocketAddress *addr;
771 #endif
772   SalutFileTransferChannel *self = SALUT_FILE_TRANSFER_CHANNEL (object);
773 
774   /* free any data held directly by the object here */
775   g_free (self->priv->filename);
776 #ifdef G_OS_UNIX
777   if (self->priv->socket != NULL)
778     {
779       addr = g_socket_get_local_address (self->priv->socket, NULL);
780       if (g_socket_address_get_family (addr) == G_SOCKET_FAMILY_UNIX)
781         {
782           const gchar *path;
783           path = g_unix_socket_address_get_path ((GUnixSocketAddress *) addr);
784           g_unlink (path);
785         }
786       g_object_unref (addr);
787       g_object_unref (self->priv->socket);
788     }
789 #endif
790   g_free (self->priv->content_type);
791   g_free (self->priv->content_hash);
792   g_free (self->priv->description);
793   g_hash_table_unref (self->priv->available_socket_types);
794   g_free (self->priv->uri);
795   g_free (self->priv->service_name);
796   if (self->priv->metadata != NULL)
797     g_hash_table_unref (self->priv->metadata);
798 
799   if (G_OBJECT_CLASS (salut_file_transfer_channel_parent_class)->finalize)
800     G_OBJECT_CLASS (salut_file_transfer_channel_parent_class)->finalize (object);
801 }
802 
803 static void
error_cb(GibberFileTransfer * ft,guint domain,gint code,const gchar * message,SalutFileTransferChannel * self)804 error_cb (GibberFileTransfer *ft,
805           guint domain,
806           gint code,
807           const gchar *message,
808           SalutFileTransferChannel *self)
809 {
810   gboolean receiver = !tp_base_channel_is_requested (TP_BASE_CHANNEL (self));
811 
812   if (domain == GIBBER_FILE_TRANSFER_ERROR && code ==
813       GIBBER_FILE_TRANSFER_ERROR_NOT_FOUND && receiver)
814     {
815       /* Inform the sender we weren't able to retrieve the file */
816       gibber_file_transfer_cancel (self->priv->ft, 404);
817     }
818 
819   salut_file_transfer_channel_set_state (
820       TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
821       TP_FILE_TRANSFER_STATE_CANCELLED,
822       receiver ?
823       TP_FILE_TRANSFER_STATE_CHANGE_REASON_LOCAL_ERROR :
824       TP_FILE_TRANSFER_STATE_CHANGE_REASON_REMOTE_ERROR);
825 }
826 
827 static void
ft_finished_cb(GibberFileTransfer * ft,SalutFileTransferChannel * self)828 ft_finished_cb (GibberFileTransfer *ft,
829                 SalutFileTransferChannel *self)
830 {
831   SalutConnection *conn = SALUT_CONNECTION (
832       tp_base_channel_get_connection (TP_BASE_CHANNEL (self)));
833   WockyPorter *porter = conn->porter;
834 
835   salut_file_transfer_channel_set_state (
836       TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
837       TP_FILE_TRANSFER_STATE_COMPLETED,
838       TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE);
839 
840   wocky_meta_porter_unhold (WOCKY_META_PORTER (porter),
841       WOCKY_CONTACT (self->priv->contact));
842 }
843 
844 static void
ft_remote_cancelled_cb(GibberFileTransfer * ft,SalutFileTransferChannel * self)845 ft_remote_cancelled_cb (GibberFileTransfer *ft,
846                         SalutFileTransferChannel *self)
847 {
848   SalutConnection *conn = SALUT_CONNECTION (
849       tp_base_channel_get_connection (TP_BASE_CHANNEL (self)));
850   WockyPorter *porter = conn->porter;
851 
852   gibber_file_transfer_cancel (ft, 406);
853   salut_file_transfer_channel_set_state (
854       TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
855       TP_FILE_TRANSFER_STATE_CANCELLED,
856       TP_FILE_TRANSFER_STATE_CHANGE_REASON_REMOTE_STOPPED);
857 
858   wocky_meta_porter_unhold (WOCKY_META_PORTER (porter),
859       WOCKY_CONTACT (self->priv->contact));
860 }
861 
862 static void
remote_accepted_cb(GibberFileTransfer * ft,SalutFileTransferChannel * self)863 remote_accepted_cb (GibberFileTransfer *ft,
864                     SalutFileTransferChannel *self)
865 {
866   self->priv->remote_accepted = TRUE;
867 
868   /* if we've got the IO channel here then gibber_file_transfer_send
869    * hasn't been called so let's call it now before doing anything
870    * else. */
871   if (self->priv->channel != NULL)
872     {
873       gibber_file_transfer_send (ft, self->priv->channel);
874 
875       /* we have no need for this anymore */
876       g_io_channel_unref (self->priv->channel);
877       self->priv->channel = NULL;
878     }
879 
880   if (self->priv->socket != NULL)
881     {
882       /* ProvideFile has already been called. Channel is Open */
883       tp_svc_channel_type_file_transfer_emit_initial_offset_defined (self,
884           self->priv->initial_offset);
885 
886       salut_file_transfer_channel_set_state (
887           TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
888           TP_FILE_TRANSFER_STATE_OPEN,
889           TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE);
890     }
891   else
892     {
893       /* Client has to call ProvideFile to open the channel */
894       salut_file_transfer_channel_set_state (
895           TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
896           TP_FILE_TRANSFER_STATE_ACCEPTED,
897           TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE);
898     }
899 
900   g_signal_connect (ft, "finished", G_CALLBACK (ft_finished_cb), self);
901 }
902 
903 static gboolean setup_local_socket (SalutFileTransferChannel *self,
904     TpSocketAddressType address_type, guint access_control);
905 static void ft_transferred_chunk_cb (GibberFileTransfer *ft, guint64 count,
906     SalutFileTransferChannel *self);
907 
908 static GList *
add_metadata_forms(SalutFileTransferChannel * self,GibberFileTransfer * ft)909 add_metadata_forms (SalutFileTransferChannel *self,
910     GibberFileTransfer *ft)
911 {
912   GError *error = NULL;
913   GQueue queue = G_QUEUE_INIT;
914 
915   if (!tp_str_empty (self->priv->service_name))
916     {
917       WockyStanza *tmp = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ,
918           WOCKY_STANZA_SUB_TYPE_RESULT, NULL, NULL,
919           '(', "x",
920             ':', WOCKY_XMPP_NS_DATA,
921             '@', "type", "result",
922             '(', "field",
923               '@', "var", "FORM_TYPE",
924               '@', "type", "hidden",
925               '(', "value",
926                 '$', NS_TP_FT_METADATA_SERVICE,
927               ')',
928             ')',
929             '(', "field",
930               '@', "var", "ServiceName",
931               '(', "value",
932                 '$', self->priv->service_name,
933               ')',
934             ')',
935           ')',
936           NULL);
937       WockyNode *x = wocky_node_get_first_child (wocky_stanza_get_top_node (tmp));
938       WockyDataForm *form = wocky_data_form_new_from_node (x, &error);
939 
940       if (form == NULL)
941         {
942           DEBUG ("Failed to parse form (wat): %s", error->message);
943           g_clear_error (&error);
944         }
945       else
946         {
947           g_queue_push_tail (&queue, form);
948         }
949 
950       g_object_unref (tmp);
951     }
952 
953   if (self->priv->metadata != NULL
954       && g_hash_table_size (self->priv->metadata) > 0)
955     {
956       WockyStanza *tmp = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ,
957           WOCKY_STANZA_SUB_TYPE_RESULT, NULL, NULL,
958           '(', "x",
959             ':', WOCKY_XMPP_NS_DATA,
960             '@', "type", "result",
961             '(', "field",
962               '@', "var", "FORM_TYPE",
963               '@', "type", "hidden",
964               '(', "value",
965                 '$', NS_TP_FT_METADATA,
966               ')',
967             ')',
968           ')',
969           NULL);
970       WockyNode *x = wocky_node_get_first_child (wocky_stanza_get_top_node (tmp));
971       WockyDataForm *form;
972       GHashTableIter iter;
973       gpointer key, val;
974 
975       g_hash_table_iter_init (&iter, self->priv->metadata);
976       while (g_hash_table_iter_next (&iter, &key, &val))
977         {
978           const gchar * const *values = val;
979 
980           WockyNode *field = wocky_node_add_child (x, "field");
981           wocky_node_set_attribute (field, "var", key);
982 
983           for (; values != NULL && *values != NULL; values++)
984             {
985               wocky_node_add_child_with_content (field, "value", *values);
986             }
987         }
988 
989       form = wocky_data_form_new_from_node (x, &error);
990 
991       if (form == NULL)
992         {
993           DEBUG ("Failed to parse form (wat): %s", error->message);
994           g_clear_error (&error);
995         }
996       else
997         {
998           g_queue_push_tail (&queue, form);
999         }
1000 
1001       g_object_unref (tmp);
1002     }
1003 
1004   return queue.head;
1005 }
1006 
1007 static void
send_file_offer(SalutFileTransferChannel * self)1008 send_file_offer (SalutFileTransferChannel *self)
1009 {
1010   GibberFileTransfer *ft;
1011   SalutConnection *conn = SALUT_CONNECTION (
1012       tp_base_channel_get_connection (TP_BASE_CHANNEL (self)));
1013   WockyPorter *porter = conn->porter;
1014 
1015   ft = g_object_new (GIBBER_TYPE_OOB_FILE_TRANSFER,
1016       "self-id", conn->name,
1017       "peer-id", self->priv->contact->name,
1018       "filename", self->priv->filename,
1019       "porter", porter,
1020       "contact", self->priv->contact,
1021       "description", self->priv->description,
1022       "content-type", self->priv->content_type,
1023       NULL);
1024 
1025   g_signal_connect (ft, "remote-accepted",
1026       G_CALLBACK (remote_accepted_cb), self);
1027   g_signal_connect (ft, "error", G_CALLBACK (error_cb), self);
1028   g_signal_connect (ft, "cancelled", G_CALLBACK (ft_remote_cancelled_cb), self);
1029 
1030   self->priv->ft = ft;
1031 
1032   g_signal_connect (ft, "transferred-chunk",
1033       G_CALLBACK (ft_transferred_chunk_cb), self);
1034 
1035   gibber_file_transfer_set_size (ft, self->priv->size);
1036 
1037   g_assert (ft->dataforms == NULL);
1038   ft->dataforms = add_metadata_forms (self, ft);
1039 
1040   gibber_file_transfer_offer (ft);
1041 }
1042 
1043 static void
salut_file_transfer_channel_set_state(TpSvcChannelTypeFileTransfer * iface,TpFileTransferState state,TpFileTransferStateChangeReason reason)1044 salut_file_transfer_channel_set_state (
1045     TpSvcChannelTypeFileTransfer *iface,
1046     TpFileTransferState state,
1047     TpFileTransferStateChangeReason reason)
1048 {
1049   SalutFileTransferChannel *self = SALUT_FILE_TRANSFER_CHANNEL (iface);
1050 
1051   if (self->priv->state == state)
1052     return;
1053 
1054   self->priv->state = state;
1055   tp_svc_channel_type_file_transfer_emit_file_transfer_state_changed (iface,
1056       state, reason);
1057 }
1058 
1059 static void
emit_progress_update(SalutFileTransferChannel * self)1060 emit_progress_update (SalutFileTransferChannel *self)
1061 {
1062   TpSvcChannelTypeFileTransfer *iface = \
1063       TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self);
1064 
1065   g_get_current_time (&self->priv->last_transferred_bytes_emitted);
1066 
1067   tp_svc_channel_type_file_transfer_emit_transferred_bytes_changed (
1068     iface, self->priv->transferred_bytes);
1069 
1070   if (self->priv->progress_timer != 0)
1071     {
1072       g_source_remove (self->priv->progress_timer);
1073       self->priv->progress_timer = 0;
1074     }
1075 }
1076 
1077 static gboolean
emit_progress_update_cb(gpointer user_data)1078 emit_progress_update_cb (gpointer user_data)
1079 {
1080   SalutFileTransferChannel *self = \
1081       SALUT_FILE_TRANSFER_CHANNEL (user_data);
1082 
1083   emit_progress_update (self);
1084 
1085   return FALSE;
1086 }
1087 
1088 static void
ft_transferred_chunk_cb(GibberFileTransfer * ft,guint64 count,SalutFileTransferChannel * self)1089 ft_transferred_chunk_cb (GibberFileTransfer *ft,
1090                          guint64 count,
1091                          SalutFileTransferChannel *self)
1092 {
1093   GTimeVal timeval;
1094   gint interval;
1095 
1096   self->priv->transferred_bytes += count;
1097 
1098   if (self->priv->transferred_bytes >= self->priv->size)
1099     {
1100       /* If the transfer has finished send an update right away */
1101       emit_progress_update (self);
1102       return;
1103     }
1104 
1105   if (self->priv->progress_timer != 0)
1106     {
1107       /* A progress update signal is already scheduled */
1108       return;
1109     }
1110 
1111   /* Only emit the TransferredBytes signal if it has been one second since its
1112    * last emission.
1113    */
1114   g_get_current_time (&timeval);
1115   interval = timeval.tv_sec -
1116     self->priv->last_transferred_bytes_emitted.tv_sec;
1117 
1118   if (interval > 1)
1119     {
1120       /* At least more then a second apart, emit right away */
1121       emit_progress_update (self);
1122       return;
1123     }
1124 
1125   /* Convert interval to milliseconds and calculate it more precisely */
1126   interval *= 1000;
1127 
1128   interval += (timeval.tv_usec -
1129     self->priv->last_transferred_bytes_emitted.tv_usec)/1000;
1130 
1131   /* Protect against clock skew, if the interval is negative the worst thing
1132    * that can happen is that we wait an extra second before emitting the signal
1133    */
1134   interval = ABS(interval);
1135 
1136   if (interval > 1000)
1137     emit_progress_update (self);
1138   else
1139     self->priv->progress_timer = g_timeout_add (1000 - interval,
1140        emit_progress_update_cb, self);
1141 }
1142 
1143 static gboolean
check_address_and_access_control(SalutFileTransferChannel * self,TpSocketAddressType address_type,TpSocketAccessControl access_control,const GValue * access_control_param,GError ** error)1144 check_address_and_access_control (SalutFileTransferChannel *self,
1145                                   TpSocketAddressType address_type,
1146                                   TpSocketAccessControl access_control,
1147                                   const GValue *access_control_param,
1148                                   GError **error)
1149 {
1150   GArray *access_arr;
1151   guint i;
1152 
1153   /* Do we support this AddressType? */
1154   access_arr = g_hash_table_lookup (self->priv->available_socket_types,
1155       GUINT_TO_POINTER (address_type));
1156   if (access_arr == NULL)
1157     {
1158       g_set_error (error, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED,
1159           "AddressType %u is not implemented", address_type);
1160       return FALSE;
1161     }
1162 
1163   /* Do we support this AccessControl? */
1164   for (i = 0; i < access_arr->len; i++)
1165     {
1166       TpSocketAccessControl control;
1167 
1168       control = g_array_index (access_arr, TpSocketAccessControl, i);
1169       if (control == access_control)
1170         return TRUE;
1171     }
1172 
1173   g_set_error (error, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED,
1174       "AccesControl %u is not implemented with AddressType %u",
1175       access_control, address_type);
1176 
1177   return FALSE;
1178 }
1179 
1180 gboolean
salut_file_transfer_channel_offer_file(SalutFileTransferChannel * self,GError ** error)1181 salut_file_transfer_channel_offer_file (SalutFileTransferChannel *self,
1182                                         GError **error)
1183 {
1184   g_assert (!CHECK_STR_EMPTY (self->priv->filename));
1185   g_assert (self->priv->size != SALUT_UNDEFINED_FILE_SIZE);
1186 
1187   DEBUG ("Offering file transfer");
1188 
1189   send_file_offer (self);
1190 
1191   return TRUE;
1192 }
1193 
1194 /**
1195  * salut_file_transfer_channel_accept_file
1196  *
1197  * Implements D-Bus method AcceptFile
1198  * on interface org.freedesktop.Telepathy.Channel.Type.FileTransfer
1199  */
1200 static void
salut_file_transfer_channel_accept_file(TpSvcChannelTypeFileTransfer * iface,TpSocketAddressType address_type,guint access_control,const GValue * access_control_param,guint64 offset,DBusGMethodInvocation * context)1201 salut_file_transfer_channel_accept_file (TpSvcChannelTypeFileTransfer *iface,
1202                                          TpSocketAddressType address_type,
1203                                          guint access_control,
1204                                          const GValue *access_control_param,
1205                                          guint64 offset,
1206                                          DBusGMethodInvocation *context)
1207 {
1208   SalutFileTransferChannel *self = SALUT_FILE_TRANSFER_CHANNEL (iface);
1209   GError *error = NULL;
1210   GibberFileTransfer *ft;
1211   GValue *addr;
1212   GSocketAddress *socket_addr;
1213 
1214   ft = self->priv->ft;
1215   if (ft == NULL)
1216     {
1217       dbus_g_method_return_error (context, error);
1218       g_error_free (error);
1219     }
1220 
1221   if (self->priv->state != TP_FILE_TRANSFER_STATE_PENDING)
1222     {
1223       g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
1224         "State is not pending; cannot accept file");
1225       dbus_g_method_return_error (context, error);
1226       return;
1227     }
1228 
1229   if (!check_address_and_access_control (self, address_type, access_control,
1230         access_control_param, &error))
1231     {
1232       dbus_g_method_return_error (context, error);
1233       g_error_free (error);
1234       return;
1235     }
1236 
1237   g_signal_connect (ft, "finished", G_CALLBACK (ft_finished_cb), self);
1238   g_signal_connect (ft, "transferred-chunk",
1239       G_CALLBACK (ft_transferred_chunk_cb), self);
1240   g_signal_connect (ft, "cancelled", G_CALLBACK (ft_remote_cancelled_cb), self);
1241 
1242   if (!setup_local_socket (self, address_type, access_control))
1243     {
1244       DEBUG ("Could not set up local socket");
1245       g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
1246           "Could not set up local socket");
1247       dbus_g_method_return_error (context, error);
1248     }
1249 
1250   salut_file_transfer_channel_set_state (iface,
1251       TP_FILE_TRANSFER_STATE_ACCEPTED,
1252       TP_FILE_TRANSFER_STATE_CHANGE_REASON_REQUESTED);
1253 
1254   socket_addr = g_socket_get_local_address (self->priv->socket, NULL);
1255   addr = tp_address_variant_from_g_socket_address (socket_addr, NULL, NULL);
1256   tp_svc_channel_type_file_transfer_return_from_accept_file (context,
1257       addr);
1258   tp_g_value_slice_free (addr);
1259   g_object_unref (socket_addr);
1260 
1261   self->priv->initial_offset = 0;
1262 
1263   tp_svc_channel_type_file_transfer_emit_initial_offset_defined (self,
1264       self->priv->initial_offset);
1265 
1266   salut_file_transfer_channel_set_state (iface, TP_FILE_TRANSFER_STATE_OPEN,
1267       TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE);
1268 }
1269 
1270 /**
1271  * salut_file_transfer_channel_provide_file
1272  *
1273  * Implements D-Bus method ProvideFile
1274  * on interface org.freedesktop.Telepathy.Channel.Type.FileTransfer
1275  */
1276 static void
salut_file_transfer_channel_provide_file(TpSvcChannelTypeFileTransfer * iface,guint address_type,guint access_control,const GValue * access_control_param,DBusGMethodInvocation * context)1277 salut_file_transfer_channel_provide_file (
1278     TpSvcChannelTypeFileTransfer *iface,
1279     guint address_type,
1280     guint access_control,
1281     const GValue *access_control_param,
1282     DBusGMethodInvocation *context)
1283 {
1284   SalutFileTransferChannel *self = SALUT_FILE_TRANSFER_CHANNEL (iface);
1285   TpBaseChannel *base = TP_BASE_CHANNEL (self);
1286   GError *error = NULL;
1287   GValue *addr;
1288   GSocketAddress *socket_addr;
1289 
1290   if (!tp_base_channel_is_requested (base))
1291     {
1292       g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
1293           "Channel is not an outgoing transfer");
1294       dbus_g_method_return_error (context, error);
1295       return;
1296     }
1297 
1298   if (self->priv->socket != NULL)
1299     {
1300       g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
1301           "ProvideFile has already been called for this channel");
1302       dbus_g_method_return_error (context, error);
1303       return;
1304     }
1305 
1306   if (!check_address_and_access_control (self, address_type, access_control,
1307         access_control_param, &error))
1308     {
1309       dbus_g_method_return_error (context, error);
1310       g_error_free (error);
1311       return;
1312     }
1313 
1314   if (!setup_local_socket (self, address_type, access_control))
1315     {
1316       DEBUG ("Could not set up local socket");
1317       g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
1318           "Could not set up local socket");
1319       dbus_g_method_return_error (context, error);
1320     }
1321 
1322   if (self->priv->remote_accepted)
1323     {
1324       /* Remote already accepted the file. Channel is Open.
1325        * If not channel stay Pending. */
1326       tp_svc_channel_type_file_transfer_emit_initial_offset_defined (self,
1327           self->priv->initial_offset);
1328 
1329       salut_file_transfer_channel_set_state (iface,
1330           TP_FILE_TRANSFER_STATE_OPEN,
1331           TP_FILE_TRANSFER_STATE_CHANGE_REASON_REQUESTED);
1332     }
1333 
1334   socket_addr = g_socket_get_local_address (self->priv->socket, &error);
1335   addr = tp_address_variant_from_g_socket_address (socket_addr, NULL, NULL);
1336   tp_svc_channel_type_file_transfer_return_from_provide_file (context,
1337       addr);
1338   tp_g_value_slice_free (addr);
1339   g_object_unref (socket_addr);
1340 }
1341 
1342 static void
file_transfer_iface_init(gpointer g_iface,gpointer iface_data)1343 file_transfer_iface_init (gpointer g_iface,
1344                           gpointer iface_data)
1345 {
1346   TpSvcChannelTypeFileTransferClass *klass =
1347       (TpSvcChannelTypeFileTransferClass *) g_iface;
1348 
1349 #define IMPLEMENT(x) tp_svc_channel_type_file_transfer_implement_##x (\
1350     klass, salut_file_transfer_channel_##x)
1351   IMPLEMENT (accept_file);
1352   IMPLEMENT (provide_file);
1353 #undef IMPLEMENT
1354 }
1355 
1356 #ifdef G_OS_UNIX
1357 static GSocketAddress *
get_local_unix_socket_address(SalutFileTransferChannel * self)1358 get_local_unix_socket_address (SalutFileTransferChannel *self)
1359 {
1360   GSocketAddress *addr = NULL;
1361   gchar *path = NULL;
1362   gint32 random_int;
1363   gchar *random_str;
1364   struct stat buf;
1365 
1366   while (TRUE)
1367     {
1368       random_int = g_random_int_range (0, G_MAXINT32);
1369       random_str = g_strdup_printf ("tp-ft-%i", random_int);
1370       path = g_build_filename (g_get_tmp_dir (), random_str, NULL);
1371       g_free (random_str);
1372 
1373       if (g_stat (path, &buf) != 0)
1374         break;
1375 
1376       g_free (path);
1377     }
1378 
1379   addr = g_unix_socket_address_new (path);
1380   g_free (path);
1381 
1382   return addr;
1383 }
1384 #endif
1385 
1386 static GSocketAddress *
get_local_tcp_socket_address(SalutFileTransferChannel * self,GSocketFamily family)1387 get_local_tcp_socket_address (SalutFileTransferChannel *self, GSocketFamily family)
1388 {
1389   GInetAddress *inet_address;
1390   GSocketAddress *addr;
1391   inet_address = g_inet_address_new_loopback (family);
1392   addr = g_inet_socket_address_new (inet_address, 0);
1393   g_object_unref (inet_address);
1394   return addr;
1395 }
1396 
1397 /*
1398  * Return a GIOChannel for a local socket
1399  */
1400 static GIOChannel *
get_socket_channel(SalutFileTransferChannel * self,TpSocketAddressType address_type,guint access_control)1401 get_socket_channel (SalutFileTransferChannel *self,
1402     TpSocketAddressType address_type, guint access_control)
1403 {
1404   GSocket *sock;
1405   GSocketAddress *addr;
1406   GIOChannel *io_channel = NULL;
1407   GError *error = NULL;
1408   int fd;
1409 
1410   switch (address_type)
1411     {
1412 #ifdef G_OS_UNIX
1413       case TP_SOCKET_ADDRESS_TYPE_UNIX:
1414         sock = g_socket_new (G_SOCKET_FAMILY_UNIX,
1415                              G_SOCKET_TYPE_STREAM,
1416                              G_SOCKET_PROTOCOL_DEFAULT,
1417                              &error);
1418         addr = get_local_unix_socket_address (self);
1419         break;
1420 #endif
1421       case TP_SOCKET_ADDRESS_TYPE_IPV4:
1422         sock = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_TCP, &error);
1423         addr = get_local_tcp_socket_address (self, G_SOCKET_FAMILY_IPV4);
1424         break;
1425       case TP_SOCKET_ADDRESS_TYPE_IPV6:
1426         sock = g_socket_new (G_SOCKET_FAMILY_IPV6, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_TCP, &error);
1427         addr = get_local_tcp_socket_address (self, G_SOCKET_FAMILY_IPV6);
1428         break;
1429       default:
1430         return NULL;
1431     }
1432 
1433   if (sock == NULL)
1434     {
1435       DEBUG ("Socket creation error: %s", error->message);
1436       g_error_free (error);
1437       return NULL;
1438     }
1439 
1440   if (!g_socket_bind (sock, addr, FALSE, &error))
1441     {
1442       DEBUG ("Bind error: %s", error->message);
1443       g_error_free (error);
1444       g_object_unref (addr);
1445       g_object_unref (sock);
1446       return NULL;
1447     }
1448   g_object_unref (addr);
1449 
1450   if (!g_socket_listen (sock, &error))
1451   {
1452     DEBUG ("Listen error: %s", error->message);
1453     g_error_free (error);
1454     g_object_unref (sock);
1455     return NULL;
1456   }
1457 
1458   self->priv->socket = sock;
1459 
1460   fd = g_socket_get_fd (sock);
1461   io_channel = g_io_channel_unix_new (fd);
1462   return io_channel;
1463 }
1464 
1465 /*
1466  * Some client is connecting to the Unix socket.
1467  */
1468 static gboolean
accept_local_socket_connection(GIOChannel * source,GIOCondition condition,gpointer user_data)1469 accept_local_socket_connection (GIOChannel *source,
1470                                 GIOCondition condition,
1471                                 gpointer user_data)
1472 {
1473   SalutFileTransferChannel *self = SALUT_FILE_TRANSFER_CHANNEL (user_data);
1474   GibberFileTransfer *ft;
1475   int new_fd;
1476   GIOChannel *channel;
1477 
1478   ft = self->priv->ft;
1479 
1480   g_assert (ft != NULL);
1481 
1482   if (condition & G_IO_IN)
1483     {
1484       DEBUG ("Client connected to local socket");
1485 
1486       new_fd = accept (g_io_channel_unix_get_fd (source),
1487           NULL, NULL);
1488       if (new_fd < 0)
1489         {
1490           DEBUG ("accept() failed");
1491           return FALSE;
1492         }
1493 #ifdef G_OS_WIN32
1494       channel = g_io_channel_win32_new_fd (new_fd);
1495 #else
1496       channel = g_io_channel_unix_new (new_fd);
1497 #endif
1498       g_io_channel_set_close_on_unref (channel, TRUE);
1499       g_io_channel_set_encoding (channel, NULL, NULL);
1500       if (ft->direction == GIBBER_FILE_TRANSFER_DIRECTION_INCOMING)
1501         {
1502           gibber_file_transfer_receive (ft, channel);
1503           g_io_channel_unref (channel);
1504         }
1505       else
1506         {
1507           /* gibber_file_transfer_send needs ::remote-accepted to have
1508            * already been fired, so let's wait for that, keeping
1509            * around the GIOChannel, if it hasn't already happened. */
1510           if (self->priv->remote_accepted)
1511             {
1512               gibber_file_transfer_send (ft, channel);
1513               g_io_channel_unref (channel);
1514             }
1515           else
1516             {
1517               self->priv->channel = channel;
1518             }
1519         }
1520     }
1521 
1522   return FALSE;
1523 }
1524 
1525 static gboolean
setup_local_socket(SalutFileTransferChannel * self,TpSocketAddressType address_type,guint access_control)1526 setup_local_socket (SalutFileTransferChannel *self,
1527     TpSocketAddressType address_type,
1528     guint access_control)
1529 {
1530   GIOChannel *io_channel;
1531 
1532   io_channel = get_socket_channel (self, address_type, access_control);
1533   if (io_channel == NULL)
1534     {
1535       return FALSE;
1536     }
1537 
1538   g_io_add_watch (io_channel, G_IO_IN | G_IO_HUP,
1539       accept_local_socket_connection, self);
1540   g_io_channel_unref (io_channel);
1541 
1542   return TRUE;
1543 }
1544 
1545 static WockyDataForm *
find_data_form(GibberFileTransfer * ft,const gchar * form_type)1546 find_data_form (GibberFileTransfer *ft,
1547     const gchar *form_type)
1548 {
1549   GList *l;
1550 
1551   for (l = ft->dataforms; l != NULL; l = l->next)
1552     {
1553       WockyDataForm *form = l->data;
1554       WockyDataFormField *field;
1555 
1556       field = g_hash_table_lookup (form->fields, "FORM_TYPE");
1557 
1558       if (field == NULL)
1559         {
1560           DEBUG ("Data form doesn't have FORM_TYPE field!");
1561           continue;
1562         }
1563 
1564       /* found it! */
1565       if (!tp_strdiff (field->raw_value_contents[0], form_type))
1566         return form;
1567     }
1568 
1569   return NULL;
1570 }
1571 
1572 static gchar *
extract_service_name(GibberFileTransfer * ft)1573 extract_service_name (GibberFileTransfer *ft)
1574 {
1575   WockyDataForm *form = find_data_form (ft, NS_TP_FT_METADATA_SERVICE);
1576   WockyDataFormField *field;
1577   gchar *service_name = NULL;
1578 
1579   if (form == NULL)
1580     return NULL;
1581 
1582   field = g_hash_table_lookup (form->fields, "ServiceName");
1583 
1584   if (field == NULL)
1585     {
1586       DEBUG ("ServiceName propery not present in data form; odd...");
1587       goto out;
1588     }
1589 
1590   if (field->raw_value_contents == NULL
1591       || field->raw_value_contents[0] == NULL)
1592     {
1593       DEBUG ("ServiceName property doesn't have a real value; odd...");
1594     }
1595   else
1596     {
1597       service_name = g_strdup (field->raw_value_contents[0]);
1598     }
1599 
1600 out:
1601   return service_name;
1602 }
1603 
1604 static GHashTable *
extract_metadata(GibberFileTransfer * ft)1605 extract_metadata (GibberFileTransfer *ft)
1606 {
1607   WockyDataForm *form = find_data_form (ft, NS_TP_FT_METADATA);
1608   GHashTable *metadata;
1609   GHashTableIter iter;
1610   gpointer key, value;
1611 
1612   if (form == NULL)
1613     return NULL;
1614 
1615   metadata = g_hash_table_new_full (g_str_hash, g_str_equal,
1616       g_free, (GDestroyNotify) g_strfreev);
1617 
1618   g_hash_table_iter_init (&iter, form->fields);
1619   while (g_hash_table_iter_next (&iter, &key, &value))
1620     {
1621       const gchar *var = key;
1622       WockyDataFormField *field = value;
1623 
1624       if (!tp_strdiff (var, "FORM_TYPE"))
1625         continue;
1626 
1627       g_hash_table_insert (metadata,
1628           g_strdup (var),
1629           g_strdupv (field->raw_value_contents));
1630     }
1631 
1632   return metadata;
1633 }
1634 
1635 SalutFileTransferChannel *
salut_file_transfer_channel_new(SalutConnection * conn,SalutContact * contact,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,const gchar * file_uri,const gchar * service_name,const GHashTable * metadata)1636 salut_file_transfer_channel_new (SalutConnection *conn,
1637                                  SalutContact *contact,
1638                                  TpHandle handle,
1639                                  TpHandle initiator_handle,
1640                                  TpFileTransferState state,
1641                                  const gchar *content_type,
1642                                  const gchar *filename,
1643                                  guint64 size,
1644                                  TpFileHashType content_hash_type,
1645                                  const gchar *content_hash,
1646                                  const gchar *description,
1647                                  guint64 date,
1648                                  guint64 initial_offset,
1649                                  const gchar *file_uri,
1650                                  const gchar *service_name,
1651                                  const GHashTable *metadata)
1652 {
1653   return g_object_new (SALUT_TYPE_FILE_TRANSFER_CHANNEL,
1654       "connection", conn,
1655       "contact", contact,
1656       "handle", handle,
1657       "initiator-handle", initiator_handle,
1658       "requested", TRUE,
1659       "state", state,
1660       "content-type", content_type,
1661       "filename", filename,
1662       "size", size,
1663       "content-hash-type", content_hash_type,
1664       "content-hash", content_hash,
1665       "description", description,
1666       "date", date,
1667       "initial-offset", initial_offset,
1668       "uri", file_uri,
1669       "service-name", service_name,
1670       "metadata", metadata,
1671       NULL);
1672 }
1673 
1674 SalutFileTransferChannel *
salut_file_transfer_channel_new_from_stanza(SalutConnection * connection,SalutContact * contact,TpHandle handle,TpFileTransferState state,WockyStanza * stanza)1675 salut_file_transfer_channel_new_from_stanza (SalutConnection *connection,
1676                                              SalutContact *contact,
1677                                              TpHandle handle,
1678                                              TpFileTransferState state,
1679                                              WockyStanza *stanza)
1680 {
1681   GError *error = NULL;
1682   GibberFileTransfer *ft;
1683   SalutFileTransferChannel *chan;
1684   gchar *service_name;
1685   GHashTable *metadata;
1686 
1687   ft = gibber_file_transfer_new_from_stanza_with_from (stanza, connection->porter,
1688       WOCKY_CONTACT (contact), contact->name, &error);
1689 
1690   if (ft == NULL)
1691     {
1692       /* Reply with an error */
1693       WockyStanza *reply;
1694       GError err = { WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_BAD_REQUEST,
1695                       "failed to parse file offer" };
1696 
1697       DEBUG ("%s", error->message);
1698       reply = wocky_stanza_build_iq_error (stanza, NULL);
1699       wocky_stanza_error_to_node (&err, wocky_stanza_get_top_node (reply));
1700 
1701       wocky_porter_send (connection->porter, reply);
1702 
1703       g_object_unref (reply);
1704       g_clear_error (&error);
1705       return NULL;
1706     }
1707 
1708   /* Metadata */
1709   service_name = extract_service_name (ft);
1710   metadata = extract_metadata (ft);
1711 
1712   DEBUG ("Received file offer with id '%s'", ft->id);
1713 
1714   chan = g_object_new (SALUT_TYPE_FILE_TRANSFER_CHANNEL,
1715       "connection", connection,
1716       "contact", contact,
1717       "handle", handle,
1718       "initiator-handle", handle,
1719       "requested", FALSE,
1720       "state", state,
1721       "filename", ft->filename,
1722       "size", gibber_file_transfer_get_size (ft),
1723       "description", ft->description,
1724       "content-type", ft->content_type,
1725       "service-name", service_name,
1726       "metadata", metadata,
1727       NULL);
1728 
1729   g_free (service_name);
1730   if (metadata != NULL)
1731     g_hash_table_unref (metadata);
1732 
1733   chan->priv->ft = ft;
1734 
1735   g_signal_connect (ft, "error", G_CALLBACK (error_cb), chan);
1736 
1737   return chan;
1738 }
1739