]> git.0d.be Git - empathy.git/blob - src/empathy-tube-dispatch.c
don't dispatch D-Bus tube having an invalid ServiceName
[empathy.git] / src / empathy-tube-dispatch.c
1 /*
2  * empathy-tube-dispatch.c - Source for EmpathyTubeDispatch
3  * Copyright (C) 2008 Collabora Ltd.
4  * @author Sjoerd Simons <sjoerd.simons@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 #include <stdio.h>
23 #include <stdlib.h>
24
25 #include <telepathy-glib/dbus.h>
26 #include <telepathy-glib/util.h>
27 #include <telepathy-glib/proxy-subclass.h>
28
29 #include <gtk/gtk.h>
30 #include <glib/gi18n.h>
31
32
33 #include <libempathy/empathy-tube-handler.h>
34 #include <extensions/extensions.h>
35
36
37 #include "empathy-tube-dispatch.h"
38 #include "empathy-tube-dispatch-enumtypes.h"
39
40 #define DEBUG_FLAG EMPATHY_DEBUG_DISPATCHER
41 #include <libempathy/empathy-debug.h>
42
43 G_DEFINE_TYPE(EmpathyTubeDispatch, empathy_tube_dispatch, G_TYPE_OBJECT)
44
45 static void empathy_tube_dispatch_set_ability (
46   EmpathyTubeDispatch *tube_dispatch,
47   EmpathyTubeDispatchAbility dispatchability);
48
49 /* private structure */
50 typedef struct _EmpathyTubeDispatchPriv EmpathyTubeDispatchPriv;
51
52 /* properties */
53 enum {
54     PROP_OPERATION = 1,
55     PROP_DISPATCHABILITY
56 };
57
58
59 struct _EmpathyTubeDispatchPriv
60 {
61   gboolean dispose_has_run;
62   EmpathyDispatchOperation *operation;
63   EmpathyTubeDispatchAbility dispatchability;
64   gchar *service;
65   gchar *bus_name;
66   gchar *object_path;
67   TpDBusDaemon *dbus;
68 };
69
70 #define GET_PRIV(o) \
71  (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
72   EMPATHY_TYPE_TUBE_DISPATCH, EmpathyTubeDispatchPriv))
73
74 static void
75 empathy_tube_dispatch_init (EmpathyTubeDispatch *obj)
76 {
77   EmpathyTubeDispatchPriv *priv = GET_PRIV (obj);
78
79   priv->dispatchability = EMPATHY_TUBE_DISPATCHABILITY_UNKNOWN;
80 }
81
82 static void empathy_tube_dispatch_dispose (GObject *object);
83 static void empathy_tube_dispatch_finalize (GObject *object);
84
85 static void
86 empathy_tube_dispatch_list_activatable_names_cb (TpDBusDaemon *proxy,
87   const gchar **names, const GError *error, gpointer user_data,
88   GObject *object)
89 {
90   EmpathyTubeDispatch *self = EMPATHY_TUBE_DISPATCH (object);
91   EmpathyTubeDispatchPriv *priv = GET_PRIV (self);
92   gchar **name;
93
94   for (name = (gchar **) names; *name != NULL; name++)
95     {
96       if (!tp_strdiff (*name, priv->bus_name))
97         {
98           DEBUG ("Found tube handler. Can dispatch it");
99           empathy_tube_dispatch_set_ability (self,
100             EMPATHY_TUBE_DISPATCHABILITY_POSSIBLE);
101           return;
102         }
103     }
104
105   DEBUG ("Didn't find tube handler. Can't dispatch it");
106   empathy_tube_dispatch_set_ability (self,
107     EMPATHY_TUBE_DISPATCHABILITY_IMPOSSIBLE);
108 }
109
110 static void
111 empathy_tube_dispatch_name_has_owner_cb (TpDBusDaemon *proxy,
112   gboolean has_owner, const GError *error, gpointer user_data,
113   GObject *object)
114 {
115   EmpathyTubeDispatch *self = EMPATHY_TUBE_DISPATCH (object);
116   EmpathyTubeDispatchPriv *priv = GET_PRIV (self);
117
118   if (error != NULL)
119     {
120       DEBUG ("NameHasOwner failed. Can't dispatch tube");
121       empathy_tube_dispatch_set_ability (self,
122         EMPATHY_TUBE_DISPATCHABILITY_IMPOSSIBLE);
123       return;
124     }
125
126   if (has_owner)
127     {
128       DEBUG ("Tube handler is running. Can dispatch it");
129       empathy_tube_dispatch_set_ability (self,
130         EMPATHY_TUBE_DISPATCHABILITY_POSSIBLE);
131     }
132   else
133     {
134       DEBUG ("Tube handler is not running. Calling ListActivatableNames");
135       tp_cli_dbus_daemon_call_list_activatable_names (priv->dbus, -1,
136         empathy_tube_dispatch_list_activatable_names_cb, NULL, NULL,
137         G_OBJECT (self));
138     }
139 }
140
141 static void
142 empathy_tube_dispatch_constructed (GObject *object)
143 {
144   EmpathyTubeDispatch *self = EMPATHY_TUBE_DISPATCH (object);
145   EmpathyTubeDispatchPriv *priv = GET_PRIV (self);
146   TpChannel *channel;
147   GHashTable *properties;
148   const gchar *service;
149   const gchar *channel_type;
150   TpTubeType type;
151
152   priv->dbus = tp_dbus_daemon_new (tp_get_bus());
153
154   channel = empathy_dispatch_operation_get_channel (priv->operation);
155   properties = tp_channel_borrow_immutable_properties (channel);
156
157   channel_type = tp_asv_get_string (properties,
158     TP_IFACE_CHANNEL ".ChannelType");
159   if (channel_type == NULL)
160     goto failed;
161
162   if (!tp_strdiff (channel_type, EMP_IFACE_CHANNEL_TYPE_STREAM_TUBE))
163     {
164       type = TP_TUBE_TYPE_STREAM;
165       service = tp_asv_get_string (properties,
166         EMP_IFACE_CHANNEL_TYPE_STREAM_TUBE  ".Service");
167     }
168   else if (!tp_strdiff (channel_type, EMP_IFACE_CHANNEL_TYPE_DBUS_TUBE))
169     {
170       GError *error = NULL;
171
172       type = TP_TUBE_TYPE_DBUS;
173       service = tp_asv_get_string (properties,
174         EMP_IFACE_CHANNEL_TYPE_DBUS_TUBE  ".ServiceName");
175
176       if (!tp_dbus_check_valid_bus_name (service, TP_DBUS_NAME_TYPE_WELL_KNOWN,
177             &error))
178         {
179           DEBUG ("Can't dispatch tube; invalid ServiceName %s: %s", service,
180               error->message);
181           g_error_free (error);
182           goto failed;
183         }
184     }
185   else
186     {
187       goto failed;
188     }
189
190
191   if (service == NULL)
192     goto failed;
193
194   priv->bus_name = empathy_tube_handler_build_bus_name (type, service);
195   priv->object_path = empathy_tube_handler_build_object_path (type, service);
196
197   priv->service = g_strdup (service);
198
199   DEBUG ("Look for tube handler %s\n", priv->bus_name);
200   tp_cli_dbus_daemon_call_name_has_owner (priv->dbus, -1, priv->bus_name,
201     empathy_tube_dispatch_name_has_owner_cb, NULL, NULL, G_OBJECT (self));
202
203   return;
204
205 failed:
206   empathy_tube_dispatch_set_ability (self,
207     EMPATHY_TUBE_DISPATCHABILITY_IMPOSSIBLE);
208 }
209
210 static void
211 empathy_tube_dispatch_set_property (GObject *object,
212   guint property_id, const GValue *value, GParamSpec *pspec)
213 {
214   EmpathyTubeDispatch *tube_dispatch = EMPATHY_TUBE_DISPATCH (object);
215   EmpathyTubeDispatchPriv *priv = GET_PRIV (tube_dispatch);
216
217   switch (property_id)
218     {
219       case PROP_OPERATION:
220         priv->operation = g_value_dup_object (value);
221         break;
222       default:
223         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
224         break;
225     }
226 }
227
228 static void
229 empathy_tube_dispatch_get_property (GObject *object,
230   guint property_id, GValue *value, GParamSpec *pspec)
231 {
232   EmpathyTubeDispatch *tube_dispatch = EMPATHY_TUBE_DISPATCH (object);
233   EmpathyTubeDispatchPriv *priv = GET_PRIV (tube_dispatch);
234
235   switch (property_id)
236     {
237       case PROP_OPERATION:
238         g_value_set_object (value, priv->operation);
239         break;
240       case PROP_DISPATCHABILITY:
241         g_value_set_enum (value, priv->dispatchability);
242         break;
243       default:
244         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
245         break;
246     }
247 }
248
249 static void
250 empathy_tube_dispatch_class_init (
251   EmpathyTubeDispatchClass *empathy_tube_dispatch_class)
252 {
253   GObjectClass *object_class = G_OBJECT_CLASS (empathy_tube_dispatch_class);
254   GParamSpec *param_spec;
255
256   g_type_class_add_private (empathy_tube_dispatch_class,
257     sizeof (EmpathyTubeDispatchPriv));
258
259   object_class->set_property = empathy_tube_dispatch_set_property;
260   object_class->get_property = empathy_tube_dispatch_get_property;
261
262   object_class->constructed = empathy_tube_dispatch_constructed;
263   object_class->dispose = empathy_tube_dispatch_dispose;
264   object_class->finalize = empathy_tube_dispatch_finalize;
265
266   param_spec = g_param_spec_object ("operation",
267     "operation", "The telepathy connection",
268     EMPATHY_TYPE_DISPATCH_OPERATION,
269     G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
270   g_object_class_install_property (object_class, PROP_OPERATION, param_spec);
271
272   param_spec = g_param_spec_enum ("dispatchability",
273     "dispatchability",
274     "Whether or not there is a handler to dispatch the operation to",
275     EMPATHY_TYPE_TUBE_DISPATCH_ABILITY, EMPATHY_TUBE_DISPATCHABILITY_UNKNOWN,
276     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
277   g_object_class_install_property (object_class, PROP_DISPATCHABILITY,
278     param_spec);
279
280 }
281
282 void
283 empathy_tube_dispatch_dispose (GObject *object)
284 {
285   EmpathyTubeDispatch *self = EMPATHY_TUBE_DISPATCH (object);
286   EmpathyTubeDispatchPriv *priv = GET_PRIV (self);
287
288   if (priv->dispose_has_run)
289     return;
290
291   priv->dispose_has_run = TRUE;
292
293   /* release any references held by the object here */
294   if (priv->operation != NULL)
295     g_object_unref (priv->operation);
296
297   priv->operation = NULL;
298
299   if (priv->dbus != NULL)
300     g_object_unref (priv->dbus);
301
302   priv->dbus = NULL;
303
304
305   if (G_OBJECT_CLASS (empathy_tube_dispatch_parent_class)->dispose)
306     G_OBJECT_CLASS (empathy_tube_dispatch_parent_class)->dispose (object);
307 }
308
309 void
310 empathy_tube_dispatch_finalize (GObject *object)
311 {
312   EmpathyTubeDispatch *self = EMPATHY_TUBE_DISPATCH (object);
313   EmpathyTubeDispatchPriv *priv = GET_PRIV (self);
314
315   g_free (priv->bus_name);
316   g_free (priv->object_path);
317   g_free (priv->service);
318
319   /* free any data held directly by the object here */
320
321   G_OBJECT_CLASS (empathy_tube_dispatch_parent_class)->finalize (object);
322 }
323
324 EmpathyTubeDispatch *
325 empathy_tube_dispatch_new (EmpathyDispatchOperation *operation)
326 {
327   return EMPATHY_TUBE_DISPATCH (g_object_new (EMPATHY_TYPE_TUBE_DISPATCH,
328       "operation", operation, NULL));
329 }
330
331 EmpathyTubeDispatchAbility
332 empathy_tube_dispatch_is_dispatchable (EmpathyTubeDispatch *tube_dispatch)
333 {
334   EmpathyTubeDispatchPriv *priv = GET_PRIV (tube_dispatch);
335
336   return priv->dispatchability;
337 }
338
339 static void
340 empathy_tube_dispatch_set_ability (EmpathyTubeDispatch *tube_dispatch,
341   EmpathyTubeDispatchAbility dispatchability)
342 {
343   EmpathyTubeDispatchPriv *priv = GET_PRIV (tube_dispatch);
344
345   if (priv->dispatchability == dispatchability)
346     return;
347
348   priv->dispatchability = dispatchability;
349   g_object_notify (G_OBJECT (tube_dispatch), "dispatchability");
350 }
351
352 static void
353 empathy_tube_dispatch_show_error (EmpathyTubeDispatch *self, gchar *message)
354 {
355   GtkWidget *dialog;
356
357   dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL,
358       GTK_MESSAGE_WARNING, GTK_BUTTONS_CLOSE, "%s", message);
359
360   gtk_dialog_run (GTK_DIALOG (dialog));
361
362   gtk_widget_destroy (dialog);
363 }
364
365 static void
366 empathy_tube_dispatch_handle_tube_cb (TpProxy *proxy, const GError *error,
367   gpointer user_data, GObject *object)
368 {
369   EmpathyTubeDispatch *self = EMPATHY_TUBE_DISPATCH (object);
370   EmpathyTubeDispatchPriv *priv = GET_PRIV (self);
371
372   if (error != NULL)
373     {
374       gchar *msg = g_strdup_printf (
375         _("Unable to start application for service %s: %s"),
376           priv->service,  error->message);
377
378       empathy_tube_dispatch_show_error (self, msg);
379       g_free (msg);
380     }
381
382   /* Remove the ref we were holding because of the dispatching */
383   g_object_unref (object);
384 }
385
386 static void
387 empathy_tube_do_dispatch (EmpathyTubeDispatch *self)
388 {
389   EmpathyTubeDispatchPriv *priv = GET_PRIV (self);
390   TpChannel *channel;
391   TpProxy *connection;
392   TpProxy *thandler;
393   gchar   *object_path;
394   guint    handle_type;
395   guint    handle;
396
397   channel = empathy_dispatch_operation_get_channel (priv->operation);
398
399   /* Create the proxy for the tube handler */
400   thandler = g_object_new (TP_TYPE_PROXY,
401     "dbus-connection", tp_get_bus (),
402     "bus-name", priv->bus_name,
403     "object-path", priv->object_path,
404     NULL);
405
406   tp_proxy_add_interface_by_id (thandler, EMP_IFACE_QUARK_TUBE_HANDLER);
407
408   /* Give the tube to the handler */
409   g_object_get (channel,
410     "connection", &connection,
411     "object-path", &object_path,
412     "handle_type", &handle_type,
413     "handle", &handle,
414     NULL);
415
416   emp_cli_tube_handler_call_handle_tube (thandler, -1,
417     connection->bus_name, connection->object_path,
418     object_path, handle_type, handle,
419     empathy_tube_dispatch_handle_tube_cb, NULL, NULL, G_OBJECT (self));
420
421   g_object_unref (thandler);
422   g_object_unref (connection);
423   g_free (object_path);
424 }
425
426 void
427 empathy_tube_dispatch_handle (EmpathyTubeDispatch *tube_dispatch)
428 {
429   EmpathyTubeDispatchPriv *priv = GET_PRIV (tube_dispatch);
430
431   /* Keep ourselves alive untill the dispatching is finished */
432   g_object_ref (tube_dispatch);
433
434   /* If we can't claim it, don't do anything */
435   if (!empathy_dispatch_operation_claim (priv->operation))
436     goto done;
437
438   if (priv->dispatchability != EMPATHY_TUBE_DISPATCHABILITY_POSSIBLE)
439     {
440       gchar *msg;
441       TpChannel *channel;
442
443       channel = empathy_dispatch_operation_get_channel (priv->operation);
444
445       msg = g_strdup_printf (
446         _("An invitation was offered for service %s, but you don't have the "
447           "needed application to handle it"), priv->service);
448
449       empathy_tube_dispatch_show_error (tube_dispatch, msg);
450
451       g_free (msg);
452
453       tp_cli_channel_call_close (channel, -1, NULL, NULL, NULL, NULL);
454
455       goto done;
456     }
457   else
458     {
459       empathy_tube_do_dispatch (tube_dispatch);
460     }
461
462   return;
463 done:
464   g_object_unref (tube_dispatch);
465 }
466