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/qstringlist.h>
45 #include <QtCore/qvector.h>
46 
47 #include "qdbusargument.h"
48 #include "qdbusunixfiledescriptor.h"
49 
50 #ifndef QT_NO_DBUS
51 
52 QT_BEGIN_NAMESPACE
53 
isValidCharacterNoDash(QChar c)54 static 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 
isValidCharacter(QChar c)63 static 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 
isValidNumber(QChar c)72 static inline bool isValidNumber(QChar c)
73 {
74     ushort u = c.unicode();
75     return (u >= '0' && u <= '9');
76 }
77 
78 #ifndef QT_BOOTSTRAPPED
79 static bool argToString(const QDBusArgument &arg, QString &out);
80 
variantToString(const QVariant & arg,QString & out)81 static bool variantToString(const QVariant &arg, QString &out)
82 {
83     int argType = arg.userType();
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         int vUserType = v.userType();
142         if (vUserType != qMetaTypeId<QDBusVariant>()
143                 && vUserType != qMetaTypeId<QDBusSignature>()
144                 && vUserType != qMetaTypeId<QDBusObjectPath>()
145                 && vUserType != qMetaTypeId<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(QMetaType::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 
argToString(const QDBusArgument & busArg,QString & out)162 bool 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 --------
243 static const char oneLetterTypes[] = "vsogybnqiuxtdh";
244 static const char basicTypes[] =      "sogybnqiuxtdh";
245 static const char fixedTypes[] =         "ybnqiuxtdh";
246 
isBasicType(int c)247 static bool isBasicType(int c)
248 {
249     return c != DBUS_TYPE_INVALID && strchr(basicTypes, c) != nullptr;
250 }
251 
isFixedType(int c)252 static 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.
validateSingleType(const char * signature)259 static 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 */
312 namespace 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     */
argumentToString(const QVariant & arg)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(const QStringRef &part)
335         See isValidObjectPath
336     */
isValidPartOfObjectPath(const QStringRef & part)337     bool isValidPartOfObjectPath(const QStringRef &part)
338     {
339         if (part.isEmpty())
340             return false;       // can't be valid if it's empty
341 
342         const QChar *c = part.unicode();
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     */
isValidInterfaceName(const QString & ifaceName)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 = ifaceName.splitRef(QLatin1Char('.'));
376         if (parts.count() < 2)
377             return false;           // at least two parts
378 
379         for (const QStringRef &part : parts)
380             if (!isValidMemberName(part))
381                 return false;
382 
383         return true;
384     }
385 
386     /*!
387         \fn bool isValidUniqueConnectionName(const QStringRef &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     */
isValidUniqueConnectionName(const QStringRef & connName)393     bool isValidUniqueConnectionName(const QStringRef &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 (const QStringRef &part : parts) {
404             if (part.isEmpty())
405                  return false;
406 
407             const QChar* c = part.unicode();
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     */
isValidBusName(const QString & busName)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 = busName.splitRef(QLatin1Char('.'));
446         if (parts.count() < 1)
447             return false;
448 
449         for (const QStringRef &part : parts) {
450             if (part.isEmpty())
451                 return false;
452 
453             const QChar *c = part.unicode();
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(const QStringRef &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     */
isValidMemberName(const QStringRef & memberName)470     bool isValidMemberName(const QStringRef &memberName)
471     {
472         if (memberName.isEmpty() || memberName.length() > DBUS_MAXIMUM_NAME_LENGTH)
473             return false;
474 
475         const QChar* c = memberName.unicode();
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     */
isValidErrorName(const QString & errorName)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     */
isValidObjectPath(const QString & path)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 = path.midRef(1).split(QLatin1Char('/'));
524         for (const QStringRef &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      */
isValidBasicType(int c)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      */
isValidFixedType(int c)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     */
isValidSignature(const QString & signature)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     */
isValidSingleSignature(const QString & signature)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 
586 QT_END_NAMESPACE
587 
588 #endif // QT_NO_DBUS
589