]> git.0d.be Git - empathy.git/blob - src/empathy-tube-dispatch.c
b1e7bce6d18d13c3ec1ef4efc087b4393a25afb6
[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       type = TP_TUBE_TYPE_DBUS;
171       service = tp_asv_get_string (properties,
172         EMP_IFACE_CHANNEL_TYPE_DBUS_TUBE  ".ServiceName");
173     }
174   else
175     {
176       goto failed;
177     }
178
179
180   if (service == NULL)
181     goto failed;
182
183   priv->bus_name = empathy_tube_handler_build_bus_name (type, service);
184   priv->object_path = empathy_tube_handler_build_object_path (type, service);
185
186   priv->service = g_strdup (service);
187
188   DEBUG ("Look for tube handler %s\n", priv->bus_name);
189   tp_cli_dbus_daemon_call_name_has_owner (priv->dbus, -1, priv->bus_name,
190     empathy_tube_dispatch_name_has_owner_cb, NULL, NULL, G_OBJECT (self));
191
192   return;
193
194 failed:
195   empathy_tube_dispatch_set_ability (self,
196     EMPATHY_TUBE_DISPATCHABILITY_IMPOSSIBLE);
197 }
198
199 static void
200 empathy_tube_dispatch_set_property (GObject *object,
201   guint property_id, const GValue *value, GParamSpec *pspec)
202 {
203   EmpathyTubeDispatch *tube_dispatch = EMPATHY_TUBE_DISPATCH (object);
204   EmpathyTubeDispatchPriv *priv = GET_PRIV (tube_dispatch);
205
206   switch (property_id)
207     {
208       case PROP_OPERATION:
209         priv->operation = g_value_dup_object (value);
210         break;
211       default:
212         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
213         break;
214     }
215 }
216
217 static void
218 empathy_tube_dispatch_get_property (GObject *object,
219   guint property_id, GValue *value, GParamSpec *pspec)
220 {
221   EmpathyTubeDispatch *tube_dispatch = EMPATHY_TUBE_DISPATCH (object);
222   EmpathyTubeDispatchPriv *priv = GET_PRIV (tube_dispatch);
223
224   switch (property_id)
225     {
226       case PROP_OPERATION:
227         g_value_set_object (value, priv->operation);
228         break;
229       case PROP_DISPATCHABILITY:
230         g_value_set_enum (value, priv->dispatchability);
231         break;
232       default:
233         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
234         break;
235     }
236 }
237
238 static void
239 empathy_tube_dispatch_class_init (
240   EmpathyTubeDispatchClass *empathy_tube_dispatch_class)
241 {
242   GObjectClass *object_class = G_OBJECT_CLASS (empathy_tube_dispatch_class);
243   GParamSpec *param_spec;
244
245   g_type_class_add_private (empathy_tube_dispatch_class,
246     sizeof (EmpathyTubeDispatchPriv));
247
248   object_class->set_property = empathy_tube_dispatch_set_property;
249   object_class->get_property = empathy_tube_dispatch_get_property;
250
251   object_class->constructed = empathy_tube_dispatch_constructed;
252   object_class->dispose = empathy_tube_dispatch_dispose;
253   object_class->finalize = empathy_tube_dispatch_finalize;
254
255   param_spec = g_param_spec_object ("operation",
256     "operation", "The telepathy connection",
257     EMPATHY_TYPE_DISPATCH_OPERATION,
258     G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
259   g_object_class_install_property (object_class, PROP_OPERATION, param_spec);
260
261   param_spec = g_param_spec_enum ("dispatchability",
262     "dispatchability",
263     "Whether or not there is a handler to dispatch the operation to",
264     EMPATHY_TYPE_TUBE_DISPATCH_ABILITY, EMPATHY_TUBE_DISPATCHABILITY_UNKNOWN,
265     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
266   g_object_class_install_property (object_class, PROP_DISPATCHABILITY,
267     param_spec);
268
269 }
270
271 void
272 empathy_tube_dispatch_dispose (GObject *object)
273 {
274   EmpathyTubeDispatch *self = EMPATHY_TUBE_DISPATCH (object);
275   EmpathyTubeDispatchPriv *priv = GET_PRIV (self);
276
277   if (priv->dispose_has_run)
278     return;
279
280   priv->dispose_has_run = TRUE;
281
282   /* release any references held by the object here */
283   if (priv->operation != NULL)
284     g_object_unref (priv->operation);
285
286   priv->operation = NULL;
287
288   if (priv->dbus != NULL)
289     g_object_unref (priv->dbus);
290
291   priv->dbus = NULL;
292
293
294   if (G_OBJECT_CLASS (empathy_tube_dispatch_parent_class)->dispose)
295     G_OBJECT_CLASS (empathy_tube_dispatch_parent_class)->dispose (object);
296 }
297
298 void
299 empathy_tube_dispatch_finalize (GObject *object)
300 {
301   EmpathyTubeDispatch *self = EMPATHY_TUBE_DISPATCH (object);
302   EmpathyTubeDispatchPriv *priv = GET_PRIV (self);
303
304   g_free (priv->bus_name);
305   g_free (priv->object_path);
306   g_free (priv->service);
307
308   /* free any data held directly by the object here */
309
310   G_OBJECT_CLASS (empathy_tube_dispatch_parent_class)->finalize (object);
311 }
312
313 EmpathyTubeDispatch *
314 empathy_tube_dispatch_new (EmpathyDispatchOperation *operation)
315 {
316   return EMPATHY_TUBE_DISPATCH (g_object_new (EMPATHY_TYPE_TUBE_DISPATCH,
317       "operation", operation, NULL));
318 }
319
320 EmpathyTubeDispatchAbility
321 empathy_tube_dispatch_is_dispatchable (EmpathyTubeDispatch *tube_dispatch)
322 {
323   EmpathyTubeDispatchPriv *priv = GET_PRIV (tube_dispatch);
324
325   return priv->dispatchability;
326 }
327
328 static void
329 empathy_tube_dispatch_set_ability (EmpathyTubeDispatch *tube_dispatch,
330   EmpathyTubeDispatchAbility dispatchability)
331 {
332   EmpathyTubeDispatchPriv *priv = GET_PRIV (tube_dispatch);
333
334   if (priv->dispatchability == dispatchability)
335     return;
336
337   priv->dispatchability = dispatchability;
338   g_object_notify (G_OBJECT (tube_dispatch), "dispatchability");
339 }
340
341 static void
342 empathy_tube_dispatch_show_error (EmpathyTubeDispatch *self, gchar *message)
343 {
344   GtkWidget *dialog;
345
346   dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL,
347       GTK_MESSAGE_WARNING, GTK_BUTTONS_CLOSE, "%s", message);
348
349   gtk_dialog_run (GTK_DIALOG (dialog));
350
351   gtk_widget_destroy (dialog);
352 }
353
354 static void
355 empathy_tube_dispatch_handle_tube_cb (TpProxy *proxy, const GError *error,
356   gpointer user_data, GObject *object)
357 {
358   EmpathyTubeDispatch *self = EMPATHY_TUBE_DISPATCH (object);
359   EmpathyTubeDispatchPriv *priv = GET_PRIV (self);
360
361   if (error != NULL)
362     {
363       gchar *msg = g_strdup_printf (
364         _("Unable to start application for service %s: %s"),
365           priv->service,  error->message);
366
367       empathy_tube_dispatch_show_error (self, msg);
368       g_free (msg);
369     }
370
371   /* Remove the ref we were holding because of the dispatching */
372   g_object_unref (object);
373 }
374
375 static void
376 empathy_tube_do_dispatch (EmpathyTubeDispatch *self)
377 {
378   EmpathyTubeDispatchPriv *priv = GET_PRIV (self);
379   TpChannel *channel;
380   TpProxy *connection;
381   TpProxy *thandler;
382   gchar   *object_path;
383   guint    handle_type;
384   guint    handle;
385
386   channel = empathy_dispatch_operation_get_channel (priv->operation);
387
388   /* Create the proxy for the tube handler */
389   thandler = g_object_new (TP_TYPE_PROXY,
390     "dbus-connection", tp_get_bus (),
391     "bus-name", priv->bus_name,
392     "object-path", priv->object_path,
393     NULL);
394
395   tp_proxy_add_interface_by_id (thandler, EMP_IFACE_QUARK_TUBE_HANDLER);
396
397   /* Give the tube to the handler */
398   g_object_get (channel,
399     "connection", &connection,
400     "object-path", &object_path,
401     "handle_type", &handle_type,
402     "handle", &handle,
403     NULL);
404
405   emp_cli_tube_handler_call_handle_tube (thandler, -1,
406     connection->bus_name, connection->object_path,
407     object_path, handle_type, handle,
408     empathy_tube_dispatch_handle_tube_cb, NULL, NULL, G_OBJECT (self));
409
410   g_object_unref (thandler);
411   g_object_unref (connection);
412   g_free (object_path);
413 }
414
415 void
416 empathy_tube_dispatch_handle (EmpathyTubeDispatch *tube_dispatch)
417 {
418   EmpathyTubeDispatchPriv *priv = GET_PRIV (tube_dispatch);
419
420   /* Keep ourselves alive untill the dispatching is finished */
421   g_object_ref (tube_dispatch);
422
423   /* If we can't claim it, don't do anything */
424   if (!empathy_dispatch_operation_claim (priv->operation))
425     goto done;
426
427   if (priv->dispatchability != EMPATHY_TUBE_DISPATCHABILITY_POSSIBLE)
428     {
429       gchar *msg;
430       TpChannel *channel;
431
432       channel = empathy_dispatch_operation_get_channel (priv->operation);
433
434       msg = g_strdup_printf (
435         _("An invitation was offered for service %s, but you don't have the "
436           "needed application to handle it"), priv->service);
437
438       empathy_tube_dispatch_show_error (tube_dispatch, msg);
439
440       g_free (msg);
441
442       tp_cli_channel_call_close (channel, -1, NULL, NULL, NULL, NULL);
443
444       goto done;
445     }
446   else
447     {
448       empathy_tube_do_dispatch (tube_dispatch);
449     }
450
451   return;
452 done:
453   g_object_unref (tube_dispatch);
454 }
455