]> git.0d.be Git - empathy.git/blob - libempathy/empathy-tp-tube.c
Update to new stream tube spec
[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 #include <telepathy-glib/util.h>
26 #include <extensions/extensions.h>
27
28 #include "empathy-contact-factory.h"
29 #include "empathy-enum-types.h"
30 #include "empathy-tp-tube.h"
31 #include "empathy-utils.h"
32
33 #define DEBUG_FLAG EMPATHY_DEBUG_TP
34 #include "empathy-debug.h"
35
36 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyTpTube)
37 typedef struct
38 {
39   TpChannel *channel;
40   guint initiator;
41   guint type;
42   gchar *service;
43   GHashTable *parameters;
44   guint state;
45   EmpathyContact *initiator_contact;
46   EmpathyContactFactory *factory;
47 } EmpathyTpTubePriv;
48
49 enum
50 {
51   PROP_0,
52   PROP_CHANNEL,
53   PROP_STATE,
54 };
55
56 enum
57 {
58   DESTROY,
59   LAST_SIGNAL
60 };
61
62 static guint signals[LAST_SIGNAL];
63
64 G_DEFINE_TYPE (EmpathyTpTube, empathy_tp_tube, G_TYPE_OBJECT)
65
66 static void
67 tp_tube_state_changed_cb (TpProxy *proxy,
68                           guint state,
69                           gpointer user_data,
70                           GObject *tube)
71 {
72   EmpathyTpTubePriv *priv = GET_PRIV (tube);
73
74   DEBUG ("Tube state changed");
75
76   priv->state = state;
77   g_object_notify (tube, "state");
78 }
79
80 static void
81 tp_tube_invalidated_cb (TpChannel     *channel,
82                         GQuark         domain,
83                         gint           code,
84                         gchar         *message,
85                         EmpathyTpTube *tube)
86 {
87   DEBUG ("Channel invalidated: %s", message);
88   g_signal_emit (tube, signals[DESTROY], 0);
89 }
90
91 static void
92 tp_tube_async_cb (TpChannel *channel,
93                   const GError *error,
94                   gpointer user_data,
95                   GObject *tube)
96 {
97   if (error)
98       DEBUG ("Error %s: %s", (gchar*) user_data, error->message);
99 }
100
101 static void
102 tp_tube_set_property (GObject *object,
103                       guint prop_id,
104                       const GValue *value,
105                       GParamSpec *pspec)
106 {
107   EmpathyTpTubePriv *priv = GET_PRIV (object);
108
109   switch (prop_id)
110     {
111       case PROP_CHANNEL:
112         priv->channel = g_value_dup_object (value);
113         break;
114       default:
115         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
116         break;
117   }
118 }
119
120 static void
121 tp_tube_get_property (GObject *object,
122                       guint prop_id,
123                       GValue *value,
124                       GParamSpec *pspec)
125 {
126   EmpathyTpTubePriv *priv = GET_PRIV (object);
127
128   switch (prop_id)
129     {
130       case PROP_CHANNEL:
131         g_value_set_object (value, priv->channel);
132         break;
133       case PROP_STATE:
134         g_value_set_uint (value, priv->state);
135         break;
136       default:
137         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
138         break;
139   }
140 }
141
142 static GObject *
143 tp_tube_constructor (GType type,
144                      guint n_props,
145                      GObjectConstructParam *props)
146 {
147   GObject *self;
148   EmpathyTpTubePriv *priv;
149
150   self = G_OBJECT_CLASS (empathy_tp_tube_parent_class)->constructor (
151       type, n_props, props);
152   priv = GET_PRIV (self);
153
154   g_signal_connect (priv->channel, "invalidated",
155       G_CALLBACK (tp_tube_invalidated_cb), self);
156
157   emp_cli_channel_interface_tube_connect_to_tube_channel_state_changed (
158     TP_PROXY (priv->channel), tp_tube_state_changed_cb, NULL, NULL,
159     self, NULL);
160
161   return self;
162 }
163
164 static void
165 tp_tube_finalize (GObject *object)
166 {
167   EmpathyTpTubePriv *priv = GET_PRIV (object);
168
169   DEBUG ("Finalizing: %p", object);
170
171   if (priv->channel)
172     {
173       g_signal_handlers_disconnect_by_func (priv->channel,
174           tp_tube_invalidated_cb, object);
175       tp_cli_channel_call_close (priv->channel, -1, tp_tube_async_cb,
176         "closing tube", NULL, NULL);
177       g_object_unref (priv->channel);
178     }
179   if (priv->initiator_contact)
180       g_object_unref (priv->initiator_contact);
181   if (priv->factory)
182       g_object_unref (priv->factory);
183
184   g_free (priv->service);
185   g_hash_table_destroy (priv->parameters);
186
187   G_OBJECT_CLASS (empathy_tp_tube_parent_class)->finalize (object);
188 }
189
190 static void
191 empathy_tp_tube_class_init (EmpathyTpTubeClass *klass)
192 {
193   GObjectClass *object_class = G_OBJECT_CLASS (klass);
194
195   object_class->constructor = tp_tube_constructor;
196   object_class->finalize = tp_tube_finalize;
197   object_class->set_property = tp_tube_set_property;
198   object_class->get_property = tp_tube_get_property;
199
200   g_object_class_install_property (object_class, PROP_CHANNEL,
201       g_param_spec_object ("channel", "channel", "channel", TP_TYPE_CHANNEL,
202       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME |
203         G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
204
205   g_object_class_install_property (object_class, PROP_STATE,
206       g_param_spec_uint ("state", "state", "state", 0, G_MAXUINT, 0,
207         G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
208         G_PARAM_STATIC_BLURB));
209
210   signals[DESTROY] = g_signal_new ("destroy",
211       G_TYPE_FROM_CLASS (klass),
212       G_SIGNAL_RUN_LAST,
213       0, NULL, NULL,
214       g_cclosure_marshal_VOID__VOID,
215       G_TYPE_NONE, 0);
216
217   g_type_class_add_private (klass, sizeof (EmpathyTpTubePriv));
218 }
219
220 static void
221 empathy_tp_tube_init (EmpathyTpTube *tube)
222 {
223   EmpathyTpTubePriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (tube,
224                 EMPATHY_TYPE_TP_TUBE, EmpathyTpTubePriv);
225
226   tube->priv = priv;
227
228   priv->factory = empathy_contact_factory_dup_singleton ();
229 }
230
231 EmpathyTpTube *
232 empathy_tp_tube_new (TpChannel *channel)
233 {
234   g_return_val_if_fail (TP_IS_CHANNEL (channel), NULL);
235
236   return g_object_new (EMPATHY_TYPE_TP_TUBE, "channel", channel,  NULL);
237 }
238
239 EmpathyTpTube *
240 empathy_tp_tube_new_stream_tube (EmpathyContact *contact,
241                                  TpSocketAddressType type,
242                                  const gchar *hostname,
243                                  guint port,
244                                  const gchar *service)
245 {
246   MissionControl *mc;
247   McAccount *account;
248   TpConnection *connection;
249   TpChannel *channel;
250   gchar *object_path;
251   GHashTable *params;
252   GValue *address;
253   GValue *control_param;
254   EmpathyTpTube *tube = NULL;
255   GError *error = NULL;
256   GHashTable *request;
257   GHashTable *channel_properties;
258   GValue *value;
259
260   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
261   g_return_val_if_fail (hostname != NULL, NULL);
262   g_return_val_if_fail (service != NULL, NULL);
263
264   mc = empathy_mission_control_new ();
265   account = empathy_contact_get_account (contact);
266   connection = mission_control_get_tpconnection (mc, account, NULL);
267   g_object_unref (mc);
268
269   tp_connection_run_until_ready (connection, FALSE, NULL, NULL);
270
271   request = g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
272       (GDestroyNotify) tp_g_value_slice_free);
273
274   /* org.freedesktop.Telepathy.Channel.ChannelType */
275   value = tp_g_value_slice_new (G_TYPE_STRING);
276   g_value_set_string (value, EMP_IFACE_CHANNEL_TYPE_STREAM_TUBE);
277   g_hash_table_insert (request, TP_IFACE_CHANNEL ".ChannelType", value);
278
279   /* org.freedesktop.Telepathy.Channel.TargetHandleType */
280   value = tp_g_value_slice_new (G_TYPE_UINT);
281   g_value_set_uint (value, TP_HANDLE_TYPE_CONTACT);
282   g_hash_table_insert (request, TP_IFACE_CHANNEL ".TargetHandleType", value);
283
284   /* org.freedesktop.Telepathy.Channel.TargetHandleType */
285   value = tp_g_value_slice_new (G_TYPE_UINT);
286   g_value_set_uint (value, empathy_contact_get_handle (contact));
287   g_hash_table_insert (request, TP_IFACE_CHANNEL ".TargetHandle", value);
288
289   /* org.freedesktop.Telepathy.Channel.Type.StreamTube.Service */
290   value = tp_g_value_slice_new (G_TYPE_STRING);
291   g_value_set_string (value, service);
292   g_hash_table_insert (request,
293     EMP_IFACE_CHANNEL_TYPE_STREAM_TUBE  ".Service", value);
294
295   if (!tp_cli_connection_interface_requests_run_create_channel (connection, -1,
296     request, &object_path, &channel_properties, &error, NULL))
297     {
298       DEBUG ("Error requesting channel: %s", error->message);
299       g_clear_error (&error);
300       g_object_unref (connection);
301       return NULL;
302     }
303
304   DEBUG ("Offering a new stream tube");
305
306   channel = tp_channel_new_from_properties (connection, object_path,
307       channel_properties, NULL);
308
309   tp_channel_run_until_ready (channel, NULL, NULL);
310
311   #define ADDRESS_TYPE dbus_g_type_get_struct ("GValueArray",\
312       G_TYPE_STRING, G_TYPE_UINT, G_TYPE_INVALID)
313   params = g_hash_table_new (g_str_hash, g_str_equal);
314   address = tp_g_value_slice_new (ADDRESS_TYPE);
315   g_value_take_boxed (address, dbus_g_type_specialized_construct (ADDRESS_TYPE));
316   dbus_g_type_struct_set (address, 0, hostname, 1, port, G_MAXUINT);
317   control_param = tp_g_value_slice_new (G_TYPE_STRING);
318
319   if (!emp_cli_channel_type_stream_tube_run_offer_stream_tube (
320         TP_PROXY(channel), -1, type, address,
321         TP_SOCKET_ACCESS_CONTROL_LOCALHOST, control_param, &error, NULL))
322     {
323       DEBUG ("Couldn't offer tube: %s", error->message);
324       g_clear_error (&error);
325       goto OUT;
326     }
327
328   DEBUG ("Stream tube offered");
329
330   tube = empathy_tp_tube_new (channel);
331
332 OUT:
333   g_object_unref (channel);
334   g_free (object_path);
335   g_hash_table_destroy (request);
336   g_hash_table_destroy (channel_properties);
337   tp_g_value_slice_free (address);
338   tp_g_value_slice_free (control_param);
339   g_object_unref (connection);
340
341   return tube;
342 }
343
344 static void
345 tp_tube_accept_stream_cb (TpProxy *proxy,
346                           const GValue *address,
347                           const GError *error,
348                           gpointer user_data,
349                           GObject *weak_object)
350 {
351   if (error)
352       DEBUG ("Error accepting tube: %s", error->message);
353 }
354
355 void
356 empathy_tp_tube_accept_stream_tube (EmpathyTpTube *tube,
357                                     TpSocketAddressType type)
358 {
359   EmpathyTpTubePriv *priv = GET_PRIV (tube);
360   GValue *control_param;
361
362   g_return_if_fail (EMPATHY_IS_TP_TUBE (tube));
363
364   DEBUG ("Accepting stream tube");
365
366   control_param = tp_g_value_slice_new (G_TYPE_STRING);
367   emp_cli_channel_type_stream_tube_call_accept_stream_tube (
368      TP_PROXY (priv->channel), -1, type, TP_SOCKET_ACCESS_CONTROL_LOCALHOST,
369      control_param, tp_tube_accept_stream_cb, NULL, NULL, G_OBJECT (tube));
370
371   tp_g_value_slice_free (control_param);
372 }
373
374 void
375 empathy_tp_tube_get_socket (EmpathyTpTube *tube,
376                             gchar **hostname,
377                             guint *port)
378 {
379   EmpathyTpTubePriv *priv = GET_PRIV (tube);
380   GValue *address;
381   guint address_type;
382   gchar *ret_hostname = NULL;
383   guint ret_port;
384   GError *error = NULL;
385
386   g_assert_not_reached ();
387
388   g_return_if_fail (EMPATHY_IS_TP_TUBE (tube));
389   g_return_if_fail (hostname != NULL || port != NULL);
390
391   DEBUG ("Getting stream tube socket address");
392
393   if (!tp_cli_channel_type_tubes_run_get_stream_tube_socket_address (priv->channel,
394       -1, 0, &address_type, &address, &error, NULL))
395     {
396       DEBUG ("Couldn't get socket address: %s", error->message);
397       g_clear_error (&error);
398       return;
399     }
400
401   switch (address_type)
402     {
403     case TP_SOCKET_ADDRESS_TYPE_UNIX:
404     case TP_SOCKET_ADDRESS_TYPE_ABSTRACT_UNIX:
405         dbus_g_type_struct_get (address, 0, &ret_hostname, G_MAXUINT);
406         break;
407     case TP_SOCKET_ADDRESS_TYPE_IPV4:
408     case TP_SOCKET_ADDRESS_TYPE_IPV6:
409         dbus_g_type_struct_get (address, 0, &ret_hostname, 1, &ret_port, G_MAXUINT);    
410         break;
411     }
412
413   if (hostname) {
414         *hostname = g_strdup (ret_hostname);
415   }
416   if (port) {
417         *port = ret_port;
418   }
419   g_boxed_free (G_TYPE_VALUE, address);
420 }
421