Merge remote-tracking branch 'origin/api_changes'
[profile/ivi/qtbase.git] / src / dbus / qdbusxmlparser.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtDBus module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qdbusxmlparser_p.h"
43 #include "qdbusutil_p.h"
44
45 #include <QtCore/qmap.h>
46 #include <QtCore/qvariant.h>
47 #include <QtCore/qtextstream.h>
48 #include <QtCore/qxmlstream.h>
49 #include <QtCore/qdebug.h>
50
51 #ifndef QT_NO_DBUS
52
53 //#define QDBUS_PARSER_DEBUG
54 #ifdef QDBUS_PARSER_DEBUG
55 # define qDBusParserError qDebug
56 #else
57 # define qDBusParserError if (true) {} else qDebug
58 #endif
59
60 QT_BEGIN_NAMESPACE
61
62 static bool parseArg(const QXmlStreamAttributes &attributes, QDBusIntrospection::Argument &argData)
63 {
64     const QString argType = attributes.value(QLatin1String("type")).toString();
65
66     bool ok = QDBusUtil::isValidSingleSignature(argType);
67     if (!ok) {
68         qDBusParserError("Invalid D-BUS type signature '%s' found while parsing introspection",
69                 qPrintable(argType));
70     }
71
72     argData.name = attributes.value(QLatin1String("name")).toString();
73     argData.type = argType;
74
75     return ok;
76 }
77
78 static bool parseAnnotation(const QXmlStreamReader &xml, QDBusIntrospection::Annotations &annotations)
79 {
80     Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("annotation"));
81
82     const QXmlStreamAttributes attributes = xml.attributes();
83     const QString name = attributes.value(QLatin1String("name")).toString();
84
85     if (!QDBusUtil::isValidInterfaceName(name)) {
86         qDBusParserError("Invalid D-BUS annotation '%s' found while parsing introspection",
87                 qPrintable(name));
88         return false;
89     }
90     annotations.insert(name, attributes.value(QLatin1String("value")).toString());
91     return true;
92 }
93
94 static bool parseProperty(QXmlStreamReader &xml, QDBusIntrospection::Property &propertyData,
95                 const QString &ifaceName)
96 {
97     Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("property"));
98
99     QXmlStreamAttributes attributes = xml.attributes();
100     const QString propertyName = attributes.value(QLatin1String("name")).toString();
101     if (!QDBusUtil::isValidMemberName(propertyName)) {
102         qDBusParserError("Invalid D-BUS member name '%s' found in interface '%s' while parsing introspection",
103                 qPrintable(propertyName), qPrintable(ifaceName));
104         xml.skipCurrentElement();
105         return false;
106     }
107
108     // parse data
109     propertyData.name = propertyName;
110     propertyData.type = attributes.value(QLatin1String("type")).toString();
111
112     if (!QDBusUtil::isValidSingleSignature(propertyData.type)) {
113         // cannot be!
114         qDBusParserError("Invalid D-BUS type signature '%s' found in property '%s.%s' while parsing introspection",
115                 qPrintable(propertyData.type), qPrintable(ifaceName),
116                 qPrintable(propertyName));
117     }
118
119     const QString access = attributes.value(QLatin1String("access")).toString();
120     if (access == QLatin1String("read"))
121         propertyData.access = QDBusIntrospection::Property::Read;
122     else if (access == QLatin1String("write"))
123         propertyData.access = QDBusIntrospection::Property::Write;
124     else if (access == QLatin1String("readwrite"))
125         propertyData.access = QDBusIntrospection::Property::ReadWrite;
126     else {
127         qDBusParserError("Invalid D-BUS property access '%s' found in property '%s.%s' while parsing introspection",
128                 qPrintable(access), qPrintable(ifaceName),
129                 qPrintable(propertyName));
130         return false;       // invalid one!
131     }
132
133     while (xml.readNextStartElement()) {
134         if (xml.name() == QLatin1String("annotation")) {
135             parseAnnotation(xml, propertyData.annotations);
136         } else if (xml.prefix().isEmpty()) {
137             qDBusParserError() << "Unknown element" << xml.name() << "while checking for annotations";
138         }
139         xml.skipCurrentElement();
140     }
141
142     if (!xml.isEndElement() || xml.name() != QLatin1String("property")) {
143         qDBusParserError() << "Invalid property specification" << xml.tokenString() << xml.name();
144         return false;
145     }
146
147     return true;
148 }
149
150 static bool parseMethod(QXmlStreamReader &xml, QDBusIntrospection::Method &methodData,
151         const QString &ifaceName)
152 {
153     Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("method"));
154
155     const QXmlStreamAttributes attributes = xml.attributes();
156     const QString methodName = attributes.value(QLatin1String("name")).toString();
157     if (!QDBusUtil::isValidMemberName(methodName)) {
158         qDBusParserError("Invalid D-BUS member name '%s' found in interface '%s' while parsing introspection",
159                 qPrintable(methodName), qPrintable(ifaceName));
160         return false;
161     }
162
163     methodData.name = methodName;
164
165     QDBusIntrospection::Arguments outArguments;
166     QDBusIntrospection::Arguments inArguments;
167     QDBusIntrospection::Annotations annotations;
168
169     while (xml.readNextStartElement()) {
170         if (xml.name() == QLatin1String("annotation")) {
171             parseAnnotation(xml, annotations);
172         } else if (xml.name() == QLatin1String("arg")) {
173             const QXmlStreamAttributes attributes = xml.attributes();
174             const QString direction = attributes.value(QLatin1String("direction")).toString();
175             QDBusIntrospection::Argument argument;
176             if (!attributes.hasAttribute(QLatin1String("direction"))
177                     || direction == QLatin1String("in")) {
178                 parseArg(attributes, argument);
179                 inArguments << argument;
180             } else if (direction == QLatin1String("out")) {
181                 parseArg(attributes, argument);
182                 outArguments << argument;
183             }
184         } else if (xml.prefix().isEmpty()) {
185             qDBusParserError() << "Unknown element" << xml.name() << "while checking for method arguments";
186         }
187         xml.skipCurrentElement();
188     }
189
190     methodData.inputArgs = inArguments;
191     methodData.outputArgs = outArguments;
192     methodData.annotations = annotations;
193
194     return true;
195 }
196
197
198 static bool parseSignal(QXmlStreamReader &xml, QDBusIntrospection::Signal &signalData,
199         const QString &ifaceName)
200 {
201     Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("signal"));
202
203     const QXmlStreamAttributes attributes = xml.attributes();
204     const QString signalName = attributes.value(QLatin1String("name")).toString();
205
206     if (!QDBusUtil::isValidMemberName(signalName)) {
207         qDBusParserError("Invalid D-BUS member name '%s' found in interface '%s' while parsing introspection",
208                 qPrintable(signalName), qPrintable(ifaceName));
209         return false;
210     }
211
212     signalData.name = signalName;
213
214
215     QDBusIntrospection::Arguments arguments;
216     QDBusIntrospection::Annotations annotations;
217
218     while (xml.readNextStartElement()) {
219         if (xml.name() == QLatin1String("annotation")) {
220             parseAnnotation(xml, annotations);
221         } else if (xml.name() == QLatin1String("arg")) {
222             const QXmlStreamAttributes attributes = xml.attributes();
223             QDBusIntrospection::Argument argument;
224             if (!attributes.hasAttribute(QLatin1String("direction")) ||
225                 attributes.value(QLatin1String("direction")) == QLatin1String("out")) {
226                 parseArg(attributes, argument);
227                 arguments << argument;
228             }
229         } else {
230             qDBusParserError() << "Unknown element" << xml.name() << "while checking for signal arguments";
231         }
232         xml.skipCurrentElement();
233     }
234
235     signalData.outputArgs = arguments;
236     signalData.annotations = annotations;
237
238     return true;
239 }
240
241 static void readInterface(QXmlStreamReader &xml, QDBusIntrospection::Object *objData,
242         QDBusIntrospection::Interfaces *interfaces)
243 {
244     const QString ifaceName = xml.attributes().value(QLatin1String("name")).toString();
245     if (!QDBusUtil::isValidInterfaceName(ifaceName)) {
246         qDBusParserError("Invalid D-BUS interface name '%s' found while parsing introspection",
247                 qPrintable(ifaceName));
248         return;
249     }
250
251     objData->interfaces.append(ifaceName);
252
253     QDBusIntrospection::Interface *ifaceData = new QDBusIntrospection::Interface;
254     ifaceData->name = ifaceName;
255
256     while (xml.readNextStartElement()) {
257         if (xml.name() == QLatin1String("method")) {
258             QDBusIntrospection::Method methodData;
259             if (parseMethod(xml, methodData, ifaceName))
260                 ifaceData->methods.insert(methodData.name, methodData);
261         } else if (xml.name() == QLatin1String("signal")) {
262             QDBusIntrospection::Signal signalData;
263             if (parseSignal(xml, signalData, ifaceName))
264                 ifaceData->signals_.insert(signalData.name, signalData);
265         } else if (xml.name() == QLatin1String("property")) {
266             QDBusIntrospection::Property propertyData;
267             if (parseProperty(xml, propertyData, ifaceName))
268                 ifaceData->properties.insert(propertyData.name, propertyData);
269         } else if (xml.name() == QLatin1String("annotation")) {
270             parseAnnotation(xml, ifaceData->annotations);
271             xml.skipCurrentElement(); // skip over annotation object
272         } else {
273             if (xml.prefix().isEmpty()) {
274                 qDBusParserError() << "Unknown element while parsing interface" << xml.name();
275             }
276             xml.skipCurrentElement();
277         }
278     }
279
280     interfaces->insert(ifaceName, QSharedDataPointer<QDBusIntrospection::Interface>(ifaceData));
281
282     if (!xml.isEndElement() || xml.name() != QLatin1String("interface")) {
283         qDBusParserError() << "Invalid Interface specification";
284     }
285 }
286
287 static void readNode(const QXmlStreamReader &xml, QDBusIntrospection::Object *objData, int nodeLevel)
288 {
289     const QString objName = xml.attributes().value(QLatin1String("name")).toString();
290     const QString fullName = objData->path.endsWith(QLatin1Char('/'))
291                                 ? (objData->path + objName)
292                                 : QString(objData->path + QLatin1Char('/') + objName);
293     if (!QDBusUtil::isValidObjectPath(fullName)) {
294         qDBusParserError("Invalid D-BUS object path '%s' found while parsing introspection",
295                  qPrintable(fullName));
296         return;
297     }
298
299     if (nodeLevel > 0)
300         objData->childObjects.append(objName);
301 }
302
303 QDBusXmlParser::QDBusXmlParser(const QString& service, const QString& path,
304                                const QString& xmlData)
305     : m_service(service), m_path(path), m_object(new QDBusIntrospection::Object)
306 {
307 //    qDBusParserError() << "parsing" << xmlData;
308
309     m_object->service = m_service;
310     m_object->path = m_path;
311
312     QXmlStreamReader xml(xmlData);
313
314     int nodeLevel = -1;
315
316     while (!xml.atEnd()) {
317         xml.readNext();
318
319         switch (xml.tokenType()) {
320         case QXmlStreamReader::StartElement:
321             if (xml.name() == QLatin1String("node")) {
322                 readNode(xml, m_object, ++nodeLevel);
323             } else if (xml.name() == QLatin1String("interface")) {
324                 readInterface(xml, m_object, &m_interfaces);
325             } else {
326                 if (xml.prefix().isEmpty()) {
327                     qDBusParserError() << "skipping unknown element" << xml.name();
328                 }
329                 xml.skipCurrentElement();
330             }
331             break;
332         case QXmlStreamReader::EndElement:
333             if (xml.name() == QLatin1String("node")) {
334                 --nodeLevel;
335             } else {
336                 qDBusParserError() << "Invalid Node declaration" << xml.name();
337             }
338             break;
339         case QXmlStreamReader::StartDocument:
340         case QXmlStreamReader::EndDocument:
341         case QXmlStreamReader::DTD:
342             // not interested
343             break;
344         case QXmlStreamReader::Comment:
345             // ignore comments and processing instructions
346             break;
347         default:
348             qDBusParserError() << "unknown token" << xml.name() << xml.tokenString();
349             break;
350         }
351     }
352
353     if (xml.hasError()) {
354         qDBusParserError() << "xml error" << xml.errorString() << "doc" << xmlData;
355     }
356 }
357
358 QT_END_NAMESPACE
359
360 #endif // QT_NO_DBUS