1/****************************************************************************
2**
3** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4** Contact: http://www.qt-project.org/legal
5**
6** This file is part of the QtDBus module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Digia. For licensing terms and
14** conditions see http://qt.digia.com/licensing. For further information
15** use the contact form at http://qt.digia.com/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 2.1 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 2.1 requirements
23** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24**
25** In addition, as a special exception, Digia gives you certain additional
26** rights. These rights are described in the Digia Qt LGPL Exception
27** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28**
29** GNU General Public License Usage
30** Alternatively, this file may be used under the terms of the GNU
31** General Public License version 3.0 as published by the Free Software
32** Foundation and appearing in the file LICENSE.GPL included in the
33** packaging of this file. Please review the following information to
34** ensure the GNU General Public License version 3.0 requirements will be
35** met: http://www.gnu.org/copyleft/gpl.html.
36**
37**
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qdbusxmlparser_p.h"
43#include "qdbusinterface.h"
44#include "qdbusinterface_p.h"
45#include "qdbusconnection_p.h"
46#include "qdbusutil_p.h"
47
48#include <QtXml/qdom.h>
49#include <QtCore/qmap.h>
50#include <QtCore/qvariant.h>
51#include <QtCore/qtextstream.h>
52
53#ifndef QT_NO_DBUS
54
55//#define QDBUS_PARSER_DEBUG
56#ifdef QDBUS_PARSER_DEBUG
57# define qDBusParserError qWarning
58#else
59# define qDBusParserError if (true) {} else qDebug
60#endif
61
62QT_BEGIN_NAMESPACE
63
64static QDBusIntrospection::Annotations
65parseAnnotations(const QDomElement& elem)
66{
67 QDBusIntrospection::Annotations retval;
68 QDomNodeList list = elem.elementsByTagName(QLatin1String("annotation"));
69 for (int i = 0; i < list.count(); ++i)
70 {
71 QDomElement ann = list.item(i).toElement();
72 if (ann.isNull())
73 continue;
74
75 QString name = ann.attribute(QLatin1String("name")),
76 value = ann.attribute(QLatin1String("value"));
77
78 if (!QDBusUtil::isValidInterfaceName(name)) {
79 qDBusParserError("Invalid D-BUS annotation '%s' found while parsing introspection",
80 qPrintable(name));
81 continue;
82 }
83
84 retval.insert(name, value);
85 }
86
87 return retval;
88}
89
90static QDBusIntrospection::Arguments
91parseArgs(const QDomElement& elem, const QLatin1String& direction, bool acceptEmpty)
92{
93 QDBusIntrospection::Arguments retval;
94 QDomNodeList list = elem.elementsByTagName(QLatin1String("arg"));
95 for (int i = 0; i < list.count(); ++i)
96 {
97 QDomElement arg = list.item(i).toElement();
98 if (arg.isNull())
99 continue;
100
101 if ((acceptEmpty && !arg.hasAttribute(QLatin1String("direction"))) ||
102 arg.attribute(QLatin1String("direction")) == direction) {
103
104 QDBusIntrospection::Argument argData;
105 if (arg.hasAttribute(QLatin1String("name")))
106 argData.name = arg.attribute(QLatin1String("name")); // can be empty
107 argData.type = arg.attribute(QLatin1String("type"));
108 if (!QDBusUtil::isValidSingleSignature(argData.type)) {
109 qDBusParserError("Invalid D-BUS type signature '%s' found while parsing introspection",
110 qPrintable(argData.type));
111 }
112
113 retval << argData;
114 }
115 }
116 return retval;
117}
118
119QDBusXmlParser::QDBusXmlParser(const QString& service, const QString& path,
120 const QString& xmlData)
121 : m_service(service), m_path(path)
122{
123 QDomDocument doc;
124 doc.setContent(xmlData);
125 m_node = doc.firstChildElement(QLatin1String("node"));
126}
127
128QDBusXmlParser::QDBusXmlParser(const QString& service, const QString& path,
129 const QDomElement& node)
130 : m_service(service), m_path(path), m_node(node)
131{
132}
133
134QDBusIntrospection::Interfaces
135QDBusXmlParser::interfaces() const
136{
137 QDBusIntrospection::Interfaces retval;
138
139 if (m_node.isNull())
140 return retval;
141
142 QDomNodeList interfaceList = m_node.elementsByTagName(QLatin1String("interface"));
143 for (int i = 0; i < interfaceList.count(); ++i)
144 {
145 QDomElement iface = interfaceList.item(i).toElement();
146 QString ifaceName = iface.attribute(QLatin1String("name"));
147 if (iface.isNull())
148 continue; // for whatever reason
149 if (!QDBusUtil::isValidInterfaceName(ifaceName)) {
150 qDBusParserError("Invalid D-BUS interface name '%s' found while parsing introspection",
151 qPrintable(ifaceName));
152 continue;
153 }
154
155 QDBusIntrospection::Interface *ifaceData = new QDBusIntrospection::Interface;
156 ifaceData->name = ifaceName;
157 {
158 // save the data
159 QTextStream ts(&ifaceData->introspection);
160 iface.save(ts,2);
161 }
162
163 // parse annotations
164 ifaceData->annotations = parseAnnotations(iface);
165
166 // parse methods
167 QDomNodeList list = iface.elementsByTagName(QLatin1String("method"));
168 for (int j = 0; j < list.count(); ++j)
169 {
170 QDomElement method = list.item(j).toElement();
171 QString methodName = method.attribute(QLatin1String("name"));
172 if (method.isNull())
173 continue;
174 if (!QDBusUtil::isValidMemberName(methodName)) {
175 qDBusParserError("Invalid D-BUS member name '%s' found in interface '%s' while parsing introspection",
176 qPrintable(methodName), qPrintable(ifaceName));
177 continue;
178 }
179
180 QDBusIntrospection::Method methodData;
181 methodData.name = methodName;
182
183 // parse arguments
184 methodData.inputArgs = parseArgs(method, QLatin1String("in"), true);
185 methodData.outputArgs = parseArgs(method, QLatin1String("out"), false);
186 methodData.annotations = parseAnnotations(method);
187
188 // add it
189 ifaceData->methods.insert(methodName, methodData);
190 }
191
192 // parse signals
193 list = iface.elementsByTagName(QLatin1String("signal"));
194 for (int j = 0; j < list.count(); ++j)
195 {
196 QDomElement signal = list.item(j).toElement();
197 QString signalName = signal.attribute(QLatin1String("name"));
198 if (signal.isNull())
199 continue;
200 if (!QDBusUtil::isValidMemberName(signalName)) {
201 qDBusParserError("Invalid D-BUS member name '%s' found in interface '%s' while parsing introspection",
202 qPrintable(signalName), qPrintable(ifaceName));
203 continue;
204 }
205
206 QDBusIntrospection::Signal signalData;
207 signalData.name = signalName;
208
209 // parse data
210 signalData.outputArgs = parseArgs(signal, QLatin1String("out"), true);
211 signalData.annotations = parseAnnotations(signal);
212
213 // add it
214 ifaceData->signals_.insert(signalName, signalData);
215 }
216
217 // parse properties
218 list = iface.elementsByTagName(QLatin1String("property"));
219 for (int j = 0; j < list.count(); ++j)
220 {
221 QDomElement property = list.item(j).toElement();
222 QString propertyName = property.attribute(QLatin1String("name"));
223 if (property.isNull())
224 continue;
225 if (!QDBusUtil::isValidMemberName(propertyName)) {
226 qDBusParserError("Invalid D-BUS member name '%s' found in interface '%s' while parsing introspection",
227 qPrintable(propertyName), qPrintable(ifaceName));
228 continue;
229 }
230
231 QDBusIntrospection::Property propertyData;
232
233 // parse data
234 propertyData.name = propertyName;
235 propertyData.type = property.attribute(QLatin1String("type"));
236 propertyData.annotations = parseAnnotations(property);
237
238 if (!QDBusUtil::isValidSingleSignature(propertyData.type)) {
239 // cannot be!
240 qDBusParserError("Invalid D-BUS type signature '%s' found in property '%s.%s' while parsing introspection",
241 qPrintable(propertyData.type), qPrintable(ifaceName),
242 qPrintable(propertyName));
243 }
244
245 QString access = property.attribute(QLatin1String("access"));
246 if (access == QLatin1String("read"))
247 propertyData.access = QDBusIntrospection::Property::Read;
248 else if (access == QLatin1String("write"))
249 propertyData.access = QDBusIntrospection::Property::Write;
250 else if (access == QLatin1String("readwrite"))
251 propertyData.access = QDBusIntrospection::Property::ReadWrite;
252 else {
253 qDBusParserError("Invalid D-BUS property access '%s' found in property '%s.%s' while parsing introspection",
254 qPrintable(access), qPrintable(ifaceName),
255 qPrintable(propertyName));
256 continue; // invalid one!
257 }
258
259 // add it
260 ifaceData->properties.insert(propertyName, propertyData);
261 }
262
263 // add it
264 retval.insert(ifaceName, QSharedDataPointer<QDBusIntrospection::Interface>(ifaceData));
265 }
266
267 return retval;
268}
269
270QSharedDataPointer<QDBusIntrospection::Object>
271QDBusXmlParser::object() const
272{
273 if (m_node.isNull())
274 return QSharedDataPointer<QDBusIntrospection::Object>();
275
276 QDBusIntrospection::Object* objData;
277 objData = new QDBusIntrospection::Object;
278 objData->service = m_service;
279 objData->path = m_path;
280
281 // check if we have anything to process
282 if (objData->introspection.isNull() && !m_node.firstChild().isNull()) {
283 // yes, introspect this object
284 QTextStream ts(&objData->introspection);
285 m_node.save(ts,2);
286
287 QDomNodeList objects = m_node.elementsByTagName(QLatin1String("node"));
288 for (int i = 0; i < objects.count(); ++i) {
289 QDomElement obj = objects.item(i).toElement();
290 QString objName = obj.attribute(QLatin1String("name"));
291 if (obj.isNull())
292 continue; // for whatever reason
293 if (!QDBusUtil::isValidObjectPath(m_path + QLatin1Char('/') + objName)) {
294 qDBusParserError("Invalid D-BUS object path '%s/%s' found while parsing introspection",
295 qPrintable(m_path), qPrintable(objName));
296 continue;
297 }
298
299 objData->childObjects.append(objName);
300 }
301
302 QDomNodeList interfaceList = m_node.elementsByTagName(QLatin1String("interface"));
303 for (int i = 0; i < interfaceList.count(); ++i) {
304 QDomElement iface = interfaceList.item(i).toElement();
305 QString ifaceName = iface.attribute(QLatin1String("name"));
306 if (iface.isNull())
307 continue;
308 if (!QDBusUtil::isValidInterfaceName(ifaceName)) {
309 qDBusParserError("Invalid D-BUS interface name '%s' found while parsing introspection",
310 qPrintable(ifaceName));
311 continue;
312 }
313
314 objData->interfaces.append(ifaceName);
315 }
316 } else {
317 objData->introspection = QLatin1String("<node/>\n");
318 }
319
320 QSharedDataPointer<QDBusIntrospection::Object> retval;
321 retval = objData;
322 return retval;
323}
324
325QSharedDataPointer<QDBusIntrospection::ObjectTree>
326QDBusXmlParser::objectTree() const
327{
328 QSharedDataPointer<QDBusIntrospection::ObjectTree> retval;
329
330 if (m_node.isNull())
331 return retval;
332
333 retval = new QDBusIntrospection::ObjectTree;
334
335 retval->service = m_service;
336 retval->path = m_path;
337
338 QTextStream ts(&retval->introspection);
339 m_node.save(ts,2);
340
341 // interfaces are easy:
342 retval->interfaceData = interfaces();
343 retval->interfaces = retval->interfaceData.keys();
344
345 // sub-objects are slightly more difficult:
346 QDomNodeList objects = m_node.elementsByTagName(QLatin1String("node"));
347 for (int i = 0; i < objects.count(); ++i) {
348 QDomElement obj = objects.item(i).toElement();
349 QString objName = obj.attribute(QLatin1String("name"));
350 if (obj.isNull() || objName.isEmpty())
351 continue; // for whatever reason
352
353 // check if we have anything to process
354 if (!obj.firstChild().isNull()) {
355 // yes, introspect this object
356 QString xml;
357 QTextStream ts2(&xml);
358 obj.save(ts2,0);
359
360 // parse it
361 QString objAbsName = m_path;
362 if (!objAbsName.endsWith(QLatin1Char('/')))
363 objAbsName.append(QLatin1Char('/'));
364 objAbsName += objName;
365
366 QDBusXmlParser parser(m_service, objAbsName, obj);
367 retval->childObjectData.insert(objName, parser.objectTree());
368 }
369
370 retval->childObjects << objName;
371 }
372
373 return QSharedDataPointer<QDBusIntrospection::ObjectTree>( retval );
374}
375
376QT_END_NAMESPACE
377
378#endif // QT_NO_DBUS
379