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