1/****************************************************************************
2**
3** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4** Contact: http://www.qt-project.org/legal
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 Digia. For licensing terms and
14** conditions see http://qt.digia.com/licensing. For further information
15** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 2.1 requirements
23** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24**
25** In addition, as a special exception, Digia gives you certain additional
26** rights. These rights are described in the Digia Qt LGPL Exception
27** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28**
29** GNU General Public License Usage
30** Alternatively, this file may be used under the terms of the GNU
31** General Public License version 3.0 as published by the Free Software
32** Foundation and appearing in the file LICENSE.GPL included in the
33** packaging of this file. Please review the following information to
34** ensure the GNU General Public License version 3.0 requirements will be
35** met: http://www.gnu.org/copyleft/gpl.html.
36**
37**
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qdbusutil_p.h"
43
44#include "qdbus_symbols_p.h"
45
46#include <QtCore/qstringlist.h>
47
48#include "qdbusargument.h"
49#include "qdbusunixfiledescriptor.h"
50
51#ifndef QT_NO_DBUS
52
53QT_BEGIN_NAMESPACE
54
55static inline bool isValidCharacterNoDash(const QChar &c)
56{
57 register ushort u = c.unicode();
58 return (u >= 'a' && u <= 'z')
59 || (u >= 'A' && u <= 'Z')
60 || (u >= '0' && u <= '9')
61 || (u == '_');
62}
63
64static inline bool isValidCharacter(const QChar &c)
65{
66 register ushort u = c.unicode();
67 return (u >= 'a' && u <= 'z')
68 || (u >= 'A' && u <= 'Z')
69 || (u >= '0' && u <= '9')
70 || (u == '_') || (u == '-');
71}
72
73static inline bool isValidNumber(const QChar &c)
74{
75 register ushort u = c.unicode();
76 return (u >= '0' && u <= '9');
77}
78
79static bool argToString(const QDBusArgument &arg, QString &out);
80
81static bool variantToString(const QVariant &arg, QString &out)
82{
83 int argType = arg.userType();
84
85 if (argType == QVariant::StringList) {
86 out += QLatin1Char('{');
87 QStringList list = arg.toStringList();
88 foreach (QString item, list)
89 out += QLatin1Char('\"') + item + QLatin1String("\", ");
90 if (!list.isEmpty())
91 out.chop(2);
92 out += QLatin1Char('}');
93 } else if (argType == QVariant::ByteArray) {
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 == QVariant::List) {
104 out += QLatin1Char('{');
105 QList<QVariant> list = arg.toList();
106 foreach (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(QVariant::String)) {
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
241//------- D-Bus Types --------
242static const char oneLetterTypes[] = "vsogybnqiuxtdh";
243static const char basicTypes[] = "sogybnqiuxtdh";
244static const char fixedTypes[] = "ybnqiuxtdh";
245
246static bool isBasicType(int c)
247{
248 return c != DBUS_TYPE_INVALID && strchr(basicTypes, c) != NULL;
249}
250
251static bool isFixedType(int c)
252{
253 return c != DBUS_TYPE_INVALID && strchr(fixedTypes, c) != NULL;
254}
255
256// Returns a pointer to one-past-end of this type if it's valid;
257// returns NULL if it isn't valid.
258static const char *validateSingleType(const char *signature)
259{
260 register char c = *signature;
261 if (c == DBUS_TYPE_INVALID)
262 return 0;
263
264 // is it one of the one-letter types?
265 if (strchr(oneLetterTypes, c) != NULL)
266 return signature + 1;
267
268 // is it an array?
269 if (c == DBUS_TYPE_ARRAY) {
270 // then it's valid if the next type is valid
271 // or if it's a dict-entry
272 c = *++signature;
273 if (c == DBUS_DICT_ENTRY_BEGIN_CHAR) {
274 // beginning of a dictionary entry
275 // a dictionary entry has a key which is of basic types
276 // and a free value
277 c = *++signature;
278 if (!isBasicType(c))
279 return 0;
280 signature = validateSingleType(signature + 1);
281 return signature && *signature == DBUS_DICT_ENTRY_END_CHAR ? signature + 1 : 0;
282 }
283
284 return validateSingleType(signature);
285 }
286
287 if (c == DBUS_STRUCT_BEGIN_CHAR) {
288 // beginning of a struct
289 ++signature;
290 while (true) {
291 signature = validateSingleType(signature);
292 if (!signature)
293 return 0;
294 if (*signature == DBUS_STRUCT_END_CHAR)
295 return signature + 1;
296 }
297 }
298
299 // invalid/unknown type
300 return 0;
301}
302
303/*!
304 \namespace QDBusUtil
305 \inmodule QtDBus
306 \internal
307
308 \brief The QDBusUtil namespace contains a few functions that are of general use when
309 dealing with D-Bus strings.
310*/
311namespace QDBusUtil
312{
313 /*!
314 \internal
315 \since 4.5
316 Dumps the contents of a QtDBus argument from \a arg into a string.
317 */
318 QString argumentToString(const QVariant &arg)
319 {
320 QString out;
321
322 variantToString(arg, out);
323
324 return out;
325 }
326
327 /*!
328 \internal
329 \fn bool QDBusUtil::isValidPartOfObjectPath(const QString &part)
330 See QDBusUtil::isValidObjectPath
331 */
332 bool isValidPartOfObjectPath(const QString &part)
333 {
334 if (part.isEmpty())
335 return false; // can't be valid if it's empty
336
337 const QChar *c = part.unicode();
338 for (int i = 0; i < part.length(); ++i)
339 if (!isValidCharacterNoDash(c[i]))
340 return false;
341
342 return true;
343 }
344
345 /*!
346 \fn bool QDBusUtil::isValidInterfaceName(const QString &ifaceName)
347 Returns true if this is \a ifaceName is a valid interface name.
348
349 Valid interface names must:
350 \list
351 \o not be empty
352 \o not exceed 255 characters in length
353 \o be composed of dot-separated string components that contain only ASCII letters, digits
354 and the underscore ("_") character
355 \o contain at least two such components
356 \endlist
357 */
358 bool isValidInterfaceName(const QString& ifaceName)
359 {
360 if (ifaceName.isEmpty() || ifaceName.length() > DBUS_MAXIMUM_NAME_LENGTH)
361 return false;
362
363 QStringList parts = ifaceName.split(QLatin1Char('.'));
364 if (parts.count() < 2)
365 return false; // at least two parts
366
367 for (int i = 0; i < parts.count(); ++i)
368 if (!isValidMemberName(parts.at(i)))
369 return false;
370
371 return true;
372 }
373
374 /*!
375 \fn bool QDBusUtil::isValidUniqueConnectionName(const QString &connName)
376 Returns true if \a connName is a valid unique connection name.
377
378 Unique connection names start with a colon (":") and are followed by a list of dot-separated
379 components composed of ASCII letters, digits, the hyphen or the underscore ("_") character.
380 */
381 bool isValidUniqueConnectionName(const QString &connName)
382 {
383 if (connName.isEmpty() || connName.length() > DBUS_MAXIMUM_NAME_LENGTH ||
384 !connName.startsWith(QLatin1Char(':')))
385 return false;
386
387 QStringList parts = connName.mid(1).split(QLatin1Char('.'));
388 if (parts.count() < 1)
389 return false;
390
391 for (int i = 0; i < parts.count(); ++i) {
392 const QString &part = parts.at(i);
393 if (part.isEmpty())
394 return false;
395
396 const QChar* c = part.unicode();
397 for (int j = 0; j < part.length(); ++j)
398 if (!isValidCharacter(c[j]))
399 return false;
400 }
401
402 return true;
403 }
404
405 /*!
406 \fn bool QDBusUtil::isValidBusName(const QString &busName)
407 Returns true if \a busName is a valid bus name.
408
409 A valid bus name is either a valid unique connection name or follows the rules:
410 \list
411 \o is not empty
412 \o does not exceed 255 characters in length
413 \o be composed of dot-separated string components that contain only ASCII letters, digits,
414 hyphens or underscores ("_"), but don't start with a digit
415 \o contains at least two such elements
416 \endlist
417
418 \sa isValidUniqueConnectionName()
419 */
420 bool isValidBusName(const QString &busName)
421 {
422 if (busName.isEmpty() || busName.length() > DBUS_MAXIMUM_NAME_LENGTH)
423 return false;
424
425 if (busName.startsWith(QLatin1Char(':')))
426 return isValidUniqueConnectionName(busName);
427
428 QStringList parts = busName.split(QLatin1Char('.'));
429 if (parts.count() < 1)
430 return false;
431
432 for (int i = 0; i < parts.count(); ++i) {
433 const QString &part = parts.at(i);
434 if (part.isEmpty())
435 return false;
436
437 const QChar *c = part.unicode();
438 if (isValidNumber(c[0]))
439 return false;
440 for (int j = 0; j < part.length(); ++j)
441 if (!isValidCharacter(c[j]))
442 return false;
443 }
444
445 return true;
446 }
447
448 /*!
449 \fn bool QDBusUtil::isValidMemberName(const QString &memberName)
450 Returns true if \a memberName is a valid member name. A valid member name does not exceed
451 255 characters in length, is not empty, is composed only of ASCII letters, digits and
452 underscores, but does not start with a digit.
453 */
454 bool isValidMemberName(const QString &memberName)
455 {
456 if (memberName.isEmpty() || memberName.length() > DBUS_MAXIMUM_NAME_LENGTH)
457 return false;
458
459 const QChar* c = memberName.unicode();
460 if (isValidNumber(c[0]))
461 return false;
462 for (int j = 0; j < memberName.length(); ++j)
463 if (!isValidCharacterNoDash(c[j]))
464 return false;
465 return true;
466 }
467
468 /*!
469 \fn bool QDBusUtil::isValidErrorName(const QString &errorName)
470 Returns true if \a errorName is a valid error name. Valid error names are valid interface
471 names and vice-versa, so this function is actually an alias for isValidInterfaceName.
472 */
473 bool isValidErrorName(const QString &errorName)
474 {
475 return isValidInterfaceName(errorName);
476 }
477
478 /*!
479 \fn bool QDBusUtil::isValidObjectPath(const QString &path)
480 Returns true if \a path is valid object path.
481
482 Valid object paths follow the rules:
483 \list
484 \o start with the slash character ("/")
485 \o do not end in a slash, unless the path is just the initial slash
486 \o do not contain any two slashes in sequence
487 \o contain slash-separated parts, each of which is composed of ASCII letters, digits and
488 underscores ("_")
489 \endlist
490 */
491 bool isValidObjectPath(const QString &path)
492 {
493 if (path == QLatin1String("/"))
494 return true;
495
496 if (!path.startsWith(QLatin1Char('/')) || path.indexOf(QLatin1String("//")) != -1 ||
497 path.endsWith(QLatin1Char('/')))
498 return false;
499
500 QStringList parts = path.split(QLatin1Char('/'));
501 Q_ASSERT(parts.count() >= 1);
502 parts.removeFirst(); // it starts with /, so we get an empty first part
503
504 for (int i = 0; i < parts.count(); ++i)
505 if (!isValidPartOfObjectPath(parts.at(i)))
506 return false;
507
508 return true;
509 }
510
511 /*!
512 \fn bool QDBusUtil::isValidBasicType(int type)
513 Returns true if \a c is a valid, basic D-Bus type.
514 */
515 bool isValidBasicType(int c)
516 {
517 return isBasicType(c);
518 }
519
520 /*!
521 \fn bool QDBusUtil::isValidFixedType(int type)
522 Returns true if \a c is a valid, fixed D-Bus type.
523 */
524 bool isValidFixedType(int c)
525 {
526 return isFixedType(c);
527 }
528
529
530 /*!
531 \fn bool QDBusUtil::isValidSignature(const QString &signature)
532 Returns true if \a signature is a valid D-Bus type signature for one or more types.
533 This function returns true if it can all of \a signature into valid, individual types and no
534 characters remain in \a signature.
535
536 \sa isValidSingleSignature()
537 */
538 bool isValidSignature(const QString &signature)
539 {
540 QByteArray ba = signature.toLatin1();
541 const char *data = ba.constData();
542 while (true) {
543 data = validateSingleType(data);
544 if (!data)
545 return false;
546 if (*data == '\0')
547 return true;
548 }
549 }
550
551 /*!
552 \fn bool QDBusUtil::isValidSingleSignature(const QString &signature)
553 Returns true if \a signature is a valid D-Bus type signature for exactly one full type. This
554 function tries to convert the type signature into a D-Bus type and, if it succeeds and no
555 characters remain in the signature, it returns true.
556 */
557 bool isValidSingleSignature(const QString &signature)
558 {
559 QByteArray ba = signature.toLatin1();
560 const char *data = validateSingleType(ba.constData());
561 return data && *data == '\0';
562 }
563
564} // namespace QDBusUtil
565
566QT_END_NAMESPACE
567
568#endif // QT_NO_DBUS
569