GDBus: Make gdbus(1) print annotations when introspecting data
[platform/upstream/glib.git] / gio / gdbus-tool.c
1 /* GDBus - GLib D-Bus Library
2  *
3  * Copyright (C) 2008-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  * Author: David Zeuthen <davidz@redhat.com>
21  */
22
23 #include "config.h"
24
25 #include <string.h>
26
27 #include <gio/gio.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30
31 #include <gi18n.h>
32
33 /* ---------------------------------------------------------------------------------------------------- */
34
35 G_GNUC_UNUSED static void completion_debug (const gchar *format, ...);
36
37 /* Uncomment to get debug traces in /tmp/gdbus-completion-debug.txt (nice
38  * to not have it interfere with stdout/stderr)
39  */
40 #if 0
41 G_GNUC_UNUSED static void
42 completion_debug (const gchar *format, ...)
43 {
44   va_list var_args;
45   gchar *s;
46   static FILE *f = NULL;
47
48   va_start (var_args, format);
49   s = g_strdup_vprintf (format, var_args);
50   if (f == NULL)
51     {
52       f = fopen ("/tmp/gdbus-completion-debug.txt", "a+");
53     }
54   fprintf (f, "%s\n", s);
55   g_free (s);
56 }
57 #else
58 static void
59 completion_debug (const gchar *format, ...)
60 {
61 }
62 #endif
63
64 /* ---------------------------------------------------------------------------------------------------- */
65
66
67 static void
68 remove_arg (gint num, gint *argc, gchar **argv[])
69 {
70   gint n;
71
72   g_assert (num <= (*argc));
73
74   for (n = num; (*argv)[n] != NULL; n++)
75     (*argv)[n] = (*argv)[n+1];
76   (*argv)[n] = NULL;
77   (*argc) = (*argc) - 1;
78 }
79
80 static void
81 usage (gint *argc, gchar **argv[], gboolean use_stdout)
82 {
83   GOptionContext *o;
84   gchar *s;
85   gchar *program_name;
86
87   o = g_option_context_new (_("COMMAND"));
88   g_option_context_set_help_enabled (o, FALSE);
89   /* Ignore parsing result */
90   g_option_context_parse (o, argc, argv, NULL);
91   program_name = g_path_get_basename ((*argv)[0]);
92   s = g_strdup_printf (_("Commands:\n"
93                          "  help         Shows this information\n"
94                          "  introspect   Introspect a remote object\n"
95                          "  call         Invoke a method on a remote object\n"
96                          "\n"
97                          "Use \"%s COMMAND --help\" to get help on each command.\n"),
98                        program_name);
99   g_free (program_name);
100   g_option_context_set_description (o, s);
101   g_free (s);
102   s = g_option_context_get_help (o, FALSE, NULL);
103   if (use_stdout)
104     g_print ("%s", s);
105   else
106     g_printerr ("%s", s);
107   g_free (s);
108   g_option_context_free (o);
109 }
110
111 static void
112 modify_argv0_for_command (gint *argc, gchar **argv[], const gchar *command)
113 {
114   gchar *s;
115   gchar *program_name;
116
117   /* TODO:
118    *  1. get a g_set_prgname() ?; or
119    *  2. save old argv[0] and restore later
120    */
121
122   g_assert (g_strcmp0 ((*argv)[1], command) == 0);
123   remove_arg (1, argc, argv);
124
125   program_name = g_path_get_basename ((*argv)[0]);
126   s = g_strdup_printf ("%s %s", (*argv)[0], command);
127   (*argv)[0] = s;
128   g_free (program_name);
129 }
130
131 /* ---------------------------------------------------------------------------------------------------- */
132
133 static void
134 print_methods (GDBusConnection *c,
135                const gchar *name,
136                const gchar *path)
137 {
138   GVariant *result;
139   GError *error;
140   const gchar *xml_data;
141   GDBusNodeInfo *node;
142   guint n;
143   guint m;
144
145   error = NULL;
146   result = g_dbus_connection_call_sync (c,
147                                         name,
148                                         path,
149                                         "org.freedesktop.DBus.Introspectable",
150                                         "Introspect",
151                                         NULL,
152                                         G_DBUS_CALL_FLAGS_NONE,
153                                         3000, /* 3 secs */
154                                         NULL,
155                                         &error);
156   if (result == NULL)
157     {
158       g_printerr (_("Error: %s\n"), error->message);
159       g_error_free (error);
160       goto out;
161     }
162   if (!g_variant_is_of_type (result, G_VARIANT_TYPE ("(s)")))
163     {
164       g_printerr (_("Error: Result is type `%s', expected `(s)'\n"),
165                   g_variant_get_type_string (result));
166       g_variant_unref (result);
167       goto out;
168     }
169   g_variant_get (result, "(s)", &xml_data);
170
171   error = NULL;
172   node = g_dbus_node_info_new_for_xml (xml_data, &error);
173   g_variant_unref (result);
174   if (node == NULL)
175     {
176       g_printerr (_("Error parsing introspection XML: %s\n"), error->message);
177       g_error_free (error);
178       goto out;
179     }
180
181   for (n = 0; node->interfaces != NULL && node->interfaces[n] != NULL; n++)
182     {
183       const GDBusInterfaceInfo *iface = node->interfaces[n];
184       for (m = 0; iface->methods != NULL && iface->methods[m] != NULL; m++)
185         {
186           const GDBusMethodInfo *method = iface->methods[m];
187           g_print ("%s.%s \n", iface->name, method->name);
188         }
189     }
190   g_dbus_node_info_unref (node);
191
192  out:
193   ;
194 }
195
196 static void
197 print_paths (GDBusConnection *c,
198              const gchar *name,
199              const gchar *path)
200 {
201   GVariant *result;
202   GError *error;
203   const gchar *xml_data;
204   GDBusNodeInfo *node;
205   guint n;
206
207   error = NULL;
208   result = g_dbus_connection_call_sync (c,
209                                         name,
210                                         path,
211                                         "org.freedesktop.DBus.Introspectable",
212                                         "Introspect",
213                                         NULL,
214                                         G_DBUS_CALL_FLAGS_NONE,
215                                         3000, /* 3 secs */
216                                         NULL,
217                                         &error);
218   if (result == NULL)
219     {
220       g_printerr (_("Error: %s\n"), error->message);
221       g_error_free (error);
222       goto out;
223     }
224   if (!g_variant_is_of_type (result, G_VARIANT_TYPE ("(s)")))
225     {
226       g_printerr (_("Error: Result is type `%s', expected `(s)'\n"),
227                   g_variant_get_type_string (result));
228       g_variant_unref (result);
229       goto out;
230     }
231   g_variant_get (result, "(s)", &xml_data);
232
233   error = NULL;
234   node = g_dbus_node_info_new_for_xml (xml_data, &error);
235   g_variant_unref (result);
236   if (node == NULL)
237     {
238       g_printerr (_("Error parsing introspection XML: %s\n"), error->message);
239       g_error_free (error);
240       goto out;
241     }
242
243   //g_printerr ("xml=`%s'", xml_data);
244
245   //g_printerr ("bar `%s'\n", path);
246
247   if (node->interfaces != NULL)
248     g_print ("%s \n", path);
249
250   for (n = 0; node->nodes != NULL && node->nodes[n] != NULL; n++)
251     {
252       gchar *s;
253
254       //g_printerr ("foo `%s'\n", node->nodes[n].path);
255
256       if (g_strcmp0 (path, "/") == 0)
257         s = g_strdup_printf ("/%s", node->nodes[n]->path);
258       else
259         s = g_strdup_printf ("%s/%s", path, node->nodes[n]->path);
260
261       print_paths (c, name, s);
262
263       g_free (s);
264     }
265   g_dbus_node_info_unref (node);
266
267  out:
268   ;
269 }
270
271 static void
272 print_names (GDBusConnection *c,
273              gboolean         include_unique_names)
274 {
275   GVariant *result;
276   GError *error;
277   GVariantIter *iter;
278   gchar *str;
279   GHashTable *name_set;
280   GList *keys;
281   GList *l;
282
283   name_set = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
284
285   error = NULL;
286   result = g_dbus_connection_call_sync (c,
287                                         "org.freedesktop.DBus",
288                                         "/org/freedesktop/DBus",
289                                         "org.freedesktop.DBus",
290                                         "ListNames",
291                                         NULL,
292                                         G_DBUS_CALL_FLAGS_NONE,
293                                         3000, /* 3 secs */
294                                         NULL,
295                                         &error);
296   if (result == NULL)
297     {
298       g_printerr (_("Error: %s\n"), error->message);
299       g_error_free (error);
300       goto out;
301     }
302   if (!g_variant_is_of_type (result, G_VARIANT_TYPE ("(as)")))
303     {
304       g_printerr (_("Error: Result is type `%s', expected `(as)'\n"), g_variant_get_type_string (result));
305       g_variant_unref (result);
306       goto out;
307     }
308   g_variant_get (result, "(as)", &iter);
309   while (g_variant_iter_loop (iter, "s", &str))
310     g_hash_table_insert (name_set, g_strdup (str), NULL);
311   g_variant_iter_free (iter);
312   g_variant_unref (result);
313
314   error = NULL;
315   result = g_dbus_connection_call_sync (c,
316                                         "org.freedesktop.DBus",
317                                         "/org/freedesktop/DBus",
318                                         "org.freedesktop.DBus",
319                                         "ListActivatableNames",
320                                         NULL,
321                                         G_DBUS_CALL_FLAGS_NONE,
322                                         3000, /* 3 secs */
323                                         NULL,
324                                         &error);
325   if (result == NULL)
326     {
327       g_printerr (_("Error: %s\n"), error->message);
328       g_error_free (error);
329       goto out;
330     }
331   if (!g_variant_is_of_type (result, G_VARIANT_TYPE ("(as)")))
332     {
333       g_printerr (_("Error: Result is type `%s', expected `(as)'\n"), g_variant_get_type_string (result));
334       g_variant_unref (result);
335       goto out;
336     }
337   g_variant_get (result, "(as)", &iter);
338   while (g_variant_iter_loop (iter, "s", &str))
339     g_hash_table_insert (name_set, g_strdup (str), NULL);
340   g_variant_iter_free (iter);
341   g_variant_unref (result);
342
343   keys = g_hash_table_get_keys (name_set);
344   keys = g_list_sort (keys, (GCompareFunc) g_strcmp0);
345   for (l = keys; l != NULL; l = l->next)
346     {
347       const gchar *name = l->data;
348       if (!include_unique_names && g_str_has_prefix (name, ":"))
349         continue;
350
351       g_print ("%s \n", name);
352     }
353   g_list_free (keys);
354
355  out:
356   g_hash_table_unref (name_set);
357 }
358
359 /* ---------------------------------------------------------------------------------------------------- */
360
361 static gboolean  opt_connection_system  = FALSE;
362 static gboolean  opt_connection_session = FALSE;
363 static gchar    *opt_connection_address = NULL;
364
365 static const GOptionEntry connection_entries[] =
366 {
367   { "system", 'y', 0, G_OPTION_ARG_NONE, &opt_connection_system, N_("Connect to the system bus"), NULL},
368   { "session", 'e', 0, G_OPTION_ARG_NONE, &opt_connection_session, N_("Connect to the session bus"), NULL},
369   { "address", 'a', 0, G_OPTION_ARG_STRING, &opt_connection_address, N_("Connect to given D-Bus address"), NULL},
370   { NULL }
371 };
372
373 static GOptionGroup *
374 connection_get_group (void)
375 {
376   static GOptionGroup *g;
377
378   g = g_option_group_new ("connection",
379                           N_("Connection Endpoint Options:"),
380                           N_("Options specifying the connection endpoint"),
381                           NULL,
382                           NULL);
383   g_option_group_add_entries (g, connection_entries);
384   return g;
385 }
386
387 static GDBusConnection *
388 connection_get_dbus_connection (GError **error)
389 {
390   GDBusConnection *c;
391
392   c = NULL;
393
394   /* First, ensure we have exactly one connect */
395   if (!opt_connection_system && !opt_connection_session && opt_connection_address == NULL)
396     {
397       g_set_error (error,
398                    G_IO_ERROR,
399                    G_IO_ERROR_FAILED,
400                    _("No connection endpoint specified"));
401       goto out;
402     }
403   else if ((opt_connection_system && (opt_connection_session || opt_connection_address != NULL)) ||
404            (opt_connection_session && (opt_connection_system || opt_connection_address != NULL)) ||
405            (opt_connection_address != NULL && (opt_connection_system || opt_connection_session)))
406     {
407       g_set_error (error,
408                    G_IO_ERROR,
409                    G_IO_ERROR_FAILED,
410                    _("Multiple connection endpoints specified"));
411       goto out;
412     }
413
414   if (opt_connection_system)
415     {
416       c = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error);
417     }
418   else if (opt_connection_session)
419     {
420       c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error);
421     }
422   else if (opt_connection_address != NULL)
423     {
424       c = g_dbus_connection_new_for_address_sync (opt_connection_address,
425                                                   G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
426                                                   NULL, /* GCancellable */
427                                                   error);
428     }
429
430  out:
431   return c;
432 }
433
434 /* ---------------------------------------------------------------------------------------------------- */
435
436 static GPtrArray *
437 call_helper_get_method_in_signature (GDBusConnection  *c,
438                                      const gchar      *dest,
439                                      const gchar      *path,
440                                      const gchar      *interface_name,
441                                      const gchar      *method_name,
442                                      GError          **error)
443 {
444   GPtrArray *ret;
445   GVariant *result;
446   GDBusNodeInfo *node_info;
447   const gchar *xml_data;
448   const GDBusInterfaceInfo *interface_info;
449   const GDBusMethodInfo *method_info;
450   guint n;
451
452   ret = NULL;
453   result = NULL;
454   node_info = NULL;
455
456   result = g_dbus_connection_call_sync (c,
457                                         dest,
458                                         path,
459                                         "org.freedesktop.DBus.Introspectable",
460                                         "Introspect",
461                                         NULL,
462                                         G_DBUS_CALL_FLAGS_NONE,
463                                         3000, /* 3 secs */
464                                         NULL,
465                                         error);
466   if (result == NULL)
467     goto out;
468
469   if (!g_variant_is_of_type (result, G_VARIANT_TYPE ("(s)")))
470     {
471       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
472                    _("Error: Result is type `%s', expected `(s)'\n"),
473                    g_variant_get_type_string (result));
474       goto out;
475     }
476
477   g_variant_get (result, "(s)", &xml_data);
478   node_info = g_dbus_node_info_new_for_xml (xml_data, error);
479   if (node_info == NULL)
480       goto out;
481
482   interface_info = g_dbus_node_info_lookup_interface (node_info, interface_name);
483   if (interface_info == NULL)
484     {
485       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
486                    _("Warning: According to introspection data, interface `%s' does not exist\n"),
487                    interface_name);
488       goto out;
489     }
490
491   method_info = g_dbus_interface_info_lookup_method (interface_info, method_name);
492   if (method_info == NULL)
493     {
494       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
495                    _("Warning: According to introspection data, method `%s' does not exist on interface `%s'\n"),
496                    method_name,
497                    interface_name);
498       goto out;
499     }
500
501   ret = g_ptr_array_new_with_free_func ((GDestroyNotify) g_variant_type_free);
502   for (n = 0; method_info->in_args != NULL && method_info->in_args[n] != NULL; n++)
503     {
504       g_ptr_array_add (ret, g_variant_type_new (method_info->in_args[n]->signature));
505     }
506
507  out:
508   if (node_info != NULL)
509     g_dbus_node_info_unref (node_info);
510   if (result != NULL)
511     g_variant_unref (result);
512
513   return ret;
514 }
515
516 /* ---------------------------------------------------------------------------------------------------- */
517
518 static GVariant *
519 _g_variant_parse_me_harder (GVariantType   *type,
520                             const gchar    *given_str,
521                             GError        **error)
522 {
523   GVariant *value;
524   gchar *s;
525   guint n;
526   GString *str;
527
528   str = g_string_new ("\"");
529   for (n = 0; given_str[n] != '\0'; n++)
530     {
531       if (G_UNLIKELY (given_str[n] == '\"'))
532         g_string_append (str, "\\\"");
533       else
534         g_string_append_c (str, given_str[n]);
535     }
536   g_string_append_c (str, '"');
537   s = g_string_free (str, FALSE);
538
539   value = g_variant_parse (type,
540                            s,
541                            NULL,
542                            NULL,
543                            error);
544   g_free (s);
545
546   return value;
547 }
548
549 /* ---------------------------------------------------------------------------------------------------- */
550
551 static gchar *opt_call_dest = NULL;
552 static gchar *opt_call_object_path = NULL;
553 static gchar *opt_call_method = NULL;
554
555 static const GOptionEntry call_entries[] =
556 {
557   { "dest", 'd', 0, G_OPTION_ARG_STRING, &opt_call_dest, N_("Destination name to invoke method on"), NULL},
558   { "object-path", 'o', 0, G_OPTION_ARG_STRING, &opt_call_object_path, N_("Object path to invoke method on"), NULL},
559   { "method", 'm', 0, G_OPTION_ARG_STRING, &opt_call_method, N_("Method and interface name"), NULL},
560   { NULL }
561 };
562
563 static gboolean
564 handle_call (gint        *argc,
565              gchar      **argv[],
566              gboolean     request_completion,
567              const gchar *completion_cur,
568              const gchar *completion_prev)
569 {
570   gint ret;
571   GOptionContext *o;
572   gchar *s;
573   GError *error;
574   GDBusConnection *c;
575   GVariant *parameters;
576   gchar *interface_name;
577   gchar *method_name;
578   GVariant *result;
579   GPtrArray *in_signature_types;
580   gboolean complete_names;
581   gboolean complete_paths;
582   gboolean complete_methods;
583   GVariantBuilder builder;
584   guint n;
585
586   ret = FALSE;
587   c = NULL;
588   parameters = NULL;
589   interface_name = NULL;
590   method_name = NULL;
591   result = NULL;
592   in_signature_types = NULL;
593
594   modify_argv0_for_command (argc, argv, "call");
595
596   o = g_option_context_new (NULL);
597   g_option_context_set_help_enabled (o, FALSE);
598   g_option_context_set_summary (o, _("Invoke a method on a remote object."));
599   g_option_context_add_main_entries (o, call_entries, NULL /* GETTEXT_PACKAGE*/);
600   g_option_context_add_group (o, connection_get_group ());
601
602   complete_names = FALSE;
603   if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--dest") == 0)
604     {
605       complete_names = TRUE;
606       remove_arg ((*argc) - 1, argc, argv);
607     }
608
609   complete_paths = FALSE;
610   if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--object-path") == 0)
611     {
612       complete_paths = TRUE;
613       remove_arg ((*argc) - 1, argc, argv);
614     }
615
616   complete_methods = FALSE;
617   if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--method") == 0)
618     {
619       complete_methods = TRUE;
620       remove_arg ((*argc) - 1, argc, argv);
621     }
622
623   if (!g_option_context_parse (o, argc, argv, NULL))
624     {
625       if (!request_completion)
626         {
627           s = g_option_context_get_help (o, FALSE, NULL);
628           g_printerr ("%s", s);
629           g_free (s);
630           goto out;
631         }
632     }
633
634   error = NULL;
635   c = connection_get_dbus_connection (&error);
636   if (c == NULL)
637     {
638       if (request_completion)
639         {
640           if (g_strcmp0 (completion_prev, "--address") == 0)
641             {
642               g_print ("unix:\n"
643                        "tcp:\n"
644                        "nonce-tcp:\n");
645             }
646           else
647             {
648               g_print ("--system \n--session \n--address \n");
649             }
650         }
651       else
652         {
653           g_printerr (_("Error connecting: %s\n"), error->message);
654           g_error_free (error);
655         }
656       goto out;
657     }
658
659   /* validate and complete destination (bus name) */
660   if (g_dbus_connection_get_unique_name (c) != NULL)
661     {
662       /* this only makes sense on message bus connections */
663       if (complete_names)
664         {
665           print_names (c, FALSE);
666           goto out;
667         }
668       if (opt_call_dest == NULL)
669         {
670           if (request_completion)
671             g_print ("--dest \n");
672           else
673             g_printerr (_("Error: Destination is not specified\n"));
674           goto out;
675         }
676       if (request_completion && g_strcmp0 ("--dest", completion_prev) == 0)
677         {
678           print_names (c, g_str_has_prefix (opt_call_dest, ":"));
679           goto out;
680         }
681     }
682
683   /* validate and complete object path */
684   if (complete_paths)
685     {
686       print_paths (c, opt_call_dest, "/");
687       goto out;
688     }
689   if (opt_call_object_path == NULL)
690     {
691       if (request_completion)
692         g_print ("--object-path \n");
693       else
694         g_printerr (_("Error: Object path is not specified\n"));
695       goto out;
696     }
697   if (request_completion && g_strcmp0 ("--object-path", completion_prev) == 0)
698     {
699       gchar *p;
700       s = g_strdup (opt_call_object_path);
701       p = strrchr (s, '/');
702       if (p != NULL)
703         {
704           if (p == s)
705             p++;
706           *p = '\0';
707         }
708       print_paths (c, opt_call_dest, s);
709       g_free (s);
710       goto out;
711     }
712   if (!request_completion && !g_variant_is_object_path (opt_call_object_path))
713     {
714       g_printerr (_("Error: %s is not a valid object path\n"), opt_call_object_path);
715       goto out;
716     }
717
718   /* validate and complete method (interface + method name) */
719   if (complete_methods)
720     {
721       print_methods (c, opt_call_dest, opt_call_object_path);
722       goto out;
723     }
724   if (opt_call_method == NULL)
725     {
726       if (request_completion)
727         g_print ("--method \n");
728       else
729         g_printerr (_("Error: Method name is not specified\n"));
730       goto out;
731     }
732   if (request_completion && g_strcmp0 ("--method", completion_prev) == 0)
733     {
734       print_methods (c, opt_call_dest, opt_call_object_path);
735       goto out;
736     }
737   s = strrchr (opt_call_method, '.');
738   if (!request_completion && s == NULL)
739     {
740       g_printerr (_("Error: Method name `%s' is invalid\n"), opt_call_method);
741       goto out;
742     }
743   method_name = g_strdup (s + 1);
744   interface_name = g_strndup (opt_call_method, s - opt_call_method);
745
746   /* All done with completion now */
747   if (request_completion)
748     goto out;
749
750   /* Introspect, for easy conversion - it's not fatal if we can't do this */
751   in_signature_types = call_helper_get_method_in_signature (c,
752                                                             opt_call_dest,
753                                                             opt_call_object_path,
754                                                             interface_name,
755                                                             method_name,
756                                                             &error);
757   if (in_signature_types == NULL)
758     {
759       //g_printerr ("Error getting introspection data: %s\n", error->message);
760       g_error_free (error);
761       error = NULL;
762     }
763
764   /* Read parameters */
765   g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
766   for (n = 1; n < (guint) *argc; n++)
767     {
768       GVariant *value;
769       GVariantType *type;
770
771       type = NULL;
772       if (in_signature_types != NULL)
773         {
774           if (n - 1 >= in_signature_types->len)
775             {
776               /* Only warn for the first param */
777               if (n - 1 == in_signature_types->len)
778                 {
779                   g_printerr ("Warning: Introspection data indicates %d parameters but more was passed\n",
780                               in_signature_types->len);
781                 }
782             }
783           else
784             {
785               type = in_signature_types->pdata[n - 1];
786             }
787         }
788
789       error = NULL;
790       value = g_variant_parse (type,
791                                (*argv)[n],
792                                NULL,
793                                NULL,
794                                &error);
795       if (value == NULL)
796         {
797           g_error_free (error);
798           error = NULL;
799           value = _g_variant_parse_me_harder (type, (*argv)[n], &error);
800           if (value == NULL)
801             {
802               if (type != NULL)
803                 {
804                   s = g_variant_type_dup_string (type);
805                   g_printerr (_("Error parsing parameter %d of type `%s': %s\n"),
806                               n,
807                               s,
808                               error->message);
809                   g_free (s);
810                 }
811               else
812                 {
813                   g_printerr (_("Error parsing parameter %d: %s\n"),
814                               n,
815                               error->message);
816                 }
817               g_error_free (error);
818               g_variant_builder_clear (&builder);
819               goto out;
820             }
821         }
822       g_variant_builder_add_value (&builder, value);
823     }
824   parameters = g_variant_builder_end (&builder);
825
826   if (parameters != NULL)
827     parameters = g_variant_ref_sink (parameters);
828   result = g_dbus_connection_call_sync (c,
829                                         opt_call_dest,
830                                         opt_call_object_path,
831                                         interface_name,
832                                         method_name,
833                                         parameters,
834                                         G_DBUS_CALL_FLAGS_NONE,
835                                         -1,
836                                         NULL,
837                                         &error);
838   if (result == NULL)
839     {
840       g_printerr (_("Error: %s\n"), error->message);
841       g_error_free (error);
842       if (in_signature_types != NULL)
843         {
844           GString *s;
845           s = g_string_new (NULL);
846           for (n = 0; n < in_signature_types->len; n++)
847             {
848               GVariantType *type = in_signature_types->pdata[n];
849               g_string_append_len (s,
850                                    g_variant_type_peek_string (type),
851                                    g_variant_type_get_string_length (type));
852             }
853           g_printerr ("(According to introspection data, you need to pass `%s')\n", s->str);
854           g_string_free (s, TRUE);
855         }
856       goto out;
857     }
858
859   s = g_variant_print (result, TRUE);
860   g_print ("%s\n", s);
861   g_free (s);
862
863   ret = TRUE;
864
865  out:
866   if (in_signature_types != NULL)
867     g_ptr_array_unref (in_signature_types);
868   if (result != NULL)
869     g_variant_unref (result);
870   if (c != NULL)
871     g_object_unref (c);
872   if (parameters != NULL)
873     g_variant_unref (parameters);
874   g_free (interface_name);
875   g_free (method_name);
876   g_option_context_free (o);
877   return ret;
878 }
879
880 /* ---------------------------------------------------------------------------------------------------- */
881
882 /* TODO: dump annotations */
883
884 static void
885 dump_annotation (const GDBusAnnotationInfo *o,
886                  guint indent,
887                  gboolean ignore_indent)
888 {
889   guint n;
890   g_print ("%*s@%s(\"%s\")\n",
891            ignore_indent ? 0 : indent, "",
892            o->key,
893            o->value);
894   for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
895     dump_annotation (o->annotations[n], indent + 2, FALSE);
896 }
897
898 static void
899 dump_arg (const GDBusArgInfo *o,
900           guint indent,
901           const gchar *direction,
902           gboolean ignore_indent,
903           gboolean include_newline)
904 {
905   guint n;
906
907   for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
908     {
909       dump_annotation (o->annotations[n], indent, ignore_indent);
910       ignore_indent = FALSE;
911     }
912
913   g_print ("%*s%s%s %s%s",
914            ignore_indent ? 0 : indent, "",
915            direction,
916            o->signature,
917            o->name,
918            include_newline ? ",\n" : "");
919 }
920
921 static guint
922 count_args (GDBusArgInfo **args)
923 {
924   guint n;
925   n = 0;
926   if (args == NULL)
927     goto out;
928   while (args[n] != NULL)
929     n++;
930  out:
931   return n;
932 }
933
934 static void
935 dump_method (const GDBusMethodInfo *o,
936              guint                  indent)
937 {
938   guint n;
939   guint m;
940   guint name_len;
941   guint total_num_args;
942
943   for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
944     dump_annotation (o->annotations[n], indent, FALSE);
945
946   g_print ("%*s%s(", indent, "", o->name);
947   name_len = strlen (o->name);
948   total_num_args = count_args (o->in_args) + count_args (o->out_args);
949   for (n = 0, m = 0; o->in_args != NULL && o->in_args[n] != NULL; n++, m++)
950     {
951       gboolean ignore_indent = (m == 0);
952       gboolean include_newline = (m != total_num_args - 1);
953
954       dump_arg (o->in_args[n],
955                 indent + name_len + 1,
956                 "in  ",
957                 ignore_indent,
958                 include_newline);
959     }
960   for (n = 0; o->out_args != NULL && o->out_args[n] != NULL; n++, m++)
961     {
962       gboolean ignore_indent = (m == 0);
963       gboolean include_newline = (m != total_num_args - 1);
964       dump_arg (o->out_args[n],
965                 indent + name_len + 1,
966                 "out ",
967                 ignore_indent,
968                 include_newline);
969     }
970   g_print (");\n");
971 }
972
973 static void
974 dump_signal (const GDBusSignalInfo *o,
975              guint                  indent)
976 {
977   guint n;
978   guint name_len;
979   guint total_num_args;
980
981   for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
982     dump_annotation (o->annotations[n], indent, FALSE);
983
984   g_print ("%*s%s(", indent, "", o->name);
985   name_len = strlen (o->name);
986   total_num_args = count_args (o->args);
987   for (n = 0; o->args != NULL && o->args[n] != NULL; n++)
988     {
989       gboolean ignore_indent = (n == 0);
990       gboolean include_newline = (n != total_num_args - 1);
991       dump_arg (o->args[n],
992                 indent + name_len + 1,
993                 "",
994                 ignore_indent,
995                 include_newline);
996     }
997   g_print (");\n");
998 }
999
1000 static void
1001 dump_property (const GDBusPropertyInfo *o,
1002                guint                    indent,
1003                GVariant                *value)
1004 {
1005   const gchar *access;
1006   guint n;
1007
1008   if (o->flags == G_DBUS_PROPERTY_INFO_FLAGS_READABLE)
1009     access = "readonly";
1010   else if (o->flags == G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE)
1011     access = "writeonly";
1012   else if (o->flags == (G_DBUS_PROPERTY_INFO_FLAGS_READABLE | G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE))
1013     access = "readwrite";
1014   else
1015     g_assert_not_reached ();
1016
1017   for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
1018     dump_annotation (o->annotations[n], indent, FALSE);
1019
1020   if (value != NULL)
1021     {
1022       gchar *s = g_variant_print (value, FALSE);
1023       g_print ("%*s%s %s %s = %s;\n", indent, "", access, o->signature, o->name, s);
1024       g_free (s);
1025     }
1026   else
1027     {
1028       g_print ("%*s%s %s %s;\n", indent, "", access, o->signature, o->name);
1029     }
1030 }
1031
1032 static void
1033 dump_interface (GDBusConnection          *c,
1034                 const gchar              *name,
1035                 const GDBusInterfaceInfo *o,
1036                 guint                     indent,
1037                 const gchar              *object_path)
1038 {
1039   guint n;
1040   GHashTable *properties;
1041
1042   properties = g_hash_table_new_full (g_str_hash,
1043                                       g_str_equal,
1044                                       g_free,
1045                                       (GDestroyNotify) g_variant_unref);
1046
1047   /* Try to get properties */
1048   if (c != NULL && name != NULL && object_path != NULL)
1049     {
1050       GVariant *result;
1051       result = g_dbus_connection_call_sync (c,
1052                                             name,
1053                                             object_path,
1054                                             "org.freedesktop.DBus.Properties",
1055                                             "GetAll",
1056                                             g_variant_new ("(s)", o->name),
1057                                             G_DBUS_CALL_FLAGS_NONE,
1058                                             3000,
1059                                             NULL,
1060                                             NULL);
1061       if (result != NULL)
1062         {
1063           if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(a{sv})")))
1064             {
1065               GVariantIter *iter;
1066               GVariant *item;
1067               g_variant_get (result,
1068                              "(a{sv})",
1069                              &iter);
1070               while ((item = g_variant_iter_next_value (iter)))
1071                 {
1072                   const gchar *key;
1073                   GVariant *value;
1074                   g_variant_get (item,
1075                                  "{sv}",
1076                                  &key,
1077                                  &value);
1078
1079                   g_hash_table_insert (properties, g_strdup (key), g_variant_ref (value));
1080                 }
1081             }
1082           g_variant_unref (result);
1083         }
1084       else
1085         {
1086           guint n;
1087           for (n = 0; o->properties != NULL && o->properties[n] != NULL; n++)
1088             {
1089               result = g_dbus_connection_call_sync (c,
1090                                                     name,
1091                                                     object_path,
1092                                                     "org.freedesktop.DBus.Properties",
1093                                                     "Get",
1094                                                     g_variant_new ("(ss)", o->name, o->properties[n]->name),
1095                                                     G_DBUS_CALL_FLAGS_NONE,
1096                                                     3000,
1097                                                     NULL,
1098                                                     NULL);
1099               if (result != NULL)
1100                 {
1101                   GVariant *property_value;
1102                   g_variant_get (result,
1103                                  "(v)",
1104                                  &property_value);
1105                   g_hash_table_insert (properties,
1106                                        g_strdup (o->properties[n]->name),
1107                                        g_variant_ref (property_value));
1108                   g_variant_unref (result);
1109                 }
1110             }
1111         }
1112     }
1113
1114   for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
1115     dump_annotation (o->annotations[n], indent, FALSE);
1116
1117   g_print ("%*sinterface %s {\n", indent, "", o->name);
1118   if (o->methods != NULL)
1119     {
1120       g_print ("%*s  methods:\n", indent, "");
1121       for (n = 0; o->methods[n] != NULL; n++)
1122         dump_method (o->methods[n], indent + 4);
1123     }
1124   if (o->signals != NULL)
1125     {
1126       g_print ("%*s  signals:\n", indent, "");
1127       for (n = 0; o->signals[n] != NULL; n++)
1128         dump_signal (o->signals[n], indent + 4);
1129     }
1130   if (o->properties != NULL)
1131     {
1132       g_print ("%*s  properties:\n", indent, "");
1133       for (n = 0; o->properties[n] != NULL; n++)
1134         {
1135           dump_property (o->properties[n],
1136                          indent + 4,
1137                          g_hash_table_lookup (properties, (o->properties[n])->name));
1138         }
1139     }
1140   g_print ("%*s};\n",
1141            indent, "");
1142
1143   g_hash_table_unref (properties);
1144 }
1145
1146 static void
1147 dump_node (GDBusConnection      *c,
1148            const gchar          *name,
1149            const GDBusNodeInfo  *o,
1150            guint                 indent,
1151            const gchar          *object_path)
1152 {
1153   guint n;
1154   const gchar *object_path_to_print;
1155
1156   object_path_to_print = object_path;
1157   if (o->path != NULL)
1158     object_path_to_print = o->path;
1159
1160   for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
1161     dump_annotation (o->annotations[n], indent, FALSE);
1162
1163   g_print ("%*snode %s", indent, "", object_path_to_print != NULL ? object_path_to_print : "(not set)");
1164   if (o->interfaces != NULL || o->nodes != NULL)
1165     {
1166       g_print (" {\n");
1167       for (n = 0; o->interfaces != NULL && o->interfaces[n] != NULL; n++)
1168         dump_interface (c, name, o->interfaces[n], indent + 2, object_path);
1169       for (n = 0; o->nodes != NULL && o->nodes[n] != NULL; n++)
1170         dump_node (NULL, NULL, o->nodes[n], indent + 2, NULL);
1171       g_print ("%*s};\n",
1172                indent, "");
1173     }
1174   else
1175     {
1176       g_print ("\n");
1177     }
1178 }
1179
1180 static gchar *opt_introspect_dest = NULL;
1181 static gchar *opt_introspect_object_path = NULL;
1182
1183 static const GOptionEntry introspect_entries[] =
1184 {
1185   { "dest", 'd', 0, G_OPTION_ARG_STRING, &opt_introspect_dest, N_("Destination name to introspect"), NULL},
1186   { "object-path", 'o', 0, G_OPTION_ARG_STRING, &opt_introspect_object_path, N_("Object path to introspect"), NULL},
1187   { NULL }
1188 };
1189
1190 static gboolean
1191 handle_introspect (gint        *argc,
1192                    gchar      **argv[],
1193                    gboolean     request_completion,
1194                    const gchar *completion_cur,
1195                    const gchar *completion_prev)
1196 {
1197   gint ret;
1198   GOptionContext *o;
1199   gchar *s;
1200   GError *error;
1201   GDBusConnection *c;
1202   GVariant *result;
1203   const gchar *xml_data;
1204   GDBusNodeInfo *node;
1205   gboolean complete_names;
1206   gboolean complete_paths;
1207
1208   ret = FALSE;
1209   c = NULL;
1210   node = NULL;
1211   result = NULL;
1212
1213   modify_argv0_for_command (argc, argv, "introspect");
1214
1215   o = g_option_context_new (NULL);
1216   if (request_completion)
1217     g_option_context_set_ignore_unknown_options (o, TRUE);
1218   g_option_context_set_help_enabled (o, FALSE);
1219   g_option_context_set_summary (o, _("Introspect a remote object."));
1220   g_option_context_add_main_entries (o, introspect_entries, NULL /* GETTEXT_PACKAGE*/);
1221   g_option_context_add_group (o, connection_get_group ());
1222
1223   complete_names = FALSE;
1224   if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--dest") == 0)
1225     {
1226       complete_names = TRUE;
1227       remove_arg ((*argc) - 1, argc, argv);
1228     }
1229
1230   complete_paths = FALSE;
1231   if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--object-path") == 0)
1232     {
1233       complete_paths = TRUE;
1234       remove_arg ((*argc) - 1, argc, argv);
1235     }
1236
1237   if (!g_option_context_parse (o, argc, argv, NULL))
1238     {
1239       if (!request_completion)
1240         {
1241           s = g_option_context_get_help (o, FALSE, NULL);
1242           g_printerr ("%s", s);
1243           g_free (s);
1244           goto out;
1245         }
1246     }
1247
1248   error = NULL;
1249   c = connection_get_dbus_connection (&error);
1250   if (c == NULL)
1251     {
1252       if (request_completion)
1253         {
1254           if (g_strcmp0 (completion_prev, "--address") == 0)
1255             {
1256               g_print ("unix:\n"
1257                        "tcp:\n"
1258                        "nonce-tcp:\n");
1259             }
1260           else
1261             {
1262               g_print ("--system \n--session \n--address \n");
1263             }
1264         }
1265       else
1266         {
1267           g_printerr (_("Error connecting: %s\n"), error->message);
1268           g_error_free (error);
1269         }
1270       goto out;
1271     }
1272
1273   if (g_dbus_connection_get_unique_name (c) != NULL)
1274     {
1275       if (complete_names)
1276         {
1277           print_names (c, FALSE);
1278           goto out;
1279         }
1280       /* this only makes sense on message bus connections */
1281       if (opt_introspect_dest == NULL)
1282         {
1283           if (request_completion)
1284             g_print ("--dest \n");
1285           else
1286             g_printerr (_("Error: Destination is not specified\n"));
1287           goto out;
1288         }
1289       if (request_completion && g_strcmp0 ("--dest", completion_prev) == 0)
1290         {
1291           print_names (c, g_str_has_prefix (opt_introspect_dest, ":"));
1292           goto out;
1293         }
1294     }
1295   if (complete_paths)
1296     {
1297       print_paths (c, opt_introspect_dest, "/");
1298       goto out;
1299     }
1300   if (opt_introspect_object_path == NULL)
1301     {
1302       if (request_completion)
1303         g_print ("--object-path \n");
1304       else
1305         g_printerr (_("Error: Object path is not specified\n"));
1306       goto out;
1307     }
1308   if (request_completion && g_strcmp0 ("--object-path", completion_prev) == 0)
1309     {
1310       gchar *p;
1311       s = g_strdup (opt_introspect_object_path);
1312       p = strrchr (s, '/');
1313       if (p != NULL)
1314         {
1315           if (p == s)
1316             p++;
1317           *p = '\0';
1318         }
1319       print_paths (c, opt_introspect_dest, s);
1320       g_free (s);
1321       goto out;
1322     }
1323   if (!request_completion && !g_variant_is_object_path (opt_introspect_object_path))
1324     {
1325       g_printerr (_("Error: %s is not a valid object path\n"), opt_introspect_object_path);
1326       goto out;
1327     }
1328
1329   /* All done with completion now */
1330   if (request_completion)
1331     goto out;
1332
1333   result = g_dbus_connection_call_sync (c,
1334                                         opt_introspect_dest,
1335                                         opt_introspect_object_path,
1336                                         "org.freedesktop.DBus.Introspectable",
1337                                         "Introspect",
1338                                         NULL,
1339                                         G_DBUS_CALL_FLAGS_NONE,
1340                                         3000, /* 3 sec */
1341                                         NULL,
1342                                         &error);
1343   if (result == NULL)
1344     {
1345       g_printerr (_("Error: %s\n"), error->message);
1346       g_error_free (error);
1347       goto out;
1348     }
1349   if (!g_variant_is_of_type (result, G_VARIANT_TYPE ("(s)")))
1350     {
1351       g_printerr (_("Error: Result is type `%s', expected `(s)'\n"),
1352                   g_variant_get_type_string (result));
1353       goto out;
1354     }
1355   g_variant_get (result, "(s)", &xml_data);
1356
1357   error = NULL;
1358   node = g_dbus_node_info_new_for_xml (xml_data, &error);
1359   if (node == NULL)
1360     {
1361       g_printerr (_("Error parsing introspection XML: %s\n"), error->message);
1362       g_error_free (error);
1363       goto out;
1364     }
1365
1366   dump_node (c, opt_introspect_dest, node, 0, opt_introspect_object_path);
1367
1368   ret = TRUE;
1369
1370  out:
1371   if (node != NULL)
1372     g_dbus_node_info_unref (node);
1373   if (result != NULL)
1374     g_variant_unref (result);
1375   if (c != NULL)
1376     g_object_unref (c);
1377   g_option_context_free (o);
1378   return ret;
1379 }
1380
1381 /* ---------------------------------------------------------------------------------------------------- */
1382
1383 static gchar *
1384 pick_word_at (const gchar  *s,
1385               gint          cursor,
1386               gint         *out_word_begins_at)
1387 {
1388   gint begin;
1389   gint end;
1390
1391   if (s[0] == '\0')
1392     {
1393       if (out_word_begins_at != NULL)
1394         *out_word_begins_at = -1;
1395       return NULL;
1396     }
1397
1398   if (g_ascii_isspace (s[cursor]) && ((cursor > 0 && g_ascii_isspace(s[cursor-1])) || cursor == 0))
1399     {
1400       if (out_word_begins_at != NULL)
1401         *out_word_begins_at = cursor;
1402       return g_strdup ("");
1403     }
1404
1405   while (!g_ascii_isspace (s[cursor - 1]) && cursor > 0)
1406     cursor--;
1407   begin = cursor;
1408
1409   end = begin;
1410   while (!g_ascii_isspace (s[end]) && s[end] != '\0')
1411     end++;
1412
1413   if (out_word_begins_at != NULL)
1414     *out_word_begins_at = begin;
1415
1416   return g_strndup (s + begin, end - begin);
1417 }
1418
1419 gint
1420 main (gint argc, gchar *argv[])
1421 {
1422   gint ret;
1423   const gchar *command;
1424   gboolean request_completion;
1425   gchar *completion_cur;
1426   gchar *completion_prev;
1427
1428   ret = 1;
1429   completion_cur = NULL;
1430   completion_prev = NULL;
1431
1432   g_type_init ();
1433
1434   if (argc < 2)
1435     {
1436       usage (&argc, &argv, FALSE);
1437       goto out;
1438     }
1439
1440   request_completion = FALSE;
1441
1442   //completion_debug ("---- argc=%d --------------------------------------------------------", argc);
1443
1444  again:
1445   command = argv[1];
1446   if (g_strcmp0 (command, "help") == 0)
1447     {
1448       if (request_completion)
1449         {
1450           /* do nothing */
1451         }
1452       else
1453         {
1454           usage (&argc, &argv, TRUE);
1455           ret = 0;
1456         }
1457       goto out;
1458     }
1459   else if (g_strcmp0 (command, "call") == 0)
1460     {
1461       if (handle_call (&argc,
1462                        &argv,
1463                        request_completion,
1464                        completion_cur,
1465                        completion_prev))
1466         ret = 0;
1467       goto out;
1468     }
1469   else if (g_strcmp0 (command, "introspect") == 0)
1470     {
1471       if (handle_introspect (&argc,
1472                              &argv,
1473                              request_completion,
1474                              completion_cur,
1475                              completion_prev))
1476         ret = 0;
1477       goto out;
1478     }
1479   else if (g_strcmp0 (command, "complete") == 0 && argc == 4 && !request_completion)
1480     {
1481       const gchar *completion_line;
1482       gchar **completion_argv;
1483       gint completion_argc;
1484       gint completion_point;
1485       gchar *endp;
1486       gint cur_begin;
1487
1488       request_completion = TRUE;
1489
1490       completion_line = argv[2];
1491       completion_point = strtol (argv[3], &endp, 10);
1492       if (endp == argv[3] || *endp != '\0')
1493         goto out;
1494
1495 #if 0
1496       completion_debug ("completion_point=%d", completion_point);
1497       completion_debug ("----");
1498       completion_debug (" 0123456789012345678901234567890123456789012345678901234567890123456789");
1499       completion_debug ("`%s'", completion_line);
1500       completion_debug (" %*s^",
1501                          completion_point, "");
1502       completion_debug ("----");
1503 #endif
1504
1505       if (!g_shell_parse_argv (completion_line,
1506                                &completion_argc,
1507                                &completion_argv,
1508                                NULL))
1509         {
1510           /* it's very possible the command line can't be parsed (for
1511            * example, missing quotes etc) - in that case, we just
1512            * don't autocomplete at all
1513            */
1514           goto out;
1515         }
1516
1517       /* compute cur and prev */
1518       completion_prev = NULL;
1519       completion_cur = pick_word_at (completion_line, completion_point, &cur_begin);
1520       if (cur_begin > 0)
1521         {
1522           gint prev_end;
1523           for (prev_end = cur_begin - 1; prev_end >= 0; prev_end--)
1524             {
1525               if (!g_ascii_isspace (completion_line[prev_end]))
1526                 {
1527                   completion_prev = pick_word_at (completion_line, prev_end, NULL);
1528                   break;
1529                 }
1530             }
1531         }
1532 #if 0
1533       completion_debug (" cur=`%s'", completion_cur);
1534       completion_debug ("prev=`%s'", completion_prev);
1535 #endif
1536
1537       argc = completion_argc;
1538       argv = completion_argv;
1539
1540       ret = 0;
1541
1542       goto again;
1543     }
1544   else
1545     {
1546       if (request_completion)
1547         {
1548           g_print ("help \ncall \nintrospect \n");
1549           ret = 0;
1550           goto out;
1551         }
1552       else
1553         {
1554           g_printerr ("Unknown command `%s'\n", command);
1555           usage (&argc, &argv, FALSE);
1556           goto out;
1557         }
1558     }
1559
1560  out:
1561   g_free (completion_cur);
1562   g_free (completion_prev);
1563   return ret;
1564 }