migration from private to rsa
[external/dbus-glib.git] / dbus / dbus-bash-completion-helper.c
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /* dbus-bash-completion-helper.c  Bash Completion helper routines
3  *
4  * Copyright (C) 2008 David Zeuthen <davidz@redhat.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  *
20  */
21
22 #include <config.h>
23 #include <ctype.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include <dbus/dbus.h>
29 #include <glib.h>
30 #include "dbus-gparser.h"
31
32 static void
33 print_services (DBusConnection *connection)
34 {
35   DBusMessage *message;
36   DBusMessage *reply;
37   DBusError error;
38   DBusMessageIter iter;
39   DBusMessageIter iter_array;
40   const char *name;
41
42   /* list both active and activatable names (the shell will sort and
43    * uniquify them) - also avoid names that are not well-known
44    * (e.g. :1.42).
45    */
46
47   message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
48                                           DBUS_PATH_DBUS,
49                                           DBUS_INTERFACE_DBUS,
50                                           "ListNames");
51   dbus_error_init (&error);
52   reply = dbus_connection_send_with_reply_and_block (connection,
53                                                      message,
54                                                      -1,
55                                                      &error);
56   dbus_message_unref (message);
57   dbus_message_iter_init (reply, &iter);
58   dbus_message_iter_recurse (&iter, &iter_array);
59   while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
60     {
61       dbus_message_iter_get_basic (&iter_array, &name);
62       if (name[0] != ':')
63         printf ("%s \n", name);
64       dbus_message_iter_next (&iter_array);
65     }
66   dbus_message_unref (reply);
67
68   message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
69                                           DBUS_PATH_DBUS,
70                                           DBUS_INTERFACE_DBUS,
71                                           "ListActivatableNames");
72   dbus_error_init (&error);
73   reply = dbus_connection_send_with_reply_and_block (connection,
74                                                      message,
75                                                      -1,
76                                                      &error);
77   dbus_message_unref (message);
78   dbus_message_iter_init (reply, &iter);
79   dbus_message_iter_recurse (&iter, &iter_array);
80   while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
81     {
82       dbus_message_iter_get_basic (&iter_array, &name);
83       printf ("%s \n", name);
84       dbus_message_iter_next (&iter_array);
85     }
86   dbus_message_unref (reply);
87 }
88
89 static gboolean
90 have_option (char **tokens, const char *option)
91 {
92   int n;
93   for (n = 0; tokens[n] != NULL; n++)
94     if (strcmp (tokens[n], option) == 0)
95       return TRUE;
96   return FALSE;
97 }
98
99 static gboolean
100 have_option_with_value (char **tokens, const char *option, const char **value)
101 {
102   int n;
103   for (n = 0; tokens[n] != NULL; n++)
104     {
105       if (g_str_has_prefix (tokens[n], option))
106         {
107           if (strlen (tokens[n]) > strlen (option))
108             *value = tokens[n] + strlen (option);
109           return TRUE;
110         }
111     }
112   return FALSE;
113 }
114
115 static void
116 print_objects (DBusConnection *connection, const char *service_name, const char *cur)
117 {
118   DBusMessage *message;
119   DBusMessage *reply;
120   DBusError error;
121   DBusMessageIter iter;
122   const char *introspection_xml;
123   NodeInfo *root;
124   GSList *nodes;
125   GSList *l;
126
127   if (cur == NULL)
128     cur = "/";
129
130   message = dbus_message_new_method_call (service_name,
131                                           cur,
132                                           DBUS_INTERFACE_INTROSPECTABLE,
133                                           "Introspect");
134   dbus_error_init (&error);
135   reply = dbus_connection_send_with_reply_and_block (connection,
136                                                      message,
137                                                      -1,
138                                                      &error);
139   dbus_message_unref (message);
140   dbus_message_iter_init (reply, &iter);
141   dbus_message_iter_get_basic (&iter, &introspection_xml);
142
143   root = description_load_from_string (introspection_xml, strlen (introspection_xml), NULL);
144   nodes = node_info_get_nodes (root);
145
146   if (g_slist_length (node_info_get_interfaces (root)) > 0)
147     printf ("%s \n", cur);
148
149   for (l = nodes; l != NULL; l = l->next)
150     {
151       NodeInfo *node = (NodeInfo *) l->data;
152       const char *name;
153       char *new_path;
154
155       name = node_info_get_name (node);
156       if (strcmp (cur, "/") == 0)
157         new_path = g_strdup_printf ("/%s", name);
158       else
159         new_path = g_strdup_printf ("%s/%s", cur, name);
160
161       print_objects (connection, service_name, new_path);
162
163       g_free (new_path);
164     }
165   node_info_unref (root);
166
167   dbus_message_unref (reply);
168 }
169
170 static gboolean
171 is_object_path_with_interfaces (DBusConnection *connection, const char *service_name, const char *object_path)
172 {
173   DBusMessage *message;
174   DBusMessage *reply;
175   DBusError error;
176   DBusMessageIter iter;
177   const char *introspection_xml;
178   NodeInfo *root;
179   gboolean ret;
180
181   ret = FALSE;
182
183   message = dbus_message_new_method_call (service_name,
184                                           object_path,
185                                           DBUS_INTERFACE_INTROSPECTABLE,
186                                           "Introspect");
187   dbus_error_init (&error);
188   reply = dbus_connection_send_with_reply_and_block (connection,
189                                                      message,
190                                                      -1,
191                                                      &error);
192   dbus_message_unref (message);
193   dbus_message_iter_init (reply, &iter);
194   dbus_message_iter_get_basic (&iter, &introspection_xml);
195
196   root = description_load_from_string (introspection_xml, strlen (introspection_xml), NULL);
197
198   if (g_slist_length (node_info_get_interfaces (root)) > 0)
199     ret = TRUE;
200
201   node_info_unref (root);
202   dbus_message_unref (reply);
203
204   return ret;
205 }
206
207 static void
208 print_methods (DBusConnection *connection, const char *service_name, const char *object_path)
209 {
210   DBusMessage *message;
211   DBusMessage *reply;
212   DBusError error;
213   DBusMessageIter iter;
214   const char *introspection_xml;
215   NodeInfo *root;
216   GSList *interfaces;
217   GSList *l;
218
219   message = dbus_message_new_method_call (service_name,
220                                           object_path,
221                                           DBUS_INTERFACE_INTROSPECTABLE,
222                                           "Introspect");
223   dbus_error_init (&error);
224   reply = dbus_connection_send_with_reply_and_block (connection,
225                                                      message,
226                                                      -1,
227                                                      &error);
228   dbus_message_unref (message);
229   dbus_message_iter_init (reply, &iter);
230   dbus_message_iter_get_basic (&iter, &introspection_xml);
231
232   root = description_load_from_string (introspection_xml, strlen (introspection_xml), NULL);
233   interfaces = node_info_get_interfaces (root);
234   for (l = interfaces; l != NULL; l = l->next)
235     {
236       InterfaceInfo *interface = (InterfaceInfo *) l->data;
237       GSList *methods;
238       GSList *ll;
239       methods = interface_info_get_methods (interface);
240       for (ll = methods; ll != NULL; ll = ll->next)
241         {
242           MethodInfo *method = (MethodInfo *) ll->data;
243           printf ("%s.%s \n", interface_info_get_name (interface), method_info_get_name (method));
244         }
245     }
246   node_info_unref (root);
247   dbus_message_unref (reply);
248 }
249
250 static void
251 print_signature (DBusConnection *connection, const char *service_name, const char *object_path, const char *method)
252 {
253   DBusMessage *message;
254   DBusMessage *reply;
255   DBusError error;
256   DBusMessageIter iter;
257   const char *introspection_xml;
258   NodeInfo *root;
259   GSList *interfaces;
260   GSList *l;
261   char *s;
262   char *method_name;
263   char *interface_name;
264   int n;
265
266   method_name = NULL;
267   interface_name = NULL;
268
269   s = strrchr (method, '.');
270   if (s == NULL || strlen (s) < 2 || s - method < 1)
271     goto fail;
272   method_name = g_strdup (s + 1);
273   interface_name = g_strndup (method, s - method);
274   printf (" \n");
275
276   message = dbus_message_new_method_call (service_name,
277                                           object_path,
278                                           DBUS_INTERFACE_INTROSPECTABLE,
279                                           "Introspect");
280   dbus_error_init (&error);
281   reply = dbus_connection_send_with_reply_and_block (connection,
282                                                      message,
283                                                      -1,
284                                                      &error);
285   dbus_message_unref (message);
286   dbus_message_iter_init (reply, &iter);
287   dbus_message_iter_get_basic (&iter, &introspection_xml);
288
289   root = description_load_from_string (introspection_xml, strlen (introspection_xml), NULL);
290   interfaces = node_info_get_interfaces (root);
291   for (l = interfaces; l != NULL; l = l->next)
292     {
293       InterfaceInfo *interface = (InterfaceInfo *) l->data;
294
295       if (strcmp (interface_name, interface_info_get_name (interface)) == 0)
296         {
297           GSList *methods;
298           GSList *ll;
299           methods = interface_info_get_methods (interface);
300           for (ll = methods; ll != NULL; ll = ll->next)
301             {
302               MethodInfo *method = (MethodInfo *) ll->data;
303               if (strcmp (method_name, method_info_get_name (method)) == 0)
304                 {
305                   GSList *args;
306                   GSList *lll;
307                   args = method_info_get_args (method);
308                   for (lll = args, n = 0; lll != NULL; lll = lll->next, n++)
309                     {
310                       ArgInfo *arg = (ArgInfo *) lll->data;
311                       printf ("#    %s: arg %d: %s (%s)         \n",
312                               arg_info_get_direction (arg) == ARG_IN ? " IN" : "OUT",
313                               n,
314                               arg_info_get_name (arg),
315                               arg_info_get_type (arg));
316                     }
317                   break;
318                 }
319             }
320         }
321     }
322   node_info_unref (root);
323   dbus_message_unref (reply);
324  fail:
325   g_free (method_name);
326   g_free (interface_name);
327 }
328
329
330 static int
331 complete_dbus_send (char *str)
332 {
333   int ret;
334   char **tokens;
335   int num_tokens;
336   const char *cur;
337   gboolean have_system;
338   gboolean have_session;
339   gboolean have_print_reply;
340   gboolean have_dest;
341   DBusConnection *connection;
342   DBusBusType bus_type;
343   DBusError error;
344   const char *target_service;
345   const char *object_path;
346   const char *method;
347   int n;
348   int object_path_index;
349
350   ret = 1;
351   connection = NULL;
352   target_service = NULL;
353
354   tokens = g_strsplit (str, " ", 0);
355   num_tokens = g_strv_length (tokens);
356   if (num_tokens >= 1) {
357     cur = tokens[num_tokens - 1];
358   } else {
359     cur = "";
360   }
361
362   have_system = have_option (tokens, "--system");
363   have_session = have_option (tokens, "--session");
364   have_print_reply = have_option (tokens, "--print-reply");
365   have_dest = have_option_with_value (tokens, "--dest=", &target_service);
366
367   if (!have_print_reply)
368     printf ("--print-reply \n");
369
370   if (!have_system && !have_session)
371     {
372       printf ("--system \n");
373       printf ("--session \n");
374       goto done;
375     }
376
377   if (!have_dest && !g_str_has_prefix (cur, "--dest="))
378     {
379       printf ("--dest=\n");
380       goto done;
381     }
382
383   if (have_system || have_session)
384     {
385       bus_type = have_system ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION;
386
387       dbus_error_init (&error);
388       connection = dbus_bus_get (bus_type, &error);
389       if (connection == NULL)
390         {
391           fprintf (stderr, "Failed to open connection to %s message bus: %s: %s\n",
392                    (bus_type == DBUS_BUS_SYSTEM) ? "system" : "session",
393                    error.name, error.message);
394           dbus_error_free (&error);
395           goto fail;
396         }
397     }
398
399   if (connection != NULL && g_str_has_prefix (cur, "--dest="))
400     {
401       print_services (connection);
402       goto done;
403     }
404
405   /* see if we have an object path */
406   object_path = NULL;
407   object_path_index = 0;
408   if (connection != NULL && target_service != NULL)
409     {
410       for (n = 0; tokens[n] != NULL; n++)
411         {
412           if (tokens[n] == cur)
413             continue;
414
415           if (*(tokens[n]) == '/')
416             {
417               if (is_object_path_with_interfaces (connection, target_service, tokens[n]))
418                 {
419                   object_path = tokens[n];
420                   object_path_index = n;
421                 }
422             }
423         }
424     }
425
426   /* if we have a connection and a destination but no object path, go ahead and list the object paths */
427   if (connection != NULL && target_service != NULL && object_path == NULL)
428     {
429       print_objects (connection, target_service, NULL);
430       goto done;
431     }
432
433   /* see if we have a method; it's directly after the object_path */
434   method = NULL;
435   if (connection != NULL && target_service != NULL && object_path != NULL)
436     {
437       if ((object_path_index + 1 < num_tokens - 1) &&
438           (strlen (tokens[object_path_index + 1]) > 0) &&
439           !(strcmp (cur, tokens[object_path_index + 1]) == 0))
440         method = tokens[object_path_index + 1];
441     }
442
443   /* if we have connection, destination and object path but no method yet, list the methods */
444   if (connection != NULL && target_service != NULL && object_path != NULL && method == NULL)
445     {
446       print_methods (connection, target_service, object_path);
447       goto done;
448     }
449
450   /* print signature as comment */
451   if (connection != NULL && target_service != NULL && object_path != NULL && method != NULL)
452     {
453       print_signature (connection, target_service, object_path, method);
454     }
455
456  done:
457   ret = 0;
458
459  fail:
460
461   g_strfreev (tokens);
462
463   if (connection != NULL)
464     dbus_connection_unref (connection);
465   return ret;
466 }
467
468 int
469 main (int argc, char *argv[])
470 {
471   int ret;
472   char *cur;
473   gboolean dbus_send;
474
475   ret = 1;
476   dbus_send = FALSE;
477
478   if (argc != 3)
479     {
480       fprintf (stderr, "invalid use\n");
481       goto out;
482     }
483
484   if (strcmp (argv[1], "dbus-send") == 0)
485     {
486       dbus_send = TRUE;
487     }
488   else
489     {
490       fprintf (stderr, "unknown program '%s'\n", argv[1]);
491       goto out;
492     }
493
494   if (strlen (argv[2]) < strlen (argv[1]) + 1)
495     {
496       fprintf (stderr, "error");
497       goto out;
498     }
499
500   cur = argv[2] + strlen (argv[1]) + 1;
501
502   if (dbus_send)
503     ret = complete_dbus_send (cur);
504
505  out:
506   return ret;
507 }