gdbusactiongroup: Add prototype for g_dbus_action_group_sync()
[platform/upstream/glib.git] / gio / gapplicationimpl-dbus.c
1 /*
2  * Copyright © 2010 Codethink Limited
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 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
15  * Public License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
17  * Boston, MA 02111-1307, USA.
18  *
19  * Authors: Ryan Lortie <desrt@desrt.ca>
20  */
21
22 #include "gapplicationimpl.h"
23
24 #include "gactiongroup.h"
25 #include "gactiongroupexporter.h"
26 #include "gremoteactiongroup.h"
27 #include "gdbusactiongroup-private.h"
28 #include "gapplication.h"
29 #include "gfile.h"
30 #include "gdbusconnection.h"
31 #include "gdbusintrospection.h"
32 #include "gdbuserror.h"
33
34 #include <string.h>
35 #include <stdio.h>
36
37 #include "gapplicationcommandline.h"
38 #include "gdbusmethodinvocation.h"
39
40 /* DBus Interface definition {{{1 */
41
42 /* For documentation of these interfaces, see
43  * http://live.gnome.org/GTK+/GApplication-dbus-apis
44  */
45 static const gchar org_gtk_Application_xml[] =
46   "<node>"
47     "<interface name='org.gtk.Application'>"
48       "<method name='Activate'>"
49         "<arg type='a{sv}' name='platform-data' direction='in'/>"
50       "</method>"
51       "<method name='Open'>"
52         "<arg type='as' name='uris' direction='in'/>"
53         "<arg type='s' name='hint' direction='in'/>"
54         "<arg type='a{sv}' name='platform-data' direction='in'/>"
55       "</method>"
56       "<method name='CommandLine'>"
57         "<arg type='o' name='path' direction='in'/>"
58         "<arg type='aay' name='arguments' direction='in'/>"
59         "<arg type='a{sv}' name='platform-data' direction='in'/>"
60         "<arg type='i' name='exit-status' direction='out'/>"
61       "</method>"
62     "</interface>"
63   "</node>";
64
65 static GDBusInterfaceInfo *org_gtk_Application;
66
67 static const gchar org_gtk_private_CommandLine_xml[] =
68   "<node>"
69     "<interface name='org.gtk.private.CommandLine'>"
70       "<method name='Print'>"
71         "<arg type='s' name='message' direction='in'/>"
72       "</method>"
73       "<method name='PrintError'>"
74         "<arg type='s' name='message' direction='in'/>"
75       "</method>"
76     "</interface>"
77   "</node>";
78
79 static GDBusInterfaceInfo *org_gtk_private_CommandLine;
80
81 /* GApplication implementation {{{1 */
82 struct _GApplicationImpl
83 {
84   GDBusConnection *session_bus;
85   GActionGroup    *exported_actions;
86   const gchar     *bus_name;
87
88   gchar           *object_path;
89   guint            object_id;
90   guint            actions_id;
91
92   gboolean         properties_live;
93   gboolean         primary;
94   GApplication    *app;
95 };
96
97
98 static GApplicationCommandLine *
99 g_dbus_command_line_new (GDBusMethodInvocation *invocation);
100
101
102 static void
103 g_application_impl_method_call (GDBusConnection       *connection,
104                                 const gchar           *sender,
105                                 const gchar           *object_path,
106                                 const gchar           *interface_name,
107                                 const gchar           *method_name,
108                                 GVariant              *parameters,
109                                 GDBusMethodInvocation *invocation,
110                                 gpointer               user_data)
111 {
112   GApplicationImpl *impl = user_data;
113   GApplicationClass *class;
114
115   class = G_APPLICATION_GET_CLASS (impl->app);
116
117   if (strcmp (method_name, "Activate") == 0)
118     {
119       GVariant *platform_data;
120
121       g_variant_get (parameters, "(@a{sv})", &platform_data);
122       class->before_emit (impl->app, platform_data);
123       g_signal_emit_by_name (impl->app, "activate");
124       class->after_emit (impl->app, platform_data);
125       g_variant_unref (platform_data);
126
127       g_dbus_method_invocation_return_value (invocation, NULL);
128     }
129
130   else if (strcmp (method_name, "Open") == 0)
131     {
132       GVariant *platform_data;
133       const gchar *hint;
134       GVariant *array;
135       GFile **files;
136       gint n, i;
137
138       g_variant_get (parameters, "(@ass@a{sv})",
139                      &array, &hint, &platform_data);
140
141       n = g_variant_n_children (array);
142       files = g_new (GFile *, n + 1);
143
144       for (i = 0; i < n; i++)
145         {
146           const gchar *uri;
147
148           g_variant_get_child (array, i, "&s", &uri);
149           files[i] = g_file_new_for_uri (uri);
150         }
151       g_variant_unref (array);
152       files[n] = NULL;
153
154       class->before_emit (impl->app, platform_data);
155       g_signal_emit_by_name (impl->app, "open", files, n, hint);
156       class->after_emit (impl->app, platform_data);
157
158       g_variant_unref (platform_data);
159
160       for (i = 0; i < n; i++)
161         g_object_unref (files[i]);
162       g_free (files);
163
164       g_dbus_method_invocation_return_value (invocation, NULL);
165     }
166
167   else if (strcmp (method_name, "CommandLine") == 0)
168     {
169       GApplicationCommandLine *cmdline;
170       GVariant *platform_data;
171       int status;
172
173       cmdline = g_dbus_command_line_new (invocation);
174       platform_data = g_variant_get_child_value (parameters, 2);
175       class->before_emit (impl->app, platform_data);
176       g_signal_emit_by_name (impl->app, "command-line", cmdline, &status);
177       g_application_command_line_set_exit_status (cmdline, status);
178       class->after_emit (impl->app, platform_data);
179       g_variant_unref (platform_data);
180       g_object_unref (cmdline);
181     }
182   else
183     g_assert_not_reached ();
184 }
185
186 static gchar *
187 application_path_from_appid (const gchar *appid)
188 {
189   gchar *appid_path, *iter;
190
191   if (appid == NULL)
192     /* this is a private implementation detail */
193     return g_strdup ("/org/gtk/Application/anonymous");
194
195   appid_path = g_strconcat ("/", appid, NULL);
196   for (iter = appid_path; *iter; iter++)
197     {
198       if (*iter == '.')
199         *iter = '/';
200
201       if (*iter == '-')
202         *iter = '_';
203     }
204
205   return appid_path;
206 }
207
208 /* Attempt to become the primary instance.
209  *
210  * Returns %TRUE if everything went OK, regardless of if we became the
211  * primary instance or not.  %FALSE is reserved for when something went
212  * seriously wrong (and @error will be set too, in that case).
213  *
214  * After a %TRUE return, impl->primary will be TRUE if we were
215  * successful.
216  */
217 static gboolean
218 g_application_impl_attempt_primary (GApplicationImpl  *impl,
219                                     GCancellable      *cancellable,
220                                     GError           **error)
221 {
222   const static GDBusInterfaceVTable vtable = {
223     g_application_impl_method_call,
224   };
225   GApplicationClass *app_class = G_APPLICATION_GET_CLASS (impl->app);
226   GVariant *reply;
227   guint32 rval;
228
229   if (org_gtk_Application == NULL)
230     {
231       GError *error = NULL;
232       GDBusNodeInfo *info;
233
234       info = g_dbus_node_info_new_for_xml (org_gtk_Application_xml, &error);
235       if G_UNLIKELY (info == NULL)
236         g_error ("%s", error->message);
237       org_gtk_Application = g_dbus_node_info_lookup_interface (info, "org.gtk.Application");
238       g_assert (org_gtk_Application != NULL);
239       g_dbus_interface_info_ref (org_gtk_Application);
240       g_dbus_node_info_unref (info);
241     }
242
243   /* We could possibly have been D-Bus activated as a result of incoming
244    * requests on either the application or actiongroup interfaces.
245    * Because of how GDBus dispatches messages, we need to ensure that
246    * both of those things are registered before we attempt to request
247    * our name.
248    *
249    * The action group need not be populated yet, as long as it happens
250    * before we return to the mainloop.  The reason for that is because
251    * GDBus does the check to make sure the object exists from the worker
252    * thread but doesn't actually dispatch the action invocation until we
253    * hit the mainloop in this thread.  There is also no danger of
254    * receiving 'activate' or 'open' signals until after 'startup' runs,
255    * for the same reason.
256    */
257   impl->object_id = g_dbus_connection_register_object (impl->session_bus, impl->object_path,
258                                                        org_gtk_Application, &vtable, impl, NULL, error);
259
260   if (impl->object_id == 0)
261     return FALSE;
262
263   impl->actions_id = g_dbus_connection_export_action_group (impl->session_bus, impl->object_path,
264                                                             impl->exported_actions, error);
265
266   if (impl->actions_id == 0)
267     return FALSE;
268
269   if (!app_class->dbus_register (impl->app,
270                                  impl->session_bus,
271                                  impl->object_path,
272                                  error))
273     return FALSE;
274
275   if (impl->bus_name == NULL)
276     {
277       /* If this is a non-unique application then it is sufficient to
278        * have our object paths registered. We can return now.
279        *
280        * Note: non-unique applications always act as primary-instance.
281        */
282       impl->primary = TRUE;
283       return TRUE;
284     }
285
286   /* If this is a unique application then we need to attempt to own
287    * the well-known name and fall back to remote mode (!is_primary)
288    * in the case that we can't do that.
289    */
290   /* DBUS_NAME_FLAG_DO_NOT_QUEUE: 0x4 */
291   reply = g_dbus_connection_call_sync (impl->session_bus, "org.freedesktop.DBus", "/org/freedesktop/DBus",
292                                        "org.freedesktop.DBus", "RequestName",
293                                        g_variant_new ("(su)", impl->bus_name, 0x4), G_VARIANT_TYPE ("(u)"),
294                                        0, -1, cancellable, error);
295
296   if (reply == NULL)
297     return FALSE;
298
299   g_variant_get (reply, "(u)", &rval);
300   g_variant_unref (reply);
301
302   /* DBUS_REQUEST_NAME_REPLY_EXISTS: 3 */
303   impl->primary = (rval != 3);
304
305   return TRUE;
306 }
307
308 /* Stop doing the things that the primary instance does.
309  *
310  * This should be called if attempting to become the primary instance
311  * failed (in order to clean up any partial success) and should also
312  * be called when freeing the GApplication.
313  *
314  * It is safe to call this multiple times.
315  */
316 static void
317 g_application_impl_stop_primary (GApplicationImpl *impl)
318 {
319   GApplicationClass *app_class = G_APPLICATION_GET_CLASS (impl->app);
320
321   app_class->dbus_unregister (impl->app,
322                               impl->session_bus,
323                               impl->object_path);
324
325   if (impl->object_id)
326     {
327       g_dbus_connection_unregister_object (impl->session_bus, impl->object_id);
328       impl->object_id = 0;
329     }
330
331   if (impl->actions_id)
332     {
333       g_dbus_connection_unexport_action_group (impl->session_bus, impl->actions_id);
334       impl->actions_id = 0;
335     }
336
337   if (impl->primary && impl->bus_name)
338     {
339       g_dbus_connection_call (impl->session_bus, "org.freedesktop.DBus",
340                               "/org/freedesktop/DBus", "org.freedesktop.DBus",
341                               "ReleaseName", g_variant_new ("(s)", impl->bus_name),
342                               NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
343       impl->primary = FALSE;
344     }
345 }
346
347 void
348 g_application_impl_destroy (GApplicationImpl *impl)
349 {
350   g_application_impl_stop_primary (impl);
351
352   if (impl->session_bus)
353     g_object_unref (impl->session_bus);
354
355   g_free (impl->object_path);
356
357   g_slice_free (GApplicationImpl, impl);
358 }
359
360 GApplicationImpl *
361 g_application_impl_register (GApplication        *application,
362                              const gchar         *appid,
363                              GApplicationFlags    flags,
364                              GActionGroup        *exported_actions,
365                              GRemoteActionGroup **remote_actions,
366                              GCancellable        *cancellable,
367                              GError             **error)
368 {
369   GDBusActionGroup *actions;
370   GApplicationImpl *impl;
371
372   g_assert ((flags & G_APPLICATION_NON_UNIQUE) || appid != NULL);
373
374   impl = g_slice_new0 (GApplicationImpl);
375
376   impl->app = application;
377   impl->exported_actions = exported_actions;
378
379   /* non-unique applications do not attempt to acquire a bus name */
380   if (~flags & G_APPLICATION_NON_UNIQUE)
381     impl->bus_name = appid;
382
383   impl->session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, cancellable, NULL);
384
385   if (impl->session_bus == NULL)
386     {
387       /* If we can't connect to the session bus, proceed as a normal
388        * non-unique application.
389        */
390       *remote_actions = NULL;
391       return impl;
392     }
393
394   impl->object_path = application_path_from_appid (appid);
395
396   /* Only try to be the primary instance if
397    * G_APPLICATION_IS_LAUNCHER was not specified.
398    */
399   if (~flags & G_APPLICATION_IS_LAUNCHER)
400     {
401       if (!g_application_impl_attempt_primary (impl, cancellable, error))
402         {
403           g_application_impl_destroy (impl);
404           return NULL;
405         }
406
407       if (impl->primary)
408         return impl;
409
410       /* We didn't make it.  Drop our service-side stuff. */
411       g_application_impl_stop_primary (impl);
412
413       if (flags & G_APPLICATION_IS_SERVICE)
414         {
415           g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
416                        "Unable to acquire bus name `%s'", appid);
417           g_application_impl_destroy (impl);
418
419           return NULL;
420         }
421     }
422
423   /* We are non-primary.  Try to get the primary's list of actions.
424    * This also serves as a mechanism to ensure that the primary exists
425    * (ie: DBus service files installed correctly, etc).
426    */
427   actions = g_dbus_action_group_get (impl->session_bus, impl->bus_name, impl->object_path);
428   if (!g_dbus_action_group_sync (actions, cancellable, error))
429     {
430       /* The primary appears not to exist.  Fail the registration. */
431       g_application_impl_destroy (impl);
432       g_object_unref (actions);
433
434       return NULL;
435     }
436
437   *remote_actions = G_REMOTE_ACTION_GROUP (actions);
438
439   return impl;
440 }
441
442 void
443 g_application_impl_activate (GApplicationImpl *impl,
444                              GVariant         *platform_data)
445 {
446   g_dbus_connection_call (impl->session_bus,
447                           impl->bus_name,
448                           impl->object_path,
449                           "org.gtk.Application",
450                           "Activate",
451                           g_variant_new ("(@a{sv})", platform_data),
452                           NULL, 0, -1, NULL, NULL, NULL);
453 }
454
455 void
456 g_application_impl_open (GApplicationImpl  *impl,
457                          GFile            **files,
458                          gint               n_files,
459                          const gchar       *hint,
460                          GVariant          *platform_data)
461 {
462   GVariantBuilder builder;
463   gint i;
464
465   g_variant_builder_init (&builder, G_VARIANT_TYPE ("(assa{sv})"));
466   g_variant_builder_open (&builder, G_VARIANT_TYPE_STRING_ARRAY);
467   for (i = 0; i < n_files; i++)
468     {
469       gchar *uri = g_file_get_uri (files[i]);
470       g_variant_builder_add (&builder, "s", uri);
471       g_free (uri);
472     }
473   g_variant_builder_close (&builder);
474   g_variant_builder_add (&builder, "s", hint);
475   g_variant_builder_add_value (&builder, platform_data);
476
477   g_dbus_connection_call (impl->session_bus,
478                           impl->bus_name,
479                           impl->object_path,
480                           "org.gtk.Application",
481                           "Open",
482                           g_variant_builder_end (&builder),
483                           NULL, 0, -1, NULL, NULL, NULL);
484 }
485
486 static void
487 g_application_impl_cmdline_method_call (GDBusConnection       *connection,
488                                         const gchar           *sender,
489                                         const gchar           *object_path,
490                                         const gchar           *interface_name,
491                                         const gchar           *method_name,
492                                         GVariant              *parameters,
493                                         GDBusMethodInvocation *invocation,
494                                         gpointer               user_data)
495 {
496   const gchar *message;
497
498   g_variant_get_child (parameters, 0, "&s", &message);
499
500   if (strcmp (method_name, "Print") == 0)
501     g_print ("%s", message);
502   else if (strcmp (method_name, "PrintError") == 0)
503     g_printerr ("%s", message);
504   else
505     g_assert_not_reached ();
506
507   g_dbus_method_invocation_return_value (invocation, NULL);
508 }
509
510 typedef struct
511 {
512   GMainLoop *loop;
513   int status;
514 } CommandLineData;
515
516 static void
517 g_application_impl_cmdline_done (GObject      *source,
518                                  GAsyncResult *result,
519                                  gpointer      user_data)
520 {
521   CommandLineData *data = user_data;
522   GError *error = NULL;
523   GVariant *reply;
524
525   reply = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source),
526                                          result, &error);
527
528   if (reply != NULL)
529     {
530       g_variant_get (reply, "(i)", &data->status);
531       g_variant_unref (reply);
532     }
533
534   else
535     {
536       g_printerr ("%s\n", error->message);
537       g_error_free (error);
538       data->status = 1;
539     }
540
541   g_main_loop_quit (data->loop);
542 }
543
544 int
545 g_application_impl_command_line (GApplicationImpl  *impl,
546                                  gchar            **arguments,
547                                  GVariant          *platform_data)
548 {
549   const static GDBusInterfaceVTable vtable = {
550     g_application_impl_cmdline_method_call
551   };
552   const gchar *object_path = "/org/gtk/Application/CommandLine";
553   GMainContext *context;
554   CommandLineData data;
555   guint object_id;
556
557   context = g_main_context_new ();
558   data.loop = g_main_loop_new (context, FALSE);
559   g_main_context_push_thread_default (context);
560
561   if (org_gtk_private_CommandLine == NULL)
562     {
563       GError *error = NULL;
564       GDBusNodeInfo *info;
565
566       info = g_dbus_node_info_new_for_xml (org_gtk_private_CommandLine_xml, &error);
567       if G_UNLIKELY (info == NULL)
568         g_error ("%s", error->message);
569       org_gtk_private_CommandLine = g_dbus_node_info_lookup_interface (info, "org.gtk.private.CommandLine");
570       g_assert (org_gtk_private_CommandLine != NULL);
571       g_dbus_interface_info_ref (org_gtk_private_CommandLine);
572       g_dbus_node_info_unref (info);
573     }
574
575   object_id = g_dbus_connection_register_object (impl->session_bus, object_path,
576                                                  org_gtk_private_CommandLine,
577                                                  &vtable, &data, NULL, NULL);
578   /* In theory we should try other paths... */
579   g_assert (object_id != 0);
580
581   g_dbus_connection_call (impl->session_bus,
582                           impl->bus_name,
583                           impl->object_path,
584                           "org.gtk.Application",
585                           "CommandLine",
586                           g_variant_new ("(o^aay@a{sv})", object_path,
587                                          arguments, platform_data),
588                           G_VARIANT_TYPE ("(i)"), 0, G_MAXINT, NULL,
589                           g_application_impl_cmdline_done, &data);
590
591   g_main_loop_run (data.loop);
592
593   g_main_context_pop_thread_default (context);
594   g_main_context_unref (context);
595   g_main_loop_unref (data.loop);
596
597   return data.status;
598 }
599
600 void
601 g_application_impl_flush (GApplicationImpl *impl)
602 {
603   if (impl->session_bus)
604     g_dbus_connection_flush_sync (impl->session_bus, NULL, NULL);
605 }
606
607 GDBusConnection *
608 g_application_impl_get_dbus_connection (GApplicationImpl *impl)
609 {
610   return impl->session_bus;
611 }
612
613 const gchar *
614 g_application_impl_get_dbus_object_path (GApplicationImpl *impl)
615 {
616   return impl->object_path;
617 }
618
619 /* GDBusCommandLine implementation {{{1 */
620
621 typedef GApplicationCommandLineClass GDBusCommandLineClass;
622 static GType g_dbus_command_line_get_type (void);
623 typedef struct
624 {
625   GApplicationCommandLine  parent_instance;
626   GDBusMethodInvocation   *invocation;
627
628   GDBusConnection *connection;
629   const gchar     *bus_name;
630   const gchar     *object_path;
631 } GDBusCommandLine;
632
633
634 G_DEFINE_TYPE (GDBusCommandLine,
635                g_dbus_command_line,
636                G_TYPE_APPLICATION_COMMAND_LINE)
637
638 static void
639 g_dbus_command_line_print_literal (GApplicationCommandLine *cmdline,
640                                    const gchar             *message)
641 {
642   GDBusCommandLine *gdbcl = (GDBusCommandLine *) cmdline;
643
644   g_dbus_connection_call (gdbcl->connection,
645                           gdbcl->bus_name,
646                           gdbcl->object_path,
647                           "org.gtk.private.CommandLine", "Print",
648                           g_variant_new ("(s)", message),
649                           NULL, 0, -1, NULL, NULL, NULL);
650 }
651
652 static void
653 g_dbus_command_line_printerr_literal (GApplicationCommandLine *cmdline,
654                                       const gchar             *message)
655 {
656   GDBusCommandLine *gdbcl = (GDBusCommandLine *) cmdline;
657
658   g_dbus_connection_call (gdbcl->connection,
659                           gdbcl->bus_name,
660                           gdbcl->object_path,
661                           "org.gtk.private.CommandLine", "PrintError",
662                           g_variant_new ("(s)", message),
663                           NULL, 0, -1, NULL, NULL, NULL);
664 }
665
666 static void
667 g_dbus_command_line_finalize (GObject *object)
668 {
669   GApplicationCommandLine *cmdline = G_APPLICATION_COMMAND_LINE (object);
670   GDBusCommandLine *gdbcl = (GDBusCommandLine *) object;
671   gint status;
672
673   status = g_application_command_line_get_exit_status (cmdline);
674
675   g_dbus_method_invocation_return_value (gdbcl->invocation,
676                                          g_variant_new ("(i)", status));
677   g_object_unref (gdbcl->invocation);
678
679   G_OBJECT_CLASS (g_dbus_command_line_parent_class)
680     ->finalize (object);
681 }
682
683 static void
684 g_dbus_command_line_init (GDBusCommandLine *gdbcl)
685 {
686 }
687
688 static void
689 g_dbus_command_line_class_init (GApplicationCommandLineClass *class)
690 {
691   GObjectClass *object_class = G_OBJECT_CLASS (class);
692
693   object_class->finalize = g_dbus_command_line_finalize;
694   class->printerr_literal = g_dbus_command_line_printerr_literal;
695   class->print_literal = g_dbus_command_line_print_literal;
696 }
697
698 static GApplicationCommandLine *
699 g_dbus_command_line_new (GDBusMethodInvocation *invocation)
700 {
701   GDBusCommandLine *gdbcl;
702   GVariant *args;
703
704   args = g_dbus_method_invocation_get_parameters (invocation);
705
706   gdbcl = g_object_new (g_dbus_command_line_get_type (),
707                         "arguments", g_variant_get_child_value (args, 1),
708                         "platform-data", g_variant_get_child_value (args, 2),
709                         NULL);
710   gdbcl->connection = g_dbus_method_invocation_get_connection (invocation);
711   gdbcl->bus_name = g_dbus_method_invocation_get_sender (invocation);
712   g_variant_get_child (args, 0, "&o", &gdbcl->object_path);
713   gdbcl->invocation = g_object_ref (invocation);
714
715   return G_APPLICATION_COMMAND_LINE (gdbcl);
716 }
717
718 /* Epilogue {{{1 */
719
720 /* vim:set foldmethod=marker: */