GDBus: Fix up i18n
[platform/upstream/glib.git] / gio / gdbus-tool.c
1 /* GDBus - GLib D-Bus Library
2  *
3  * Copyright (C) 2008-2009 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_invoke_method_sync (c,
147                                                  name,
148                                                  path,
149                                                  "org.freedesktop.DBus.Introspectable",
150                                                  "Introspect",
151                                                  NULL,
152                                                  G_DBUS_INVOKE_METHOD_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_invoke_method_sync (c,
209                                                  name,
210                                                  path,
211                                                  "org.freedesktop.DBus.Introspectable",
212                                                  "Introspect",
213                                                  NULL,
214                                                  G_DBUS_INVOKE_METHOD_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_invoke_method_sync (c,
287                                                  "org.freedesktop.DBus",
288                                                  "/org/freedesktop/DBus",
289                                                  "org.freedesktop.DBus",
290                                                  "ListNames",
291                                                  NULL,
292                                                  G_DBUS_INVOKE_METHOD_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_invoke_method_sync (c,
316                                                  "org.freedesktop.DBus",
317                                                  "/org/freedesktop/DBus",
318                                                  "org.freedesktop.DBus",
319                                                  "ListActivatableNames",
320                                                  NULL,
321                                                  G_DBUS_INVOKE_METHOD_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_invoke_method_sync (c,
457                                                  dest,
458                                                  path,
459                                                  "org.freedesktop.DBus.Introspectable",
460                                                  "Introspect",
461                                                  NULL,
462                                                  G_DBUS_INVOKE_METHOD_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_invoke_method_sync (c,
829                                                  opt_call_dest,
830                                                  opt_call_object_path,
831                                                  interface_name,
832                                                  method_name,
833                                                  parameters,
834                                                  G_DBUS_INVOKE_METHOD_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_arg (const GDBusArgInfo *o,
886           guint indent,
887           const gchar *direction,
888           gboolean ignore_indent,
889           gboolean include_newline)
890 {
891   g_print ("%*s%s%s %s%s",
892            ignore_indent ? 0 : indent, "",
893            direction,
894            o->signature,
895            o->name,
896            include_newline ? ",\n" : "");
897 }
898
899 static guint
900 count_args (GDBusArgInfo **args)
901 {
902   guint n;
903   n = 0;
904   if (args == NULL)
905     goto out;
906   while (args[n] != NULL)
907     n++;
908  out:
909   return n;
910 }
911
912 static void
913 dump_method (const GDBusMethodInfo *o,
914              guint                  indent)
915 {
916   guint n;
917   guint m;
918   guint name_len;
919   guint total_num_args;
920   g_print ("%*s%s(", indent, "", o->name);
921   name_len = strlen (o->name);
922   total_num_args = count_args (o->in_args) + count_args (o->out_args);
923   for (n = 0, m = 0; o->in_args != NULL && o->in_args[n] != NULL; n++, m++)
924     {
925       gboolean ignore_indent = (m == 0);
926       gboolean include_newline = (m != total_num_args - 1);
927       dump_arg (o->in_args[n],
928                 indent + name_len + 1,
929                 "in  ",
930                 ignore_indent,
931                 include_newline);
932     }
933   for (n = 0; o->out_args != NULL && o->out_args[n] != NULL; n++, m++)
934     {
935       gboolean ignore_indent = (m == 0);
936       gboolean include_newline = (m != total_num_args - 1);
937       dump_arg (o->out_args[n],
938                 indent + name_len + 1,
939                 "out ",
940                 ignore_indent,
941                 include_newline);
942     }
943   g_print (");\n");
944 }
945
946 static void
947 dump_signal (const GDBusSignalInfo *o,
948              guint                  indent)
949 {
950   guint n;
951   guint name_len;
952   guint total_num_args;
953   g_print ("%*s%s(", indent, "", o->name);
954   name_len = strlen (o->name);
955   total_num_args = count_args (o->args);
956   for (n = 0; o->args != NULL && o->args[n] != NULL; n++)
957     {
958       gboolean ignore_indent = (n == 0);
959       gboolean include_newline = (n != total_num_args - 1);
960       dump_arg (o->args[n],
961                 indent + name_len + 1,
962                 "",
963                 ignore_indent,
964                 include_newline);
965     }
966   g_print (");\n");
967 }
968
969 static void
970 dump_property (const GDBusPropertyInfo *o,
971                guint                    indent,
972                GVariant                *value)
973 {
974   const gchar *access;
975   if (o->flags == G_DBUS_PROPERTY_INFO_FLAGS_READABLE)
976     access = "readonly";
977   else if (o->flags == G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE)
978     access = "writeonly";
979   else if (o->flags == (G_DBUS_PROPERTY_INFO_FLAGS_READABLE | G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE))
980     access = "readwrite";
981   else
982     g_assert_not_reached ();
983   if (value != NULL)
984     {
985       gchar *s = g_variant_print (value, FALSE);
986       g_print ("%*s%s %s %s = %s;\n", indent, "", access, o->signature, o->name, s);
987       g_free (s);
988     }
989   else
990     {
991       g_print ("%*s%s %s %s;\n", indent, "", access, o->signature, o->name);
992     }
993 }
994
995 static void
996 dump_interface (GDBusConnection          *c,
997                 const gchar              *name,
998                 const GDBusInterfaceInfo *o,
999                 guint                     indent,
1000                 const gchar              *object_path)
1001 {
1002   guint n;
1003   GHashTable *properties;
1004
1005   properties = g_hash_table_new_full (g_str_hash,
1006                                       g_str_equal,
1007                                       g_free,
1008                                       (GDestroyNotify) g_variant_unref);
1009
1010   /* Try to get properties */
1011   if (c != NULL && name != NULL && object_path != NULL)
1012     {
1013       GVariant *result;
1014       result = g_dbus_connection_invoke_method_sync (c,
1015                                                      name,
1016                                                      object_path,
1017                                                      "org.freedesktop.DBus.Properties",
1018                                                      "GetAll",
1019                                                      g_variant_new ("(s)", o->name),
1020                                                      G_DBUS_INVOKE_METHOD_FLAGS_NONE,
1021                                                      3000,
1022                                                      NULL,
1023                                                      NULL);
1024       if (result != NULL)
1025         {
1026           if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(a{sv})")))
1027             {
1028               GVariantIter *iter;
1029               GVariant *item;
1030               g_variant_get (result,
1031                              "(a{sv})",
1032                              &iter);
1033               while ((item = g_variant_iter_next_value (iter)))
1034                 {
1035                   const gchar *key;
1036                   GVariant *value;
1037                   g_variant_get (item,
1038                                  "{sv}",
1039                                  &key,
1040                                  &value);
1041
1042                   g_hash_table_insert (properties, g_strdup (key), g_variant_ref (value));
1043                 }
1044             }
1045           g_variant_unref (result);
1046         }
1047     }
1048
1049   g_print ("%*sinterface %s {\n", indent, "", o->name);
1050   if (o->methods != NULL)
1051     {
1052       g_print ("%*s  methods:\n", indent, "");
1053       for (n = 0; o->methods[n] != NULL; n++)
1054         dump_method (o->methods[n], indent + 4);
1055     }
1056   if (o->signals != NULL)
1057     {
1058       g_print ("%*s  signals:\n", indent, "");
1059       for (n = 0; o->signals[n] != NULL; n++)
1060         dump_signal (o->signals[n], indent + 4);
1061     }
1062   if (o->properties != NULL)
1063     {
1064       g_print ("%*s  properties:\n", indent, "");
1065       for (n = 0; o->properties[n] != NULL; n++)
1066         {
1067           dump_property (o->properties[n],
1068                          indent + 4,
1069                          g_hash_table_lookup (properties, (o->properties[n])->name));
1070         }
1071     }
1072   g_print ("%*s};\n",
1073            indent, "");
1074
1075   g_hash_table_unref (properties);
1076 }
1077
1078 static void
1079 dump_node (GDBusConnection      *c,
1080            const gchar          *name,
1081            const GDBusNodeInfo  *o,
1082            guint                 indent,
1083            const gchar          *object_path)
1084 {
1085   guint n;
1086   const gchar *object_path_to_print;
1087
1088   object_path_to_print = object_path;
1089   if (o->path != NULL)
1090     object_path_to_print = o->path;
1091
1092   g_print ("%*snode %s", indent, "", object_path_to_print != NULL ? object_path_to_print : "(not set)");
1093   if (o->interfaces != NULL || o->nodes != NULL)
1094     {
1095       g_print (" {\n");
1096       for (n = 0; o->interfaces != NULL && o->interfaces[n] != NULL; n++)
1097         dump_interface (c, name, o->interfaces[n], indent + 2, object_path);
1098       for (n = 0; o->nodes != NULL && o->nodes[n] != NULL; n++)
1099         dump_node (NULL, NULL, o->nodes[n], indent + 2, NULL);
1100       g_print ("%*s};\n",
1101                indent, "");
1102     }
1103   else
1104     {
1105       g_print ("\n");
1106     }
1107 }
1108
1109 static gchar *opt_introspect_dest = NULL;
1110 static gchar *opt_introspect_object_path = NULL;
1111
1112 static const GOptionEntry introspect_entries[] =
1113 {
1114   { "dest", 'd', 0, G_OPTION_ARG_STRING, &opt_introspect_dest, N_("Destination name to introspect"), NULL},
1115   { "object-path", 'o', 0, G_OPTION_ARG_STRING, &opt_introspect_object_path, N_("Object path to introspect"), NULL},
1116   { NULL }
1117 };
1118
1119 static gboolean
1120 handle_introspect (gint        *argc,
1121                    gchar      **argv[],
1122                    gboolean     request_completion,
1123                    const gchar *completion_cur,
1124                    const gchar *completion_prev)
1125 {
1126   gint ret;
1127   GOptionContext *o;
1128   gchar *s;
1129   GError *error;
1130   GDBusConnection *c;
1131   GVariant *result;
1132   const gchar *xml_data;
1133   GDBusNodeInfo *node;
1134   gboolean complete_names;
1135   gboolean complete_paths;
1136
1137   ret = FALSE;
1138   c = NULL;
1139   node = NULL;
1140   result = NULL;
1141
1142   modify_argv0_for_command (argc, argv, "introspect");
1143
1144   o = g_option_context_new (NULL);
1145   if (request_completion)
1146     g_option_context_set_ignore_unknown_options (o, TRUE);
1147   g_option_context_set_help_enabled (o, FALSE);
1148   g_option_context_set_summary (o, _("Introspect a remote object."));
1149   g_option_context_add_main_entries (o, introspect_entries, NULL /* GETTEXT_PACKAGE*/);
1150   g_option_context_add_group (o, connection_get_group ());
1151
1152   complete_names = FALSE;
1153   if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--dest") == 0)
1154     {
1155       complete_names = TRUE;
1156       remove_arg ((*argc) - 1, argc, argv);
1157     }
1158
1159   complete_paths = FALSE;
1160   if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--object-path") == 0)
1161     {
1162       complete_paths = TRUE;
1163       remove_arg ((*argc) - 1, argc, argv);
1164     }
1165
1166   if (!g_option_context_parse (o, argc, argv, NULL))
1167     {
1168       if (!request_completion)
1169         {
1170           s = g_option_context_get_help (o, FALSE, NULL);
1171           g_printerr ("%s", s);
1172           g_free (s);
1173           goto out;
1174         }
1175     }
1176
1177   error = NULL;
1178   c = connection_get_dbus_connection (&error);
1179   if (c == NULL)
1180     {
1181       if (request_completion)
1182         {
1183           if (g_strcmp0 (completion_prev, "--address") == 0)
1184             {
1185               g_print ("unix:\n"
1186                        "tcp:\n"
1187                        "nonce-tcp:\n");
1188             }
1189           else
1190             {
1191               g_print ("--system \n--session \n--address \n");
1192             }
1193         }
1194       else
1195         {
1196           g_printerr (_("Error connecting: %s\n"), error->message);
1197           g_error_free (error);
1198         }
1199       goto out;
1200     }
1201
1202   if (g_dbus_connection_get_unique_name (c) != NULL)
1203     {
1204       if (complete_names)
1205         {
1206           print_names (c, FALSE);
1207           goto out;
1208         }
1209       /* this only makes sense on message bus connections */
1210       if (opt_introspect_dest == NULL)
1211         {
1212           if (request_completion)
1213             g_print ("--dest \n");
1214           else
1215             g_printerr (_("Error: Destination is not specified\n"));
1216           goto out;
1217         }
1218       if (request_completion && g_strcmp0 ("--dest", completion_prev) == 0)
1219         {
1220           print_names (c, g_str_has_prefix (opt_introspect_dest, ":"));
1221           goto out;
1222         }
1223     }
1224   if (complete_paths)
1225     {
1226       print_paths (c, opt_introspect_dest, "/");
1227       goto out;
1228     }
1229   if (opt_introspect_object_path == NULL)
1230     {
1231       if (request_completion)
1232         g_print ("--object-path \n");
1233       else
1234         g_printerr (_("Error: Object path is not specified\n"));
1235       goto out;
1236     }
1237   if (request_completion && g_strcmp0 ("--object-path", completion_prev) == 0)
1238     {
1239       gchar *p;
1240       s = g_strdup (opt_introspect_object_path);
1241       p = strrchr (s, '/');
1242       if (p != NULL)
1243         {
1244           if (p == s)
1245             p++;
1246           *p = '\0';
1247         }
1248       print_paths (c, opt_introspect_dest, s);
1249       g_free (s);
1250       goto out;
1251     }
1252   if (!request_completion && !g_variant_is_object_path (opt_introspect_object_path))
1253     {
1254       g_printerr (_("Error: %s is not a valid object path\n"), opt_introspect_object_path);
1255       goto out;
1256     }
1257
1258   /* All done with completion now */
1259   if (request_completion)
1260     goto out;
1261
1262   result = g_dbus_connection_invoke_method_sync (c,
1263                                                  opt_introspect_dest,
1264                                                  opt_introspect_object_path,
1265                                                  "org.freedesktop.DBus.Introspectable",
1266                                                  "Introspect",
1267                                                  NULL,
1268                                                  G_DBUS_INVOKE_METHOD_FLAGS_NONE,
1269                                                  3000, /* 3 sec */
1270                                                  NULL,
1271                                                  &error);
1272   if (result == NULL)
1273     {
1274       g_printerr (_("Error: %s\n"), error->message);
1275       g_error_free (error);
1276       goto out;
1277     }
1278   if (!g_variant_is_of_type (result, G_VARIANT_TYPE ("(s)")))
1279     {
1280       g_printerr (_("Error: Result is type `%s', expected `(s)'\n"),
1281                   g_variant_get_type_string (result));
1282       goto out;
1283     }
1284   g_variant_get (result, "(s)", &xml_data);
1285
1286   error = NULL;
1287   node = g_dbus_node_info_new_for_xml (xml_data, &error);
1288   if (node == NULL)
1289     {
1290       g_printerr (_("Error parsing introspection XML: %s\n"), error->message);
1291       g_error_free (error);
1292       goto out;
1293     }
1294
1295   dump_node (c, opt_introspect_dest, node, 0, opt_introspect_object_path);
1296
1297   ret = TRUE;
1298
1299  out:
1300   if (node != NULL)
1301     g_dbus_node_info_unref (node);
1302   if (result != NULL)
1303     g_variant_unref (result);
1304   if (c != NULL)
1305     g_object_unref (c);
1306   g_option_context_free (o);
1307   return ret;
1308 }
1309
1310 /* ---------------------------------------------------------------------------------------------------- */
1311
1312 static gchar *
1313 pick_word_at (const gchar  *s,
1314               gint          cursor,
1315               gint         *out_word_begins_at)
1316 {
1317   gint begin;
1318   gint end;
1319
1320   if (s[0] == '\0')
1321     {
1322       if (out_word_begins_at != NULL)
1323         *out_word_begins_at = -1;
1324       return NULL;
1325     }
1326
1327   if (g_ascii_isspace (s[cursor]) && ((cursor > 0 && g_ascii_isspace(s[cursor-1])) || cursor == 0))
1328     {
1329       if (out_word_begins_at != NULL)
1330         *out_word_begins_at = cursor;
1331       return g_strdup ("");
1332     }
1333
1334   while (!g_ascii_isspace (s[cursor - 1]) && cursor > 0)
1335     cursor--;
1336   begin = cursor;
1337
1338   end = begin;
1339   while (!g_ascii_isspace (s[end]) && s[end] != '\0')
1340     end++;
1341
1342   if (out_word_begins_at != NULL)
1343     *out_word_begins_at = begin;
1344
1345   return g_strndup (s + begin, end - begin);
1346 }
1347
1348 gint
1349 main (gint argc, gchar *argv[])
1350 {
1351   gint ret;
1352   const gchar *command;
1353   gboolean request_completion;
1354   gchar *completion_cur;
1355   gchar *completion_prev;
1356
1357   ret = 1;
1358   completion_cur = NULL;
1359   completion_prev = NULL;
1360
1361   g_type_init ();
1362
1363   if (argc < 2)
1364     {
1365       usage (&argc, &argv, FALSE);
1366       goto out;
1367     }
1368
1369   request_completion = FALSE;
1370
1371   //completion_debug ("---- argc=%d --------------------------------------------------------", argc);
1372
1373  again:
1374   command = argv[1];
1375   if (g_strcmp0 (command, "help") == 0)
1376     {
1377       if (request_completion)
1378         {
1379           /* do nothing */
1380         }
1381       else
1382         {
1383           usage (&argc, &argv, TRUE);
1384           ret = 0;
1385         }
1386       goto out;
1387     }
1388   else if (g_strcmp0 (command, "call") == 0)
1389     {
1390       if (handle_call (&argc,
1391                        &argv,
1392                        request_completion,
1393                        completion_cur,
1394                        completion_prev))
1395         ret = 0;
1396       goto out;
1397     }
1398   else if (g_strcmp0 (command, "introspect") == 0)
1399     {
1400       if (handle_introspect (&argc,
1401                              &argv,
1402                              request_completion,
1403                              completion_cur,
1404                              completion_prev))
1405         ret = 0;
1406       goto out;
1407     }
1408   else if (g_strcmp0 (command, "complete") == 0 && argc == 4 && !request_completion)
1409     {
1410       const gchar *completion_line;
1411       gchar **completion_argv;
1412       gint completion_argc;
1413       gint completion_point;
1414       gchar *endp;
1415       gint cur_begin;
1416
1417       request_completion = TRUE;
1418
1419       completion_line = argv[2];
1420       completion_point = strtol (argv[3], &endp, 10);
1421       if (endp == argv[3] || *endp != '\0')
1422         goto out;
1423
1424 #if 0
1425       completion_debug ("completion_point=%d", completion_point);
1426       completion_debug ("----");
1427       completion_debug (" 0123456789012345678901234567890123456789012345678901234567890123456789");
1428       completion_debug ("`%s'", completion_line);
1429       completion_debug (" %*s^",
1430                          completion_point, "");
1431       completion_debug ("----");
1432 #endif
1433
1434       if (!g_shell_parse_argv (completion_line,
1435                                &completion_argc,
1436                                &completion_argv,
1437                                NULL))
1438         {
1439           /* it's very possible the command line can't be parsed (for
1440            * example, missing quotes etc) - in that case, we just
1441            * don't autocomplete at all
1442            */
1443           goto out;
1444         }
1445
1446       /* compute cur and prev */
1447       completion_prev = NULL;
1448       completion_cur = pick_word_at (completion_line, completion_point, &cur_begin);
1449       if (cur_begin > 0)
1450         {
1451           gint prev_end;
1452           for (prev_end = cur_begin - 1; prev_end >= 0; prev_end--)
1453             {
1454               if (!g_ascii_isspace (completion_line[prev_end]))
1455                 {
1456                   completion_prev = pick_word_at (completion_line, prev_end, NULL);
1457                   break;
1458                 }
1459             }
1460         }
1461 #if 0
1462       completion_debug (" cur=`%s'", completion_cur);
1463       completion_debug ("prev=`%s'", completion_prev);
1464 #endif
1465
1466       argc = completion_argc;
1467       argv = completion_argv;
1468
1469       ret = 0;
1470
1471       goto again;
1472     }
1473   else
1474     {
1475       if (request_completion)
1476         {
1477           g_print ("help \ncall \nintrospect \n");
1478           ret = 0;
1479           goto out;
1480         }
1481       else
1482         {
1483           g_printerr ("Unknown command `%s'\n", command);
1484           usage (&argc, &argv, FALSE);
1485           goto out;
1486         }
1487     }
1488
1489  out:
1490   g_free (completion_cur);
1491   g_free (completion_prev);
1492   return ret;
1493 }