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