1 /*
2  * stream-tube-channel.h - high level API for StreamTube channels
3  *
4  * Copyright (C) 2010 Collabora Ltd. <http://www.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 /**
22  * SECTION:stream-tube-channel
23  * @title: TpStreamTubeChannel
24  * @short_description: proxy object for a stream tube channel
25  *
26  * #TpStreamTubeChannel is a sub-class of #TpChannel providing convenient API
27  * to offer and accept a stream tube.
28  *
29  * Since: 0.13.2
30  */
31 
32 /**
33  * TpStreamTubeChannel:
34  *
35  * Data structure representing a #TpStreamTubeChannel.
36  *
37  * Since: 0.13.2
38  */
39 
40 /**
41  * TpStreamTubeChannelClass:
42  *
43  * The class of a #TpStreamTubeChannel.
44  *
45  * Since: 0.13.2
46  */
47 
48 #include "config.h"
49 
50 #include "telepathy-glib/stream-tube-channel.h"
51 
52 #include <telepathy-glib/contact.h>
53 #include <telepathy-glib/dbus.h>
54 #include <telepathy-glib/enums.h>
55 #include <telepathy-glib/gnio-util.h>
56 #include <telepathy-glib/gtypes.h>
57 #include <telepathy-glib/interfaces.h>
58 #include <telepathy-glib/proxy-subclass.h>
59 #include <telepathy-glib/stream-tube-connection-internal.h>
60 #include <telepathy-glib/util-internal.h>
61 #include <telepathy-glib/util.h>
62 #include <telepathy-glib/variant-util-internal.h>
63 
64 #define DEBUG_FLAG TP_DEBUG_CHANNEL
65 #include "telepathy-glib/channel-internal.h"
66 #include "telepathy-glib/debug-internal.h"
67 #include "telepathy-glib/automatic-client-factory-internal.h"
68 
69 #include <stdio.h>
70 #include <glib/gstdio.h>
71 
72 #ifdef HAVE_GIO_UNIX
73 #include <gio/gunixsocketaddress.h>
74 #include <gio/gunixconnection.h>
75 #endif /* HAVE_GIO_UNIX */
76 
77 G_DEFINE_TYPE (TpStreamTubeChannel, tp_stream_tube_channel, TP_TYPE_CHANNEL)
78 
79 /* Used to store the data of a NewRemoteConnection signal while we are waiting
80  * for the TCP connection identified by this signal */
81 typedef struct
82 {
83   TpHandle handle;
84   GValue *param;
85   guint connection_id;
86   gboolean rejected;
87 } SigWaitingConn;
88 
89 static SigWaitingConn *
sig_waiting_conn_new(TpHandle handle,const GValue * param,guint connection_id,gboolean rejected)90 sig_waiting_conn_new (TpHandle handle,
91     const GValue *param,
92     guint connection_id,
93     gboolean rejected)
94 {
95   SigWaitingConn *ret = g_slice_new0 (SigWaitingConn);
96 
97   ret->handle = handle;
98   ret->param = tp_g_value_slice_dup (param);
99   ret->connection_id = connection_id;
100   ret->rejected = rejected;
101   return ret;
102 }
103 
104 static void
sig_waiting_conn_free(SigWaitingConn * sig)105 sig_waiting_conn_free (SigWaitingConn *sig)
106 {
107   g_assert (sig != NULL);
108 
109   tp_g_value_slice_free (sig->param);
110   g_slice_free (SigWaitingConn, sig);
111 }
112 
113 typedef struct
114 {
115   GSocketConnection *conn;
116   /* Used only with TP_SOCKET_ACCESS_CONTROL_CREDENTIALS to store the byte
117    * read with the credentials. */
118   guchar byte;
119 } ConnWaitingSig;
120 
121 static ConnWaitingSig *
conn_waiting_sig_new(GSocketConnection * conn,guchar byte)122 conn_waiting_sig_new (GSocketConnection *conn,
123     guchar byte)
124 {
125   ConnWaitingSig *ret = g_slice_new0 (ConnWaitingSig);
126 
127   ret->conn = g_object_ref (conn);
128   ret->byte = byte;
129   return ret;
130 }
131 
132 static void
conn_waiting_sig_free(ConnWaitingSig * c)133 conn_waiting_sig_free (ConnWaitingSig *c)
134 {
135   g_assert (c != NULL);
136 
137   g_object_unref (c->conn);
138   g_slice_free (ConnWaitingSig, c);
139 }
140 
141 struct _TpStreamTubeChannelPrivate
142 {
143   GHashTable *parameters;
144 
145   /* Offering side */
146   GSocketService *service;
147   GSocketAddress *address;
148   gchar *unix_tmpdir;
149   /* GSocketConnection we have accepted but are still waiting a
150    * NewRemoteConnection to identify them. Owned ConnWaitingSig. */
151   GSList *conn_waiting_sig;
152   /* NewRemoteConnection signals we have received but didn't accept their TCP
153    * connection yet. Owned SigWaitingConn. */
154   GSList *sig_waiting_conn;
155 
156   /* Accepting side */
157   GSocket *client_socket;
158   /* The access_control_param we passed to Accept */
159   GValue *access_control_param;
160   /* Connection to the CM while we are waiting for its
161    * ID (NewLocalConnection) */
162   GSocketConnection *local_conn_waiting_id;
163   /* ID received from NewLocalConnection stored while the connection has not
164    * be connected yet. */
165   guint local_conn_id;
166   /* TRUE if local_conn_id is meaningfull (0 can be a valid ID so we can't use
167    * it to check if NewLocalConnection has been received :\ ) */
168   gboolean local_conn_id_set;
169 
170   TpSocketAddressType socket_type;
171   TpSocketAccessControl access_control;
172 
173   GSimpleAsyncResult *result;
174 
175   /* (guint) connection ID => weakly reffed TpStreamTubeConnection */
176   GHashTable *tube_connections;
177 };
178 
179 enum
180 {
181   PROP_SERVICE = 1,
182   PROP_PARAMETERS,
183   PROP_PARAMETERS_VARDICT
184 };
185 
186 enum /* signals */
187 {
188   INCOMING,
189   LAST_SIGNAL
190 };
191 
192 static guint _signals[LAST_SIGNAL] = { 0, };
193 
194 static void
remote_connection_destroyed_cb(gpointer user_data,GObject * conn)195 remote_connection_destroyed_cb (gpointer user_data,
196     GObject *conn)
197 {
198   /* The GSocketConnection has been destroyed, removing it from the hash */
199   TpStreamTubeChannel *self = user_data;
200   GHashTableIter iter;
201   gpointer value;
202 
203   g_hash_table_iter_init (&iter, self->priv->tube_connections);
204   while (g_hash_table_iter_next (&iter, NULL, &value))
205     {
206       if (value == conn)
207         {
208           g_hash_table_iter_remove (&iter);
209           break;
210         }
211     }
212 }
213 
214 static void
tp_stream_tube_channel_dispose(GObject * obj)215 tp_stream_tube_channel_dispose (GObject *obj)
216 {
217   TpStreamTubeChannel *self = (TpStreamTubeChannel *) obj;
218 
219   if (self->priv->service != NULL)
220     {
221       g_socket_service_stop (self->priv->service);
222 
223       tp_clear_object (&self->priv->service);
224     }
225 
226   tp_clear_object (&self->priv->result);
227   tp_clear_pointer (&self->priv->parameters, g_hash_table_unref);
228 
229   g_slist_foreach (self->priv->conn_waiting_sig, (GFunc) conn_waiting_sig_free,
230       NULL);
231   tp_clear_pointer (&self->priv->conn_waiting_sig, g_slist_free);
232 
233   g_slist_foreach (self->priv->sig_waiting_conn, (GFunc) sig_waiting_conn_free,
234       NULL);
235   tp_clear_pointer (&self->priv->sig_waiting_conn, g_slist_free);
236 
237   if (self->priv->tube_connections != NULL)
238     {
239       GHashTableIter iter;
240       gpointer conn;
241 
242       g_hash_table_iter_init (&iter, self->priv->tube_connections);
243       while (g_hash_table_iter_next (&iter, NULL, &conn))
244         {
245           g_object_weak_unref (conn, remote_connection_destroyed_cb, self);
246         }
247 
248       g_hash_table_unref (self->priv->tube_connections);
249       self->priv->tube_connections = NULL;
250     }
251 
252   if (self->priv->address != NULL)
253     {
254 #ifdef HAVE_GIO_UNIX
255       /* check if we need to remove the temporary file we created */
256       if (G_IS_UNIX_SOCKET_ADDRESS (self->priv->address))
257         {
258           const gchar *path;
259 
260           path = g_unix_socket_address_get_path (
261               G_UNIX_SOCKET_ADDRESS (self->priv->address));
262           g_unlink (path);
263         }
264 #endif /* HAVE_GIO_UNIX */
265 
266       g_object_unref (self->priv->address);
267       self->priv->address = NULL;
268     }
269 
270     if (self->priv->unix_tmpdir != NULL)
271       {
272         g_rmdir (self->priv->unix_tmpdir);
273         g_free (self->priv->unix_tmpdir);
274         self->priv->unix_tmpdir = NULL;
275       }
276 
277   tp_clear_pointer (&self->priv->access_control_param, tp_g_value_slice_free);
278   tp_clear_object (&self->priv->local_conn_waiting_id);
279   tp_clear_object (&self->priv->client_socket);
280 
281   G_OBJECT_CLASS (tp_stream_tube_channel_parent_class)->dispose (obj);
282 }
283 
284 static void
tp_stream_tube_channel_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)285 tp_stream_tube_channel_get_property (GObject *object,
286     guint property_id,
287     GValue *value,
288     GParamSpec *pspec)
289 {
290   TpStreamTubeChannel *self = (TpStreamTubeChannel *) object;
291 
292   switch (property_id)
293     {
294       case PROP_SERVICE:
295         g_value_set_string (value, tp_stream_tube_channel_get_service (self));
296         break;
297 
298       case PROP_PARAMETERS:
299         g_value_set_boxed (value, self->priv->parameters);
300         break;
301 
302       case PROP_PARAMETERS_VARDICT:
303         g_value_take_variant (value,
304             tp_stream_tube_channel_dup_parameters_vardict (self));
305         break;
306 
307       default:
308         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
309         break;
310     }
311 }
312 
313 static void
connection_closed_cb(TpChannel * channel,guint connection_id,const gchar * err,const gchar * message,gpointer user_data,GObject * weak_object)314 connection_closed_cb (TpChannel *channel,
315     guint connection_id,
316     const gchar *err,
317     const gchar *message,
318     gpointer user_data,
319     GObject *weak_object)
320 {
321   TpStreamTubeChannel *self = (TpStreamTubeChannel *) weak_object;
322   TpStreamTubeConnection *tube_conn;
323   GError *error = NULL;
324 
325   DEBUG ("Got ConnectionClosed signal on connection %u: %s (%s)",
326       connection_id, err, message);
327 
328   tube_conn = g_hash_table_lookup (self->priv->tube_connections,
329       GUINT_TO_POINTER (connection_id));
330   if (tube_conn == NULL)
331     {
332       DEBUG ("No connection with ID %u; ignoring", connection_id);
333       return;
334     }
335 
336   tp_proxy_dbus_error_to_gerror (self, err, message, &error);
337 
338   _tp_stream_tube_connection_fire_closed (tube_conn, error);
339 
340   g_error_free (error);
341 }
342 
343 static void
tp_stream_tube_channel_constructed(GObject * obj)344 tp_stream_tube_channel_constructed (GObject *obj)
345 {
346   TpStreamTubeChannel *self = (TpStreamTubeChannel *) obj;
347   void (*chain_up) (GObject *) =
348     ((GObjectClass *) tp_stream_tube_channel_parent_class)->constructed;
349   TpChannel *chan = (TpChannel *) obj;
350   GHashTable *props;
351   GError *err = NULL;
352 
353   if (chain_up != NULL)
354     chain_up (obj);
355 
356   if (tp_channel_get_channel_type_id (chan) !=
357       TP_IFACE_QUARK_CHANNEL_TYPE_STREAM_TUBE)
358     {
359       GError error = { TP_DBUS_ERRORS, TP_DBUS_ERROR_INCONSISTENT,
360           "Channel is not a stream tube" };
361 
362       DEBUG ("Channel is not a stream tube: %s", tp_channel_get_channel_type (
363             chan));
364 
365       tp_proxy_invalidate (TP_PROXY (self), &error);
366       return;
367     }
368 
369   props = _tp_channel_get_immutable_properties (TP_CHANNEL (self));
370 
371   if (tp_asv_get_string (props, TP_PROP_CHANNEL_TYPE_STREAM_TUBE_SERVICE)
372       == NULL)
373     {
374       GError error = { TP_DBUS_ERRORS, TP_DBUS_ERROR_INCONSISTENT,
375           "Tube doesn't have StreamTube.Service property" };
376 
377       DEBUG ("%s", error.message);
378 
379       tp_proxy_invalidate (TP_PROXY (self), &error);
380       return;
381     }
382 
383    /*  Tube.Parameters is immutable for incoming tubes. For outgoing ones,
384     *  it's defined when offering the tube. */
385   if (!tp_channel_get_requested (TP_CHANNEL (self)))
386     {
387       GHashTable *params;
388 
389       params = tp_asv_get_boxed (props,
390           TP_PROP_CHANNEL_INTERFACE_TUBE_PARAMETERS,
391           TP_HASH_TYPE_STRING_VARIANT_MAP);
392 
393       if (params == NULL)
394         {
395           DEBUG ("Incoming tube doesn't have Tube.Parameters property");
396 
397           self->priv->parameters = tp_asv_new (NULL, NULL);
398         }
399       else
400         {
401           self->priv->parameters = g_boxed_copy (
402               TP_HASH_TYPE_STRING_VARIANT_MAP, params);
403         }
404     }
405 
406   tp_cli_channel_type_stream_tube_connect_to_connection_closed (
407       TP_CHANNEL (self), connection_closed_cb, NULL, NULL,
408       G_OBJECT (self), &err);
409 
410   if (err != NULL)
411     {
412       DEBUG ("Failed to connect to ConnectionClosed signal: %s",
413           err->message);
414 
415       g_error_free (err);
416     }
417 }
418 
419 static void
tp_stream_tube_channel_class_init(TpStreamTubeChannelClass * klass)420 tp_stream_tube_channel_class_init (TpStreamTubeChannelClass *klass)
421 {
422   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
423   GParamSpec *param_spec;
424 
425   gobject_class->constructed = tp_stream_tube_channel_constructed;
426   gobject_class->get_property = tp_stream_tube_channel_get_property;
427   gobject_class->dispose = tp_stream_tube_channel_dispose;
428 
429   /**
430    * TpStreamTubeChannel:service:
431    *
432    * A string representing the service name that will be used over the tube.
433    *
434    * Since: 0.13.2
435    */
436   param_spec = g_param_spec_string ("service", "Service",
437       "The service of the stream tube",
438       NULL,
439       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
440   g_object_class_install_property (gobject_class, PROP_SERVICE, param_spec);
441 
442   /**
443    * TpStreamTubeChannel:parameters:
444    *
445    * A string to #GValue #GHashTable representing the parameters of the tube.
446    *
447    * Will be %NULL for outgoing tubes until the tube has been offered.
448    *
449    * In high-level language bindings, use
450    * #TpStreamTubeChannel:parameters-vardict or
451    * tp_stream_tube_channel_dup_parameters_vardict() to get the same
452    * information in a more convenient format.
453    *
454    * Since: 0.13.2
455    */
456   param_spec = g_param_spec_boxed ("parameters", "Parameters",
457       "The parameters of the stream tube",
458       TP_HASH_TYPE_STRING_VARIANT_MAP,
459       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
460   g_object_class_install_property (gobject_class, PROP_PARAMETERS, param_spec);
461 
462   /**
463    * TpStreamTubeChannel:parameters-vardict:
464    *
465    * A %G_VARIANT_TYPE_VARDICT representing the parameters of the tube.
466    *
467    * Will be %NULL for outgoing tubes until the tube has been offered.
468    *
469    * Since: 0.19.10
470    */
471   param_spec = g_param_spec_variant ("parameters-vardict", "Parameters",
472       "The parameters of the stream tube",
473       G_VARIANT_TYPE_VARDICT, NULL,
474       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
475   g_object_class_install_property (gobject_class, PROP_PARAMETERS_VARDICT,
476       param_spec);
477 
478   /**
479    * TpStreamTubeChannel::incoming:
480    * @self: the #TpStreamTubeChannel
481    * @tube_connection: the #TpStreamTubeConnection for the connection
482    *
483    * The ::incoming signal is emitted on offered Tubes when a new incoming
484    * connection is made from a remote user (one accepting the Tube).
485    *
486    * Consumers of this signal must take their own references to
487    * @tube_connection
488    */
489   _signals[INCOMING] = g_signal_new ("incoming",
490       G_OBJECT_CLASS_TYPE (klass),
491       G_SIGNAL_RUN_LAST,
492       0, NULL, NULL, NULL,
493       G_TYPE_NONE,
494       1, TP_TYPE_STREAM_TUBE_CONNECTION);
495 
496   g_type_class_add_private (gobject_class, sizeof (TpStreamTubeChannelPrivate));
497 }
498 
499 static void
tp_stream_tube_channel_init(TpStreamTubeChannel * self)500 tp_stream_tube_channel_init (TpStreamTubeChannel *self)
501 {
502   self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), TP_TYPE_STREAM_TUBE_CHANNEL,
503       TpStreamTubeChannelPrivate);
504 
505   self->priv->tube_connections = g_hash_table_new (NULL, NULL);
506 }
507 
508 
509 /**
510  * tp_stream_tube_channel_new:
511  * @conn: a #TpConnection; may not be %NULL
512  * @object_path: the object path of the channel; may not be %NULL
513  * @immutable_properties: (transfer none) (element-type utf8 GObject.Value):
514  *  the immutable properties of the channel,
515  *  as signalled by the NewChannel D-Bus signal or returned by the
516  *  CreateChannel and EnsureChannel D-Bus methods: a mapping from
517  *  strings (D-Bus interface name + "." + property name) to #GValue instances
518  * @error: used to indicate the error if %NULL is returned
519  *
520  * Creates a new #TpStreamTubeChannel proxy object from the provided path and
521  * properties. Most developers will not need to use this function; use
522  * #TpAutomaticProxyFactory to automatically create #TpStreamTubeChannel proxy
523  * objects.
524  *
525  * Returns: (transfer full): a newly-created #TpStreamTubeChannel proxy
526  *
527  * Since: 0.13.2
528  * Deprecated: Use tp_simple_client_factory_ensure_channel() instead.
529  */
530 TpStreamTubeChannel *
tp_stream_tube_channel_new(TpConnection * conn,const gchar * object_path,const GHashTable * immutable_properties,GError ** error)531 tp_stream_tube_channel_new (TpConnection *conn,
532     const gchar *object_path,
533     const GHashTable *immutable_properties,
534     GError **error)
535 {
536   return _tp_stream_tube_channel_new_with_factory (NULL, conn, object_path,
537       immutable_properties, error);
538 }
539 
540 TpStreamTubeChannel *
_tp_stream_tube_channel_new_with_factory(TpSimpleClientFactory * factory,TpConnection * conn,const gchar * object_path,const GHashTable * immutable_properties,GError ** error)541 _tp_stream_tube_channel_new_with_factory (
542     TpSimpleClientFactory *factory,
543     TpConnection *conn,
544     const gchar *object_path,
545     const GHashTable *immutable_properties,
546     GError **error)
547 {
548   TpProxy *conn_proxy = (TpProxy *) conn;
549 
550   g_return_val_if_fail (TP_IS_CONNECTION (conn), NULL);
551   g_return_val_if_fail (object_path != NULL, NULL);
552   g_return_val_if_fail (immutable_properties != NULL, NULL);
553 
554   if (!tp_dbus_check_valid_object_path (object_path, error))
555     return NULL;
556 
557   return g_object_new (TP_TYPE_STREAM_TUBE_CHANNEL,
558       "connection", conn,
559        "dbus-daemon", conn_proxy->dbus_daemon,
560        "bus-name", conn_proxy->bus_name,
561        "object-path", object_path,
562        "handle-type", (guint) TP_UNKNOWN_HANDLE_TYPE,
563        "channel-properties", immutable_properties,
564        "factory", factory,
565        NULL);
566 }
567 
568 static void
operation_failed(TpStreamTubeChannel * self,const GError * error)569 operation_failed (TpStreamTubeChannel *self,
570     const GError *error)
571 {
572   g_simple_async_result_set_from_error (self->priv->result, error);
573 
574   g_simple_async_result_complete_in_idle (self->priv->result);
575   tp_clear_object (&self->priv->result);
576 }
577 
578 static void
complete_accept_operation(TpStreamTubeChannel * self,TpStreamTubeConnection * tube_conn)579 complete_accept_operation (TpStreamTubeChannel *self,
580     TpStreamTubeConnection *tube_conn)
581 {
582   g_simple_async_result_set_op_res_gpointer (self->priv->result,
583       g_object_ref (tube_conn), g_object_unref);
584   g_simple_async_result_complete (self->priv->result);
585   tp_clear_object (&self->priv->result);
586 }
587 
588 static void
new_local_connection_with_contact(TpConnection * conn,guint n_contacts,TpContact * const * contacts,guint n_failed,const TpHandle * failed,const GError * in_error,gpointer user_data,GObject * obj)589 new_local_connection_with_contact (TpConnection *conn,
590     guint n_contacts,
591     TpContact * const *contacts,
592     guint n_failed,
593     const TpHandle *failed,
594     const GError *in_error,
595     gpointer user_data,
596     GObject *obj)
597 {
598   TpStreamTubeChannel *self = (TpStreamTubeChannel *) obj;
599   TpContact *contact;
600   TpStreamTubeConnection *tube_conn = user_data;
601 
602   if (in_error != NULL)
603     {
604       DEBUG ("Failed to prepare TpContact: %s", in_error->message);
605       return;
606     }
607 
608   if (n_failed > 0)
609     {
610       DEBUG ("Failed to prepare TpContact (InvalidHandle)");
611       return;
612     }
613 
614   contact = contacts[0];
615   _tp_stream_tube_connection_set_contact (tube_conn, contact);
616 
617   complete_accept_operation (self, tube_conn);
618 }
619 
620 static void
new_local_connection_identified(TpStreamTubeChannel * self,GSocketConnection * conn,guint connection_id)621 new_local_connection_identified (TpStreamTubeChannel *self,
622     GSocketConnection *conn,
623     guint connection_id)
624 {
625   TpHandle initiator_handle;
626   TpStreamTubeConnection *tube_conn;
627   TpConnection *connection;
628   GArray *features;
629 
630   tube_conn = _tp_stream_tube_connection_new (conn, self);
631 
632   g_hash_table_insert (self->priv->tube_connections,
633       GUINT_TO_POINTER (connection_id), tube_conn);
634 
635   g_object_weak_ref (G_OBJECT (tube_conn), remote_connection_destroyed_cb,
636       self);
637 
638   /* We are accepting a tube so the contact of the connection is the
639    * initiator of the tube */
640   G_GNUC_BEGIN_IGNORE_DEPRECATIONS
641   initiator_handle = tp_channel_get_initiator_handle (TP_CHANNEL (self));
642 
643   connection = tp_channel_get_connection (TP_CHANNEL (self));
644   features = tp_simple_client_factory_dup_contact_features (
645       tp_proxy_get_factory (connection), connection);
646 
647   /* Pass ownership of tube_conn to the function */
648   tp_connection_get_contacts_by_handle (connection,
649       1, &initiator_handle,
650       features->len, (TpContactFeature *) features->data,
651       new_local_connection_with_contact,
652       tube_conn, g_object_unref, G_OBJECT (self));
653   G_GNUC_END_IGNORE_DEPRECATIONS
654 
655   g_array_unref (features);
656 }
657 
658 #ifdef HAVE_GIO_UNIX
659 static void
send_credentials_cb(GObject * source,GAsyncResult * result,gpointer user_data)660 send_credentials_cb (GObject *source,
661     GAsyncResult *result,
662     gpointer user_data)
663 {
664   TpStreamTubeChannel *self = user_data;
665   GError *error = NULL;
666 
667   if (!tp_unix_connection_send_credentials_with_byte_finish (
668           (GSocketConnection *) source, result, &error))
669     {
670       DEBUG ("Failed to send credentials: %s", error->message);
671 
672       operation_failed (self, error);
673       g_clear_error (&error);
674     }
675 }
676 #endif
677 
678 static void
client_socket_connected(TpStreamTubeChannel * self)679 client_socket_connected (TpStreamTubeChannel *self)
680 {
681   GSocketConnection *conn;
682 
683   conn = g_socket_connection_factory_create_connection (
684       self->priv->client_socket);
685   g_assert (conn);
686 
687   DEBUG ("Stream Tube socket connected");
688 
689 #ifdef HAVE_GIO_UNIX
690   if (self->priv->access_control == TP_SOCKET_ACCESS_CONTROL_CREDENTIALS)
691     {
692       guchar byte;
693 
694       byte = g_value_get_uchar (self->priv->access_control_param);
695       tp_unix_connection_send_credentials_with_byte_async (conn, byte, NULL,
696           send_credentials_cb, self);
697     }
698 #endif
699 
700   if (self->priv->local_conn_id_set)
701     {
702       new_local_connection_identified (self, conn, self->priv->local_conn_id);
703 
704       self->priv->local_conn_id_set = FALSE;
705     }
706   else
707     {
708       /* Wait for NewLocalConnection signal */
709 
710       /* This assume that we never connect more than once. Or at least that we
711        * wait to have identify a connection before making a new connection. */
712       g_assert (self->priv->local_conn_waiting_id == NULL);
713       self->priv->local_conn_waiting_id = g_object_ref (conn);
714     }
715 
716   g_object_unref (conn);
717 }
718 
719 static gboolean
client_socket_cb(GSocket * socket,GIOCondition condition,TpStreamTubeChannel * self)720 client_socket_cb (GSocket *socket,
721     GIOCondition condition,
722     TpStreamTubeChannel *self)
723 {
724   GError *error = NULL;
725 
726   if (!g_socket_check_connect_result (socket, &error))
727     {
728       DEBUG ("Failed to connect to socket: %s", error->message);
729 
730       operation_failed (self, error);
731       g_error_free (error);
732       return FALSE;
733     }
734 
735   client_socket_connected (self);
736 
737   return FALSE;
738 }
739 
740 static void
new_local_connection_cb(TpChannel * proxy,guint connection_id,gpointer user_data,GObject * weak_object)741 new_local_connection_cb (TpChannel *proxy,
742     guint connection_id,
743     gpointer user_data,
744     GObject *weak_object)
745 {
746   TpStreamTubeChannel *self = (TpStreamTubeChannel *) weak_object;
747 
748   if (self->priv->local_conn_waiting_id != NULL)
749     {
750       /* We got the ID of the connection */
751 
752       new_local_connection_identified (self, self->priv->local_conn_waiting_id,
753           connection_id);
754 
755       tp_clear_object (&self->priv->local_conn_waiting_id);
756       return;
757     }
758 
759   /* Wait that the connection is connected */
760   self->priv->local_conn_id = connection_id;
761   self->priv->local_conn_id_set = TRUE;
762 }
763 
764 static void
_channel_accepted(TpChannel * channel,const GValue * addressv,const GError * in_error,gpointer user_data,GObject * obj)765 _channel_accepted (TpChannel *channel,
766     const GValue *addressv,
767     const GError *in_error,
768     gpointer user_data,
769     GObject *obj)
770 {
771   TpStreamTubeChannel *self = (TpStreamTubeChannel *) obj;
772   GSocketAddress *remote_address;
773   GError *error = NULL;
774 
775   if (in_error != NULL)
776     {
777       DEBUG ("Failed to Accept Stream Tube: %s", in_error->message);
778 
779       operation_failed (self, in_error);
780       return;
781     }
782 
783   tp_cli_channel_type_stream_tube_connect_to_new_local_connection (
784       TP_CHANNEL (self), new_local_connection_cb, NULL, NULL,
785       G_OBJECT (self), &error);
786 
787   if (error != NULL)
788     {
789       DEBUG ("Failed to connect to NewLocalConnection signal");
790       operation_failed (self, error);
791 
792       g_error_free (error);
793       return;
794     }
795 
796   remote_address = tp_g_socket_address_from_variant (self->priv->socket_type,
797       addressv, &error);
798   if (error != NULL)
799     {
800       DEBUG ("Failed to convert address: %s", error->message);
801 
802       operation_failed (self, error);
803       g_error_free (error);
804       return;
805     }
806 
807   /* Connect to CM */
808   g_socket_set_blocking (self->priv->client_socket, FALSE);
809   g_socket_connect (self->priv->client_socket, remote_address, NULL, &error);
810 
811   if (error == NULL)
812     {
813       /* Socket is connected */
814       client_socket_connected (self);
815       goto out;
816     }
817   else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_PENDING))
818     {
819       /* We have to wait that the socket is connected */
820       GSource *source;
821 
822       source = g_socket_create_source (self->priv->client_socket,
823           G_IO_OUT, NULL);
824 
825       g_source_attach (source, g_main_context_get_thread_default ());
826 
827       g_source_set_callback (source, (GSourceFunc) client_socket_cb,
828           self, NULL);
829 
830       g_error_free (error);
831       g_source_unref (source);
832     }
833   else
834     {
835       DEBUG ("Failed to connect to CM: %s", error->message);
836 
837       operation_failed (self, error);
838 
839       g_error_free (error);
840     }
841 
842 out:
843   g_object_unref (remote_address);
844 }
845 
846 
847 /**
848  * tp_stream_tube_channel_accept_async:
849  * @self: an incoming #TpStreamTubeChannel
850  * @callback: a callback to call when the tube has been accepted
851  * @user_data: data to pass to @callback
852  *
853  * Accept an incoming stream tube. When the tube has been accepted, @callback
854  * will be called. You can then call tp_stream_tube_channel_accept_finish()
855  * to get a #TpStreamTubeConnection connected to the tube.
856  *
857  * Since: 0.13.2
858  */
859 void
tp_stream_tube_channel_accept_async(TpStreamTubeChannel * self,GAsyncReadyCallback callback,gpointer user_data)860 tp_stream_tube_channel_accept_async (TpStreamTubeChannel *self,
861     GAsyncReadyCallback callback,
862     gpointer user_data)
863 {
864   GHashTable *properties;
865   GHashTable *supported_sockets;
866   GError *error = NULL;
867 
868   g_return_if_fail (TP_IS_STREAM_TUBE_CHANNEL (self));
869   g_return_if_fail (self->priv->result == NULL);
870 
871   if (self->priv->access_control_param != NULL)
872     {
873       g_simple_async_report_error_in_idle (G_OBJECT (self), callback, user_data,
874           TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Tube has already be accepted");
875 
876       return;
877     }
878 
879   self->priv->result = g_simple_async_result_new (G_OBJECT (self), callback,
880       user_data, tp_stream_tube_channel_accept_async);
881 
882   properties = _tp_channel_get_immutable_properties (TP_CHANNEL (self));
883   supported_sockets = tp_asv_get_boxed (properties,
884       TP_PROP_CHANNEL_TYPE_STREAM_TUBE_SUPPORTED_SOCKET_TYPES,
885       TP_HASH_TYPE_SUPPORTED_SOCKET_MAP);
886 
887   if (!_tp_set_socket_address_type_and_access_control_type (supported_sockets,
888       &self->priv->socket_type, &self->priv->access_control, &error))
889     {
890       operation_failed (self, error);
891 
892       g_clear_error (&error);
893       return;
894     }
895 
896   DEBUG ("Using socket type %u with access control %u", self->priv->socket_type,
897       self->priv->access_control);
898 
899   self->priv->client_socket = _tp_create_client_socket (self->priv->socket_type,
900       &error);
901 
902   if (error != NULL)
903     {
904       DEBUG ("Failed to create socket: %s", error->message);
905 
906       operation_failed (self, error);
907       g_clear_error (&error);
908       return;
909     }
910 
911   switch (self->priv->access_control)
912     {
913       case TP_SOCKET_ACCESS_CONTROL_LOCALHOST:
914         /* Put a dummy value */
915         self->priv->access_control_param = tp_g_value_slice_new_uint (0);
916         break;
917 
918       case TP_SOCKET_ACCESS_CONTROL_PORT:
919         {
920           GSocketAddress *addr;
921           guint16 port;
922 
923           addr = g_socket_get_local_address (self->priv->client_socket, &error);
924           if (addr == NULL)
925             {
926               DEBUG ("Failed to get local address of client socket: %s",
927                   error->message);
928 
929               operation_failed (self, error);
930               g_error_free (error);
931               return;
932             }
933 
934           port = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr));
935           self->priv->access_control_param = tp_g_value_slice_new_uint (port);
936 
937           g_object_unref (addr);
938         }
939         break;
940 
941       case TP_SOCKET_ACCESS_CONTROL_CREDENTIALS:
942         self->priv->access_control_param = tp_g_value_slice_new_byte (
943             g_random_int_range (0, G_MAXUINT8));
944         break;
945 
946       default:
947         g_assert_not_reached ();
948     }
949 
950   /* Call Accept */
951   tp_cli_channel_type_stream_tube_call_accept (TP_CHANNEL (self), -1,
952       self->priv->socket_type, self->priv->access_control,
953       self->priv->access_control_param, _channel_accepted,
954       NULL, NULL, G_OBJECT (self));
955 }
956 
957 
958 /**
959  * tp_stream_tube_channel_accept_finish:
960  * @self: a #TpStreamTubeChannel
961  * @result: a #GAsyncResult
962  * @error: a #GError to fill
963  *
964  * Finishes accepting an incoming stream tube. The returned
965  * #TpStreamTubeConnection can then be used to exchange data through the tube.
966  *
967  * Returns: (transfer full): a newly created #TpStreamTubeConnection
968  *
969  * Since: 0.13.2
970  */
971 TpStreamTubeConnection *
tp_stream_tube_channel_accept_finish(TpStreamTubeChannel * self,GAsyncResult * result,GError ** error)972 tp_stream_tube_channel_accept_finish (TpStreamTubeChannel *self,
973     GAsyncResult *result,
974     GError **error)
975 {
976   _tp_implement_finish_return_copy_pointer (self,
977       tp_stream_tube_channel_accept_async, g_object_ref)
978 }
979 
980 static void
_new_remote_connection_with_contact(TpConnection * conn,guint n_contacts,TpContact * const * contacts,guint n_failed,const TpHandle * failed,const GError * in_error,gpointer user_data,GObject * obj)981 _new_remote_connection_with_contact (TpConnection *conn,
982     guint n_contacts,
983     TpContact * const *contacts,
984     guint n_failed,
985     const TpHandle *failed,
986     const GError *in_error,
987     gpointer user_data,
988     GObject *obj)
989 {
990   TpStreamTubeChannel *self = (TpStreamTubeChannel *) obj;
991   TpContact *contact;
992   TpStreamTubeConnection *tube_conn = user_data;
993 
994   if (in_error != NULL)
995     {
996       DEBUG ("Failed to prepare TpContact: %s", in_error->message);
997       return;
998     }
999 
1000   if (n_failed > 0)
1001     {
1002       DEBUG ("Failed to prepare TpContact (InvalidHandle)");
1003       return;
1004     }
1005 
1006   contact = contacts[0];
1007 
1008   _tp_stream_tube_connection_set_contact (tube_conn, contact);
1009 
1010   DEBUG ("Accepting incoming GIOStream from %s",
1011       tp_contact_get_identifier (contact));
1012 
1013   g_signal_emit (self, _signals[INCOMING], 0, tube_conn);
1014 
1015   /* anyone receiving the signal is required to hold their own reference */
1016 }
1017 
1018 static gboolean
sig_match_conn(TpStreamTubeChannel * self,SigWaitingConn * sig,ConnWaitingSig * c)1019 sig_match_conn (TpStreamTubeChannel *self,
1020     SigWaitingConn *sig,
1021     ConnWaitingSig *c)
1022 {
1023   if (self->priv->access_control == TP_SOCKET_ACCESS_CONTROL_PORT)
1024     {
1025       /* Use the port to identify the connection */
1026       guint port;
1027       GSocketAddress *address;
1028       GError *error = NULL;
1029 
1030       address = g_socket_connection_get_remote_address (c->conn, &error);
1031       if (address == NULL)
1032         {
1033           DEBUG ("Failed to get connection address: %s", error->message);
1034 
1035           g_error_free (error);
1036           return FALSE;
1037         }
1038 
1039       dbus_g_type_struct_get (sig->param, 1, &port, G_MAXINT);
1040 
1041       if (port == g_inet_socket_address_get_port (
1042             G_INET_SOCKET_ADDRESS (address)))
1043         {
1044           DEBUG ("Identified connection %u using port %u",
1045               port, sig->connection_id);
1046 
1047           g_object_unref (address);
1048           return TRUE;
1049         }
1050 
1051       g_object_unref (address);
1052     }
1053   else if (self->priv->access_control == TP_SOCKET_ACCESS_CONTROL_CREDENTIALS)
1054     {
1055       guchar byte;
1056 
1057       byte = g_value_get_uchar (sig->param);
1058 
1059       return byte == c->byte;
1060     }
1061   else
1062     {
1063       DEBUG ("Can't properly identify connection as we are using "
1064           "access control %u. Assume it's the head of the list",
1065           self->priv->access_control);
1066 
1067       return TRUE;
1068     }
1069 
1070   return FALSE;
1071 }
1072 
1073 static gboolean
can_identify_contact(TpStreamTubeChannel * self)1074 can_identify_contact (TpStreamTubeChannel *self)
1075 {
1076   TpHandleType handle_type;
1077 
1078   tp_channel_get_handle (TP_CHANNEL (self), &handle_type);
1079 
1080   /* With contact stream tube, it's always the same contact connecting to the
1081    * tube */
1082   if (handle_type == TP_HANDLE_TYPE_CONTACT)
1083     return TRUE;
1084 
1085   /* Room stream tube, we need either the Credentials or Port access control
1086    * to properly identify connections. */
1087   if (self->priv->access_control == TP_SOCKET_ACCESS_CONTROL_CREDENTIALS ||
1088       self->priv->access_control == TP_SOCKET_ACCESS_CONTROL_PORT)
1089     return TRUE;
1090 
1091   return FALSE;
1092 }
1093 
1094 static void
connection_identified(TpStreamTubeChannel * self,GSocketConnection * conn,TpHandle handle,guint connection_id)1095 connection_identified (TpStreamTubeChannel *self,
1096     GSocketConnection *conn,
1097     TpHandle handle,
1098     guint connection_id)
1099 {
1100   TpStreamTubeConnection *tube_conn;
1101 
1102   tube_conn = _tp_stream_tube_connection_new (conn, self);
1103 
1104   g_hash_table_insert (self->priv->tube_connections,
1105       GUINT_TO_POINTER (connection_id), tube_conn);
1106 
1107   g_object_weak_ref (G_OBJECT (tube_conn), remote_connection_destroyed_cb,
1108       self);
1109 
1110   if (can_identify_contact (self))
1111     {
1112       TpConnection *connection;
1113       GArray *features;
1114 
1115       connection = tp_channel_get_connection (TP_CHANNEL (self));
1116       features = tp_simple_client_factory_dup_contact_features (
1117           tp_proxy_get_factory (connection), connection);
1118 
1119       /* Spec does not give the id with the handle */
1120       G_GNUC_BEGIN_IGNORE_DEPRECATIONS
1121       /* Pass the ref on tube_conn to the function */
1122       tp_connection_get_contacts_by_handle (connection,
1123           1, &handle,
1124           features->len, (TpContactFeature *) features->data,
1125           _new_remote_connection_with_contact,
1126           tube_conn, g_object_unref, G_OBJECT (self));
1127        G_GNUC_END_IGNORE_DEPRECATIONS
1128 
1129       g_array_unref (features);
1130     }
1131   else
1132     {
1133       g_signal_emit (self, _signals[INCOMING], 0, tube_conn);
1134 
1135       g_object_unref (tube_conn);
1136     }
1137 }
1138 
1139 static void
stream_tube_connection_closed_cb(GObject * source,GAsyncResult * result,gpointer user_data)1140 stream_tube_connection_closed_cb (GObject *source,
1141     GAsyncResult *result,
1142     gpointer user_data)
1143 {
1144   GError *error = NULL;
1145 
1146   if (!g_io_stream_close_finish (G_IO_STREAM (source), result, &error))
1147     {
1148       DEBUG ("Failed to close connection: %s", error->message);
1149 
1150       g_error_free (error);
1151       return;
1152     }
1153 }
1154 
1155 static void
connection_rejected(TpStreamTubeChannel * self,GSocketConnection * conn,TpHandle handle,guint connection_id)1156 connection_rejected (TpStreamTubeChannel *self,
1157     GSocketConnection *conn,
1158     TpHandle handle,
1159     guint connection_id)
1160 {
1161   DEBUG ("Reject connection %u with contact %u", connection_id, handle);
1162 
1163   g_io_stream_close_async (G_IO_STREAM (conn), G_PRIORITY_DEFAULT, NULL,
1164       stream_tube_connection_closed_cb, self);
1165 }
1166 
1167 static void
_new_remote_connection(TpChannel * channel,guint handle,const GValue * param,guint connection_id,gpointer user_data,GObject * obj)1168 _new_remote_connection (TpChannel *channel,
1169     guint handle,
1170     const GValue *param,
1171     guint connection_id,
1172     gpointer user_data,
1173     GObject *obj)
1174 {
1175   TpStreamTubeChannel *self = (TpStreamTubeChannel *) obj;
1176   GSList *l;
1177   ConnWaitingSig *found_conn = NULL;
1178   SigWaitingConn *sig;
1179   TpHandle chan_handle;
1180   TpHandleType handle_type;
1181   gboolean rejected = FALSE;
1182 
1183   chan_handle = tp_channel_get_handle (channel, &handle_type);
1184   if (handle_type == TP_HANDLE_TYPE_CONTACT &&
1185       handle != chan_handle)
1186     {
1187       DEBUG ("CM claimed that handle %u connected to the stream tube, "
1188           "but as a contact stream tube we should only get connection from "
1189           "handle %u", handle, chan_handle);
1190 
1191       rejected = TRUE;
1192     }
1193 
1194   sig = sig_waiting_conn_new (handle, param, connection_id, rejected);
1195 
1196   for (l = self->priv->conn_waiting_sig; l != NULL && found_conn == NULL;
1197       l = g_slist_next (l))
1198     {
1199       ConnWaitingSig *conn = l->data;
1200 
1201       if (sig_match_conn (self, sig, conn))
1202         found_conn = conn;
1203 
1204     }
1205 
1206   if (found_conn == NULL)
1207     {
1208       DEBUG ("Didn't find any connection for %u. Waiting for more",
1209           connection_id);
1210 
1211       /* Pass ownership of sig to the list */
1212       self->priv->sig_waiting_conn = g_slist_append (
1213           self->priv->sig_waiting_conn, sig);
1214       return;
1215     }
1216 
1217   /* We found a connection */
1218   self->priv->conn_waiting_sig = g_slist_remove (
1219       self->priv->conn_waiting_sig, found_conn);
1220 
1221   if (rejected)
1222     connection_rejected (self, found_conn->conn, handle, connection_id);
1223   else
1224     connection_identified (self, found_conn->conn, handle, connection_id);
1225 
1226   sig_waiting_conn_free (sig);
1227   conn_waiting_sig_free (found_conn);
1228 }
1229 
1230 static void
_channel_offered(TpChannel * channel,const GError * in_error,gpointer user_data,GObject * obj)1231 _channel_offered (TpChannel *channel,
1232     const GError *in_error,
1233     gpointer user_data,
1234     GObject *obj)
1235 {
1236   TpStreamTubeChannel *self = (TpStreamTubeChannel *) obj;
1237 
1238   if (in_error != NULL)
1239     {
1240       DEBUG ("Failed to Offer Stream Tube: %s", in_error->message);
1241 
1242       operation_failed (self, in_error);
1243       return;
1244     }
1245 
1246   DEBUG ("Stream Tube offered");
1247 
1248   g_simple_async_result_complete_in_idle (self->priv->result);
1249   tp_clear_object (&self->priv->result);
1250 }
1251 
1252 
1253 static void
_offer_with_address(TpStreamTubeChannel * self,GHashTable * params)1254 _offer_with_address (TpStreamTubeChannel *self,
1255     GHashTable *params)
1256 {
1257   GValue *addressv = NULL;
1258   GError *error = NULL;
1259 
1260   addressv = tp_address_variant_from_g_socket_address (self->priv->address,
1261       &self->priv->socket_type, &error);
1262   if (error != NULL)
1263     {
1264       operation_failed (self, error);
1265 
1266       g_clear_error (&error);
1267       goto finally;
1268     }
1269 
1270   /* Connect the NewRemoteConnection signal */
1271   tp_cli_channel_type_stream_tube_connect_to_new_remote_connection (
1272       TP_CHANNEL (self), _new_remote_connection,
1273       NULL, NULL, G_OBJECT (self), &error);
1274   if (error != NULL)
1275     {
1276       operation_failed (self, error);
1277 
1278       g_clear_error (&error);
1279       goto finally;
1280     }
1281 
1282   g_assert (self->priv->parameters == NULL);
1283   if (params != NULL)
1284     self->priv->parameters = g_hash_table_ref (params);
1285   else
1286     self->priv->parameters = tp_asv_new (NULL, NULL);
1287 
1288   g_object_notify (G_OBJECT (self), "parameters");
1289   g_object_notify (G_OBJECT (self), "parameters-vardict");
1290 
1291   /* Call Offer */
1292   tp_cli_channel_type_stream_tube_call_offer (TP_CHANNEL (self), -1,
1293       self->priv->socket_type, addressv, self->priv->access_control,
1294       self->priv->parameters, _channel_offered, NULL, NULL, G_OBJECT (self));
1295 
1296 finally:
1297   if (addressv != NULL)
1298     tp_g_value_slice_free (addressv);
1299 }
1300 
1301 static SigWaitingConn *
find_sig_for_conn(TpStreamTubeChannel * self,ConnWaitingSig * c)1302 find_sig_for_conn (TpStreamTubeChannel *self,
1303     ConnWaitingSig *c)
1304 {
1305   GSList *l;
1306 
1307   for (l = self->priv->sig_waiting_conn; l != NULL; l = g_slist_next (l))
1308     {
1309       SigWaitingConn *sig = l->data;
1310 
1311       if (sig_match_conn (self, sig, c))
1312         return sig;
1313     }
1314 
1315   return NULL;
1316 }
1317 
1318 static void
credentials_received(TpStreamTubeChannel * self,GSocketConnection * conn,guchar byte)1319 credentials_received (TpStreamTubeChannel *self,
1320     GSocketConnection *conn,
1321     guchar byte)
1322 {
1323   SigWaitingConn *sig;
1324   ConnWaitingSig *c;
1325 
1326   c = conn_waiting_sig_new (conn, byte);
1327 
1328   sig = find_sig_for_conn (self, c);
1329   if (sig == NULL)
1330     {
1331       DEBUG ("Can't identify the connection, wait for NewRemoteConnection sig");
1332 
1333       /* Pass ownership to the list */
1334       self->priv->conn_waiting_sig = g_slist_append (
1335           self->priv->conn_waiting_sig, c);
1336 
1337       return;
1338     }
1339 
1340   /* Connection has been identified */
1341   self->priv->sig_waiting_conn = g_slist_remove (self->priv->sig_waiting_conn,
1342       sig);
1343 
1344   if (sig->rejected)
1345     connection_rejected (self, conn, sig->handle, sig->connection_id);
1346   else
1347     connection_identified (self, conn, sig->handle, sig->connection_id);
1348 
1349   sig_waiting_conn_free (sig);
1350   conn_waiting_sig_free (c);
1351 }
1352 
1353 #ifdef HAVE_GIO_UNIX
1354 static void
receive_credentials_cb(GObject * source,GAsyncResult * result,gpointer user_data)1355 receive_credentials_cb (GObject *source,
1356     GAsyncResult *result,
1357     gpointer user_data)
1358 {
1359   TpStreamTubeChannel *self = user_data;
1360   GSocketConnection *conn = (GSocketConnection *) source;
1361   GCredentials *creds;
1362   guchar byte;
1363   uid_t uid;
1364   GError *error = NULL;
1365 
1366   creds = tp_unix_connection_receive_credentials_with_byte_finish (conn, result,
1367       &byte, &error);
1368 
1369   if (creds == NULL)
1370     {
1371       DEBUG ("Failed to receive credentials: %s", error->message);
1372       g_error_free (error);
1373       return;
1374     }
1375 
1376   uid = g_credentials_get_unix_user (creds, &error);
1377   if (uid != geteuid ())
1378     {
1379       DEBUG ("Wrong credentials received (user: %u)", uid);
1380       return;
1381     }
1382 
1383   credentials_received (self, conn, byte);
1384 
1385   g_object_unref (creds);
1386 }
1387 #endif
1388 
1389 static void
service_incoming_cb(GSocketService * service,GSocketConnection * conn,GObject * source_object,gpointer user_data)1390 service_incoming_cb (GSocketService *service,
1391     GSocketConnection *conn,
1392     GObject *source_object,
1393     gpointer user_data)
1394 {
1395   TpStreamTubeChannel *self = user_data;
1396 
1397   DEBUG ("New incoming connection");
1398 
1399 #ifdef HAVE_GIO_UNIX
1400   /* Check the credentials if needed */
1401   if (self->priv->access_control == TP_SOCKET_ACCESS_CONTROL_CREDENTIALS)
1402     {
1403       tp_unix_connection_receive_credentials_with_byte_async (conn, NULL,
1404           receive_credentials_cb, self);
1405       return;
1406     }
1407 #endif
1408 
1409   credentials_received (self, conn, 0);
1410 }
1411 
1412 /**
1413  * tp_stream_tube_channel_offer_async:
1414  * @self: an outgoing #TpStreamTubeChannel
1415  * @params: (allow-none) (transfer none): parameters of the tube, or %NULL
1416  * @callback: a callback to call when the tube has been offered
1417  * @user_data: data to pass to @callback
1418  *
1419  * Offer an outgoing stream tube. When the tube has been offered, @callback
1420  * will be called. You can then call tp_stream_tube_channel_offer_finish()
1421  * to get the result of the operation.
1422  *
1423  * You have to connect to the #TpStreamTubeChannel::incoming signal to get a
1424  * #TpStreamTubeConnection each time a contact establishes a connection to
1425  * the tube.
1426  *
1427  * Since: 0.13.2
1428  */
1429 void
tp_stream_tube_channel_offer_async(TpStreamTubeChannel * self,GHashTable * params,GAsyncReadyCallback callback,gpointer user_data)1430 tp_stream_tube_channel_offer_async (TpStreamTubeChannel *self,
1431     GHashTable *params,
1432     GAsyncReadyCallback callback,
1433     gpointer user_data)
1434 {
1435   GHashTable *properties;
1436   GHashTable *supported_sockets;
1437   GError *error = NULL;
1438 
1439   g_return_if_fail (TP_IS_STREAM_TUBE_CHANNEL (self));
1440   g_return_if_fail (self->priv->result == NULL);
1441   g_return_if_fail (tp_channel_get_requested (TP_CHANNEL (self)));
1442 
1443   if (self->priv->service != NULL)
1444     {
1445       g_critical ("Can't reoffer Tube!");
1446       return;
1447     }
1448 
1449   self->priv->result = g_simple_async_result_new (G_OBJECT (self), callback,
1450       user_data, tp_stream_tube_channel_offer_async);
1451 
1452   properties = _tp_channel_get_immutable_properties (TP_CHANNEL (self));
1453   supported_sockets = tp_asv_get_boxed (properties,
1454       TP_PROP_CHANNEL_TYPE_STREAM_TUBE_SUPPORTED_SOCKET_TYPES,
1455       TP_HASH_TYPE_SUPPORTED_SOCKET_MAP);
1456 
1457   if (!_tp_set_socket_address_type_and_access_control_type (supported_sockets,
1458       &self->priv->socket_type, &self->priv->access_control, &error))
1459     {
1460       operation_failed (self, error);
1461 
1462       g_clear_error (&error);
1463       return;
1464     }
1465 
1466   DEBUG ("Using socket type %u with access control %u", self->priv->socket_type,
1467       self->priv->access_control);
1468 
1469   self->priv->service = g_socket_service_new ();
1470 
1471   switch (self->priv->socket_type)
1472     {
1473 #ifdef HAVE_GIO_UNIX
1474       case TP_SOCKET_ADDRESS_TYPE_UNIX:
1475         {
1476           self->priv->address = _tp_create_temp_unix_socket (
1477               self->priv->service, &self->priv->unix_tmpdir, &error);
1478 
1479           /* check there wasn't an error on the final attempt */
1480           if (self->priv->address == NULL)
1481             {
1482               operation_failed (self, error);
1483 
1484               g_clear_error (&error);
1485               return;
1486             }
1487         }
1488 
1489         break;
1490 #endif /* HAVE_GIO_UNIX */
1491 
1492       case TP_SOCKET_ADDRESS_TYPE_IPV4:
1493       case TP_SOCKET_ADDRESS_TYPE_IPV6:
1494         {
1495           GInetAddress *localhost;
1496           GSocketAddress *in_address;
1497 
1498           localhost = g_inet_address_new_loopback (
1499               self->priv->socket_type == TP_SOCKET_ADDRESS_TYPE_IPV4 ?
1500               G_SOCKET_FAMILY_IPV4 : G_SOCKET_FAMILY_IPV6);
1501           in_address = g_inet_socket_address_new (localhost, 0);
1502 
1503           g_socket_listener_add_address (
1504               G_SOCKET_LISTENER (self->priv->service), in_address,
1505               G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT,
1506               NULL, &self->priv->address, &error);
1507 
1508           g_object_unref (localhost);
1509           g_object_unref (in_address);
1510 
1511           if (error != NULL)
1512             {
1513               operation_failed (self, error);
1514 
1515               g_clear_error (&error);
1516               return;
1517             }
1518 
1519           break;
1520         }
1521 
1522       default:
1523         /* should have already errored */
1524         g_assert_not_reached ();
1525         break;
1526     }
1527 
1528   tp_g_signal_connect_object (self->priv->service, "incoming",
1529       G_CALLBACK (service_incoming_cb), self, 0);
1530 
1531   g_socket_service_start (self->priv->service);
1532 
1533   _offer_with_address (self, params);
1534 }
1535 
1536 /**
1537  * tp_stream_tube_channel_offer_finish:
1538  * @self: a #TpStreamTubeChannel
1539  * @result: a #GAsyncResult
1540  * @error: a #GError to fill
1541  *
1542  * Finishes offering an outgoing stream tube.
1543  *
1544  * Returns: %TRUE when a Tube has been successfully offered; %FALSE otherwise
1545  *
1546  * Since: 0.13.2
1547  */
1548 gboolean
tp_stream_tube_channel_offer_finish(TpStreamTubeChannel * self,GAsyncResult * result,GError ** error)1549 tp_stream_tube_channel_offer_finish (TpStreamTubeChannel *self,
1550     GAsyncResult *result,
1551     GError **error)
1552 {
1553   _tp_implement_finish_void (self, tp_stream_tube_channel_offer_async)
1554 }
1555 
1556 /**
1557  * tp_stream_tube_channel_get_service:
1558  * @self: a #TpStreamTubeChannel
1559  *
1560  * Return the #TpStreamTubeChannel:service property
1561  *
1562  * Returns: (transfer none): the value of #TpStreamTubeChannel:service
1563  *
1564  * Since: 0.13.2
1565  */
1566 const gchar *
tp_stream_tube_channel_get_service(TpStreamTubeChannel * self)1567 tp_stream_tube_channel_get_service (TpStreamTubeChannel *self)
1568 {
1569   GHashTable *props;
1570 
1571   props = _tp_channel_get_immutable_properties (TP_CHANNEL (self));
1572 
1573   return tp_asv_get_string (props, TP_PROP_CHANNEL_TYPE_STREAM_TUBE_SERVICE);
1574 }
1575 
1576 /**
1577  * tp_stream_tube_channel_get_parameters: (skip)
1578  * @self: a #TpStreamTubeChannel
1579  *
1580  * Return the #TpStreamTubeChannel:parameters property
1581  *
1582  * Returns: (transfer none) (element-type utf8 GObject.Value):
1583  * the value of #TpStreamTubeChannel:parameters
1584  *
1585  * Since: 0.13.2
1586  */
1587 GHashTable *
tp_stream_tube_channel_get_parameters(TpStreamTubeChannel * self)1588 tp_stream_tube_channel_get_parameters (TpStreamTubeChannel *self)
1589 {
1590   return self->priv->parameters;
1591 }
1592 
1593 /**
1594  * tp_stream_tube_channel_dup_parameters_vardict:
1595  * @self: a #TpStreamTubeChannel
1596  *
1597  * Return the parameters of the dbus-tube channel in a variant of
1598  * type %G_VARIANT_TYPE_VARDICT whose keys are strings representing
1599  * parameter names and values are variants representing corresponding
1600  * parameter values set by the offerer when offering this channel.
1601  *
1602  * The GVariant returned is %NULL if this is an outgoing tube that has not
1603  * yet been offered or the parameters property has not been set.
1604  *
1605  * Use g_variant_lookup(), g_variant_lookup_value(), or tp_vardict_get_uint32()
1606  * and similar functions for convenient access to the values.
1607  *
1608  * Returns: (transfer full): a new reference to a #GVariant
1609  *
1610  * Since: 0.19.10
1611  */
1612 GVariant *
tp_stream_tube_channel_dup_parameters_vardict(TpStreamTubeChannel * self)1613 tp_stream_tube_channel_dup_parameters_vardict (TpStreamTubeChannel *self)
1614 {
1615   g_return_val_if_fail (TP_IS_STREAM_TUBE_CHANNEL (self), NULL);
1616 
1617   if (self->priv->parameters == NULL)
1618       return NULL;
1619 
1620   return _tp_asv_to_vardict (self->priv->parameters);
1621 }
1622