gkdbus: Fix underflow and unreachable code bug
[platform/upstream/glib.git] / gio / gapplicationimpl-dbus.c
1 /*
2  * Copyright © 2010 Codethink Limited
3  *
4  * SPDX-License-Identifier: LGPL-2.1-or-later
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
17  * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
18  *
19  * Authors: Ryan Lortie <desrt@desrt.ca>
20  */
21
22 #include "config.h"
23
24 #include "gapplicationimpl.h"
25
26 #include "gactiongroup.h"
27 #include "gactiongroupexporter.h"
28 #include "gremoteactiongroup.h"
29 #include "gdbusactiongroup-private.h"
30 #include "gapplication.h"
31 #include "gfile.h"
32 #include "gdbusconnection.h"
33 #include "gdbusintrospection.h"
34 #include "gdbuserror.h"
35 #include "glib/gstdio.h"
36
37 #include <string.h>
38 #include <stdio.h>
39
40 #include "gapplicationcommandline.h"
41 #include "gdbusmethodinvocation.h"
42
43 #ifdef G_OS_UNIX
44 #include "gunixinputstream.h"
45 #include "gunixfdlist.h"
46 #endif
47
48 /* D-Bus Interface definition {{{1 */
49
50 /* For documentation of these interfaces, see
51  * https://wiki.gnome.org/Projects/GLib/GApplication/DBusAPI
52  */
53 static const gchar org_gtk_Application_xml[] =
54   "<node>"
55     "<interface name='org.gtk.Application'>"
56       "<method name='Activate'>"
57         "<arg type='a{sv}' name='platform-data' direction='in'/>"
58       "</method>"
59       "<method name='Open'>"
60         "<arg type='as' name='uris' direction='in'/>"
61         "<arg type='s' name='hint' direction='in'/>"
62         "<arg type='a{sv}' name='platform-data' direction='in'/>"
63       "</method>"
64       "<method name='CommandLine'>"
65         "<arg type='o' name='path' direction='in'/>"
66         "<arg type='aay' name='arguments' direction='in'/>"
67         "<arg type='a{sv}' name='platform-data' direction='in'/>"
68         "<arg type='i' name='exit-status' direction='out'/>"
69       "</method>"
70     "<property name='Busy' type='b' access='read'/>"
71     "</interface>"
72   "</node>";
73
74 static GDBusInterfaceInfo *org_gtk_Application;
75
76 static const gchar org_freedesktop_Application_xml[] =
77   "<node>"
78     "<interface name='org.freedesktop.Application'>"
79       "<method name='Activate'>"
80         "<arg type='a{sv}' name='platform-data' direction='in'/>"
81       "</method>"
82       "<method name='Open'>"
83         "<arg type='as' name='uris' direction='in'/>"
84         "<arg type='a{sv}' name='platform-data' direction='in'/>"
85       "</method>"
86       "<method name='ActivateAction'>"
87         "<arg type='s' name='action-name' direction='in'/>"
88         "<arg type='av' name='parameter' direction='in'/>"
89         "<arg type='a{sv}' name='platform-data' direction='in'/>"
90       "</method>"
91     "</interface>"
92   "</node>";
93
94 static GDBusInterfaceInfo *org_freedesktop_Application;
95
96 static const gchar org_gtk_private_CommandLine_xml[] =
97   "<node>"
98     "<interface name='org.gtk.private.CommandLine'>"
99       "<method name='Print'>"
100         "<arg type='s' name='message' direction='in'/>"
101       "</method>"
102       "<method name='PrintError'>"
103         "<arg type='s' name='message' direction='in'/>"
104       "</method>"
105     "</interface>"
106   "</node>";
107
108 static GDBusInterfaceInfo *org_gtk_private_CommandLine;
109
110 /* GApplication implementation {{{1 */
111 struct _GApplicationImpl
112 {
113   GDBusConnection *session_bus;
114   GActionGroup    *exported_actions;
115   const gchar     *bus_name;
116   guint            name_lost_signal;
117
118   gchar           *object_path;
119   guint            object_id;
120   guint            fdo_object_id;
121   guint            actions_id;
122
123   gboolean         properties_live;
124   gboolean         primary;
125   gboolean         busy;
126   gboolean         registered;
127   GApplication    *app;
128 };
129
130
131 static GApplicationCommandLine *
132 g_dbus_command_line_new (GDBusMethodInvocation *invocation);
133
134 static GVariant *
135 g_application_impl_get_property (GDBusConnection *connection,
136                                  const gchar  *sender,
137                                  const gchar  *object_path,
138                                  const gchar  *interface_name,
139                                  const gchar  *property_name,
140                                  GError      **error,
141                                  gpointer      user_data)
142 {
143   GApplicationImpl *impl = user_data;
144
145   if (strcmp (property_name, "Busy") == 0)
146     return g_variant_new_boolean (impl->busy);
147
148   g_assert_not_reached ();
149
150   return NULL;
151 }
152
153 static void
154 send_property_change (GApplicationImpl *impl)
155 {
156   GVariantBuilder builder;
157
158   g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
159   g_variant_builder_add (&builder,
160                          "{sv}",
161                          "Busy", g_variant_new_boolean (impl->busy));
162
163   g_dbus_connection_emit_signal (impl->session_bus,
164                                  NULL,
165                                  impl->object_path,
166                                  "org.freedesktop.DBus.Properties",
167                                  "PropertiesChanged",
168                                  g_variant_new ("(sa{sv}as)",
169                                                 "org.gtk.Application",
170                                                 &builder,
171                                                 NULL),
172                                  NULL);
173 }
174
175 static void
176 g_application_impl_method_call (GDBusConnection       *connection,
177                                 const gchar           *sender,
178                                 const gchar           *object_path,
179                                 const gchar           *interface_name,
180                                 const gchar           *method_name,
181                                 GVariant              *parameters,
182                                 GDBusMethodInvocation *invocation,
183                                 gpointer               user_data)
184 {
185   GApplicationImpl *impl = user_data;
186   GApplicationClass *class;
187
188   class = G_APPLICATION_GET_CLASS (impl->app);
189
190   if (strcmp (method_name, "Activate") == 0)
191     {
192       GVariant *platform_data;
193
194       /* Completely the same for both freedesktop and gtk interfaces */
195
196       g_variant_get (parameters, "(@a{sv})", &platform_data);
197
198       class->before_emit (impl->app, platform_data);
199       g_signal_emit_by_name (impl->app, "activate");
200       class->after_emit (impl->app, platform_data);
201       g_variant_unref (platform_data);
202
203       g_dbus_method_invocation_return_value (invocation, NULL);
204     }
205
206   else if (strcmp (method_name, "Open") == 0)
207     {
208       GApplicationFlags flags;
209       GVariant *platform_data;
210       const gchar *hint;
211       GVariant *array;
212       GFile **files;
213       gint n, i;
214
215       flags = g_application_get_flags (impl->app);
216       if ((flags & G_APPLICATION_HANDLES_OPEN) == 0)
217         {
218           g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "Application does not open files");
219           return;
220         }
221
222       /* freedesktop interface has no hint parameter */
223       if (g_str_equal (interface_name, "org.freedesktop.Application"))
224         {
225           g_variant_get (parameters, "(@as@a{sv})", &array, &platform_data);
226           hint = "";
227         }
228       else
229         g_variant_get (parameters, "(@as&s@a{sv})", &array, &hint, &platform_data);
230
231       n = g_variant_n_children (array);
232       files = g_new (GFile *, n + 1);
233
234       for (i = 0; i < n; i++)
235         {
236           const gchar *uri;
237
238           g_variant_get_child (array, i, "&s", &uri);
239           files[i] = g_file_new_for_uri (uri);
240         }
241       g_variant_unref (array);
242       files[n] = NULL;
243
244       class->before_emit (impl->app, platform_data);
245       g_signal_emit_by_name (impl->app, "open", files, n, hint);
246       class->after_emit (impl->app, platform_data);
247
248       g_variant_unref (platform_data);
249
250       for (i = 0; i < n; i++)
251         g_object_unref (files[i]);
252       g_free (files);
253
254       g_dbus_method_invocation_return_value (invocation, NULL);
255     }
256
257   else if (strcmp (method_name, "CommandLine") == 0)
258     {
259       GApplicationFlags flags;
260       GApplicationCommandLine *cmdline;
261       GVariant *platform_data;
262       int status;
263
264       flags = g_application_get_flags (impl->app);
265       if ((flags & G_APPLICATION_HANDLES_COMMAND_LINE) == 0)
266         {
267           g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED,
268                                                  "Application does not handle command line arguments");
269           return;
270         }
271
272       /* Only on the GtkApplication interface */
273
274       cmdline = g_dbus_command_line_new (invocation);
275       platform_data = g_variant_get_child_value (parameters, 2);
276       class->before_emit (impl->app, platform_data);
277       g_signal_emit_by_name (impl->app, "command-line", cmdline, &status);
278       g_application_command_line_set_exit_status (cmdline, status);
279       class->after_emit (impl->app, platform_data);
280       g_variant_unref (platform_data);
281       g_object_unref (cmdline);
282     }
283   else if (g_str_equal (method_name, "ActivateAction"))
284     {
285       GVariant *parameter = NULL;
286       GVariant *platform_data;
287       GVariantIter *iter;
288       const gchar *name;
289       const GVariantType *parameter_type = NULL;
290
291       /* Only on the freedesktop interface */
292
293       g_variant_get (parameters, "(&sav@a{sv})", &name, &iter, &platform_data);
294       g_variant_iter_next (iter, "v", &parameter);
295       g_variant_iter_free (iter);
296
297       /* Check the action exists and the parameter type matches. */
298       if (!g_action_group_query_action (impl->exported_actions,
299                                         name, NULL, &parameter_type,
300                                         NULL, NULL, NULL))
301         {
302           g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
303                                                  "Unknown action ‘%s’", name);
304           g_clear_pointer (&parameter, g_variant_unref);
305           g_variant_unref (platform_data);
306           return;
307         }
308
309       if (!((parameter_type == NULL && parameter == NULL) ||
310             (parameter_type != NULL && parameter != NULL && g_variant_is_of_type (parameter, parameter_type))))
311         {
312           g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
313                                                  "Invalid parameter for action ‘%s’: expected type %s but got type %s",
314                                                  name,
315                                                  (parameter_type != NULL) ? (const gchar *) parameter_type : "()",
316                                                  (parameter != NULL) ? g_variant_get_type_string (parameter) : "()");
317           g_clear_pointer (&parameter, g_variant_unref);
318           g_variant_unref (platform_data);
319           return;
320         }
321
322       class->before_emit (impl->app, platform_data);
323       g_action_group_activate_action (impl->exported_actions, name, parameter);
324       class->after_emit (impl->app, platform_data);
325
326       if (parameter)
327         g_variant_unref (parameter);
328
329       g_variant_unref (platform_data);
330
331       g_dbus_method_invocation_return_value (invocation, NULL);
332     }
333   else
334     g_assert_not_reached ();
335 }
336
337 static gchar *
338 application_path_from_appid (const gchar *appid)
339 {
340   gchar *appid_path, *iter;
341
342   if (appid == NULL)
343     /* this is a private implementation detail */
344     return g_strdup ("/org/gtk/Application/anonymous");
345
346   appid_path = g_strconcat ("/", appid, NULL);
347   for (iter = appid_path; *iter; iter++)
348     {
349       if (*iter == '.')
350         *iter = '/';
351
352       if (*iter == '-')
353         *iter = '_';
354     }
355
356   return appid_path;
357 }
358
359 static void g_application_impl_stop_primary (GApplicationImpl *impl);
360
361 static void
362 name_lost (GDBusConnection *bus,
363            const char      *sender_name,
364            const char      *object_path,
365            const char      *interface_name,
366            const char      *signal_name,
367            GVariant        *parameters,
368            gpointer         user_data)
369 {
370   GApplicationImpl *impl = user_data;
371   gboolean handled;
372
373   impl->primary = FALSE;
374   g_application_impl_stop_primary (impl);
375   g_signal_emit_by_name (impl->app, "name-lost", &handled);
376 }
377
378 /* Attempt to become the primary instance.
379  *
380  * Returns %TRUE if everything went OK, regardless of if we became the
381  * primary instance or not.  %FALSE is reserved for when something went
382  * seriously wrong (and @error will be set too, in that case).
383  *
384  * After a %TRUE return, impl->primary will be TRUE if we were
385  * successful.
386  */
387 static gboolean
388 g_application_impl_attempt_primary (GApplicationImpl  *impl,
389                                     GCancellable      *cancellable,
390                                     GError           **error)
391 {
392   static const GDBusInterfaceVTable vtable = {
393     g_application_impl_method_call,
394     g_application_impl_get_property,
395     NULL, /* set_property */
396     { 0 }
397   };
398   GApplicationClass *app_class = G_APPLICATION_GET_CLASS (impl->app);
399   GBusNameOwnerFlags name_owner_flags;
400   GApplicationFlags app_flags;
401   GVariant *reply;
402   guint32 rval;
403   GError *local_error = NULL;
404
405   if (org_gtk_Application == NULL)
406     {
407       GError *my_error = NULL;
408       GDBusNodeInfo *info;
409
410       info = g_dbus_node_info_new_for_xml (org_gtk_Application_xml, &my_error);
411       if G_UNLIKELY (info == NULL)
412         g_error ("%s", my_error->message);
413       org_gtk_Application = g_dbus_node_info_lookup_interface (info, "org.gtk.Application");
414       g_assert (org_gtk_Application != NULL);
415       g_dbus_interface_info_ref (org_gtk_Application);
416       g_dbus_node_info_unref (info);
417
418       info = g_dbus_node_info_new_for_xml (org_freedesktop_Application_xml, &my_error);
419       if G_UNLIKELY (info == NULL)
420         g_error ("%s", my_error->message);
421       org_freedesktop_Application = g_dbus_node_info_lookup_interface (info, "org.freedesktop.Application");
422       g_assert (org_freedesktop_Application != NULL);
423       g_dbus_interface_info_ref (org_freedesktop_Application);
424       g_dbus_node_info_unref (info);
425     }
426
427   /* We could possibly have been D-Bus activated as a result of incoming
428    * requests on either the application or actiongroup interfaces.
429    * Because of how GDBus dispatches messages, we need to ensure that
430    * both of those things are registered before we attempt to request
431    * our name.
432    *
433    * The action group need not be populated yet, as long as it happens
434    * before we return to the mainloop.  The reason for that is because
435    * GDBus does the check to make sure the object exists from the worker
436    * thread but doesn't actually dispatch the action invocation until we
437    * hit the mainloop in this thread.  There is also no danger of
438    * receiving 'activate' or 'open' signals until after 'startup' runs,
439    * for the same reason.
440    */
441   impl->object_id = g_dbus_connection_register_object (impl->session_bus, impl->object_path,
442                                                        org_gtk_Application, &vtable, impl, NULL, error);
443
444   if (impl->object_id == 0)
445     return FALSE;
446
447   impl->fdo_object_id = g_dbus_connection_register_object (impl->session_bus, impl->object_path,
448                                                            org_freedesktop_Application, &vtable, impl, NULL, error);
449
450   if (impl->fdo_object_id == 0)
451     return FALSE;
452
453   impl->actions_id = g_dbus_connection_export_action_group (impl->session_bus, impl->object_path,
454                                                             impl->exported_actions, error);
455
456   if (impl->actions_id == 0)
457     return FALSE;
458
459   impl->registered = TRUE;
460   if (!app_class->dbus_register (impl->app,
461                                  impl->session_bus,
462                                  impl->object_path,
463                                  &local_error))
464     {
465       g_return_val_if_fail (local_error != NULL, FALSE);
466       g_propagate_error (error, g_steal_pointer (&local_error));
467       return FALSE;
468     }
469
470   g_return_val_if_fail (local_error == NULL, FALSE);
471
472   if (impl->bus_name == NULL)
473     {
474       /* If this is a non-unique application then it is sufficient to
475        * have our object paths registered. We can return now.
476        *
477        * Note: non-unique applications always act as primary-instance.
478        */
479       impl->primary = TRUE;
480       return TRUE;
481     }
482
483   /* If this is a unique application then we need to attempt to own
484    * the well-known name and fall back to remote mode (!is_primary)
485    * in the case that we can't do that.
486    */
487   name_owner_flags = G_BUS_NAME_OWNER_FLAGS_DO_NOT_QUEUE;
488   app_flags = g_application_get_flags (impl->app);
489
490   if (app_flags & G_APPLICATION_ALLOW_REPLACEMENT)
491     {
492       impl->name_lost_signal = g_dbus_connection_signal_subscribe (impl->session_bus,
493                                                                    "org.freedesktop.DBus",
494                                                                    "org.freedesktop.DBus",
495                                                                    "NameLost",
496                                                                    "/org/freedesktop/DBus",
497                                                                    impl->bus_name,
498                                                                    G_DBUS_SIGNAL_FLAGS_NONE,
499                                                                    name_lost,
500                                                                    impl,
501                                                                    NULL);
502
503       name_owner_flags |= G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT;
504     }
505   if (app_flags & G_APPLICATION_REPLACE)
506     name_owner_flags |= G_BUS_NAME_OWNER_FLAGS_REPLACE;
507
508   reply = g_dbus_connection_call_sync (impl->session_bus,
509                                        "org.freedesktop.DBus",
510                                        "/org/freedesktop/DBus",
511                                        "org.freedesktop.DBus",
512                                        "RequestName",
513                                        g_variant_new ("(su)", impl->bus_name, name_owner_flags),
514                                        G_VARIANT_TYPE ("(u)"),
515                                        0, -1, cancellable, error);
516
517   if (reply == NULL)
518     return FALSE;
519
520   g_variant_get (reply, "(u)", &rval);
521   g_variant_unref (reply);
522
523   /* DBUS_REQUEST_NAME_REPLY_EXISTS: 3 */
524   impl->primary = (rval != 3);
525
526   if (!impl->primary && impl->name_lost_signal)
527     {
528       g_dbus_connection_signal_unsubscribe (impl->session_bus, impl->name_lost_signal);
529       impl->name_lost_signal = 0;
530     }
531
532   return TRUE;
533 }
534
535 /* Stop doing the things that the primary instance does.
536  *
537  * This should be called if attempting to become the primary instance
538  * failed (in order to clean up any partial success) and should also
539  * be called when freeing the GApplication.
540  *
541  * It is safe to call this multiple times.
542  */
543 static void
544 g_application_impl_stop_primary (GApplicationImpl *impl)
545 {
546   GApplicationClass *app_class = G_APPLICATION_GET_CLASS (impl->app);
547
548   if (impl->registered)
549     {
550       app_class->dbus_unregister (impl->app,
551                                   impl->session_bus,
552                                   impl->object_path);
553       impl->registered = FALSE;
554     }
555
556   if (impl->object_id)
557     {
558       g_dbus_connection_unregister_object (impl->session_bus, impl->object_id);
559       impl->object_id = 0;
560     }
561
562   if (impl->fdo_object_id)
563     {
564       g_dbus_connection_unregister_object (impl->session_bus, impl->fdo_object_id);
565       impl->fdo_object_id = 0;
566     }
567
568   if (impl->actions_id)
569     {
570       g_dbus_connection_unexport_action_group (impl->session_bus, impl->actions_id);
571       impl->actions_id = 0;
572     }
573
574   if (impl->name_lost_signal)
575     {
576       g_dbus_connection_signal_unsubscribe (impl->session_bus, impl->name_lost_signal);
577       impl->name_lost_signal = 0;
578     }
579
580   if (impl->primary && impl->bus_name)
581     {
582       g_dbus_connection_call (impl->session_bus, "org.freedesktop.DBus",
583                               "/org/freedesktop/DBus", "org.freedesktop.DBus",
584                               "ReleaseName", g_variant_new ("(s)", impl->bus_name),
585                               NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
586       impl->primary = FALSE;
587     }
588 }
589
590 void
591 g_application_impl_set_busy_state (GApplicationImpl *impl,
592                                    gboolean          busy)
593 {
594   if (impl->busy != busy)
595     {
596       impl->busy = busy;
597       send_property_change (impl);
598     }
599 }
600
601 void
602 g_application_impl_destroy (GApplicationImpl *impl)
603 {
604   g_application_impl_stop_primary (impl);
605
606   if (impl->session_bus)
607     g_object_unref (impl->session_bus);
608
609   g_free (impl->object_path);
610
611   g_slice_free (GApplicationImpl, impl);
612 }
613
614 GApplicationImpl *
615 g_application_impl_register (GApplication        *application,
616                              const gchar         *appid,
617                              GApplicationFlags    flags,
618                              GActionGroup        *exported_actions,
619                              GRemoteActionGroup **remote_actions,
620                              GCancellable        *cancellable,
621                              GError             **error)
622 {
623   GDBusActionGroup *actions;
624   GApplicationImpl *impl;
625
626   g_assert ((flags & G_APPLICATION_NON_UNIQUE) || appid != NULL);
627
628   impl = g_slice_new0 (GApplicationImpl);
629
630   impl->app = application;
631   impl->exported_actions = exported_actions;
632
633   /* non-unique applications do not attempt to acquire a bus name */
634   if (~flags & G_APPLICATION_NON_UNIQUE)
635     impl->bus_name = appid;
636
637   impl->session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, cancellable, NULL);
638
639   if (impl->session_bus == NULL)
640     {
641       /* If we can't connect to the session bus, proceed as a normal
642        * non-unique application.
643        */
644       *remote_actions = NULL;
645       return impl;
646     }
647
648   impl->object_path = application_path_from_appid (appid);
649
650   /* Only try to be the primary instance if
651    * G_APPLICATION_IS_LAUNCHER was not specified.
652    */
653   if (~flags & G_APPLICATION_IS_LAUNCHER)
654     {
655       if (!g_application_impl_attempt_primary (impl, cancellable, error))
656         {
657           g_application_impl_destroy (impl);
658           return NULL;
659         }
660
661       if (impl->primary)
662         return impl;
663
664       /* We didn't make it.  Drop our service-side stuff. */
665       g_application_impl_stop_primary (impl);
666
667       if (flags & G_APPLICATION_IS_SERVICE)
668         {
669           g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
670                        "Unable to acquire bus name '%s'", appid);
671           g_application_impl_destroy (impl);
672
673           return NULL;
674         }
675     }
676
677   /* We are non-primary.  Try to get the primary's list of actions.
678    * This also serves as a mechanism to ensure that the primary exists
679    * (ie: D-Bus service files installed correctly, etc).
680    */
681   actions = g_dbus_action_group_get (impl->session_bus, impl->bus_name, impl->object_path);
682   if (!g_dbus_action_group_sync (actions, cancellable, error))
683     {
684       /* The primary appears not to exist.  Fail the registration. */
685       g_application_impl_destroy (impl);
686       g_object_unref (actions);
687
688       return NULL;
689     }
690
691   *remote_actions = G_REMOTE_ACTION_GROUP (actions);
692
693   return impl;
694 }
695
696 void
697 g_application_impl_activate (GApplicationImpl *impl,
698                              GVariant         *platform_data)
699 {
700   g_dbus_connection_call (impl->session_bus,
701                           impl->bus_name,
702                           impl->object_path,
703                           "org.gtk.Application",
704                           "Activate",
705                           g_variant_new ("(@a{sv})", platform_data),
706                           NULL, 0, -1, NULL, NULL, NULL);
707 }
708
709 void
710 g_application_impl_open (GApplicationImpl  *impl,
711                          GFile            **files,
712                          gint               n_files,
713                          const gchar       *hint,
714                          GVariant          *platform_data)
715 {
716   GVariantBuilder builder;
717   gint i;
718
719   g_variant_builder_init (&builder, G_VARIANT_TYPE ("(assa{sv})"));
720   g_variant_builder_open (&builder, G_VARIANT_TYPE_STRING_ARRAY);
721   for (i = 0; i < n_files; i++)
722     {
723       gchar *uri = g_file_get_uri (files[i]);
724       g_variant_builder_add (&builder, "s", uri);
725       g_free (uri);
726     }
727   g_variant_builder_close (&builder);
728   g_variant_builder_add (&builder, "s", hint);
729   g_variant_builder_add_value (&builder, platform_data);
730
731   g_dbus_connection_call (impl->session_bus,
732                           impl->bus_name,
733                           impl->object_path,
734                           "org.gtk.Application",
735                           "Open",
736                           g_variant_builder_end (&builder),
737                           NULL, 0, -1, NULL, NULL, NULL);
738 }
739
740 static void
741 g_application_impl_cmdline_method_call (GDBusConnection       *connection,
742                                         const gchar           *sender,
743                                         const gchar           *object_path,
744                                         const gchar           *interface_name,
745                                         const gchar           *method_name,
746                                         GVariant              *parameters,
747                                         GDBusMethodInvocation *invocation,
748                                         gpointer               user_data)
749 {
750   const gchar *message;
751
752   g_variant_get_child (parameters, 0, "&s", &message);
753
754   if (strcmp (method_name, "Print") == 0)
755     g_print ("%s", message);
756   else if (strcmp (method_name, "PrintError") == 0)
757     g_printerr ("%s", message);
758   else
759     g_assert_not_reached ();
760
761   g_dbus_method_invocation_return_value (invocation, NULL);
762 }
763
764 typedef struct
765 {
766   GMainLoop *loop;
767   int status;
768 } CommandLineData;
769
770 static void
771 g_application_impl_cmdline_done (GObject      *source,
772                                  GAsyncResult *result,
773                                  gpointer      user_data)
774 {
775   CommandLineData *data = user_data;
776   GError *error = NULL;
777   GVariant *reply;
778
779 #ifdef G_OS_UNIX
780   reply = g_dbus_connection_call_with_unix_fd_list_finish (G_DBUS_CONNECTION (source), NULL, result, &error);
781 #else
782   reply = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error);
783 #endif
784
785
786   if (reply != NULL)
787     {
788       g_variant_get (reply, "(i)", &data->status);
789       g_variant_unref (reply);
790     }
791
792   else
793     {
794       g_printerr ("%s\n", error->message);
795       g_error_free (error);
796       data->status = 1;
797     }
798
799   g_main_loop_quit (data->loop);
800 }
801
802 int
803 g_application_impl_command_line (GApplicationImpl    *impl,
804                                  const gchar * const *arguments,
805                                  GVariant            *platform_data)
806 {
807   static const GDBusInterfaceVTable vtable = {
808     g_application_impl_cmdline_method_call, NULL, NULL, { 0 }
809   };
810   const gchar *object_path = "/org/gtk/Application/CommandLine";
811   GMainContext *context;
812   CommandLineData data;
813   guint object_id G_GNUC_UNUSED  /* when compiling with G_DISABLE_ASSERT */;
814
815   context = g_main_context_new ();
816   data.loop = g_main_loop_new (context, FALSE);
817   g_main_context_push_thread_default (context);
818
819   if (org_gtk_private_CommandLine == NULL)
820     {
821       GError *error = NULL;
822       GDBusNodeInfo *info;
823
824       info = g_dbus_node_info_new_for_xml (org_gtk_private_CommandLine_xml, &error);
825       if G_UNLIKELY (info == NULL)
826         g_error ("%s", error->message);
827       org_gtk_private_CommandLine = g_dbus_node_info_lookup_interface (info, "org.gtk.private.CommandLine");
828       g_assert (org_gtk_private_CommandLine != NULL);
829       g_dbus_interface_info_ref (org_gtk_private_CommandLine);
830       g_dbus_node_info_unref (info);
831     }
832
833   object_id = g_dbus_connection_register_object (impl->session_bus, object_path,
834                                                  org_gtk_private_CommandLine,
835                                                  &vtable, &data, NULL, NULL);
836   /* In theory we should try other paths... */
837   g_assert (object_id != 0);
838
839 #ifdef G_OS_UNIX
840   {
841     GError *error = NULL;
842     GUnixFDList *fd_list;
843
844     /* send along the stdin in case
845      * g_application_command_line_get_stdin_data() is called
846      */
847     fd_list = g_unix_fd_list_new ();
848     g_unix_fd_list_append (fd_list, 0, &error);
849     g_assert_no_error (error);
850
851     g_dbus_connection_call_with_unix_fd_list (impl->session_bus, impl->bus_name, impl->object_path,
852                                               "org.gtk.Application", "CommandLine",
853                                               g_variant_new ("(o^aay@a{sv})", object_path, arguments, platform_data),
854                                               G_VARIANT_TYPE ("(i)"), 0, G_MAXINT, fd_list, NULL,
855                                               g_application_impl_cmdline_done, &data);
856     g_object_unref (fd_list);
857   }
858 #else
859   g_dbus_connection_call (impl->session_bus, impl->bus_name, impl->object_path,
860                           "org.gtk.Application", "CommandLine",
861                           g_variant_new ("(o^aay@a{sv})", object_path, arguments, platform_data),
862                           G_VARIANT_TYPE ("(i)"), 0, G_MAXINT, NULL,
863                           g_application_impl_cmdline_done, &data);
864 #endif
865
866   g_main_loop_run (data.loop);
867
868   g_main_context_pop_thread_default (context);
869   g_main_context_unref (context);
870   g_main_loop_unref (data.loop);
871
872   return data.status;
873 }
874
875 void
876 g_application_impl_flush (GApplicationImpl *impl)
877 {
878   if (impl->session_bus)
879     g_dbus_connection_flush_sync (impl->session_bus, NULL, NULL);
880 }
881
882 GDBusConnection *
883 g_application_impl_get_dbus_connection (GApplicationImpl *impl)
884 {
885   return impl->session_bus;
886 }
887
888 const gchar *
889 g_application_impl_get_dbus_object_path (GApplicationImpl *impl)
890 {
891   return impl->object_path;
892 }
893
894 /* GDBusCommandLine implementation {{{1 */
895
896 typedef GApplicationCommandLineClass GDBusCommandLineClass;
897 static GType g_dbus_command_line_get_type (void);
898 typedef struct
899 {
900   GApplicationCommandLine  parent_instance;
901   GDBusMethodInvocation   *invocation;
902
903   GDBusConnection *connection;
904   const gchar     *bus_name;
905   const gchar     *object_path;
906 } GDBusCommandLine;
907
908
909 G_DEFINE_TYPE (GDBusCommandLine,
910                g_dbus_command_line,
911                G_TYPE_APPLICATION_COMMAND_LINE)
912
913 static void
914 g_dbus_command_line_print_literal (GApplicationCommandLine *cmdline,
915                                    const gchar             *message)
916 {
917   GDBusCommandLine *gdbcl = (GDBusCommandLine *) cmdline;
918
919   g_dbus_connection_call (gdbcl->connection,
920                           gdbcl->bus_name,
921                           gdbcl->object_path,
922                           "org.gtk.private.CommandLine", "Print",
923                           g_variant_new ("(s)", message),
924                           NULL, 0, -1, NULL, NULL, NULL);
925 }
926
927 static void
928 g_dbus_command_line_printerr_literal (GApplicationCommandLine *cmdline,
929                                       const gchar             *message)
930 {
931   GDBusCommandLine *gdbcl = (GDBusCommandLine *) cmdline;
932
933   g_dbus_connection_call (gdbcl->connection,
934                           gdbcl->bus_name,
935                           gdbcl->object_path,
936                           "org.gtk.private.CommandLine", "PrintError",
937                           g_variant_new ("(s)", message),
938                           NULL, 0, -1, NULL, NULL, NULL);
939 }
940
941 static GInputStream *
942 g_dbus_command_line_get_stdin (GApplicationCommandLine *cmdline)
943 {
944 #ifdef G_OS_UNIX
945   GDBusCommandLine *gdbcl = (GDBusCommandLine *) cmdline;
946   GInputStream *result = NULL;
947   GDBusMessage *message;
948   GUnixFDList *fd_list;
949
950   message = g_dbus_method_invocation_get_message (gdbcl->invocation);
951   fd_list = g_dbus_message_get_unix_fd_list (message);
952
953   if (fd_list && g_unix_fd_list_get_length (fd_list))
954     {
955       gint *fds, n_fds, i;
956
957       fds = g_unix_fd_list_steal_fds (fd_list, &n_fds);
958       result = g_unix_input_stream_new (fds[0], TRUE);
959       for (i = 1; i < n_fds; i++)
960         (void) g_close (fds[i], NULL);
961       g_free (fds);
962     }
963
964   return result;
965 #else
966   return NULL;
967 #endif
968 }
969
970 static void
971 g_dbus_command_line_finalize (GObject *object)
972 {
973   GApplicationCommandLine *cmdline = G_APPLICATION_COMMAND_LINE (object);
974   GDBusCommandLine *gdbcl = (GDBusCommandLine *) object;
975   gint status;
976
977   status = g_application_command_line_get_exit_status (cmdline);
978
979   g_dbus_method_invocation_return_value (gdbcl->invocation,
980                                          g_variant_new ("(i)", status));
981   g_object_unref (gdbcl->invocation);
982
983   G_OBJECT_CLASS (g_dbus_command_line_parent_class)
984     ->finalize (object);
985 }
986
987 static void
988 g_dbus_command_line_init (GDBusCommandLine *gdbcl)
989 {
990 }
991
992 static void
993 g_dbus_command_line_class_init (GApplicationCommandLineClass *class)
994 {
995   GObjectClass *object_class = G_OBJECT_CLASS (class);
996
997   object_class->finalize = g_dbus_command_line_finalize;
998   class->printerr_literal = g_dbus_command_line_printerr_literal;
999   class->print_literal = g_dbus_command_line_print_literal;
1000   class->get_stdin = g_dbus_command_line_get_stdin;
1001 }
1002
1003 static GApplicationCommandLine *
1004 g_dbus_command_line_new (GDBusMethodInvocation *invocation)
1005 {
1006   GDBusCommandLine *gdbcl;
1007   GVariant *args;
1008   GVariant *arguments, *platform_data;
1009
1010   args = g_dbus_method_invocation_get_parameters (invocation);
1011
1012   arguments = g_variant_get_child_value (args, 1);
1013   platform_data = g_variant_get_child_value (args, 2);
1014   gdbcl = g_object_new (g_dbus_command_line_get_type (),
1015                         "arguments", arguments,
1016                         "platform-data", platform_data,
1017                         NULL);
1018   g_variant_unref (arguments);
1019   g_variant_unref (platform_data);
1020
1021   gdbcl->connection = g_dbus_method_invocation_get_connection (invocation);
1022   gdbcl->bus_name = g_dbus_method_invocation_get_sender (invocation);
1023   g_variant_get_child (args, 0, "&o", &gdbcl->object_path);
1024   gdbcl->invocation = g_object_ref (invocation);
1025
1026   return G_APPLICATION_COMMAND_LINE (gdbcl);
1027 }
1028
1029 /* Epilogue {{{1 */
1030
1031 /* vim:set foldmethod=marker: */