1 /*
2  * file-transfer-chan.c - Simple file transfer channel
3  *
4  * Copyright (C) 2010-2011 Morten Mjelva <morten.mjelva@gmail.com>
5  * Copyright (C) 2010-2011 Collabora Ltd. <http://www.collabora.co.uk/>
6  *
7  * Copying and distribution of this file, with or without modification,
8  * are permitted in any medium without royalty provided the copyright
9  * notice and this notice are preserved.
10  */
11 
12 #include "config.h"
13 
14 #include "file-transfer-chan.h"
15 #include "util.h"
16 #include "debug.h"
17 
18 #include <telepathy-glib/telepathy-glib.h>
19 
20 #include <glib/gstdio.h>
21 
22 static void file_transfer_iface_init (gpointer iface, gpointer data);
23 
24 G_DEFINE_TYPE_WITH_CODE (TpTestsFileTransferChannel,
25     tp_tests_file_transfer_channel,
26     TP_TYPE_BASE_CHANNEL,
27     G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_FILE_TRANSFER,
28       file_transfer_iface_init);
29     G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA,
30       NULL);
31     )
32 
33 enum /* properties */
34 {
35   PROP_AVAILABLE_SOCKET_TYPES = 1,
36   PROP_CONTENT_TYPE,
37   PROP_CONTENT_HASH,
38   PROP_CONTENT_HASH_TYPE,
39   PROP_DATE,
40   PROP_DESCRIPTION,
41   PROP_FILENAME,
42   PROP_INITIAL_OFFSET,
43   PROP_SIZE,
44   PROP_STATE,
45   PROP_TRANSFERRED_BYTES,
46   PROP_URI,
47   PROP_SERVICE_NAME,
48   PROP_METADATA,
49   N_PROPS,
50 };
51 
52 struct _TpTestsFileTransferChannelPrivate {
53     /* Exposed properties */
54     gchar *content_type;
55     guint64 date;
56     gchar *description;
57     gchar *filename;
58     guint64 size;
59     TpFileTransferState state;
60     guint64 transferred_bytes;
61     gchar *uri;
62     gchar *service_name;
63     GHashTable *metadata;
64 
65     /* Hidden properties */
66     TpFileHashType content_hash_type;
67     gchar *content_hash;
68     GHashTable *available_socket_types;
69     gint64 initial_offset;
70 
71     /* Accepting side */
72     GSocketService *service;
73     GValue *access_control_param;
74 
75     /* Offering side */
76     TpSocketAddressType address_type;
77     GValue *address;
78     gchar *unix_address;
79     gchar *unix_tmpdir;
80     guint connection_id;
81     TpSocketAccessControl access_control;
82 
83     guint timer_id;
84 };
85 
86 static void
tp_tests_file_transfer_channel_init(TpTestsFileTransferChannel * self)87 tp_tests_file_transfer_channel_init (TpTestsFileTransferChannel *self)
88 {
89   self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self),
90       TP_TESTS_TYPE_FILE_TRANSFER_CHANNEL, TpTestsFileTransferChannelPrivate);
91 }
92 
93 static void
create_available_socket_types(TpTestsFileTransferChannel * self)94 create_available_socket_types (TpTestsFileTransferChannel *self)
95 {
96   TpSocketAccessControl access_control;
97   GArray *unix_tab;
98 
99   g_assert (self->priv->available_socket_types == NULL);
100   self->priv->available_socket_types = g_hash_table_new_full (NULL, NULL,
101       NULL, _tp_destroy_socket_control_list);
102 
103   /* SocketAddressTypeUnix */
104   unix_tab = g_array_sized_new (FALSE, FALSE, sizeof (TpSocketAccessControl),
105       1);
106   access_control = TP_SOCKET_ACCESS_CONTROL_LOCALHOST;
107   g_array_append_val (unix_tab, access_control);
108 
109   g_hash_table_insert (self->priv->available_socket_types,
110       GUINT_TO_POINTER (TP_SOCKET_ADDRESS_TYPE_UNIX), unix_tab);
111 }
112 
113 static GObject *
constructor(GType type,guint n_props,GObjectConstructParam * props)114 constructor (GType type,
115     guint n_props,
116     GObjectConstructParam *props)
117 {
118   GObject *object =
119     G_OBJECT_CLASS (tp_tests_file_transfer_channel_parent_class)->constructor
120     (type, n_props, props);
121   TpTestsFileTransferChannel *self = TP_TESTS_FILE_TRANSFER_CHANNEL (object);
122 
123   self->priv->state = TP_FILE_TRANSFER_STATE_PENDING;
124 
125   if (self->priv->available_socket_types == NULL)
126     create_available_socket_types (self);
127 
128   tp_base_channel_register (TP_BASE_CHANNEL (self));
129 
130   return object;
131 }
132 
133 static void
dispose(GObject * object)134 dispose (GObject *object)
135 {
136   TpTestsFileTransferChannel *self = TP_TESTS_FILE_TRANSFER_CHANNEL (object);
137 
138   if (self->priv->timer_id != 0)
139     {
140       g_source_remove (self->priv->timer_id);
141       self->priv->timer_id = 0;
142     }
143 
144   g_free (self->priv->content_hash);
145   g_free (self->priv->content_type);
146   g_free (self->priv->description);
147   g_free (self->priv->filename);
148   g_free (self->priv->uri);
149   g_free (self->priv->service_name);
150 
151   tp_clear_pointer (&self->priv->address, tp_g_value_slice_free);
152   tp_clear_pointer (&self->priv->available_socket_types, g_hash_table_unref);
153   tp_clear_pointer (&self->priv->access_control_param, tp_g_value_slice_free);
154   tp_clear_pointer (&self->priv->metadata, g_hash_table_unref);
155 
156   if (self->priv->unix_address != NULL)
157     g_unlink (self->priv->unix_address);
158 
159   tp_clear_pointer (&self->priv->unix_address, g_free);
160 
161   if (self->priv->unix_tmpdir != NULL)
162     g_rmdir (self->priv->unix_tmpdir);
163 
164   tp_clear_pointer (&self->priv->unix_tmpdir, g_free);
165 
166   ((GObjectClass *) tp_tests_file_transfer_channel_parent_class)->dispose (
167       object);
168 }
169 
170 static void
get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)171 get_property (GObject *object,
172     guint property_id,
173     GValue *value,
174     GParamSpec *pspec)
175 {
176   TpTestsFileTransferChannel *self = (TpTestsFileTransferChannel *) object;
177 
178   switch (property_id)
179     {
180       case PROP_AVAILABLE_SOCKET_TYPES:
181         g_value_set_boxed (value, self->priv->available_socket_types);
182         break;
183 
184       case PROP_CONTENT_HASH:
185         g_value_set_string (value, self->priv->content_hash);
186 
187       case PROP_CONTENT_HASH_TYPE:
188         g_value_set_uint (value, self->priv->content_hash_type);
189         break;
190 
191       case PROP_CONTENT_TYPE:
192         g_value_set_string (value, self->priv->content_type);
193         break;
194 
195       case PROP_DATE:
196         g_value_set_uint64 (value, self->priv->date);
197         break;
198 
199       case PROP_DESCRIPTION:
200         g_value_set_string (value, self->priv->description);
201         break;
202 
203       case PROP_FILENAME:
204         g_value_set_string (value, self->priv->filename);
205         break;
206 
207       case PROP_INITIAL_OFFSET:
208         g_value_set_uint64 (value, self->priv->initial_offset);
209         break;
210 
211       case PROP_SIZE:
212         g_value_set_uint64 (value, self->priv->size);
213         break;
214 
215       case PROP_STATE:
216         g_value_set_uint (value, self->priv->state);
217         break;
218 
219       case PROP_TRANSFERRED_BYTES:
220         g_value_set_uint64 (value, self->priv->transferred_bytes);
221         break;
222 
223       case PROP_URI:
224         g_value_set_string (value, self->priv->uri);
225         break;
226 
227       case PROP_SERVICE_NAME:
228         g_value_set_string (value, self->priv->service_name);
229         break;
230 
231       case PROP_METADATA:
232         g_value_set_boxed (value, self->priv->metadata);
233         break;
234 
235       default:
236         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
237         break;
238     }
239 }
240 
241 static void
set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)242 set_property (GObject *object,
243     guint property_id,
244     const GValue *value,
245     GParamSpec *pspec)
246 {
247   TpTestsFileTransferChannel *self = (TpTestsFileTransferChannel *) object;
248 
249   switch (property_id)
250     {
251       case PROP_AVAILABLE_SOCKET_TYPES:
252         self->priv->available_socket_types = g_value_dup_boxed (value);
253         break;
254 
255       case PROP_CONTENT_HASH:
256         self->priv->content_hash = g_value_dup_string (value);
257         break;
258 
259       case PROP_CONTENT_HASH_TYPE:
260         break;
261 
262       case PROP_CONTENT_TYPE:
263         self->priv->content_type = g_value_dup_string (value);
264         break;
265 
266       case PROP_DATE:
267         self->priv->date = g_value_get_uint64 (value);
268         break;
269 
270       case PROP_DESCRIPTION:
271         self->priv->description = g_value_dup_string (value);
272         break;
273 
274       case PROP_FILENAME:
275         self->priv->filename = g_value_dup_string (value);
276         break;
277 
278       case PROP_INITIAL_OFFSET:
279         self->priv->initial_offset = g_value_get_uint64 (value);
280         break;
281 
282       case PROP_SIZE:
283         self->priv->size = g_value_get_uint64 (value);
284         break;
285 
286       case PROP_STATE:
287         self->priv->state = g_value_get_uint (value);
288         break;
289 
290       case PROP_TRANSFERRED_BYTES:
291         self->priv->transferred_bytes = g_value_get_uint64 (value);
292         break;
293 
294       case PROP_URI:
295         self->priv->uri = g_value_dup_string (value);
296         break;
297 
298       case PROP_SERVICE_NAME:
299         self->priv->service_name = g_value_dup_string (value);
300         break;
301 
302       case PROP_METADATA:
303         self->priv->metadata = g_value_dup_boxed (value);
304         break;
305 
306       default:
307         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
308         break;
309     }
310 }
311 
312 static void
channel_close(TpBaseChannel * self)313 channel_close (TpBaseChannel *self)
314 {
315   g_print ("entered channel_close");
316   tp_base_channel_destroyed (self);
317 }
318 
319 static void
fill_immutable_properties(TpBaseChannel * self,GHashTable * properties)320 fill_immutable_properties (TpBaseChannel *self,
321     GHashTable *properties)
322 {
323   TpBaseChannelClass *klass = TP_BASE_CHANNEL_CLASS (
324       tp_tests_file_transfer_channel_parent_class);
325 
326   klass->fill_immutable_properties (self, properties);
327 
328   tp_dbus_properties_mixin_fill_properties_hash (
329       G_OBJECT (self), properties,
330       TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "AvailableSocketTypes",
331       TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "ContentType",
332       TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "Filename",
333       TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "Size",
334       TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "Description",
335       TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "Date",
336       TP_IFACE_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA, "ServiceName",
337       TP_IFACE_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA, "Metadata",
338       NULL);
339 
340   /* URI is immutable only for outgoing transfers */
341   if (tp_base_channel_is_requested (self))
342     {
343       tp_dbus_properties_mixin_fill_properties_hash (G_OBJECT (self),
344           properties,
345           TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "URI", NULL);
346     }
347 }
348 
349 static void
change_state(TpTestsFileTransferChannel * self,TpFileTransferState state,TpFileTransferStateChangeReason reason)350 change_state (TpTestsFileTransferChannel *self,
351     TpFileTransferState state,
352     TpFileTransferStateChangeReason reason)
353 {
354   self->priv->state = state;
355 
356   tp_svc_channel_type_file_transfer_emit_file_transfer_state_changed (self,
357       state, reason);
358 }
359 
360 /* This function imitates the beginning of a filetransfer. It sets the state
361  * to open, and connects to the "incoming" signal of the GSocketService.
362  */
363 static gboolean
start_file_transfer(gpointer data)364 start_file_transfer (gpointer data)
365 {
366   TpTestsFileTransferChannel *self = (TpTestsFileTransferChannel *) data;
367 
368   DEBUG ("Setting TP_FILE_TRANSFER_STATE_OPEN");
369   change_state (self, TP_FILE_TRANSFER_STATE_OPEN,
370       TP_FILE_TRANSFER_STATE_CHANGE_REASON_REQUESTED);
371 
372   g_object_notify ((GObject *) data, "state");
373   DEBUG ("Fired state signal");
374 
375 //  g_signal_connect (self->priv->service, "incoming", G_CALLBACK
376 //      (incoming_file_transfer_cb));
377 
378   self->priv->timer_id = 0;
379   return FALSE;
380 }
381 
382 static gboolean
check_address_type(TpTestsFileTransferChannel * self,TpSocketAddressType address_type,TpSocketAccessControl access_control)383 check_address_type (TpTestsFileTransferChannel *self,
384     TpSocketAddressType address_type,
385     TpSocketAccessControl access_control)
386 {
387   GArray *arr;
388   guint i;
389 
390   arr = g_hash_table_lookup (self->priv->available_socket_types,
391       GUINT_TO_POINTER (address_type));
392   if (arr == NULL)
393     return FALSE;
394 
395   for (i = 0; i < arr->len; i++)
396     {
397       if (g_array_index (arr, TpSocketAccessControl, i) == access_control)
398         return TRUE;
399     }
400 
401   return FALSE;
402 }
403 
404 static void
service_incoming_cb(GSocketService * service,GSocketConnection * connection,GObject * source_object,gpointer user_data)405 service_incoming_cb (GSocketService *service,
406     GSocketConnection *connection,
407     GObject *source_object,
408     gpointer user_data)
409 {
410   TpTestsFileTransferChannel *self = user_data;
411   GError *error = NULL;
412 
413   DEBUG ("Servicing incoming connection");
414   if (self->priv->access_control == TP_SOCKET_ACCESS_CONTROL_CREDENTIALS)
415     {
416       GCredentials *creds;
417       guchar byte;
418 
419       /* TODO: Async version */
420       creds = tp_unix_connection_receive_credentials_with_byte (
421           connection, &byte, NULL, &error);
422       g_assert_no_error (error);
423 
424       g_assert_cmpuint (byte, ==,
425           g_value_get_uchar (self->priv->access_control_param));
426       g_object_unref (creds);
427     }
428   else if (self->priv->access_control == TP_SOCKET_ACCESS_CONTROL_PORT)
429     {
430       GSocketAddress *addr;
431       guint16 port;
432 
433       addr = g_socket_connection_get_remote_address (connection, &error);
434       g_assert_no_error (error);
435 
436       port = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr));
437 
438       g_assert_cmpuint (port, ==,
439           g_value_get_uint (self->priv->access_control_param));
440 
441       g_object_unref (addr);
442     }
443 }
444 
445 static void
file_transfer_provide_file(TpSvcChannelTypeFileTransfer * iface,TpSocketAddressType address_type,TpSocketAccessControl access_control,const GValue * access_control_param,DBusGMethodInvocation * context)446 file_transfer_provide_file (TpSvcChannelTypeFileTransfer *iface,
447     TpSocketAddressType address_type,
448     TpSocketAccessControl access_control,
449     const GValue *access_control_param,
450     DBusGMethodInvocation *context)
451 {
452   TpTestsFileTransferChannel *self = (TpTestsFileTransferChannel *) iface;
453   TpBaseChannel *base_chan = (TpBaseChannel *) iface;
454   GError *error = NULL;
455 
456   if (tp_base_channel_is_requested (base_chan) != TRUE)
457     {
458       g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
459           "File transfer is not outgoing. Cannot offer file");
460       goto fail;
461     }
462 
463   if (self->priv->state != TP_FILE_TRANSFER_STATE_PENDING &&
464       self->priv->state != TP_FILE_TRANSFER_STATE_ACCEPTED)
465     {
466       g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
467           "File transfer is not pending or accepted. Cannot offer file");
468       goto fail;
469     }
470 
471   if (self->priv->address != NULL)
472     {
473       g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
474           "ProvideFile has already been called for this channel");
475       goto fail;
476     }
477 
478   if (!check_address_type (self, address_type, access_control))
479     {
480       g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
481           "Address type %i is not supported with access control %i",
482           address_type, access_control);
483       goto fail;
484     }
485 
486   self->priv->address = _tp_create_local_socket (address_type, access_control,
487       &self->priv->service, &self->priv->unix_address,
488       &self->priv->unix_tmpdir, &error);
489 
490   if (self->priv->address == NULL)
491       {
492         g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
493             "Could not set up local socket");
494         goto fail;
495       }
496 
497   self->priv->address_type = address_type;
498   self->priv->access_control = access_control;
499 
500   DEBUG ("Waiting 500ms and setting state to OPEN");
501   self->priv->timer_id = g_timeout_add (500, start_file_transfer, self);
502 
503   // connect to self->priv->service incoming signal
504   // when the signal returns, add x bytes per n seconds using timeout
505   // then close the socket
506   // g_output_stream_write_async
507 
508   tp_svc_channel_type_file_transfer_return_from_provide_file (context,
509       self->priv->address);
510 
511   return;
512 
513 fail:
514   dbus_g_method_return_error (context, error);
515   g_error_free (error);
516 }
517 
518 static void
file_transfer_accept_file(TpSvcChannelTypeFileTransfer * iface,TpSocketAddressType address_type,TpSocketAccessControl access_control,const GValue * access_control_param,guint64 offset,DBusGMethodInvocation * context)519 file_transfer_accept_file (TpSvcChannelTypeFileTransfer *iface,
520     TpSocketAddressType address_type,
521     TpSocketAccessControl access_control,
522     const GValue *access_control_param,
523     guint64 offset,
524     DBusGMethodInvocation *context)
525 {
526   TpTestsFileTransferChannel *self = (TpTestsFileTransferChannel *) iface;
527   TpBaseChannel *base_chan = (TpBaseChannel *) iface;
528   GError *error = NULL;
529   GValue *address;
530 
531   if (tp_base_channel_is_requested (base_chan) == TRUE)
532     {
533       g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
534           "File transfer is not incoming. Cannot accept file");
535       goto fail;
536     }
537 
538   if (self->priv->state != TP_FILE_TRANSFER_STATE_PENDING)
539     {
540       g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
541           "File transfer is not in the pending state");
542       goto fail;
543     }
544 
545   if (!check_address_type (self, address_type, access_control))
546     {
547       g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
548           "Address type %i is not supported with access control %i",
549           address_type, access_control);
550       goto fail;
551     }
552 
553   address = _tp_create_local_socket (address_type, access_control,
554       &self->priv->service, &self->priv->unix_address,
555       &self->priv->unix_tmpdir, &error);
556 
557   tp_g_signal_connect_object (self->priv->service, "incoming",
558       G_CALLBACK (service_incoming_cb), self, 0);
559 
560   self->priv->access_control = access_control;
561   self->priv->access_control_param = tp_g_value_slice_dup (
562       access_control_param);
563 
564   DEBUG ("Setting TP_FILE_TRANSFER_STATE_ACCEPTED");
565   change_state (self, TP_FILE_TRANSFER_STATE_ACCEPTED,
566       TP_FILE_TRANSFER_STATE_CHANGE_REASON_REQUESTED);
567 
568   DEBUG ("Waiting 500ms and setting state to OPEN");
569   self->priv->timer_id = g_timeout_add (500, start_file_transfer, self);
570 
571   tp_svc_channel_type_file_transfer_return_from_accept_file (context,
572       address);
573 
574   tp_clear_pointer (&address, tp_g_value_slice_free);
575 
576   return;
577 
578 fail:
579   dbus_g_method_return_error (context, error);
580   g_error_free (error);
581 }
582 
583 static void
tp_tests_file_transfer_channel_class_init(TpTestsFileTransferChannelClass * klass)584 tp_tests_file_transfer_channel_class_init (
585     TpTestsFileTransferChannelClass *klass)
586 {
587   GObjectClass *object_class = (GObjectClass *) klass;
588   TpBaseChannelClass *base_class = TP_BASE_CHANNEL_CLASS (klass);
589   GParamSpec *param_spec;
590 
591   static TpDBusPropertiesMixinPropImpl file_transfer_props[] = {
592       { "AvailableSocketTypes", "available-socket-types", NULL },
593       { "ContentType", "content-type", NULL },
594       { "Date", "date", NULL },
595       { "Description", "description", NULL },
596       { "Filename", "filename", NULL },
597       { "Size", "size", NULL },
598       { "State", "state", NULL },
599       { "TransferredBytes", "transferred-bytes", NULL },
600       { "URI", "uri", NULL },
601       { NULL }
602   };
603 
604   static TpDBusPropertiesMixinPropImpl metadata_props[] = {
605       { "ServiceName", "service-name", NULL },
606       { "Metadata", "metadata", NULL },
607       { NULL }
608   };
609 
610   object_class->constructor = constructor;
611   object_class->get_property = get_property;
612   object_class->set_property = set_property;
613   object_class->dispose = dispose;
614 
615   base_class->channel_type = TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER;
616   base_class->target_handle_type = TP_HANDLE_TYPE_CONTACT;
617 
618   base_class->close = channel_close;
619   base_class->fill_immutable_properties = fill_immutable_properties;
620 
621   param_spec = g_param_spec_boxed ("available-socket-types",
622       "AvailableSocketTypes",
623       "The AvailableSocketTypes property of this channel",
624       TP_HASH_TYPE_SUPPORTED_SOCKET_MAP,
625       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
626   g_object_class_install_property (object_class, PROP_AVAILABLE_SOCKET_TYPES,
627       param_spec);
628 
629   param_spec = g_param_spec_string ("content-type",
630       "ContentType",
631       "The ContentType property of this channel",
632       NULL,
633       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
634   g_object_class_install_property (object_class, PROP_CONTENT_TYPE,
635       param_spec);
636 
637   param_spec = g_param_spec_uint64 ("date",
638       "Date",
639       "The Date property of this channel",
640       0, G_MAXUINT64, 0,
641       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
642   g_object_class_install_property (object_class, PROP_DATE,
643       param_spec);
644 
645   param_spec = g_param_spec_string ("description",
646       "Description",
647       "The Description property of this channel",
648       NULL,
649       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
650   g_object_class_install_property (object_class, PROP_DESCRIPTION,
651       param_spec);
652 
653   param_spec = g_param_spec_string ("filename",
654       "Filename",
655       "The Filename property of this channel",
656       NULL,
657       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
658   g_object_class_install_property (object_class, PROP_FILENAME,
659       param_spec);
660 
661   param_spec = g_param_spec_uint64 ("initial-offset",
662       "InitialOffset",
663       "The InitialOffset property of this channel",
664       0, G_MAXUINT64, 0,
665       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
666   g_object_class_install_property (object_class, PROP_INITIAL_OFFSET,
667       param_spec);
668 
669   param_spec = g_param_spec_uint64 ("size",
670       "Size",
671       "The Size property of this channel",
672       0, G_MAXUINT64, 0,
673       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
674   g_object_class_install_property (object_class, PROP_SIZE,
675       param_spec);
676 
677   param_spec = g_param_spec_uint ("state",
678       "State",
679       "The State property of this channel",
680       0, TP_NUM_FILE_TRANSFER_STATES, TP_FILE_TRANSFER_STATE_NONE,
681       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
682   g_object_class_install_property (object_class, PROP_STATE,
683       param_spec);
684 
685   param_spec = g_param_spec_uint64 ("transferred-bytes",
686       "TransferredBytes",
687       "The TransferredBytes property of this channel",
688       0, G_MAXUINT64, 0,
689       G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS);
690   g_object_class_install_property (object_class, PROP_TRANSFERRED_BYTES,
691       param_spec);
692 
693   param_spec = g_param_spec_string ("uri",
694       "URI",
695       "The URI property of this channel",
696       NULL,
697       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
698   g_object_class_install_property (object_class, PROP_URI,
699       param_spec);
700 
701   param_spec = g_param_spec_string ("service-name",
702       "ServiceName",
703       "The Metadata.ServiceName property of this channel",
704       "",
705       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
706   g_object_class_install_property (object_class, PROP_SERVICE_NAME,
707       param_spec);
708 
709   param_spec = g_param_spec_boxed ("metadata",
710       "Metadata",
711       "The Metadata.Metadata property of this channel",
712       TP_HASH_TYPE_METADATA,
713       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
714   g_object_class_install_property (object_class, PROP_METADATA,
715       param_spec);
716 
717   tp_dbus_properties_mixin_implement_interface (object_class,
718       TP_IFACE_QUARK_CHANNEL_TYPE_FILE_TRANSFER,
719       tp_dbus_properties_mixin_getter_gobject_properties, NULL,
720       file_transfer_props);
721 
722   tp_dbus_properties_mixin_implement_interface (object_class,
723       TP_IFACE_QUARK_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA,
724       tp_dbus_properties_mixin_getter_gobject_properties, NULL,
725       metadata_props);
726 
727   g_type_class_add_private (object_class,
728       sizeof (TpTestsFileTransferChannelPrivate));
729 }
730 
731 static void
file_transfer_iface_init(gpointer iface,gpointer data)732 file_transfer_iface_init (gpointer iface, gpointer data)
733 {
734   TpSvcChannelTypeFileTransferClass *klass = iface;
735 
736 #define IMPLEMENT(x) tp_svc_channel_type_file_transfer_implement_##x (klass, \
737     file_transfer_##x)
738   IMPLEMENT(accept_file);
739   IMPLEMENT(provide_file);
740 #undef IMPLEMENT
741 }
742 
743 /* Return the address of the file transfer's socket */
744 GSocketAddress *
tp_tests_file_transfer_channel_get_server_address(TpTestsFileTransferChannel * self)745 tp_tests_file_transfer_channel_get_server_address (
746     TpTestsFileTransferChannel *self)
747 {
748   GSocketAddress *address;
749   GError *error = NULL;
750 
751   g_assert (self->priv->address != NULL);
752 
753   address = tp_g_socket_address_from_variant (self->priv->address_type,
754       self->priv->address, &error);
755 
756   if (error != NULL)
757     {
758       g_printf ("%s\n", error->message);
759     }
760 
761   return address;
762 }
763