Use g_variant_is_of_type()
[platform/upstream/glib.git] / gio / gdbusapplication.c
1 /* GIO - GLib Input, Output and Streaming Library
2  *
3  * Copyright © 2010 Red Hat, Inc
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Authors: Colin Walters <walters@verbum.org>
21  */
22
23 #define G_APPLICATION_IFACE "org.gtk.Application"
24
25 static void
26 application_dbus_method_call (GDBusConnection       *connection,
27                               const gchar           *sender,
28                               const gchar           *object_path,
29                               const gchar           *interface_name,
30                               const gchar           *method_name,
31                               GVariant              *parameters,
32                               GDBusMethodInvocation *invocation,
33                               gpointer               user_data)
34 {
35   GApplication *app = G_APPLICATION (user_data);
36
37   if (method_name == NULL && *method_name == '\0')
38     return;
39
40   if (strcmp (method_name, "Quit") == 0)
41     {
42       guint32 timestamp;
43       g_variant_get (parameters, "(u)", &timestamp);
44
45       g_dbus_method_invocation_return_value (invocation, NULL);
46
47       g_application_quit (app, timestamp);
48     }
49   else if (strcmp (method_name, "ListActions") == 0)
50     {
51       GHashTableIter iter;
52       GApplicationAction *value;
53       GVariantBuilder builder;
54       GVariant *return_args;
55       GVariant *result;
56
57       g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
58       g_hash_table_iter_init (&iter, app->priv->actions);
59       while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&value))
60         g_variant_builder_add (&builder, "{s(sb)}",
61                                value->name,
62                                value->description ? value->description : "",
63                                value->enabled);
64
65       result = g_variant_builder_end (&builder);
66       return_args = g_variant_new_tuple (&result, 1);
67       g_dbus_method_invocation_return_value (invocation, return_args);
68       g_variant_unref (return_args);
69       g_variant_unref (result);
70     }
71   else if (strcmp (method_name, "InvokeAction") == 0)
72     {
73       const char *action_name;
74       guint32 timestamp;
75       GApplicationAction *action;
76
77       g_variant_get (parameters, "(&su)", &action_name, &timestamp);
78
79       action = g_hash_table_lookup (app->priv->actions, action_name);
80
81       if (!action)
82         {
83           char *errmsg  = g_strdup_printf ("Invalid action: %s", action_name);
84           g_dbus_method_invocation_return_dbus_error (invocation, G_APPLICATION_IFACE ".InvalidAction", errmsg);
85           g_free (errmsg);
86           return;
87         }
88
89       g_signal_emit (app, application_signals[ACTION], g_quark_from_string (action_name), action_name, (guint)timestamp);
90
91       g_dbus_method_invocation_return_value (invocation, NULL);
92     }
93   else if (strcmp (method_name, "Activate") == 0)
94     {
95       GVariant *args;
96       GVariant *platform_data;
97
98       g_variant_get (parameters, "(@aay@a{sv})", &args, &platform_data);
99
100       g_signal_emit (app, application_signals[PREPARE_ACTIVATION], 0, args, platform_data);
101
102       g_variant_unref (args);
103       g_variant_unref (platform_data);
104
105       g_dbus_method_invocation_return_value (invocation, NULL);
106     }
107 }
108
109 static const GDBusArgInfo application_quit_in_args[] =
110 {
111   {
112     -1,
113     "timestamp",
114     "u",
115     NULL
116   }
117 };
118
119 static const GDBusArgInfo * const application_quit_in_args_p[] = {
120   &application_quit_in_args[0],
121   NULL
122 };
123
124 static const GDBusArgInfo application_list_actions_out_args[] =
125 {
126   {
127     -1,
128     "actions",
129     "a{s(sb)}",
130     NULL
131   }
132 };
133
134 static const GDBusArgInfo * const application_list_actions_out_args_p[] = {
135   &application_list_actions_out_args[0],
136   NULL
137 };
138
139 static const GDBusArgInfo application_invoke_action_in_args[] =
140 {
141   {
142     -1,
143     "action",
144     "s",
145     NULL
146   },
147   {
148     -1,
149     "timestamp",
150     "u",
151     NULL
152   }
153 };
154
155 static const GDBusArgInfo * const application_invoke_action_in_args_p[] = {
156   &application_invoke_action_in_args[0],
157   &application_invoke_action_in_args[1],
158   NULL
159 };
160
161 static const GDBusMethodInfo application_quit_method_info =
162 {
163   -1,
164   "Quit",
165   (GDBusArgInfo **) &application_quit_in_args_p,
166   NULL,
167   NULL
168 };
169
170 static const GDBusMethodInfo application_list_actions_method_info =
171 {
172   -1,
173   "ListActions",
174   NULL,
175   (GDBusArgInfo **) &application_list_actions_out_args_p,
176   NULL
177 };
178
179 static const GDBusMethodInfo application_invoke_action_method_info =
180 {
181   -1,
182   "InvokeAction",
183   (GDBusArgInfo **) &application_invoke_action_in_args_p,
184   NULL,
185   NULL
186 };
187
188 static const GDBusArgInfo application_activate_in_args[] =
189 {
190   {
191     -1,
192     "arguments",
193     "aay",
194     NULL
195   },
196   {
197     -1,
198     "data",
199     "a{sv}",
200     NULL
201   }
202 };
203
204 static const GDBusArgInfo * const application_activate_in_args_p[] = {
205   &application_activate_in_args[0],
206   &application_activate_in_args[1],
207   NULL
208 };
209
210 static const GDBusMethodInfo application_activate_method_info =
211 {
212   -1,
213   "Activate",
214   (GDBusArgInfo **) &application_activate_in_args_p,
215   NULL,
216   NULL
217 };
218
219 static const GDBusMethodInfo * const application_dbus_method_info_p[] =
220 {
221   &application_quit_method_info,
222   &application_list_actions_method_info,
223   &application_invoke_action_method_info,
224   &application_activate_method_info,
225   NULL
226 };
227
228 static const GDBusSignalInfo application_dbus_signal_info[] =
229 {
230   {
231     -1,
232     "ActionsChanged",
233     NULL,
234     NULL
235   }
236 };
237
238 static const GDBusSignalInfo * const application_dbus_signal_info_p[] = {
239   &application_dbus_signal_info[0],
240   NULL
241 };
242
243 static const GDBusInterfaceInfo application_dbus_interface_info =
244 {
245   -1,
246   G_APPLICATION_IFACE,
247   (GDBusMethodInfo **) application_dbus_method_info_p,
248   (GDBusSignalInfo **) application_dbus_signal_info_p,
249   NULL,
250 };
251
252 static GDBusInterfaceVTable application_dbus_vtable =
253 {
254   application_dbus_method_call,
255   NULL,
256   NULL
257 };
258
259 static gchar *
260 application_path_from_appid (const gchar *appid)
261 {
262   gchar *appid_path, *iter;
263
264
265   appid_path = g_strconcat ("/", appid, NULL);
266   for (iter = appid_path; *iter; iter++)
267     {
268       if (*iter == '.')
269         *iter = '/';
270     }
271
272   return appid_path;
273 }
274
275 static void
276 ensure_bus (GApplication *app)
277 {
278   GError *error = NULL;
279
280   if (app->priv->session_bus == NULL)
281     app->priv->session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
282   if (app->priv->session_bus == NULL)
283     {
284       g_error ("%s", error->message);
285       g_error_free (error);
286     }
287
288   if (app->priv->dbus_path == NULL)
289     app->priv->dbus_path = application_path_from_appid (app->priv->appid);
290 }
291
292 static void
293 _g_application_platform_init (GApplication *app)
294 {
295   GError *error = NULL;
296   guint registration_id;
297
298   ensure_bus (app);
299
300   registration_id = g_dbus_connection_register_object (app->priv->session_bus,
301                                                        app->priv->dbus_path,
302                                                        &application_dbus_interface_info,
303                                                        &application_dbus_vtable,
304                                                        app, NULL,
305                                                        &error);
306   if (registration_id == 0)
307     {
308       g_error ("%s", error->message);
309       g_error_free (error);
310     }
311 }
312
313 static gboolean
314 _g_application_platform_acquire_single_instance (GApplication  *app,
315                                                  GError       **error)
316 {
317   GVariant *request_result;
318   guint32 request_status;
319
320   ensure_bus (app);
321   if (app->priv->session_bus == NULL)
322     return FALSE;
323
324   request_result = g_dbus_connection_call_sync (app->priv->session_bus,
325                                                 "org.freedesktop.DBus",
326                                                 "/org/freedesktop/DBus",
327                                                 "org.freedesktop.DBus",
328                                                 "RequestName",
329                                                 g_variant_new ("(su)", app->priv->appid, 0x4),
330                                                 NULL, 0, -1, NULL, error);
331
332   if (request_result == NULL)
333     return FALSE;
334
335   if (g_variant_is_of_type (request_result, G_VARIANT_TYPE ("(u)")))
336     g_variant_get (request_result, "(u)", &request_status);
337   else
338     request_status = 0;
339
340   g_variant_unref (request_result);
341
342   if (request_status != 1 && request_status != 4)
343     {
344       if (request_status == 3)
345         g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Another process has name \"%s\"", app->priv->appid);
346       else
347         g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unknown error");
348
349       return FALSE;
350     }
351
352   return TRUE;
353 }
354
355 static void
356 _g_application_platform_on_actions_changed (GApplication *app)
357 {
358   g_dbus_connection_emit_signal (app->priv->session_bus, NULL,
359                                  app->priv->dbus_path,
360                                  G_APPLICATION_IFACE,
361                                  "ActionsChanged", NULL, NULL);
362 }
363
364 static void
365 _g_application_platform_remote_invoke_action (GApplication  *app,
366                                               const gchar   *action,
367                                               guint          timestamp)
368 {
369   GVariant *result;
370
371   ensure_bus (app);
372
373   result = g_dbus_connection_call_sync (app->priv->session_bus,
374                                         app->priv->appid,
375                                         app->priv->dbus_path,
376                                         G_APPLICATION_IFACE,
377                                         "InvokeAction",
378                                         g_variant_new ("(su)",
379                                                        action,
380                                                        timestamp),
381                                         NULL, 0, -1, NULL, NULL);
382   if (result)
383     g_variant_unref (result);
384 }
385
386 static void
387 _g_application_platform_remote_quit (GApplication *app,
388                                      guint         timestamp)
389 {
390   GVariant *result;
391
392   ensure_bus (app);
393
394   result = g_dbus_connection_call_sync (app->priv->session_bus,
395                                         app->priv->appid,
396                                         app->priv->dbus_path,
397                                         G_APPLICATION_IFACE,
398                                         "Quit",
399                                         g_variant_new ("(u)",
400                                                        timestamp),
401                                         NULL, 0, -1, NULL, NULL);
402   if (result)
403     g_variant_unref (result);
404 }
405
406 static void
407 _g_application_platform_activate (GApplication *app,
408                                   GVariant     *data)
409 {
410   GVariant *result;
411
412   ensure_bus (app);
413
414   result = g_dbus_connection_call_sync (app->priv->session_bus,
415                                         app->priv->appid,
416                                         app->priv->dbus_path,
417                                         G_APPLICATION_IFACE,
418                                         "Activate",
419                                         data,
420                                         NULL, 0, -1, NULL, NULL);
421
422   if (result)
423     g_variant_unref (result);
424
425   exit (0);
426 }