]> git.0d.be Git - empathy.git/blob - libempathy/empathy-tp-tube.c
Call ListTubes in EmpathyTpTube's constructor to avoid races
[empathy.git] / libempathy / empathy-tp-tube.c
1 /*
2  * Copyright (C) 2008 Collabora Ltd.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  *
18  * Authors: Guillaume Desmottes <guillaume.desmottes@collabora.co.uk>
19  *          Elliot Fairweather <elliot.fairweather@collabora.co.uk>
20  */
21
22 #include <config.h>
23
24 #include <telepathy-glib/connection.h>
25
26 #include "empathy-contact-factory.h"
27 #include "empathy-debug.h"
28 #include "empathy-enum-types.h"
29 #include "empathy-tp-tube.h"
30 #include "empathy-utils.h"
31
32 #define DEBUG_DOMAIN "TpTube"
33
34 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_TP_TUBE, \
35     EmpathyTpTubePriv))
36
37 typedef struct _EmpathyTpTubePriv EmpathyTpTubePriv;
38
39 struct _EmpathyTpTubePriv
40 {
41   TpChannel *channel;
42   guint id;
43   guint initiator;
44   guint type;
45   gchar *service;
46   GHashTable *parameters;
47   guint state;
48   EmpathyContact *initiator_contact;
49   EmpathyContactFactory *factory;
50 };
51
52 enum
53 {
54   PROP_0,
55   PROP_CHANNEL,
56   PROP_TP_TUBES,
57   PROP_ID,
58   PROP_INITIATOR,
59   PROP_TYPE,
60   PROP_SERVICE,
61   PROP_PARAMETERS,
62   PROP_STATE,
63   PROP_INITIATOR_CONTACT
64 };
65
66 enum
67 {
68   DESTROY,
69   LAST_SIGNAL
70 };
71
72 static guint signals[LAST_SIGNAL];
73
74 G_DEFINE_TYPE (EmpathyTpTube, empathy_tp_tube, G_TYPE_OBJECT)
75
76 static void
77 tp_tube_state_changed_cb (TpChannel *channel,
78                           guint id,
79                           guint state,
80                           gpointer user_data,
81                           GObject *tube)
82 {
83   EmpathyTpTubePriv *priv = GET_PRIV (tube);
84
85   if (id != priv->id)
86       return;
87
88   empathy_debug (DEBUG_DOMAIN, "Tube state changed");
89
90   priv->state = state;
91   g_object_notify (tube, "state");
92 }
93
94 static void
95 tp_tube_invalidated_cb (TpChannel     *channel,
96                         GQuark         domain,
97                         gint           code,
98                         gchar         *message,
99                         EmpathyTpTube *tube)
100 {
101   empathy_debug (DEBUG_DOMAIN, "Channel invalidated: %s", message);
102   g_signal_emit (tube, signals[DESTROY], 0);
103 }
104
105 static void
106 tp_tube_closed_cb (TpChannel *channel,
107                    guint id,
108                    gpointer user_data,
109                    GObject *tube)
110 {
111   EmpathyTpTubePriv *priv = GET_PRIV (tube);
112
113   if (id != priv->id)
114       return;
115
116   empathy_debug (DEBUG_DOMAIN, "Tube closed");
117   g_signal_emit (tube, signals[DESTROY], 0);
118 }
119
120 static void
121 tp_tube_async_cb (TpChannel *channel,
122                   const GError *error,
123                   gpointer user_data,
124                   GObject *tube)
125 {
126   if (error)
127       empathy_debug (DEBUG_DOMAIN, "Error %s: %s", user_data, error->message);
128 }
129
130 static void
131 tp_tube_set_property (GObject *object,
132                       guint prop_id,
133                       const GValue *value,
134                       GParamSpec *pspec)
135 {
136   EmpathyTpTubePriv *priv = GET_PRIV (object);
137
138   switch (prop_id)
139     {
140       case PROP_CHANNEL:
141         priv->channel = g_value_dup_object (value);
142         break;
143       case PROP_ID:
144         priv->id = g_value_get_uint (value);
145         break;
146       default:
147         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
148         break;
149   }
150 }
151
152 static void
153 tp_tube_get_property (GObject *object,
154                       guint prop_id,
155                       GValue *value,
156                       GParamSpec *pspec)
157 {
158   EmpathyTpTubePriv *priv = GET_PRIV (object);
159
160   switch (prop_id)
161     {
162       case PROP_CHANNEL:
163         g_value_set_object (value, priv->channel);
164         break;
165       case PROP_ID:
166         g_value_set_uint (value, priv->id);
167         break;
168       case PROP_INITIATOR:
169         g_value_set_uint (value, priv->initiator);
170         break;
171       case PROP_TYPE:
172         g_value_set_uint (value, priv->type);
173         break;
174       case PROP_SERVICE:
175         g_value_set_string (value, priv->service);
176         break;
177       case PROP_PARAMETERS:
178         g_value_set_boxed (value, priv->parameters);
179         break;
180       case PROP_STATE:
181         g_value_set_uint (value, priv->state);
182         break;
183       case PROP_INITIATOR_CONTACT:
184         g_value_set_object (value, priv->initiator_contact);
185         break;
186       default:
187         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
188         break;
189   }
190 }
191
192 static GObject *
193 tp_tube_constructor (GType type,
194                      guint n_props,
195                      GObjectConstructParam *props)
196 {
197   GObject *self;
198   EmpathyTpTubePriv *priv;
199   GPtrArray *tubes;
200   guint i;
201   GError *error = NULL;
202
203   self = G_OBJECT_CLASS (empathy_tp_tube_parent_class)->constructor (
204       type, n_props, props);
205   priv = GET_PRIV (self);
206
207   g_signal_connect (priv->channel, "invalidated",
208       G_CALLBACK (tp_tube_invalidated_cb), self);
209   tp_cli_channel_type_tubes_connect_to_tube_closed (priv->channel,
210       tp_tube_closed_cb, NULL, NULL, self, NULL);
211   tp_cli_channel_type_tubes_connect_to_tube_state_changed (priv->channel,
212       tp_tube_state_changed_cb, NULL, NULL, self, NULL);
213
214   /* FIXME: It is absolutely not opimized to list all tubes to get information
215    * about our tube, but we don't really have the choice to avoid races. */
216   if (!tp_cli_channel_type_tubes_run_list_tubes (priv->channel, -1, &tubes,
217       &error, NULL))
218     {
219       empathy_debug (DEBUG_DOMAIN, "Couldn't list tubes: %s",
220           error->message);
221       g_clear_error (&error);
222       return self;
223     }
224
225   for (i = 0; i < tubes->len; i++)
226     {
227       GValueArray *values;
228       guint id;
229
230       values = g_ptr_array_index (tubes, i);
231       id = g_value_get_uint (g_value_array_get_nth (values, 0));
232
233       if (id == priv->id)
234         {
235           TpConnection *connection;
236           MissionControl *mc;
237           McAccount *account;
238
239           g_object_get (priv->channel, "connection", &connection, NULL);
240           mc = empathy_mission_control_new ();
241           account = mission_control_get_account_for_tpconnection (mc,
242               connection, NULL);
243
244           priv->initiator = g_value_get_uint (g_value_array_get_nth (values, 1));
245           priv->type = g_value_get_uint (g_value_array_get_nth (values, 2));
246           priv->service = g_value_dup_string (g_value_array_get_nth (values, 3));
247           priv->parameters = g_value_dup_boxed (g_value_array_get_nth (values, 4));
248           priv->state = g_value_get_uint (g_value_array_get_nth (values, 5));
249           priv->initiator_contact = empathy_contact_factory_get_from_handle (
250               priv->factory, account, priv->initiator);
251
252           g_object_unref (connection);
253           g_object_unref (mc);
254           g_object_unref (account);
255         }
256
257       g_value_array_free (values);
258     }
259   g_ptr_array_free (tubes, TRUE);
260
261   return self;
262 }
263
264 static void
265 tp_tube_finalize (GObject *object)
266 {
267   EmpathyTpTubePriv *priv = GET_PRIV (object);
268
269   empathy_debug (DEBUG_DOMAIN, "Finalizing: %p", object);
270
271   if (priv->channel)
272     {
273       g_signal_handlers_disconnect_by_func (priv->channel,
274           tp_tube_invalidated_cb, object);
275       tp_cli_channel_type_tubes_call_close_tube (priv->channel, -1, priv->id,
276           tp_tube_async_cb, "closing tube", NULL, NULL);
277       g_object_unref (priv->channel);
278     }
279   if (priv->initiator_contact)
280       g_object_unref (priv->initiator_contact);
281   if (priv->factory)
282       g_object_unref (priv->factory);
283
284   g_free (priv->service);
285   g_hash_table_destroy (priv->parameters);
286
287   G_OBJECT_CLASS (empathy_tp_tube_parent_class)->finalize (object);
288 }
289
290 static void
291 empathy_tp_tube_class_init (EmpathyTpTubeClass *klass)
292 {
293   GObjectClass *object_class = G_OBJECT_CLASS (klass);
294
295   object_class->constructor = tp_tube_constructor;
296   object_class->finalize = tp_tube_finalize;
297   object_class->set_property = tp_tube_set_property;
298   object_class->get_property = tp_tube_get_property;
299
300   g_object_class_install_property (object_class, PROP_CHANNEL,
301       g_param_spec_object ("channel", "channel", "channel", TP_TYPE_CHANNEL,
302       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME |
303         G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
304
305   g_object_class_install_property (object_class, PROP_ID,
306       g_param_spec_uint ("id", "id", "id", 0, G_MAXUINT, 0,
307         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME |
308         G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
309
310   g_object_class_install_property (object_class, PROP_INITIATOR,
311       g_param_spec_uint ("initiator", "initiator", "initiator",
312         0, G_MAXUINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_NAME |
313         G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
314
315   g_object_class_install_property (object_class, PROP_TYPE,
316       g_param_spec_uint ("type", "type", "type", 0, G_MAXUINT, 0,
317         G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
318         G_PARAM_STATIC_BLURB));
319
320   g_object_class_install_property (object_class, PROP_SERVICE,
321       g_param_spec_string ("service", "service", "service", NULL,
322       G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
323       G_PARAM_STATIC_BLURB));
324
325   g_object_class_install_property (object_class, PROP_PARAMETERS,
326       g_param_spec_boxed ("parameters", "parameters", "parameters",
327       G_TYPE_HASH_TABLE, G_PARAM_READABLE | G_PARAM_STATIC_NAME |
328       G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
329
330   g_object_class_install_property (object_class, PROP_STATE,
331       g_param_spec_uint ("state", "state", "state", 0, G_MAXUINT, 0,
332         G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
333         G_PARAM_STATIC_BLURB));
334
335   g_object_class_install_property (object_class, PROP_INITIATOR_CONTACT,
336      g_param_spec_object ("initiator-contact", "initiator contact",
337      "initiator contact", EMPATHY_TYPE_CONTACT, G_PARAM_READABLE |
338      G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
339
340   signals[DESTROY] = g_signal_new ("destroy",
341       G_TYPE_FROM_CLASS (klass),
342       G_SIGNAL_RUN_LAST,
343       0, NULL, NULL,
344       g_cclosure_marshal_VOID__VOID,
345       G_TYPE_NONE, 0);
346
347   g_type_class_add_private (klass, sizeof (EmpathyTpTubePriv));
348 }
349
350 static void
351 empathy_tp_tube_init (EmpathyTpTube *tube)
352 {
353   EmpathyTpTubePriv *priv = GET_PRIV (tube);
354
355   priv->factory = empathy_contact_factory_new ();
356 }
357
358 EmpathyTpTube *
359 empathy_tp_tube_new (TpChannel *channel, guint tube_id)
360 {
361   g_return_val_if_fail (TP_IS_CHANNEL (channel), NULL);
362
363   return g_object_new (EMPATHY_TYPE_TP_TUBE,
364       "channel", channel, "id", tube_id, NULL);
365 }
366
367 EmpathyTpTube *
368 empathy_tp_tube_new_stream_tube (EmpathyContact *contact,
369                                  TpSocketAddressType type,
370                                  const gchar *hostname,
371                                  guint port,
372                                  const gchar *service)
373 {
374   MissionControl *mc;
375   McAccount *account;
376   TpConnection *connection;
377   TpChannel *channel;
378   gchar *object_path;
379   guint id;
380   GHashTable *params;
381   GValue *address;
382   GValue *control_param;
383   EmpathyTpTube *tube;
384   GError *error = NULL;
385
386   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
387   g_return_val_if_fail (hostname != NULL, NULL);
388   g_return_val_if_fail (service != NULL, NULL);
389
390   mc = empathy_mission_control_new ();
391   account = empathy_contact_get_account (contact);
392   connection = mission_control_get_tpconnection (mc, account, NULL);
393
394   if (!tp_cli_connection_run_request_channel (connection, -1,
395       TP_IFACE_CHANNEL_TYPE_TUBES, TP_HANDLE_TYPE_CONTACT,
396       empathy_contact_get_handle (contact), FALSE, &object_path, &error, NULL))
397     {
398       g_clear_error (&error);
399       g_object_unref (mc);
400       g_object_unref (account);
401       g_object_unref (connection);
402       return NULL;
403     }
404
405   empathy_debug (DEBUG_DOMAIN, "Offering a new stream tube");
406
407   channel = tp_channel_new (connection, object_path,
408       TP_IFACE_CHANNEL_TYPE_TUBES, TP_HANDLE_TYPE_CONTACT,
409       empathy_contact_get_handle (contact), NULL);
410
411   params = g_hash_table_new (g_str_hash, g_str_equal);
412   address = g_new0 (GValue, 1);
413   g_value_init (address,
414       dbus_g_type_get_struct ("GValueArray", G_TYPE_STRING, G_TYPE_UINT,
415       G_TYPE_INVALID));
416   g_value_take_boxed (address,
417       dbus_g_type_specialized_construct (dbus_g_type_get_struct ("GValueArray",
418         G_TYPE_STRING, G_TYPE_UINT, G_TYPE_INVALID)));
419   dbus_g_type_struct_set (address, 0, hostname, 1, port, G_MAXUINT);
420
421   /* localhost access control, variant is ignored */
422   control_param = g_new0 (GValue, 1);
423   g_value_init (control_param, G_TYPE_STRING);
424
425   if (!tp_cli_channel_type_tubes_run_offer_stream_tube (channel, -1,
426         service, params, type, address,
427         TP_SOCKET_ACCESS_CONTROL_LOCALHOST, control_param, &id, &error, NULL))
428     {
429       empathy_debug (DEBUG_DOMAIN, "Couldn't offer tube: %s", error->message);
430       g_clear_error (&error);
431     }
432
433   empathy_debug (DEBUG_DOMAIN, "Stream tube id=%d offered", id);
434
435   tube = empathy_tp_tube_new (channel, id);
436
437   g_object_unref (channel);
438   g_free (object_path);
439   g_hash_table_destroy (params);
440   g_value_reset (address);
441   g_value_reset (control_param);
442   g_free (address);
443   g_free (control_param);
444   g_object_unref (mc);
445   g_object_unref (account);
446   g_object_unref (connection);
447
448   return tube;
449 }
450
451 static void
452 tp_tube_accept_stream_cb (TpChannel *proxy,
453                           const GValue *address,
454                           const GError *error,
455                           gpointer user_data,
456                           GObject *weak_object)
457 {
458   if (error)
459       empathy_debug (DEBUG_DOMAIN, "Error accepting tube: %s", error->message);
460 }
461
462 void
463 empathy_tp_tube_accept_stream_tube (EmpathyTpTube *tube,
464                                     TpSocketAddressType type)
465 {
466   EmpathyTpTubePriv *priv = GET_PRIV (tube);
467   GValue control_param = {0, };
468
469   g_return_if_fail (EMPATHY_IS_TP_TUBE (tube));
470
471   empathy_debug (DEBUG_DOMAIN, "Accepting stream tube - id: %d", priv->id);
472
473   tp_cli_channel_type_tubes_call_accept_stream_tube (priv->channel, -1, priv->id,
474       type, TP_SOCKET_ACCESS_CONTROL_LOCALHOST, &control_param,
475       tp_tube_accept_stream_cb, NULL, NULL, G_OBJECT (tube));
476
477   g_value_reset (&control_param);
478 }
479
480 void
481 empathy_tp_tube_get_socket (EmpathyTpTube *tube,
482                             gchar **hostname,
483                             guint *port)
484 {
485   EmpathyTpTubePriv *priv = GET_PRIV (tube);
486   GValue *address = g_new0 (GValue, 1);
487   guint address_type;
488   GError *error = NULL;
489
490   g_return_if_fail (EMPATHY_IS_TP_TUBE (tube));
491
492   empathy_debug (DEBUG_DOMAIN, "Getting stream tube socket address");
493
494   if (!tp_cli_channel_type_tubes_run_get_stream_tube_socket_address (priv->channel,
495       -1, priv->id, &address_type, &address, &error, NULL))
496     {
497       empathy_debug (DEBUG_DOMAIN, "Couldn't get socket address: %s",
498           error->message);
499       g_clear_error (&error);
500       return;
501     }
502
503   switch (address_type)
504     {
505     case TP_SOCKET_ADDRESS_TYPE_UNIX:
506     case TP_SOCKET_ADDRESS_TYPE_ABSTRACT_UNIX:
507         dbus_g_type_struct_get (address, 0, hostname, G_MAXUINT);
508         break;
509     case TP_SOCKET_ADDRESS_TYPE_IPV4:
510     case TP_SOCKET_ADDRESS_TYPE_IPV6:
511         dbus_g_type_struct_get (address, 0, hostname, 1, port, G_MAXUINT);    
512         break;
513     }
514
515   g_value_reset (address);
516   g_free (address);
517 }
518