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
qdbusutil.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
5#include "qdbusutil_p.h"
6
8
9#include <QtCore/qlist.h>
10#include <QtCore/qstringlist.h>
11#include <private/qtools_p.h>
12
13#include "qdbusargument.h"
15
16#ifndef QT_NO_DBUS
17
18QT_BEGIN_NAMESPACE
19
20using namespace Qt::StringLiterals;
21using namespace QtMiscUtils;
22
23static inline bool isValidCharacterNoDash(QChar c)
24{
25 ushort u = c.unicode();
26 return isAsciiLetterOrNumber(u) || (u == '_');
27}
28
29static inline bool isValidCharacter(QChar c)
30{
31 ushort u = c.unicode();
32 return isAsciiLetterOrNumber(u)
33 || (u == '_') || (u == '-');
34}
35
36static inline bool isValidNumber(QChar c)
37{
38 return (isAsciiDigit(c.toLatin1()));
39}
40
41#ifndef QT_BOOTSTRAPPED
42static bool argToString(const QDBusArgument &arg, QString &out);
43
44static bool variantToString(const QVariant &arg, QString &out)
45{
46 int argType = arg.metaType().id();
47
48 if (argType == QMetaType::QStringList) {
49 out += u'{';
50 const QStringList list = arg.toStringList();
51 for (const QString &item : list)
52 out += u'\"' + item + "\", "_L1;
53 if (!list.isEmpty())
54 out.chop(2);
55 out += u'}';
56 } else if (argType == QMetaType::QByteArray) {
57 out += u'{';
58 QByteArray list = arg.toByteArray();
59 for (int i = 0; i < list.size(); ++i) {
60 out += QString::number(list.at(i));
61 out += ", "_L1;
62 }
63 if (!list.isEmpty())
64 out.chop(2);
65 out += u'}';
66 } else if (argType == QMetaType::QVariantList) {
67 out += u'{';
68 const QList<QVariant> list = arg.toList();
69 for (const QVariant &item : list) {
70 if (!variantToString(item, out))
71 return false;
72 out += ", "_L1;
73 }
74 if (!list.isEmpty())
75 out.chop(2);
76 out += u'}';
77 } else if (argType == QMetaType::Char || argType == QMetaType::Short || argType == QMetaType::Int
78 || argType == QMetaType::Long || argType == QMetaType::LongLong) {
79 out += QString::number(arg.toLongLong());
80 } else if (argType == QMetaType::UChar || argType == QMetaType::UShort || argType == QMetaType::UInt
81 || argType == QMetaType::ULong || argType == QMetaType::ULongLong) {
82 out += QString::number(arg.toULongLong());
83 } else if (argType == QMetaType::Double) {
84 out += QString::number(arg.toDouble());
85 } else if (argType == QMetaType::Bool) {
86 out += arg.toBool() ? "true"_L1 : "false"_L1;
87 } else if (argType == qMetaTypeId<QDBusArgument>()) {
88 argToString(qvariant_cast<QDBusArgument>(arg), out);
89 } else if (argType == qMetaTypeId<QDBusObjectPath>()) {
90 const QString path = qvariant_cast<QDBusObjectPath>(arg).path();
91 out += "[ObjectPath: "_L1;
92 out += path;
93 out += u']';
94 } else if (argType == qMetaTypeId<QDBusSignature>()) {
95 out += "[Signature: "_L1 + qvariant_cast<QDBusSignature>(arg).signature();
96 out += u']';
97 } else if (argType == qMetaTypeId<QDBusUnixFileDescriptor>()) {
98 out += "[Unix FD: "_L1;
99 out += qvariant_cast<QDBusUnixFileDescriptor>(arg).isValid() ? "valid"_L1 : "not valid"_L1;
100 out += u']';
101 } else if (argType == qMetaTypeId<QDBusVariant>()) {
102 const QVariant v = qvariant_cast<QDBusVariant>(arg).variant();
103 out += "[Variant"_L1;
104 QMetaType vUserType = v.metaType();
105 if (vUserType != QMetaType::fromType<QDBusVariant>()
106 && vUserType != QMetaType::fromType<QDBusSignature>()
107 && vUserType != QMetaType::fromType<QDBusObjectPath>()
108 && vUserType != QMetaType::fromType<QDBusArgument>())
109 out += u'(' + QLatin1StringView(v.typeName()) + u')';
110 out += ": "_L1;
111 if (!variantToString(v, out))
112 return false;
113 out += u']';
114 } else if (arg.canConvert<QString>()) {
115 out += u'\"' + arg.toString() + u'\"';
116 } else {
117 out += u'[';
118 out += QLatin1StringView(arg.typeName());
119 out += u']';
120 }
121
122 return true;
123}
124
125bool argToString(const QDBusArgument &busArg, QString &out)
126{
127 QString busSig = busArg.currentSignature();
128 bool doIterate = false;
129 QDBusArgument::ElementType elementType = busArg.currentType();
130
131 if (elementType != QDBusArgument::BasicType && elementType != QDBusArgument::VariantType
132 && elementType != QDBusArgument::MapEntryType)
133 out += "[Argument: "_L1 + busSig + u' ';
134
135 switch (elementType) {
136 case QDBusArgument::BasicType:
137 case QDBusArgument::VariantType:
138 if (!variantToString(busArg.asVariant(), out))
139 return false;
140 break;
141 case QDBusArgument::StructureType:
142 busArg.beginStructure();
143 doIterate = true;
144 break;
145 case QDBusArgument::ArrayType:
146 busArg.beginArray();
147 out += u'{';
148 doIterate = true;
149 break;
150 case QDBusArgument::MapType:
151 busArg.beginMap();
152 out += u'{';
153 doIterate = true;
154 break;
155 case QDBusArgument::MapEntryType:
156 busArg.beginMapEntry();
157 if (!variantToString(busArg.asVariant(), out))
158 return false;
159 out += " = "_L1;
160 if (!argToString(busArg, out))
161 return false;
162 busArg.endMapEntry();
163 break;
164 case QDBusArgument::UnknownType:
165 default:
166 out += "<ERROR - Unknown Type>"_L1;
167 return false;
168 }
169 if (doIterate && !busArg.atEnd()) {
170 while (!busArg.atEnd()) {
171 if (!argToString(busArg, out))
172 return false;
173 out += ", "_L1;
174 }
175 out.chop(2);
176 }
177 switch (elementType) {
178 case QDBusArgument::BasicType:
179 case QDBusArgument::VariantType:
180 case QDBusArgument::UnknownType:
181 case QDBusArgument::MapEntryType:
182 // nothing to do
183 break;
184 case QDBusArgument::StructureType:
185 busArg.endStructure();
186 break;
187 case QDBusArgument::ArrayType:
188 out += u'}';
189 busArg.endArray();
190 break;
191 case QDBusArgument::MapType:
192 out += u'}';
193 busArg.endMap();
194 break;
195 }
196
197 if (elementType != QDBusArgument::BasicType && elementType != QDBusArgument::VariantType
198 && elementType != QDBusArgument::MapEntryType)
199 out += u']';
200
201 return true;
202}
203#endif
204
205//------- D-Bus Types --------
206static const char oneLetterTypes[] = "vsogybnqiuxtdh";
207static const char basicTypes[] = "sogybnqiuxtdh";
208static const char fixedTypes[] = "ybnqiuxtdh";
209
210/*
211 D-Bus signature grammar (in ABNF), as of 0.42 (2023-08-21):
212 https://dbus.freedesktop.org/doc/dbus-specification.html#type-system
213
214 <signature> = *<single-complete-type>
215 <single-complete-type> = <basic-type> / <variant> / <struct> / <array>
216 <fixed-type> = "y" / "b" / "n" / "q" / "i" / "u" / "x" / "t" / "d" / "h"
217 <variable-length-type> = "s" / "o" / "g"
218 <basic-type> = <variable-length-type> / <fixed-type>
219 <variant> = "v"
220 <struct> = "(" 1*<single-complete-type> ")"
221 <array> = "a" ( <single-complete-type> / <dict-entry> )
222 <dict-entry> = "{" <basic-type> <single-complete-type> "}"
223*/
224
225static bool isBasicType(int c)
226{
227 return c != DBUS_TYPE_INVALID && strchr(basicTypes, c) != nullptr;
228}
229
230static bool isFixedType(int c)
231{
232 return c != DBUS_TYPE_INVALID && strchr(fixedTypes, c) != nullptr;
233}
234
235// Returns a pointer to one-past-end of this type if it's valid;
236// returns NULL if it isn't valid.
237static const char *validateSingleType(const char *signature)
238{
239 char c = *signature;
240 if (c == DBUS_TYPE_INVALID)
241 return nullptr;
242
243 // is it one of the one-letter types?
244 if (strchr(oneLetterTypes, c) != nullptr)
245 return signature + 1;
246
247 // is it an array?
248 if (c == DBUS_TYPE_ARRAY) {
249 // then it's valid if the next type is valid
250 // or if it's a dict-entry
251 c = *++signature;
253 // beginning of a dictionary entry
254 // a dictionary entry has a key which is of basic types
255 // and a free value
256 c = *++signature;
257 if (!isBasicType(c))
258 return nullptr;
259 signature = validateSingleType(signature + 1);
260 return signature && *signature == DBUS_DICT_ENTRY_END_CHAR ? signature + 1 : nullptr;
261 }
262
263 return validateSingleType(signature);
264 }
265
266 if (c == DBUS_STRUCT_BEGIN_CHAR) {
267 // beginning of a struct
268 ++signature;
269 while (true) {
270 signature = validateSingleType(signature);
271 if (!signature)
272 return nullptr;
273 if (*signature == DBUS_STRUCT_END_CHAR)
274 return signature + 1;
275 }
276 }
277
278 // invalid/unknown type
279 return nullptr;
280}
281
282/*!
283 \namespace QDBusUtil
284 \inmodule QtDBus
285 \internal
286
287 \brief The QDBusUtil namespace contains a few functions that are of general use when
288 dealing with D-Bus strings.
289*/
290namespace QDBusUtil
291{
292 /*!
293 \internal
294 \since 4.5
295 Dumps the contents of a Qt D-Bus argument from \a arg into a string.
296 */
297 QString argumentToString(const QVariant &arg)
298 {
299 QString out;
300
301#ifndef QT_BOOTSTRAPPED
302 variantToString(arg, out);
303#else
304 Q_UNUSED(arg);
305#endif
306
307 return out;
308 }
309
310 /*!
311 \internal
312 \fn bool isValidPartOfObjectPath(QStringView part)
313 See isValidObjectPath
314 */
315 bool isValidPartOfObjectPath(QStringView part)
316 {
317 if (part.isEmpty())
318 return false; // can't be valid if it's empty
319
320 const QChar *c = part.data();
321 for (int i = 0; i < part.size(); ++i)
322 if (!isValidCharacterNoDash(c[i]))
323 return false;
324
325 return true;
326 }
327
328 /*!
329 \fn bool isValidInterfaceName(const QString &ifaceName)
330 Returns \c true if this is \a ifaceName is a valid interface name.
331
332 Valid interface names must:
333 \list
334 \li not be empty
335 \li not exceed 255 characters in length
336 \li be composed of dot-separated string components that contain only ASCII letters, digits
337 and the underscore ("_") character
338 \li contain at least two such components
339 \endlist
340 */
341 bool isValidInterfaceName(const QString& ifaceName)
342 {
343 if (ifaceName.isEmpty() || ifaceName.size() > DBUS_MAXIMUM_NAME_LENGTH)
344 return false;
345
346 const auto parts = QStringView{ifaceName}.split(u'.');
347 if (parts.size() < 2)
348 return false; // at least two parts
349
350 for (auto part : parts)
351 if (!isValidMemberName(part))
352 return false;
353
354 return true;
355 }
356
357 /*!
358 \fn bool isValidUniqueConnectionName(QStringView connName)
359 Returns \c true if \a connName is a valid unique connection name.
360
361 Unique connection names start with a colon (":") and are followed by a list of dot-separated
362 components composed of ASCII letters, digits, the hyphen or the underscore ("_") character.
363 */
364 bool isValidUniqueConnectionName(QStringView connName)
365 {
366 if (connName.isEmpty() || connName.size() > DBUS_MAXIMUM_NAME_LENGTH ||
367 !connName.startsWith(u':'))
368 return false;
369
370 const auto parts = connName.mid(1).split(u'.');
371 if (parts.size() < 1)
372 return false;
373
374 for (QStringView part : parts) {
375 if (part.isEmpty())
376 return false;
377
378 const QChar* c = part.data();
379 for (int j = 0; j < part.size(); ++j)
380 if (!isValidCharacter(c[j]))
381 return false;
382 }
383
384 return true;
385 }
386
387 /*!
388 \fn bool isValidBusName(const QString &busName)
389 Returns \c true if \a busName is a valid bus name.
390
391 A valid bus name is either a valid unique connection name or follows the rules:
392 \list
393 \li is not empty
394 \li does not exceed 255 characters in length
395 \li be composed of dot-separated string components that contain only ASCII letters, digits,
396 hyphens or underscores ("_"), but don't start with a digit
397 \li contains at least two such elements
398 \endlist
399
400 \sa isValidUniqueConnectionName()
401 */
402 bool isValidBusName(const QString &busName)
403 {
404 if (busName.isEmpty() || busName.size() > DBUS_MAXIMUM_NAME_LENGTH)
405 return false;
406
407 if (busName.startsWith(u':'))
408 return isValidUniqueConnectionName(busName);
409
410 const auto parts = QStringView{busName}.split(u'.');
411 if (parts.size() < 2)
412 return false;
413 for (QStringView part : parts) {
414 if (part.isEmpty())
415 return false;
416
417 const QChar *c = part.data();
418 if (isValidNumber(c[0]))
419 return false;
420 for (int j = 0; j < part.size(); ++j)
421 if (!isValidCharacter(c[j]))
422 return false;
423 }
424
425 return true;
426 }
427
428 /*!
429 \fn bool isValidMemberName(QStringView memberName)
430 Returns \c true if \a memberName is a valid member name. A valid member name does not exceed
431 255 characters in length, is not empty, is composed only of ASCII letters, digits and
432 underscores, but does not start with a digit.
433 */
434 bool isValidMemberName(QStringView memberName)
435 {
436 if (memberName.isEmpty() || memberName.size() > DBUS_MAXIMUM_NAME_LENGTH)
437 return false;
438
439 const QChar* c = memberName.data();
440 if (isValidNumber(c[0]))
441 return false;
442 for (int j = 0; j < memberName.size(); ++j)
443 if (!isValidCharacterNoDash(c[j]))
444 return false;
445 return true;
446 }
447
448 /*!
449 \fn bool isValidErrorName(const QString &errorName)
450 Returns \c true if \a errorName is a valid error name. Valid error names are valid interface
451 names and vice-versa, so this function is actually an alias for isValidInterfaceName.
452 */
453 bool isValidErrorName(const QString &errorName)
454 {
455 return isValidInterfaceName(errorName);
456 }
457
458 /*!
459 \fn bool isValidObjectPath(const QString &path)
460 Returns \c true if \a path is valid object path.
461
462 Valid object paths follow the rules:
463 \list
464 \li start with the slash character ("/")
465 \li do not end in a slash, unless the path is just the initial slash
466 \li contain slash-separated parts, each of which is not empty, and composed
467 only of ASCII letters, digits and underscores ("_").
468 \endlist
469 */
470 bool isValidObjectPath(const QString &path)
471 {
472 if (path == "/"_L1)
473 return true;
474
475 if (!path.startsWith(u'/') || path.indexOf("//"_L1) != -1 ||
476 path.endsWith(u'/'))
477 return false;
478
479 // it starts with /, so we skip the empty first part
480 const auto parts = QStringView{path}.mid(1).split(u'/');
481 for (QStringView part : parts)
482 if (!isValidPartOfObjectPath(part))
483 return false;
484
485 return true;
486 }
487
488 /*!
489 \fn bool isValidBasicType(int type)
490 Returns \c true if \a c is a valid, basic D-Bus type.
491 */
493 {
494 return isBasicType(c);
495 }
496
497 /*!
498 \fn bool isValidFixedType(int type)
499 Returns \c true if \a c is a valid, fixed D-Bus type.
500 */
502 {
503 return isFixedType(c);
504 }
505
506
507 /*!
508 \fn bool isValidSignature(const QString &signature)
509 Returns \c true if \a signature is a valid D-Bus type signature for one or more types.
510 This function returns \c true if it can all of \a signature into valid, individual types and no
511 characters remain in \a signature.
512
513 \sa isValidSingleSignature()
514 */
515 bool isValidSignature(const QString &signature)
516 {
517 QByteArray ba = signature.toLatin1();
518 const char *data = ba.constBegin();
519 const char *end = ba.constEnd();
520 while (data != end) {
521 data = validateSingleType(data);
522 if (!data)
523 return false;
524 }
525 return true;
526 }
527
528 /*!
529 \fn bool isValidSingleSignature(const QString &signature)
530 Returns \c true if \a signature is a valid D-Bus type signature for exactly one full type. This
531 function tries to convert the type signature into a D-Bus type and, if it succeeds and no
532 characters remain in the signature, it returns \c true.
533 */
534 bool isValidSingleSignature(const QString &signature)
535 {
536 QByteArray ba = signature.toLatin1();
537 const char *data = validateSingleType(ba.constData());
538 return data && *data == '\0';
539 }
540
541} // namespace QDBusUtil
542
543QT_END_NAMESPACE
544
545#endif // QT_NO_DBUS
\inmodule QtDBus
#define DBUS_DICT_ENTRY_END_CHAR
#define DBUS_STRUCT_BEGIN_CHAR
#define DBUS_MAXIMUM_NAME_LENGTH
#define DBUS_TYPE_ARRAY
#define DBUS_TYPE_INVALID
#define DBUS_DICT_ENTRY_BEGIN_CHAR
#define DBUS_STRUCT_END_CHAR
\inmodule QtDBus
Definition qdbusutil_p.h:33
Q_DBUS_EXPORT bool isValidFixedType(int c)
Returns true if c is a valid, fixed D-Bus type.
Q_DBUS_EXPORT bool isValidMemberName(QStringView memberName)
Returns true if memberName is a valid member name.
Q_DBUS_EXPORT bool isValidPartOfObjectPath(QStringView path)
Q_DBUS_EXPORT bool isValidBasicType(int c)
Returns true if c is a valid, basic D-Bus type.
Q_DBUS_EXPORT bool isValidUniqueConnectionName(QStringView busName)
Returns true if connName is a valid unique connection name.
static bool argToString(const QDBusArgument &arg, QString &out)
static bool isFixedType(int c)
static const char oneLetterTypes[]
static const char basicTypes[]
static const char fixedTypes[]
static bool variantToString(const QVariant &arg, QString &out)
Definition qdbusutil.cpp:44
static const char * validateSingleType(const char *signature)
static bool isBasicType(int c)
static bool isValidCharacterNoDash(QChar c)
Definition qdbusutil.cpp:23
static bool isValidCharacter(QChar c)
Definition qdbusutil.cpp:29
static bool isValidNumber(QChar c)
Definition qdbusutil.cpp:36