Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qdbusxmlparser.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:critical reason:data-parser
4
6#include "qdbusutil_p.h"
7
8#include <QtCore/qmap.h>
9#include <QtCore/qvariant.h>
10#include <QtCore/qtextstream.h>
11#include <QtCore/qdebug.h>
12
13#ifndef QT_NO_DBUS
14
16
17using namespace Qt::StringLiterals;
18
19Q_LOGGING_CATEGORY(dbusParser, "dbus.parser", QtWarningMsg)
20
21#define qDBusParserWarning(format, ...)
22 do {
23 if (m_reporter)
24 m_reporter->warning(m_currentLocation, format "\n", ##__VA_ARGS__);
25 else
26 qCDebug(dbusParser, "Warning: " format, ##__VA_ARGS__);
27 } while (0)
28
29#define qDBusParserError(format, ...)
30 do {
31 if (m_reporter)
32 m_reporter->error(m_currentLocation, format "\n", ##__VA_ARGS__);
33 else
34 qCDebug(dbusParser, "Error: " format, ##__VA_ARGS__);
35 } while (0)
36
37bool QDBusXmlParser::parseArg(const QXmlStreamAttributes &attributes,
38 QDBusIntrospection::Argument &argData)
39{
40 Q_ASSERT(m_currentInterface);
41
42 const QString argType = attributes.value("type"_L1).toString();
43
44 bool ok = QDBusUtil::isValidSingleSignature(argType);
45 if (!ok) {
46 qDBusParserError("Invalid D-Bus type signature '%s' found while parsing introspection",
47 qPrintable(argType));
48 }
49
50 argData.name = attributes.value("name"_L1).toString();
51 argData.type = argType;
52
53 m_currentInterface->introspection += " <arg"_L1;
54 if (attributes.hasAttribute("direction"_L1)) {
55 const QString direction = attributes.value("direction"_L1).toString();
56 m_currentInterface->introspection += " direction=\""_L1 + direction + u'"';
57 }
58 m_currentInterface->introspection += " type=\""_L1 + argData.type + u'"';
59 if (!argData.name.isEmpty())
60 m_currentInterface->introspection += " name=\""_L1 + argData.name + u'"';
61 m_currentInterface->introspection += "/>\n"_L1;
62
63 return ok;
64}
65
66bool QDBusXmlParser::parseAnnotation(QDBusIntrospection::Annotations &annotations,
67 bool interfaceAnnotation)
68{
69 Q_ASSERT(m_currentInterface);
70 Q_ASSERT(m_xml.isStartElement() && m_xml.name() == "annotation"_L1);
71
72 QDBusIntrospection::Annotation annotation;
73 annotation.location = m_currentLocation;
74
75 const QXmlStreamAttributes attributes = m_xml.attributes();
76 annotation.name = attributes.value("name"_L1).toString();
77
78 if (!QDBusUtil::isValidInterfaceName(annotation.name)) {
79 qDBusParserError("Invalid D-Bus annotation '%s' found while parsing introspection",
80 qPrintable(annotation.name));
81 return false;
82 }
83 annotation.value = attributes.value("value"_L1).toString();
84 annotations.insert(annotation.name, annotation);
85 if (!interfaceAnnotation)
86 m_currentInterface->introspection += " "_L1;
87 m_currentInterface->introspection += " <annotation value=\""_L1
88 + annotation.value.toHtmlEscaped() + "\" name=\""_L1 + annotation.name + "\"/>\n"_L1;
89 return true;
90}
91
92bool QDBusXmlParser::parseProperty(QDBusIntrospection::Property &propertyData)
93{
94 Q_ASSERT(m_currentInterface);
95 Q_ASSERT(m_xml.isStartElement() && m_xml.name() == "property"_L1);
96
97 QXmlStreamAttributes attributes = m_xml.attributes();
98 const QString propertyName = attributes.value("name"_L1).toString();
99 if (!QDBusUtil::isValidMemberName(propertyName)) {
100 qDBusParserWarning("Invalid D-Bus member name '%s' found in interface '%s' while parsing "
101 "introspection",
102 qPrintable(propertyName), qPrintable(m_currentInterface->name));
103 m_xml.skipCurrentElement();
104 return false;
105 }
106
107 // parse data
108 propertyData.name = propertyName;
109 propertyData.type = attributes.value("type"_L1).toString();
110
111 if (!QDBusUtil::isValidSingleSignature(propertyData.type)) {
112 // cannot be!
113 qDBusParserError("Invalid D-Bus type signature '%s' found in property '%s.%s' while "
114 "parsing introspection",
115 qPrintable(propertyData.type), qPrintable(m_currentInterface->name),
116 qPrintable(propertyName));
117 }
118
119 const QString access = attributes.value("access"_L1).toString();
120 if (access == "read"_L1)
121 propertyData.access = QDBusIntrospection::Property::Read;
122 else if (access == "write"_L1)
123 propertyData.access = QDBusIntrospection::Property::Write;
124 else if (access == "readwrite"_L1)
125 propertyData.access = QDBusIntrospection::Property::ReadWrite;
126 else {
127 qDBusParserError("Invalid D-Bus property access '%s' found in property '%s.%s' while "
128 "parsing introspection",
129 qPrintable(access), qPrintable(m_currentInterface->name),
130 qPrintable(propertyName));
131 return false; // invalid one!
132 }
133
134 m_currentInterface->introspection += " <property access=\""_L1 + access + "\" type=\""_L1 + propertyData.type + "\" name=\""_L1 + propertyName + u'"';
135
136 if (!readNextStartElement()) {
137 m_currentInterface->introspection += "/>\n"_L1;
138 } else {
139 m_currentInterface->introspection += ">\n"_L1;
140
141 do {
142 if (m_xml.name() == "annotation"_L1) {
143 parseAnnotation(propertyData.annotations);
144 } else if (m_xml.prefix().isEmpty()) {
145 qDBusParserWarning("Unknown element '%s' while checking for annotations",
146 qPrintable(m_xml.name().toString()));
147 }
148 m_xml.skipCurrentElement();
149 } while (readNextStartElement());
150
151 m_currentInterface->introspection += " </property>\n"_L1;
152 }
153
154 if (!m_xml.isEndElement() || m_xml.name() != "property"_L1) {
155 qDBusParserError("Invalid property specification: '%s'", qPrintable(m_xml.tokenString()));
156 return false;
157 }
158
159 return true;
160}
161
162bool QDBusXmlParser::parseMethod(QDBusIntrospection::Method &methodData)
163{
164 Q_ASSERT(m_currentInterface);
165 Q_ASSERT(m_xml.isStartElement() && m_xml.name() == "method"_L1);
166
167 const QXmlStreamAttributes attributes = m_xml.attributes();
168 const QString methodName = attributes.value("name"_L1).toString();
169 if (!QDBusUtil::isValidMemberName(methodName)) {
170 qDBusParserError("Invalid D-Bus member name '%s' found in interface '%s' while parsing "
171 "introspection",
172 qPrintable(methodName), qPrintable(m_currentInterface->name));
173 return false;
174 }
175
176 methodData.name = methodName;
177 m_currentInterface->introspection += " <method name=\""_L1 + methodName + u'"';
178
179 QDBusIntrospection::Arguments outArguments;
180 QDBusIntrospection::Arguments inArguments;
181 QDBusIntrospection::Annotations annotations;
182
183 if (!readNextStartElement()) {
184 m_currentInterface->introspection += "/>\n"_L1;
185 } else {
186 m_currentInterface->introspection += ">\n"_L1;
187
188 do {
189 if (m_xml.name() == "annotation"_L1) {
190 parseAnnotation(annotations);
191 } else if (m_xml.name() == "arg"_L1) {
192 const QXmlStreamAttributes attributes = m_xml.attributes();
193 const QString direction = attributes.value("direction"_L1).toString();
194 QDBusIntrospection::Argument argument;
195 argument.location = m_currentLocation;
196 if (!attributes.hasAttribute("direction"_L1) || direction == "in"_L1) {
197 parseArg(attributes, argument);
198 inArguments << argument;
199 } else if (direction == "out"_L1) {
200 parseArg(attributes, argument);
201 outArguments << argument;
202 }
203 } else if (m_xml.prefix().isEmpty()) {
204 qDBusParserWarning("Unknown element '%s' while checking for method arguments",
205 qPrintable(m_xml.name().toString()));
206 }
207 m_xml.skipCurrentElement();
208 } while (readNextStartElement());
209
210 m_currentInterface->introspection += " </method>\n"_L1;
211 }
212
213 methodData.inputArgs = inArguments;
214 methodData.outputArgs = outArguments;
215 methodData.annotations = annotations;
216
217 return true;
218}
219
220bool QDBusXmlParser::parseSignal(QDBusIntrospection::Signal &signalData)
221{
222 Q_ASSERT(m_currentInterface);
223 Q_ASSERT(m_xml.isStartElement() && m_xml.name() == "signal"_L1);
224
225 const QXmlStreamAttributes attributes = m_xml.attributes();
226 const QString signalName = attributes.value("name"_L1).toString();
227
228 if (!QDBusUtil::isValidMemberName(signalName)) {
229 qDBusParserError("Invalid D-Bus member name '%s' found in interface '%s' while parsing "
230 "introspection",
231 qPrintable(signalName), qPrintable(m_currentInterface->name));
232 return false;
233 }
234
235 signalData.name = signalName;
236 m_currentInterface->introspection += " <signal name=\""_L1 + signalName + u'"';
237
238 QDBusIntrospection::Arguments arguments;
239 QDBusIntrospection::Annotations annotations;
240
241 if (!readNextStartElement()) {
242 m_currentInterface->introspection += "/>\n"_L1;
243 } else {
244 m_currentInterface->introspection += ">\n"_L1;
245
246 do {
247 if (m_xml.name() == "annotation"_L1) {
248 parseAnnotation(annotations);
249 } else if (m_xml.name() == "arg"_L1) {
250 const QXmlStreamAttributes attributes = m_xml.attributes();
251 QDBusIntrospection::Argument argument;
252 argument.location = m_currentLocation;
253 if (!attributes.hasAttribute("direction"_L1) ||
254 attributes.value("direction"_L1) == "out"_L1) {
255 parseArg(attributes, argument);
256 arguments << argument;
257 }
258 } else {
259 qDBusParserWarning("Unknown element '%s' while checking for signal arguments",
260 qPrintable(m_xml.name().toString()));
261 }
262 m_xml.skipCurrentElement();
263 } while (readNextStartElement());
264
265 m_currentInterface->introspection += " </signal>\n"_L1;
266 }
267
268 signalData.outputArgs = arguments;
269 signalData.annotations = annotations;
270
271 return true;
272}
273
274void QDBusXmlParser::readInterface()
275{
276 Q_ASSERT(!m_currentInterface);
277
278 const QString ifaceName = m_xml.attributes().value("name"_L1).toString();
279 if (!QDBusUtil::isValidInterfaceName(ifaceName)) {
280 qDBusParserError("Invalid D-Bus interface name '%s' found while parsing introspection",
281 qPrintable(ifaceName));
282 return;
283 }
284
285 m_object->interfaces.append(ifaceName);
286
287 m_currentInterface = std::make_unique<QDBusIntrospection::Interface>();
288 m_currentInterface->location = m_currentLocation;
289 m_currentInterface->name = ifaceName;
290 m_currentInterface->introspection += " <interface name=\""_L1 + ifaceName + "\">\n"_L1;
291
292 while (readNextStartElement()) {
293 if (m_xml.name() == "method"_L1) {
294 QDBusIntrospection::Method methodData;
295 methodData.location = m_currentLocation;
296 if (parseMethod(methodData))
297 m_currentInterface->methods.insert(methodData.name, methodData);
298 } else if (m_xml.name() == "signal"_L1) {
299 QDBusIntrospection::Signal signalData;
300 signalData.location = m_currentLocation;
301 if (parseSignal(signalData))
302 m_currentInterface->signals_.insert(signalData.name, signalData);
303 } else if (m_xml.name() == "property"_L1) {
304 QDBusIntrospection::Property propertyData;
305 propertyData.location = m_currentLocation;
306 if (parseProperty(propertyData))
307 m_currentInterface->properties.insert(propertyData.name, propertyData);
308 } else if (m_xml.name() == "annotation"_L1) {
309 parseAnnotation(m_currentInterface->annotations, true);
310 m_xml.skipCurrentElement(); // skip over annotation object
311 } else {
312 if (m_xml.prefix().isEmpty()) {
313 qDBusParserWarning("Unknown element '%s' while parsing interface",
314 qPrintable(m_xml.name().toString()));
315 }
316 m_xml.skipCurrentElement();
317 }
318 }
319
320 m_currentInterface->introspection += " </interface>"_L1;
321
322 m_interfaces.insert(
323 ifaceName,
324 QSharedDataPointer<QDBusIntrospection::Interface>(m_currentInterface.release()));
325
326 if (!m_xml.isEndElement() || m_xml.name() != "interface"_L1) {
327 qDBusParserError("Invalid Interface specification");
328 }
329}
330
331void QDBusXmlParser::readNode(int nodeLevel)
332{
333 const QString objName = m_xml.attributes().value("name"_L1).toString();
334 QString fullName = m_object->path;
335 if (!(fullName.endsWith(u'/') || (nodeLevel == 0 && objName.startsWith(u'/'))))
336 fullName.append(u'/');
337 fullName += objName;
338
339 if (!QDBusUtil::isValidObjectPath(fullName)) {
340 qDBusParserError("Invalid D-Bus object path '%s' found while parsing introspection",
341 qPrintable(fullName));
342 return;
343 }
344
345 if (nodeLevel > 0)
346 m_object->childObjects.append(objName);
347 else
348 m_object->location = m_currentLocation;
349}
350
351void QDBusXmlParser::updateCurrentLocation()
352{
353 m_currentLocation =
354 QDBusIntrospection::SourceLocation{ m_xml.lineNumber(), m_xml.columnNumber() };
355}
356
357// Similar to m_xml.readNextElement() but sets current location to point
358// to the start element.
359bool QDBusXmlParser::readNextStartElement()
360{
361 updateCurrentLocation();
362
363 while (m_xml.readNext() != QXmlStreamReader::Invalid) {
364 if (m_xml.isEndElement())
365 return false;
366 else if (m_xml.isStartElement())
367 return true;
368 updateCurrentLocation();
369 }
370 return false;
371}
372
373QDBusXmlParser::QDBusXmlParser(const QString &service, const QString &path, const QString &xmlData,
374 QDBusIntrospection::DiagnosticsReporter *reporter)
375 : m_service(service),
376 m_path(path),
377 m_object(new QDBusIntrospection::Object),
378 m_xml(xmlData),
379 m_reporter(reporter)
380{
381 m_object->service = m_service;
382 m_object->path = m_path;
383
384 int nodeLevel = -1;
385
386 while (!m_xml.atEnd()) {
387 updateCurrentLocation();
388 m_xml.readNext();
389
390 switch (m_xml.tokenType()) {
391 case QXmlStreamReader::StartElement:
392 if (m_xml.name() == "node"_L1) {
393 readNode(++nodeLevel);
394 } else if (m_xml.name() == "interface"_L1) {
395 readInterface();
396 } else {
397 if (m_xml.prefix().isEmpty()) {
398 qDBusParserWarning("Skipping unknown element '%s'",
399 qPrintable(m_xml.name().toString()));
400 }
401 m_xml.skipCurrentElement();
402 }
403 break;
404 case QXmlStreamReader::EndElement:
405 if (m_xml.name() == "node"_L1) {
406 --nodeLevel;
407 } else {
408 qDBusParserError("Invalid node declaration '%s'",
409 qPrintable(m_xml.name().toString()));
410 }
411 break;
412 case QXmlStreamReader::StartDocument:
413 case QXmlStreamReader::EndDocument:
414 case QXmlStreamReader::DTD:
415 // not interested
416 break;
417 case QXmlStreamReader::Comment:
418 // ignore comments and processing instructions
419 break;
420 case QXmlStreamReader::Characters:
421 // ignore whitespace
422 if (m_xml.isWhitespace())
423 break;
424 Q_FALLTHROUGH();
425 default:
426 qDBusParserError("Unknown token: '%s'", qPrintable(m_xml.tokenString()));
427 break;
428 }
429 }
430
431 if (m_xml.hasError())
432 qDBusParserError("XML error: %s", qPrintable(m_xml.errorString()));
433}
434
435QT_END_NAMESPACE
436
437#endif // QT_NO_DBUS
#define qDBusParserError(format,...)
#define qDBusParserWarning(format,...)
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")