Qt 6.x
The Qt SDK
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
4#include "qdbusutil_p.h"
5
6#include "qdbus_symbols_p.h"
7
8#include <QtCore/qlist.h>
9#include <QtCore/qstringlist.h>
10#include <private/qtools_p.h>
11
12#include "qdbusargument.h"
14
15#ifndef QT_NO_DBUS
16
18
19using namespace Qt::StringLiterals;
20using namespace QtMiscUtils;
21
22static inline bool isValidCharacterNoDash(QChar c)
23{
24 ushort u = c.unicode();
25 return isAsciiLetterOrNumber(u) || (u == '_');
26}
27
28static inline bool isValidCharacter(QChar c)
29{
30 ushort u = c.unicode();
31 return isAsciiLetterOrNumber(u)
32 || (u == '_') || (u == '-');
33}
34
35static inline bool isValidNumber(QChar c)
36{
37 return (isAsciiDigit(c.toLatin1()));
38}
39
40#ifndef QT_BOOTSTRAPPED
41static bool argToString(const QDBusArgument &arg, QString &out);
42
43static bool variantToString(const QVariant &arg, QString &out)
44{
45 int argType = arg.metaType().id();
46
47 if (argType == QMetaType::QStringList) {
48 out += u'{';
49 const QStringList list = arg.toStringList();
50 for (const QString &item : list)
51 out += u'\"' + item + "\", "_L1;
52 if (!list.isEmpty())
53 out.chop(2);
54 out += u'}';
55 } else if (argType == QMetaType::QByteArray) {
56 out += u'{';
57 QByteArray list = arg.toByteArray();
58 for (int i = 0; i < list.size(); ++i) {
60 out += ", "_L1;
61 }
62 if (!list.isEmpty())
63 out.chop(2);
64 out += u'}';
65 } else if (argType == QMetaType::QVariantList) {
66 out += u'{';
67 const QList<QVariant> list = arg.toList();
68 for (const QVariant &item : list) {
70 return false;
71 out += ", "_L1;
72 }
73 if (!list.isEmpty())
74 out.chop(2);
75 out += u'}';
76 } else if (argType == QMetaType::Char || argType == QMetaType::Short || argType == QMetaType::Int
77 || argType == QMetaType::Long || argType == QMetaType::LongLong) {
78 out += QString::number(arg.toLongLong());
79 } else if (argType == QMetaType::UChar || argType == QMetaType::UShort || argType == QMetaType::UInt
80 || argType == QMetaType::ULong || argType == QMetaType::ULongLong) {
81 out += QString::number(arg.toULongLong());
82 } else if (argType == QMetaType::Double) {
83 out += QString::number(arg.toDouble());
84 } else if (argType == QMetaType::Bool) {
85 out += arg.toBool() ? "true"_L1 : "false"_L1;
86 } else if (argType == qMetaTypeId<QDBusArgument>()) {
87 argToString(qvariant_cast<QDBusArgument>(arg), out);
88 } else if (argType == qMetaTypeId<QDBusObjectPath>()) {
89 const QString path = qvariant_cast<QDBusObjectPath>(arg).path();
90 out += "[ObjectPath: "_L1;
91 out += path;
92 out += u']';
93 } else if (argType == qMetaTypeId<QDBusSignature>()) {
94 out += "[Signature: "_L1 + qvariant_cast<QDBusSignature>(arg).signature();
95 out += u']';
96 } else if (argType == qMetaTypeId<QDBusUnixFileDescriptor>()) {
97 out += "[Unix FD: "_L1;
98 out += qvariant_cast<QDBusUnixFileDescriptor>(arg).isValid() ? "valid"_L1 : "not valid"_L1;
99 out += u']';
100 } else if (argType == qMetaTypeId<QDBusVariant>()) {
101 const QVariant v = qvariant_cast<QDBusVariant>(arg).variant();
102 out += "[Variant"_L1;
103 QMetaType vUserType = v.metaType();
104 if (vUserType != QMetaType::fromType<QDBusVariant>()
105 && vUserType != QMetaType::fromType<QDBusSignature>()
106 && vUserType != QMetaType::fromType<QDBusObjectPath>()
107 && vUserType != QMetaType::fromType<QDBusArgument>())
108 out += u'(' + QLatin1StringView(v.typeName()) + u')';
109 out += ": "_L1;
110 if (!variantToString(v, out))
111 return false;
112 out += u']';
113 } else if (arg.canConvert<QString>()) {
114 out += u'\"' + arg.toString() + u'\"';
115 } else {
116 out += u'[';
117 out += QLatin1StringView(arg.typeName());
118 out += u']';
119 }
120
121 return true;
122}
123
125{
126 QString busSig = busArg.currentSignature();
127 bool doIterate = false;
128 QDBusArgument::ElementType elementType = busArg.currentType();
129
130 if (elementType != QDBusArgument::BasicType && elementType != QDBusArgument::VariantType
131 && elementType != QDBusArgument::MapEntryType)
132 out += "[Argument: "_L1 + busSig + u' ';
133
134 switch (elementType) {
137 if (!variantToString(busArg.asVariant(), out))
138 return false;
139 break;
141 busArg.beginStructure();
142 doIterate = true;
143 break;
145 busArg.beginArray();
146 out += u'{';
147 doIterate = true;
148 break;
150 busArg.beginMap();
151 out += u'{';
152 doIterate = true;
153 break;
155 busArg.beginMapEntry();
156 if (!variantToString(busArg.asVariant(), out))
157 return false;
158 out += " = "_L1;
159 if (!argToString(busArg, out))
160 return false;
161 busArg.endMapEntry();
162 break;
164 default:
165 out += "<ERROR - Unknown Type>"_L1;
166 return false;
167 }
168 if (doIterate && !busArg.atEnd()) {
169 while (!busArg.atEnd()) {
170 if (!argToString(busArg, out))
171 return false;
172 out += ", "_L1;
173 }
174 out.chop(2);
175 }
176 switch (elementType) {
181 // nothing to do
182 break;
184 busArg.endStructure();
185 break;
187 out += u'}';
188 busArg.endArray();
189 break;
191 out += u'}';
192 busArg.endMap();
193 break;
194 }
195
196 if (elementType != QDBusArgument::BasicType && elementType != QDBusArgument::VariantType
197 && elementType != QDBusArgument::MapEntryType)
198 out += u']';
199
200 return true;
201}
202#endif
203
204//------- D-Bus Types --------
205static const char oneLetterTypes[] = "vsogybnqiuxtdh";
206static const char basicTypes[] = "sogybnqiuxtdh";
207static const char fixedTypes[] = "ybnqiuxtdh";
208
209static bool isBasicType(int c)
210{
211 return c != DBUS_TYPE_INVALID && strchr(basicTypes, c) != nullptr;
212}
213
214static bool isFixedType(int c)
215{
216 return c != DBUS_TYPE_INVALID && strchr(fixedTypes, c) != nullptr;
217}
218
219// Returns a pointer to one-past-end of this type if it's valid;
220// returns NULL if it isn't valid.
221static const char *validateSingleType(const char *signature)
222{
223 char c = *signature;
224 if (c == DBUS_TYPE_INVALID)
225 return nullptr;
226
227 // is it one of the one-letter types?
228 if (strchr(oneLetterTypes, c) != nullptr)
229 return signature + 1;
230
231 // is it an array?
232 if (c == DBUS_TYPE_ARRAY) {
233 // then it's valid if the next type is valid
234 // or if it's a dict-entry
235 c = *++signature;
237 // beginning of a dictionary entry
238 // a dictionary entry has a key which is of basic types
239 // and a free value
240 c = *++signature;
241 if (!isBasicType(c))
242 return nullptr;
243 signature = validateSingleType(signature + 1);
244 return signature && *signature == DBUS_DICT_ENTRY_END_CHAR ? signature + 1 : nullptr;
245 }
246
247 return validateSingleType(signature);
248 }
249
250 if (c == DBUS_STRUCT_BEGIN_CHAR) {
251 // beginning of a struct
252 ++signature;
253 while (true) {
254 signature = validateSingleType(signature);
255 if (!signature)
256 return nullptr;
257 if (*signature == DBUS_STRUCT_END_CHAR)
258 return signature + 1;
259 }
260 }
261
262 // invalid/unknown type
263 return nullptr;
264}
265
274namespace QDBusUtil
275{
282 {
283 QString out;
284
285#ifndef QT_BOOTSTRAPPED
287#else
288 Q_UNUSED(arg);
289#endif
290
291 return out;
292 }
293
300 {
301 if (part.isEmpty())
302 return false; // can't be valid if it's empty
303
304 const QChar *c = part.data();
305 for (int i = 0; i < part.size(); ++i)
307 return false;
308
309 return true;
310 }
311
332 bool isValidInterfaceName(const QString& ifaceName)
333 {
334 if (ifaceName.isEmpty() || ifaceName.size() > DBUS_MAXIMUM_NAME_LENGTH)
335 return false;
336
337 const auto parts = QStringView{ifaceName}.split(u'.');
338 if (parts.size() < 2)
339 return false; // at least two parts
340
341 for (auto part : parts)
342 if (!isValidMemberName(part))
343 return false;
344
345 return true;
346 }
347
356 {
357 if (connName.isEmpty() || connName.size() > DBUS_MAXIMUM_NAME_LENGTH ||
358 !connName.startsWith(u':'))
359 return false;
360
361 const auto parts = connName.mid(1).split(u'.');
362 if (parts.size() < 1)
363 return false;
364
365 for (QStringView part : parts) {
366 if (part.isEmpty())
367 return false;
368
369 const QChar* c = part.data();
370 for (int j = 0; j < part.size(); ++j)
371 if (!isValidCharacter(c[j]))
372 return false;
373 }
374
375 return true;
376 }
377
399 bool isValidBusName(const QString &busName)
400 {
401 if (busName.isEmpty() || busName.size() > DBUS_MAXIMUM_NAME_LENGTH)
402 return false;
403
404 if (busName.startsWith(u':'))
405 return isValidUniqueConnectionName(busName);
406
407 const auto parts = QStringView{busName}.split(u'.');
408 if (parts.size() < 1)
409 return false;
410
411 for (QStringView part : parts) {
412 if (part.isEmpty())
413 return false;
414
415 const QChar *c = part.data();
416 if (isValidNumber(c[0]))
417 return false;
418 for (int j = 0; j < part.size(); ++j)
419 if (!isValidCharacter(c[j]))
420 return false;
421 }
422
423 return true;
424 }
425
433 {
434 if (memberName.isEmpty() || memberName.size() > DBUS_MAXIMUM_NAME_LENGTH)
435 return false;
436
437 const QChar* c = memberName.data();
438 if (isValidNumber(c[0]))
439 return false;
440 for (int j = 0; j < memberName.size(); ++j)
442 return false;
443 return true;
444 }
445
457 bool isValidErrorName(const QString &errorName)
458 {
459 return isValidInterfaceName(errorName);
460 }
461
476 {
477 if (path == "/"_L1)
478 return true;
479
480 if (!path.startsWith(u'/') || path.indexOf("//"_L1) != -1 ||
481 path.endsWith(u'/'))
482 return false;
483
484 // it starts with /, so we skip the empty first part
485 const auto parts = QStringView{path}.mid(1).split(u'/');
486 for (QStringView part : parts)
487 if (!isValidPartOfObjectPath(part))
488 return false;
489
490 return true;
491 }
492
498 {
499 return isBasicType(c);
500 }
501
507 {
508 return isFixedType(c);
509 }
510
511
520 bool isValidSignature(const QString &signature)
521 {
522 QByteArray ba = signature.toLatin1();
523 const char *data = ba.constData();
524 while (true) {
526 if (!data)
527 return false;
528 if (*data == '\0')
529 return true;
530 }
531 }
532
539 bool isValidSingleSignature(const QString &signature)
540 {
541 QByteArray ba = signature.toLatin1();
542 const char *data = validateSingleType(ba.constData());
543 return data && *data == '\0';
544 }
545
546} // namespace QDBusUtil
547
549
550#endif // QT_NO_DBUS
\inmodule QtCore
Definition qbytearray.h:57
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
\inmodule QtCore
Definition qchar.h:48
\inmodule QtDBus
void beginMapEntry()
Opens a D-Bus map entry suitable for appending the key and value entries.
void endArray()
Closes a D-Bus array opened with beginArray().
QVariant asVariant() const
void endMapEntry()
Closes a D-Bus map entry opened with beginMapEntry().
void beginStructure()
Opens a new D-Bus structure suitable for appending new arguments.
void endMap()
Closes a D-Bus map opened with beginMap().
bool atEnd() const
Returns true if there are no more elements to be extracted from this QDBusArgument.
QString currentSignature() const
void endStructure()
Closes a D-Bus structure opened with beginStructure().
ElementType currentType() const
void beginArray(int elementMetaTypeId)
void beginMap(int keyMetaTypeId, int valueMetaTypeId)
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
bool isEmpty() const noexcept
Definition qlist.h:390
const_reference at(qsizetype i) const noexcept
Definition qlist.h:429
\inmodule QtCore
Definition qmetatype.h:320
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:76
bool startsWith(QStringView s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
constexpr bool isEmpty() const noexcept
Returns whether this string view is empty - that is, whether {size() == 0}.
constexpr qsizetype size() const noexcept
Returns the size of this string view, in UTF-16 code units (that is, surrogate pairs count as two for...
const_pointer data() const noexcept
Q_CORE_EXPORT QList< QStringView > split(QStringView sep, Qt::SplitBehavior behavior=Qt::KeepEmptyParts, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Splits the view into substring views wherever sep occurs, and returns the list of those string views.
Definition qstring.cpp:7987
constexpr QStringView mid(qsizetype pos, qsizetype n=-1) const noexcept
Returns the substring of length length starting at position start in this object.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
Definition qstring.cpp:5299
QStringList split(const QString &sep, Qt::SplitBehavior behavior=Qt::KeepEmptyParts, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Splits the string into substrings wherever sep occurs, and returns the list of those strings.
Definition qstring.cpp:7956
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:7822
\inmodule QtCore
Definition qvariant.h:64
#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
bool isValidMemberName(QStringView memberName)
Returns true if memberName is a valid member name.
bool isValidInterfaceName(const QString &ifaceName)
Returns true if this is ifaceName is a valid interface name.
bool isValidErrorName(const QString &errorName)
Returns true if errorName is a valid error name.
bool isValidPartOfObjectPath(QStringView part)
bool isValidUniqueConnectionName(QStringView connName)
Returns true if connName is a valid unique connection name.
bool isValidBusName(const QString &busName)
Returns true if busName is a valid bus name.
bool isValidObjectPath(const QString &path)
Returns true if path is valid object path.
QString argumentToString(const QVariant &arg)
bool isValidBasicType(int c)
Returns true if c is a valid, basic D-Bus type.
bool isValidSignature(const QString &signature)
Returns true if signature is a valid D-Bus type signature for one or more types.
bool isValidFixedType(int c)
Returns true if c is a valid, fixed D-Bus type.
bool isValidSingleSignature(const QString &signature)
Returns true if signature is a valid D-Bus type signature for exactly one full type.
Combined button and popup list for selecting options.
constexpr bool isAsciiDigit(char32_t c) noexcept
Definition qtools_p.h:67
constexpr bool isAsciiLetterOrNumber(char32_t c) noexcept
Definition qtools_p.h:82
static bool argToString(const QDBusArgument &arg, QString &out)
static bool isFixedType(int c)
static const char basicTypes[]
static bool variantToString(const QVariant &arg, QString &out)
Definition qdbusutil.cpp:43
static const char * validateSingleType(const char *signature)
static bool isBasicType(int c)
static const char fixedTypes[]
static bool isValidCharacterNoDash(QChar c)
Definition qdbusutil.cpp:22
static const char oneLetterTypes[]
static bool isValidCharacter(QChar c)
Definition qdbusutil.cpp:28
static bool isValidNumber(QChar c)
Definition qdbusutil.cpp:35
GLsizei const GLfloat * v
[13]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
const GLubyte * c
GLsizei const GLchar *const * path
SSL_CTX int(*) void arg)
#define Q_UNUSED(x)
unsigned short ushort
Definition qtypes.h:28
QList< int > list
[14]
QByteArray ba
[0]
QTextStream out(stdout)
[7]
QGraphicsItem * item