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 | |
53 | QT_BEGIN_NAMESPACE |
54 | |
55 | static 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 | |
64 | static 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 | |
73 | static inline bool isValidNumber(const QChar &c) |
74 | { |
75 | register ushort u = c.unicode(); |
76 | return (u >= '0' && u <= '9'); |
77 | } |
78 | |
79 | static bool argToString(const QDBusArgument &arg, QString &out); |
80 | |
81 | static 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 | |
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 | |
241 | //------- D-Bus Types -------- |
242 | static const char oneLetterTypes[] = "vsogybnqiuxtdh" ; |
243 | static const char basicTypes[] = "sogybnqiuxtdh" ; |
244 | static const char fixedTypes[] = "ybnqiuxtdh" ; |
245 | |
246 | static bool isBasicType(int c) |
247 | { |
248 | return c != DBUS_TYPE_INVALID && strchr(basicTypes, c) != NULL; |
249 | } |
250 | |
251 | static 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. |
258 | static 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 | */ |
311 | namespace 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 | |
566 | QT_END_NAMESPACE |
567 | |
568 | #endif // QT_NO_DBUS |
569 | |