]> git.0d.be Git - empathy.git/blob - libempathy/empathy-tp-tube.c
Add empathy_offer_ipv4_stream_tube().
[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       case PROP_INITIATOR:
147         priv->initiator = g_value_get_uint (value);
148         break;
149       case PROP_TYPE:
150         priv->type = g_value_get_uint (value);
151         break;
152       case PROP_SERVICE:
153         priv->service = g_value_dup_string (value);
154         break;
155       case PROP_PARAMETERS:
156         priv->parameters = g_value_dup_boxed (value);
157         break;
158       case PROP_STATE:
159         priv->state = g_value_get_uint (value);
160         break;
161       default:
162         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
163         break;
164   }
165 }
166
167 static void
168 tp_tube_get_property (GObject *object,
169                       guint prop_id,
170                       GValue *value,
171                       GParamSpec *pspec)
172 {
173   EmpathyTpTubePriv *priv = GET_PRIV (object);
174
175   switch (prop_id)
176     {
177       case PROP_CHANNEL:
178         g_value_set_object (value, priv->channel);
179         break;
180       case PROP_ID:
181         g_value_set_uint (value, priv->id);
182         break;
183       case PROP_INITIATOR:
184         g_value_set_uint (value, priv->initiator);
185         break;
186       case PROP_TYPE:
187         g_value_set_uint (value, priv->type);
188         break;
189       case PROP_SERVICE:
190         g_value_set_string (value, priv->service);
191         break;
192       case PROP_PARAMETERS:
193         g_value_set_boxed (value, priv->parameters);
194         break;
195       case PROP_STATE:
196         g_value_set_uint (value, priv->state);
197         break;
198       case PROP_INITIATOR_CONTACT:
199         g_value_set_object (value, priv->initiator_contact);
200         break;
201       default:
202         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
203         break;
204   }
205 }
206
207 static GObject *
208 tp_tube_constructor (GType type,
209                      guint n_props,
210                      GObjectConstructParam *props)
211 {
212   GObject *self;
213   EmpathyTpTubePriv *priv;
214   TpConnection *connection;
215   MissionControl *mc;
216   McAccount *account;
217
218   self = G_OBJECT_CLASS (empathy_tp_tube_parent_class)->constructor (
219       type, n_props, props);
220   priv = GET_PRIV (self);
221
222   g_object_get (priv->channel, "connection", &connection, NULL);
223   mc = empathy_mission_control_new ();
224   account = mission_control_get_account_for_tpconnection (mc, connection, NULL);
225
226   priv->factory = empathy_contact_factory_new ();
227   priv->initiator_contact = empathy_contact_factory_get_from_handle (priv->factory,
228       account, priv->initiator);
229   g_object_ref (priv->initiator_contact);
230
231   g_signal_connect (priv->channel, "invalidated",
232       G_CALLBACK (tp_tube_invalidated_cb), self);
233
234   tp_cli_channel_type_tubes_connect_to_tube_closed (priv->channel,
235       tp_tube_closed_cb, NULL, NULL, self, NULL);
236   tp_cli_channel_type_tubes_connect_to_tube_state_changed (priv->channel,
237       tp_tube_state_changed_cb, NULL, NULL, self, NULL);
238
239   g_object_unref (connection);
240   g_object_unref (mc);
241   g_object_unref (account);
242
243   return self;
244 }
245
246 static void
247 tp_tube_finalize (GObject *object)
248 {
249   EmpathyTpTubePriv *priv = GET_PRIV (object);
250
251   empathy_debug (DEBUG_DOMAIN, "Finalizing: %p", object);
252
253   if (priv->channel)
254     {
255       g_signal_handlers_disconnect_by_func (priv->channel,
256           tp_tube_invalidated_cb, object);
257       tp_cli_channel_type_tubes_call_close_tube (priv->channel, -1, priv->id,
258           tp_tube_async_cb, "closing tube", NULL, NULL);
259       g_object_unref (priv->channel);
260     }
261   if (priv->initiator_contact)
262       g_object_unref (priv->initiator_contact);
263   if (priv->factory)
264       g_object_unref (priv->factory);
265
266   g_free (priv->service);
267   g_hash_table_destroy (priv->parameters);
268
269   G_OBJECT_CLASS (empathy_tp_tube_parent_class)->finalize (object);
270 }
271
272 static void
273 empathy_tp_tube_class_init (EmpathyTpTubeClass *klass)
274 {
275   GObjectClass *object_class = G_OBJECT_CLASS (klass);
276
277   object_class->constructor = tp_tube_constructor;
278   object_class->finalize = tp_tube_finalize;
279   object_class->set_property = tp_tube_set_property;
280   object_class->get_property = tp_tube_get_property;
281
282   g_object_class_install_property (object_class, PROP_CHANNEL,
283       g_param_spec_object ("channel", "channel", "channel", TP_TYPE_CHANNEL,
284       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME |
285         G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
286
287   g_object_class_install_property (object_class, PROP_ID,
288       g_param_spec_uint ("id", "id", "id", 0, G_MAXUINT, 0,
289         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME |
290         G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
291
292   g_object_class_install_property (object_class, PROP_INITIATOR,
293       g_param_spec_uint ("initiator", "initiator", "initiator",
294         0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
295         G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
296
297   g_object_class_install_property (object_class, PROP_TYPE,
298       g_param_spec_uint ("type", "type", "type", 0, G_MAXUINT, 0,
299         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME |
300         G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
301
302   g_object_class_install_property (object_class, PROP_SERVICE,
303       g_param_spec_string ("service", "service", "service", NULL,
304       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME |
305       G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
306
307   g_object_class_install_property (object_class, PROP_PARAMETERS,
308       g_param_spec_boxed ("parameters", "parameters", "parameters",
309       G_TYPE_HASH_TABLE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
310       G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
311
312   g_object_class_install_property (object_class, PROP_STATE,
313       g_param_spec_uint ("state", "state", "state", 0, G_MAXUINT, 0,
314         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
315         G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
316
317   g_object_class_install_property (object_class, PROP_INITIATOR_CONTACT,
318      g_param_spec_object ("initiator-contact", "initiator contact",
319      "initiator contact", EMPATHY_TYPE_CONTACT, G_PARAM_READABLE |
320      G_PARAM_STATIC_NAME |  G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
321
322   signals[DESTROY] = g_signal_new ("destroy",
323       G_TYPE_FROM_CLASS (klass),
324       G_SIGNAL_RUN_LAST,
325       0, NULL, NULL,
326       g_cclosure_marshal_VOID__VOID,
327       G_TYPE_NONE, 0);
328
329   g_type_class_add_private (klass, sizeof (EmpathyTpTubePriv));
330 }
331
332 static void
333 empathy_tp_tube_init (EmpathyTpTube *tp_tubes)
334 {
335 }
336
337 EmpathyTpTube *
338 empathy_tp_tube_new (TpChannel *channel, guint tube_id)
339 {
340   EmpathyTpTube *tube = NULL;
341   GPtrArray *tubes;
342   guint i;
343   GError *error = NULL;
344
345   g_return_val_if_fail (TP_IS_CHANNEL (channel), NULL);
346
347   if (!tp_cli_channel_type_tubes_run_list_tubes (channel, -1, &tubes,
348       &error, NULL))
349     {
350       empathy_debug (DEBUG_DOMAIN, "Couldn't list tubes: %s",
351           error->message);
352       g_clear_error (&error);
353       return NULL;
354     }
355
356   for (i = 0; i < tubes->len; i++)
357     {
358       GValueArray *values;
359       guint id;
360
361       values = g_ptr_array_index (tubes, i);
362       id = g_value_get_uint (g_value_array_get_nth (values, 0));
363
364       if (id != tube_id)
365         {
366           g_value_array_free (values);
367           continue;
368         }
369
370       tube = g_object_new (EMPATHY_TYPE_TP_TUBE,
371           "channel", channel,
372           "id", id,
373           "initiator", g_value_get_uint (g_value_array_get_nth (values, 1)),
374           "type", g_value_get_uint (g_value_array_get_nth (values, 2)),
375           "service", g_value_get_string (g_value_array_get_nth (values, 3)),
376           "parameters", g_value_get_boxed (g_value_array_get_nth (values, 4)),
377           "state", g_value_get_uint (g_value_array_get_nth (values, 5)),
378           NULL);
379
380       g_value_array_free (values);
381     }
382   g_ptr_array_free (tubes, TRUE);
383
384   return tube;
385 }
386
387 static void
388 tp_tube_accept_stream_cb (TpChannel *proxy,
389                           const GValue *address,
390                           const GError *error,
391                           gpointer user_data,
392                           GObject *weak_object)
393 {
394   if (error)
395       empathy_debug (DEBUG_DOMAIN, "Error accepting tube: %s", error->message);
396 }
397
398 static void
399 tp_tube_accept_stream_tube (EmpathyTpTube *tube,
400                             TpSocketAddressType address_type,
401                             TpSocketAccessControl access_type,
402                             GValue *control_param)
403 {
404   EmpathyTpTubePriv *priv = GET_PRIV (tube);
405
406   empathy_debug (DEBUG_DOMAIN, "Accepting stream tube - id: %d", priv->id);
407
408   tp_cli_channel_type_tubes_call_accept_stream_tube (priv->channel, -1, priv->id,
409       address_type, access_type, control_param,
410       tp_tube_accept_stream_cb, NULL, NULL, G_OBJECT (tube));
411 }
412
413 void
414 empathy_tp_tube_accept_unix_stream_tube (EmpathyTpTube *tube)
415 {
416   GValue control_param = {0, };
417
418   g_return_if_fail (EMPATHY_IS_TP_TUBE (tube));
419
420   g_value_init (&control_param, G_TYPE_STRING);
421   tp_tube_accept_stream_tube (tube, TP_SOCKET_ADDRESS_TYPE_UNIX,
422       TP_SOCKET_ACCESS_CONTROL_LOCALHOST, &control_param);
423
424   g_value_reset (&control_param);
425 }
426
427 void
428 empathy_tp_tube_accept_ipv4_stream_tube (EmpathyTpTube *tube)
429 {
430   GValue control_param = {0, };
431
432   g_return_if_fail (EMPATHY_IS_TP_TUBE (tube));
433
434   g_value_init (&control_param, G_TYPE_STRING);
435   tp_tube_accept_stream_tube (tube, TP_SOCKET_ADDRESS_TYPE_IPV4,
436       TP_SOCKET_ACCESS_CONTROL_LOCALHOST, &control_param);
437
438   g_value_reset (&control_param);
439 }
440
441 gchar *
442 empathy_tp_tube_get_unix_socket (EmpathyTpTube *tube)
443 {
444   EmpathyTpTubePriv *priv = GET_PRIV (tube);
445   GValue *address = g_new0 (GValue, 1);;
446   guint address_type;
447   gchar *address_name = NULL;
448   GError *error = NULL;
449
450   g_return_val_if_fail (EMPATHY_IS_TP_TUBE (tube), NULL);
451
452   empathy_debug (DEBUG_DOMAIN, "Getting stream tube socket address");
453
454   /* FIXME: We shouldn't use _run_ here because the user may not expect to
455    * reenter the mainloop.
456    * FIXME: Do we have to give an initialised GValue for address? Are we
457    * freeing it correctly? */
458   if (!tp_cli_channel_type_tubes_run_get_stream_tube_socket_address (priv->channel,
459       -1, priv->id, &address_type, &address, &error, NULL))
460     {
461       empathy_debug (DEBUG_DOMAIN, "Couldn't get socket address: %s",
462           error->message);
463       g_clear_error (&error);
464       return NULL;
465     }
466
467   dbus_g_type_struct_get (address, 0, &address_name, G_MAXUINT);
468   g_free (address);
469
470   empathy_debug (DEBUG_DOMAIN, "UNIX Socket - %s", address_name);
471
472   return address_name;
473 }
474
475 void
476 empathy_tp_tube_get_ipv4_socket (EmpathyTpTube *tube,
477                                  gchar **hostname,
478                                  guint *port)
479 {
480   EmpathyTpTubePriv *priv = GET_PRIV (tube);
481   GValue *address = g_new0 (GValue, 1);
482   guint address_type;
483   GError *error = NULL;
484
485   g_return_if_fail (EMPATHY_IS_TP_TUBE (tube));
486
487   empathy_debug (DEBUG_DOMAIN, "Getting stream tube socket address");
488
489   /* FIXME: Same than for empathy_tp_tube_get_unix_socket() */
490   if (!tp_cli_channel_type_tubes_run_get_stream_tube_socket_address (priv->channel,
491       -1, priv->id, &address_type, &address, &error, NULL))
492     {
493       empathy_debug (DEBUG_DOMAIN, "Couldn't get socket address: %s",
494           error->message);
495       g_clear_error (&error);
496       return;
497     }
498
499   dbus_g_type_struct_get (address, 0, hostname, 1, port, G_MAXUINT);
500
501   g_free (address);
502 }
503
504 void
505 empathy_offer_ipv4_stream_tube (EmpathyContact *contact,
506                                 const gchar *hostname,
507                                 guint port,
508                                 const gchar *service)
509 {
510   MissionControl *mc;
511   McAccount *account;
512   TpConnection *connection;
513   TpChannel *channel;
514   gchar *object_path;
515   guint id;
516   GHashTable *params;
517   GValue *address;
518   GValue *control_param;
519   GError *error = NULL;
520
521   mc = empathy_mission_control_new ();
522   account = empathy_contact_get_account (contact);
523   connection = mission_control_get_tpconnection (mc, account, NULL);
524
525   if (!tp_cli_connection_run_request_channel (connection, -1,
526       TP_IFACE_CHANNEL_TYPE_TUBES, TP_HANDLE_TYPE_CONTACT,
527       empathy_contact_get_handle (contact), FALSE, &object_path, &error, NULL))
528     {
529       g_clear_error (&error);
530       g_object_unref (mc);
531       g_object_unref (account);
532       g_object_unref (connection);
533       return;
534     }
535
536   empathy_debug (DEBUG_DOMAIN, "Offering a new stream tube");
537
538   channel = tp_channel_new (connection, object_path,
539       TP_IFACE_CHANNEL_TYPE_TUBES, TP_HANDLE_TYPE_CONTACT,
540       empathy_contact_get_handle (contact), NULL);
541
542   params = g_hash_table_new (g_str_hash, g_str_equal);
543   address = g_new0 (GValue, 1);
544   g_value_init (address,
545       dbus_g_type_get_struct ("GValueArray", G_TYPE_STRING, G_TYPE_UINT,
546       G_TYPE_INVALID));
547   g_value_take_boxed (address,
548       dbus_g_type_specialized_construct (dbus_g_type_get_struct ("GValueArray",
549         G_TYPE_STRING, G_TYPE_UINT, G_TYPE_INVALID)));
550   dbus_g_type_struct_set (address, 0, hostname, 1, port, G_MAXUINT);
551
552   /* localhost access control, variant is ignored */
553   control_param = g_new0 (GValue, 1);
554   g_value_init (control_param, G_TYPE_STRING);
555
556   if (!tp_cli_channel_type_tubes_run_offer_stream_tube (channel, -1,
557         service, params, TP_SOCKET_ADDRESS_TYPE_IPV4, address,
558         TP_SOCKET_ACCESS_CONTROL_LOCALHOST, control_param, &id, &error, NULL))
559     {
560       empathy_debug (DEBUG_DOMAIN, "Couldn't offer tube: %s", error->message);
561       g_clear_error (&error);
562     }
563
564   empathy_debug (DEBUG_DOMAIN, "Stream tube id=%d offered", id);
565
566   g_object_unref (channel);
567   g_free (object_path);
568   g_hash_table_destroy (params);
569   g_value_reset (address);
570   g_value_reset (control_param);
571   g_free (address);
572   g_free (control_param);
573   g_object_unref (mc);
574   g_object_unref (account);
575   g_object_unref (connection);
576 }
577