* qt/src/qdbusintegrator.cpp: Fix bug in parsing async methods
[platform/upstream/dbus.git] / qt / tools / dbus.cpp
1 /* -*- C++ -*-
2  *
3  * Copyright (C) 2006 Trolltech AS. All rights reserved.
4  *    Author: Thiago Macieira <thiago.macieira@trolltech.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 Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  */
21
22 #include <stdio.h>
23 #include <stdlib.h>
24
25 #include <dbus/qdbus.h>
26 #include <QtCore/QCoreApplication>
27 #include <QtCore/QStringList>
28 #include <QtCore/qmetaobject.h>
29 #include <QtXml/QDomDocument>
30 #include <QtXml/QDomElement>
31
32 Q_DECLARE_METATYPE(QVariant)
33 QDBusConnection *connection;
34
35 void listObjects(const QString &service, const QString &path)
36 {
37     QDBusInterfacePtr iface(*connection, service, path.isEmpty() ? "/" : path,
38                    "org.freedesktop.DBus.Introspectable");
39     if (!iface->isValid()) {
40         QDBusError err(iface->lastError());
41         fprintf(stderr, "Cannot introspect object %s at %s:\n%s (%s)\n",
42                 qPrintable(path.isEmpty() ? "/" : path), qPrintable(service),
43                 qPrintable(err.name()), qPrintable(err.message()));
44         exit(1);
45     }
46     QDBusReply<QString> xml = iface->call("Introspect");
47
48     if (xml.isError())
49         return;                 // silently
50
51     QDomDocument doc;
52     doc.setContent(xml);
53     QDomElement node = doc.documentElement();
54     QDomElement child = node.firstChildElement();
55     while (!child.isNull()) {
56         if (child.tagName() == QLatin1String("node")) {
57             QString sub = path + '/' + child.attribute("name");
58             printf("%s\n", qPrintable(sub));
59             listObjects(service, sub);
60         }
61         child = child.nextSiblingElement();
62     }
63 }
64
65 void listInterface(const QString &service, const QString &path, const QString &interface)
66 {
67     QDBusInterfacePtr iface(*connection, service, path, interface);
68     if (!iface->isValid()) {
69         QDBusError err(iface->lastError());
70         fprintf(stderr, "Interface '%s' not available in object %s at %s:\n%s (%s)\n",
71                 qPrintable(interface), qPrintable(path), qPrintable(service),
72                 qPrintable(err.name()), qPrintable(err.message()));
73         exit(1);
74     }
75     const QMetaObject *mo = iface->metaObject();
76
77     // properties
78     for (int i = mo->propertyOffset(); i < mo->propertyCount(); ++i) {
79         QMetaProperty mp = mo->property(i);
80         printf("property ");
81
82         if (mp.isReadable() && mp.isWritable())
83             printf("readwrite");
84         else if (mp.isReadable())
85             printf("read");
86         else
87             printf("write");
88
89         printf(" %s %s.%s\n", mp.typeName(), qPrintable(interface), mp.name());
90     }
91
92     // methods (signals and slots)
93     for (int i = mo->methodOffset(); i < mo->methodCount(); ++i) {
94         QMetaMethod mm = mo->method(i);
95
96         QByteArray signature = mm.signature();
97         signature.truncate(signature.indexOf('('));
98         printf("%s %s%s%s %s.%s(",
99                mm.methodType() == QMetaMethod::Signal ? "signal" : "method",
100                mm.tag(), *mm.tag() ? " " : "",
101                *mm.typeName() ? mm.typeName() : "void",
102                qPrintable(interface), signature.constData());
103
104         QList<QByteArray> types = mm.parameterTypes();
105         QList<QByteArray> names = mm.parameterNames();
106         bool first = true;
107         for (int i = 0; i < types.count(); ++i) {
108             printf("%s%s",
109                    first ? "" : ", ",
110                    types.at(i).constData());
111             if (!names.at(i).isEmpty())
112                 printf(" %s", names.at(i).constData());
113             first = false;
114         }
115         printf(")\n");
116     }
117 }
118
119 void listAllInterfaces(const QString &service, const QString &path)
120 {
121     QDBusInterfacePtr iface(*connection, service, path, "org.freedesktop.DBus.Introspectable");
122     if (!iface->isValid()) {
123         QDBusError err(iface->lastError());
124         fprintf(stderr, "Cannot introspect object %s at %s:\n%s (%s)\n",
125                 qPrintable(path), qPrintable(service),
126                 qPrintable(err.name()), qPrintable(err.message()));
127         exit(1);
128     }
129     QDBusReply<QString> xml = iface->call("Introspect");
130
131     if (xml.isError())
132         return;                 // silently
133
134     QDomDocument doc;
135     doc.setContent(xml);
136     QDomElement node = doc.documentElement();
137     QDomElement child = node.firstChildElement();
138     while (!child.isNull()) {
139         if (child.tagName() == QLatin1String("interface")) {
140             QString ifaceName = child.attribute("name");
141             if (QDBusUtil::isValidInterfaceName(ifaceName))
142                 listInterface(service, path, ifaceName);
143             else {
144                 qWarning("Invalid D-BUS interface name '%s' found while parsing introspection",
145                          qPrintable(ifaceName));
146             }
147         }
148         child = child.nextSiblingElement();
149     }
150 }
151
152 QStringList readList(int &argc, const char *const *&argv)
153 {
154     --argc;
155     ++argv;
156
157     QStringList retval;
158     while (argc && QLatin1String(argv[0]) != ")")
159         retval += QString::fromLocal8Bit(argv[0]);
160
161     return retval;
162 }
163
164 void placeCall(const QString &service, const QString &path, const QString &interface,
165                const QString &member, int argc, const char *const *argv)
166 {
167     QDBusInterfacePtr iface(*connection, service, path, interface);
168     if (!iface->isValid()) {
169         QDBusError err(iface->lastError());
170         fprintf(stderr, "Interface '%s' not available in object %s at %s:\n%s (%s)\n",
171                 qPrintable(interface), qPrintable(path), qPrintable(service),
172                 qPrintable(err.name()), qPrintable(err.message()));
173         exit(1);
174     }
175
176     const QMetaObject *mo = iface->metaObject();
177     QByteArray match = member.toLatin1();
178     match += '(';
179
180     int midx = -1;
181     for (int i = mo->methodOffset(); i < mo->methodCount(); ++i) {
182         QMetaMethod mm = mo->method(i);
183         QByteArray signature = mm.signature();
184         if (signature.startsWith(match)) {
185             midx = i;
186             break;
187         }
188     }
189
190     if (midx == -1) {
191         fprintf(stderr, "Cannot find '%s.%s' in object %s at %s\n",
192                 qPrintable(interface), qPrintable(member), qPrintable(path),
193                 qPrintable(service));
194         exit(1);
195     }
196
197     QMetaMethod mm = mo->method(midx);
198     QList<QByteArray> types = mm.parameterTypes();
199
200     QVariantList params;
201     for (int i = 0; argc && i < types.count(); ++i) {
202         int id = QVariant::nameToType(types.at(i));
203         if ((id == QVariant::UserType || id == QVariant::Map) && types.at(i) != "QVariant") {
204             fprintf(stderr, "Sorry, can't pass arg of type %s yet\n",
205                     types.at(i).constData());
206             exit(1);
207         }
208         if (id == QVariant::UserType)
209             id = QMetaType::type(types.at(i));
210
211         Q_ASSERT(id);
212
213         QVariant p;
214         if ((id == QVariant::List || id == QVariant::StringList) && QLatin1String("(") == argv[0])
215             p = readList(argc, argv);
216         else
217             p = QString::fromLocal8Bit(argv[0]);
218
219         if (id < int(QVariant::UserType)) {
220             // avoid calling it for QVariant
221             p.convert( QVariant::Type(id) );
222             if (p.type() == QVariant::Invalid) {
223                 fprintf(stderr, "Could not convert '%s' to type '%s'.\n",
224                         argv[0], types.at(i).constData());
225                 exit(1);
226             }
227         } else if (types.at(i) == "QVariant") {
228             QVariant tmp(id, p.constData());
229             p = tmp;
230         }
231         params += p;
232         --argc;
233         ++argv;
234     }
235     if (params.count() != types.count()) {
236         fprintf(stderr, "Invalid number of parameters\n");
237         exit(1);
238     }
239
240     QDBusMessage reply = iface->callWithArgs(member, params, QDBusInterface::NoUseEventLoop);
241     if (reply.type() == QDBusMessage::ErrorMessage) {
242         QDBusError err = reply;
243         printf("Error: %s\n%s\n", qPrintable(err.name()), qPrintable(err.message()));
244         exit(2);
245     } else if (reply.type() != QDBusMessage::ReplyMessage) {
246         fprintf(stderr, "Invalid reply type %d\n", int(reply.type()));
247         exit(1);
248     }
249     
250     foreach (QVariant v, reply) {
251         if (v.userType() == QVariant::StringList) {
252             foreach (QString s, v.toStringList())
253                 printf("%s\n", qPrintable(s));
254         } else {
255             if (v.userType() == qMetaTypeId<QVariant>())
256                 v = qvariant_cast<QVariant>(v);
257             printf("%s\n", qPrintable(v.toString()));
258         }
259     }
260
261     exit(0);
262 }
263
264 bool splitInterfaceAndName(const QString &interfaceAndName, const char *type,
265                            QString &interface, QString &member)
266 {
267     interface = interfaceAndName;
268     int pos = interface.lastIndexOf(QLatin1Char('.'));
269     if (pos != -1) {
270         member = interface.mid(pos + 1);
271         interface.truncate(pos);
272     }
273
274     if (!QDBusUtil::isValidInterfaceName(interface)) {
275         fprintf(stderr, "Interface '%s' is not a valid interface name.\n", qPrintable(interface));
276         return false;
277     } else if (!QDBusUtil::isValidMemberName(member)) {
278         fprintf(stderr, "%s name '%s' is not a valid member name.\n", type, qPrintable(member));
279         return false;
280     }
281     return true;
282 }
283
284 void getProperty(const QString &service, const QString &path, const QString &interfaceAndName)
285 {
286     QString property;
287     QString interface;
288     if (!splitInterfaceAndName(interfaceAndName, "Property", interface, property))
289         exit(1);
290     
291     QDBusInterfacePtr iface(*connection, service, path, interface);
292     QVariant reply = iface->property(property.toLatin1());
293     if (reply.isNull()) {
294         QDBusError error = iface->lastError();
295         fprintf(stderr, "Could not get property '%s' on interface '%s': %s (%s)\n",
296                 qPrintable(property), qPrintable(interface), qPrintable(error.name()),
297                 qPrintable(error.message()));
298         exit(1);
299     }
300
301     printf("%s\n", qPrintable(reply.toString()));
302 }
303
304 void setProperty(const QString &service, const QString &path, const QString &interfaceAndName,
305                  const QString &valueStr)
306 {
307     QString property;
308     QString interface;
309     if (!splitInterfaceAndName(interfaceAndName, "Property", interface, property))
310         exit(1);
311
312     QDBusInterfacePtr iface(*connection, service, path, interface);
313     iface->setProperty(property.toLatin1(), valueStr);
314 }
315
316 int main(int argc, char **argv)
317 {
318     QCoreApplication app(argc, argv);
319     if (argc >= 1 && qstrcmp(argv[1], "--system") == 0) {
320         connection = &QDBus::systemBus();
321         --argc;
322         ++argv;
323     } else
324         connection = &QDBus::sessionBus();
325
326     if (!connection->isConnected()) {
327         fprintf(stderr, "Could not connect to D-Bus server: %s: %s\n",
328                 qPrintable(connection->lastError().name()),
329                 qPrintable(connection->lastError().message()));
330         return 1;
331     }
332     QDBusBusService *bus = connection->busService();
333
334     if (argc == 1) {
335         QStringList names = bus->ListNames();
336         foreach (QString name, names)
337             printf("%s\n", qPrintable(name));
338         exit(0);
339     }
340     
341     QString service = QLatin1String(argv[1]);
342     if (!QDBusUtil::isValidBusName(service)) {
343         fprintf(stderr, "Service '%s' is not a valid name.\n", qPrintable(service));
344         exit(1);
345     }
346     if (!bus->NameHasOwner(service)) {
347         fprintf(stderr, "Service '%s' does not exist.\n", qPrintable(service));
348         exit(1);
349     }
350
351     if (argc == 2) {
352         printf("/\n");
353         listObjects(service, QString());
354         exit(0);
355     }
356
357     QString path = QLatin1String(argv[2]);
358     if (!QDBusUtil::isValidObjectPath(path)) {
359         fprintf(stderr, "Path '%s' is not a valid path name.\n", qPrintable(path));
360         exit(1);
361     }
362     if (argc == 3) {
363         listAllInterfaces(service, path);
364         exit(0);
365     }
366
367     QString interface = QLatin1String(argv[3]);
368     QString member;
369     int pos = interface.lastIndexOf(QLatin1Char('.'));
370     if (pos == -1) {
371         member = interface;
372         interface.clear();
373     } else {
374         member = interface.mid(pos + 1);
375         interface.truncate(pos);
376     }
377     if (!interface.isEmpty() && !QDBusUtil::isValidInterfaceName(interface)) {
378         fprintf(stderr, "Interface '%s' is not a valid interface name.\n", qPrintable(interface));
379         exit(1);
380     }
381     if (!QDBusUtil::isValidMemberName(member)) {
382         fprintf(stderr, "Method name '%s' is not a valid member name.\n", qPrintable(member));
383         exit(1);
384     }
385
386     if (interface.isEmpty()) {
387         if (member.toLower() == QLatin1String("get") && argc == 5) {
388             getProperty(service, path, QLatin1String(argv[4]));
389             return 0;
390         } else if (member.toLower() == QLatin1String("set") && argc == 6) {
391             setProperty(service, path, QLatin1String(argv[4]), QLatin1String(argv[5]));
392             return 0;
393         }
394     }    
395     placeCall(service, path, interface, member, argc - 4, argv + 4);
396 }
397