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 "qdbusargument_p.h" |
43 | #include "qdbusconnection.h" |
44 | #include "qdbusmetatype_p.h" |
45 | #include "qdbusutil_p.h" |
46 | |
47 | #ifndef QT_NO_DBUS |
48 | |
49 | QT_BEGIN_NAMESPACE |
50 | |
51 | static void qIterAppend(DBusMessageIter *it, QByteArray *ba, int type, const void *arg) |
52 | { |
53 | if (ba) |
54 | *ba += char(type); |
55 | else |
56 | q_dbus_message_iter_append_basic(it, type, arg); |
57 | } |
58 | |
59 | QDBusMarshaller::~QDBusMarshaller() |
60 | { |
61 | close(); |
62 | } |
63 | |
64 | inline QString QDBusMarshaller::currentSignature() |
65 | { |
66 | if (message) |
67 | return QString::fromUtf8(q_dbus_message_get_signature(message)); |
68 | return QString(); |
69 | } |
70 | |
71 | inline void QDBusMarshaller::append(uchar arg) |
72 | { |
73 | qIterAppend(&iterator, ba, DBUS_TYPE_BYTE, &arg); |
74 | } |
75 | |
76 | inline void QDBusMarshaller::append(bool arg) |
77 | { |
78 | dbus_bool_t cast = arg; |
79 | qIterAppend(&iterator, ba, DBUS_TYPE_BOOLEAN, &cast); |
80 | } |
81 | |
82 | inline void QDBusMarshaller::append(short arg) |
83 | { |
84 | qIterAppend(&iterator, ba, DBUS_TYPE_INT16, &arg); |
85 | } |
86 | |
87 | inline void QDBusMarshaller::append(ushort arg) |
88 | { |
89 | qIterAppend(&iterator, ba, DBUS_TYPE_UINT16, &arg); |
90 | } |
91 | |
92 | inline void QDBusMarshaller::append(int arg) |
93 | { |
94 | qIterAppend(&iterator, ba, DBUS_TYPE_INT32, &arg); |
95 | } |
96 | |
97 | inline void QDBusMarshaller::append(uint arg) |
98 | { |
99 | qIterAppend(&iterator, ba, DBUS_TYPE_UINT32, &arg); |
100 | } |
101 | |
102 | inline void QDBusMarshaller::append(qlonglong arg) |
103 | { |
104 | qIterAppend(&iterator, ba, DBUS_TYPE_INT64, &arg); |
105 | } |
106 | |
107 | inline void QDBusMarshaller::append(qulonglong arg) |
108 | { |
109 | qIterAppend(&iterator, ba, DBUS_TYPE_UINT64, &arg); |
110 | } |
111 | |
112 | inline void QDBusMarshaller::append(double arg) |
113 | { |
114 | qIterAppend(&iterator, ba, DBUS_TYPE_DOUBLE, &arg); |
115 | } |
116 | |
117 | void QDBusMarshaller::append(const QString &arg) |
118 | { |
119 | QByteArray data = arg.toUtf8(); |
120 | const char *cdata = data.constData(); |
121 | qIterAppend(&iterator, ba, DBUS_TYPE_STRING, &cdata); |
122 | } |
123 | |
124 | inline void QDBusMarshaller::append(const QDBusObjectPath &arg) |
125 | { |
126 | QByteArray data = arg.path().toUtf8(); |
127 | if (!ba && data.isEmpty()) |
128 | error(QLatin1String("Invalid object path passed in arguments" )); |
129 | const char *cdata = data.constData(); |
130 | qIterAppend(&iterator, ba, DBUS_TYPE_OBJECT_PATH, &cdata); |
131 | } |
132 | |
133 | inline void QDBusMarshaller::append(const QDBusSignature &arg) |
134 | { |
135 | QByteArray data = arg.signature().toUtf8(); |
136 | if (!ba && data.isEmpty()) |
137 | error(QLatin1String("Invalid signature passed in arguments" )); |
138 | const char *cdata = data.constData(); |
139 | qIterAppend(&iterator, ba, DBUS_TYPE_SIGNATURE, &cdata); |
140 | } |
141 | |
142 | inline void QDBusMarshaller::append(const QDBusUnixFileDescriptor &arg) |
143 | { |
144 | int fd = arg.fileDescriptor(); |
145 | if (!ba && fd == -1) { |
146 | error(QLatin1String("Invalid file descriptor passed in arguments" )); |
147 | } else { |
148 | qIterAppend(&iterator, ba, DBUS_TYPE_UNIX_FD, &fd); |
149 | } |
150 | } |
151 | |
152 | inline void QDBusMarshaller::append(const QByteArray &arg) |
153 | { |
154 | if (ba) { |
155 | *ba += DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING; |
156 | return; |
157 | } |
158 | |
159 | const char* cdata = arg.constData(); |
160 | DBusMessageIter subiterator; |
161 | q_dbus_message_iter_open_container(&iterator, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, |
162 | &subiterator); |
163 | q_dbus_message_iter_append_fixed_array(&subiterator, DBUS_TYPE_BYTE, &cdata, arg.length()); |
164 | q_dbus_message_iter_close_container(&iterator, &subiterator); |
165 | } |
166 | |
167 | inline bool QDBusMarshaller::append(const QDBusVariant &arg) |
168 | { |
169 | if (ba) { |
170 | *ba += DBUS_TYPE_VARIANT_AS_STRING; |
171 | return true; |
172 | } |
173 | |
174 | const QVariant &value = arg.variant(); |
175 | QVariant::Type id = QVariant::Type(value.userType()); |
176 | if (id == QVariant::Invalid) { |
177 | qWarning("QDBusMarshaller: cannot add a null QDBusVariant" ); |
178 | error(QLatin1String("Variant containing QVariant::Invalid passed in arguments" )); |
179 | return false; |
180 | } |
181 | |
182 | QByteArray tmpSignature; |
183 | const char *signature = 0; |
184 | if (int(id) == QDBusMetaTypeId::argument) { |
185 | // take the signature from the QDBusArgument object we're marshalling |
186 | tmpSignature = |
187 | qvariant_cast<QDBusArgument>(value).currentSignature().toLatin1(); |
188 | signature = tmpSignature.constData(); |
189 | } else { |
190 | // take the signatuer from the metatype we're marshalling |
191 | signature = QDBusMetaType::typeToSignature(id); |
192 | } |
193 | if (!signature) { |
194 | qWarning("QDBusMarshaller: type `%s' (%d) is not registered with D-BUS. " |
195 | "Use qDBusRegisterMetaType to register it" , |
196 | QVariant::typeToName( id ), id); |
197 | error(QString::fromLatin1("Unregistered type %1 passed in arguments" ) |
198 | .arg(QLatin1String(QVariant::typeToName(id)))); |
199 | return false; |
200 | } |
201 | |
202 | QDBusMarshaller sub(capabilities); |
203 | open(sub, DBUS_TYPE_VARIANT, signature); |
204 | bool isOk = sub.appendVariantInternal(value); |
205 | // don't call sub.close(): it auto-closes |
206 | |
207 | return isOk; |
208 | } |
209 | |
210 | inline void QDBusMarshaller::append(const QStringList &arg) |
211 | { |
212 | if (ba) { |
213 | *ba += DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING; |
214 | return; |
215 | } |
216 | |
217 | QDBusMarshaller sub(capabilities); |
218 | open(sub, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING); |
219 | QStringList::ConstIterator it = arg.constBegin(); |
220 | QStringList::ConstIterator end = arg.constEnd(); |
221 | for ( ; it != end; ++it) |
222 | sub.append(*it); |
223 | // don't call sub.close(): it auto-closes |
224 | } |
225 | |
226 | inline QDBusMarshaller *QDBusMarshaller::beginStructure() |
227 | { |
228 | return beginCommon(DBUS_TYPE_STRUCT, 0); |
229 | } |
230 | |
231 | inline QDBusMarshaller *QDBusMarshaller::beginArray(int id) |
232 | { |
233 | const char *signature = QDBusMetaType::typeToSignature( QVariant::Type(id) ); |
234 | if (!signature) { |
235 | qWarning("QDBusMarshaller: type `%s' (%d) is not registered with D-BUS. " |
236 | "Use qDBusRegisterMetaType to register it" , |
237 | QVariant::typeToName( QVariant::Type(id) ), id); |
238 | error(QString::fromLatin1("Unregistered type %1 passed in arguments" ) |
239 | .arg(QLatin1String(QVariant::typeToName(QVariant::Type(id))))); |
240 | return this; |
241 | } |
242 | |
243 | return beginCommon(DBUS_TYPE_ARRAY, signature); |
244 | } |
245 | |
246 | inline QDBusMarshaller *QDBusMarshaller::beginMap(int kid, int vid) |
247 | { |
248 | const char *ksignature = QDBusMetaType::typeToSignature( QVariant::Type(kid) ); |
249 | if (!ksignature) { |
250 | qWarning("QDBusMarshaller: type `%s' (%d) is not registered with D-BUS. " |
251 | "Use qDBusRegisterMetaType to register it" , |
252 | QVariant::typeToName( QVariant::Type(kid) ), kid); |
253 | error(QString::fromLatin1("Unregistered type %1 passed in arguments" ) |
254 | .arg(QLatin1String(QVariant::typeToName(QVariant::Type(kid))))); |
255 | return this; |
256 | } |
257 | if (ksignature[1] != 0 || !QDBusUtil::isValidBasicType(*ksignature)) { |
258 | qWarning("QDBusMarshaller: type '%s' (%d) cannot be used as the key type in a D-BUS map." , |
259 | QVariant::typeToName( QVariant::Type(kid) ), kid); |
260 | error(QString::fromLatin1("Type %1 passed in arguments cannot be used as a key in a map" ) |
261 | .arg(QLatin1String(QVariant::typeToName(QVariant::Type(kid))))); |
262 | return this; |
263 | } |
264 | |
265 | const char *vsignature = QDBusMetaType::typeToSignature( QVariant::Type(vid) ); |
266 | if (!vsignature) { |
267 | const char *typeName = QVariant::typeToName(QVariant::Type(vid)); |
268 | qWarning("QDBusMarshaller: type `%s' (%d) is not registered with D-BUS. " |
269 | "Use qDBusRegisterMetaType to register it" , |
270 | typeName, vid); |
271 | error(QString::fromLatin1("Unregistered type %1 passed in arguments" ) |
272 | .arg(QLatin1String(typeName))); |
273 | return this; |
274 | } |
275 | |
276 | QByteArray signature; |
277 | signature = DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING; |
278 | signature += ksignature; |
279 | signature += vsignature; |
280 | signature += DBUS_DICT_ENTRY_END_CHAR_AS_STRING; |
281 | return beginCommon(DBUS_TYPE_ARRAY, signature); |
282 | } |
283 | |
284 | inline QDBusMarshaller *QDBusMarshaller::beginMapEntry() |
285 | { |
286 | return beginCommon(DBUS_TYPE_DICT_ENTRY, 0); |
287 | } |
288 | |
289 | void QDBusMarshaller::open(QDBusMarshaller &sub, int code, const char *signature) |
290 | { |
291 | sub.parent = this; |
292 | sub.ba = ba; |
293 | sub.ok = true; |
294 | sub.capabilities = capabilities; |
295 | |
296 | if (ba) |
297 | switch (code) { |
298 | case DBUS_TYPE_ARRAY: |
299 | *ba += char(code); |
300 | *ba += signature; |
301 | // fall through |
302 | |
303 | case DBUS_TYPE_DICT_ENTRY: |
304 | sub.closeCode = 0; |
305 | break; |
306 | |
307 | case DBUS_TYPE_STRUCT: |
308 | *ba += DBUS_STRUCT_BEGIN_CHAR; |
309 | sub.closeCode = DBUS_STRUCT_END_CHAR; |
310 | break; |
311 | } |
312 | else |
313 | q_dbus_message_iter_open_container(&iterator, code, signature, &sub.iterator); |
314 | } |
315 | |
316 | QDBusMarshaller *QDBusMarshaller::beginCommon(int code, const char *signature) |
317 | { |
318 | QDBusMarshaller *d = new QDBusMarshaller(capabilities); |
319 | open(*d, code, signature); |
320 | return d; |
321 | } |
322 | |
323 | inline QDBusMarshaller *QDBusMarshaller::endStructure() |
324 | { return endCommon(); } |
325 | |
326 | inline QDBusMarshaller *QDBusMarshaller::endArray() |
327 | { return endCommon(); } |
328 | |
329 | inline QDBusMarshaller *QDBusMarshaller::endMap() |
330 | { return endCommon(); } |
331 | |
332 | inline QDBusMarshaller *QDBusMarshaller::endMapEntry() |
333 | { return endCommon(); } |
334 | |
335 | QDBusMarshaller *QDBusMarshaller::endCommon() |
336 | { |
337 | QDBusMarshaller *retval = parent; |
338 | delete this; |
339 | return retval; |
340 | } |
341 | |
342 | void QDBusMarshaller::close() |
343 | { |
344 | if (ba) { |
345 | if (closeCode) |
346 | *ba += closeCode; |
347 | } else if (parent) { |
348 | q_dbus_message_iter_close_container(&parent->iterator, &iterator); |
349 | } |
350 | } |
351 | |
352 | void QDBusMarshaller::error(const QString &msg) |
353 | { |
354 | ok = false; |
355 | if (parent) |
356 | parent->error(msg); |
357 | else |
358 | errorString = msg; |
359 | } |
360 | |
361 | bool QDBusMarshaller::appendVariantInternal(const QVariant &arg) |
362 | { |
363 | int id = arg.userType(); |
364 | if (id == QVariant::Invalid) { |
365 | qWarning("QDBusMarshaller: cannot add an invalid QVariant" ); |
366 | error(QLatin1String("Variant containing QVariant::Invalid passed in arguments" )); |
367 | return false; |
368 | } |
369 | |
370 | // intercept QDBusArgument parameters here |
371 | if (id == QDBusMetaTypeId::argument) { |
372 | QDBusArgument dbusargument = qvariant_cast<QDBusArgument>(arg); |
373 | QDBusArgumentPrivate *d = QDBusArgumentPrivate::d(dbusargument); |
374 | if (!d->message) |
375 | return false; // can't append this one... |
376 | |
377 | QDBusDemarshaller demarshaller(capabilities); |
378 | demarshaller.message = q_dbus_message_ref(d->message); |
379 | |
380 | if (d->direction == Demarshalling) { |
381 | // it's demarshalling; just copy |
382 | demarshaller.iterator = static_cast<QDBusDemarshaller *>(d)->iterator; |
383 | } else { |
384 | // it's marshalling; start over |
385 | if (!q_dbus_message_iter_init(demarshaller.message, &demarshaller.iterator)) |
386 | return false; // error! |
387 | } |
388 | |
389 | return appendCrossMarshalling(&demarshaller); |
390 | } |
391 | |
392 | const char *signature = QDBusMetaType::typeToSignature( QVariant::Type(id) ); |
393 | if (!signature) { |
394 | qWarning("QDBusMarshaller: type `%s' (%d) is not registered with D-BUS. " |
395 | "Use qDBusRegisterMetaType to register it" , |
396 | QVariant::typeToName( QVariant::Type(id) ), id); |
397 | error(QString::fromLatin1("Unregistered type %1 passed in arguments" ) |
398 | .arg(QLatin1String(QVariant::typeToName(QVariant::Type(id))))); |
399 | return false; |
400 | } |
401 | |
402 | switch (*signature) { |
403 | #ifdef __OPTIMIZE__ |
404 | case DBUS_TYPE_BYTE: |
405 | case DBUS_TYPE_INT16: |
406 | case DBUS_TYPE_UINT16: |
407 | case DBUS_TYPE_INT32: |
408 | case DBUS_TYPE_UINT32: |
409 | case DBUS_TYPE_INT64: |
410 | case DBUS_TYPE_UINT64: |
411 | case DBUS_TYPE_DOUBLE: |
412 | qIterAppend(&iterator, ba, *signature, arg.constData()); |
413 | return true; |
414 | case DBUS_TYPE_BOOLEAN: |
415 | append( arg.toBool() ); |
416 | return true; |
417 | #else |
418 | case DBUS_TYPE_BYTE: |
419 | append( qvariant_cast<uchar>(arg) ); |
420 | return true; |
421 | case DBUS_TYPE_BOOLEAN: |
422 | append( arg.toBool() ); |
423 | return true; |
424 | case DBUS_TYPE_INT16: |
425 | append( qvariant_cast<short>(arg) ); |
426 | return true; |
427 | case DBUS_TYPE_UINT16: |
428 | append( qvariant_cast<ushort>(arg) ); |
429 | return true; |
430 | case DBUS_TYPE_INT32: |
431 | append( static_cast<dbus_int32_t>(arg.toInt()) ); |
432 | return true; |
433 | case DBUS_TYPE_UINT32: |
434 | append( static_cast<dbus_uint32_t>(arg.toUInt()) ); |
435 | return true; |
436 | case DBUS_TYPE_INT64: |
437 | append( arg.toLongLong() ); |
438 | return true; |
439 | case DBUS_TYPE_UINT64: |
440 | append( arg.toULongLong() ); |
441 | return true; |
442 | case DBUS_TYPE_DOUBLE: |
443 | append( arg.toDouble() ); |
444 | return true; |
445 | #endif |
446 | |
447 | case DBUS_TYPE_STRING: |
448 | append( arg.toString() ); |
449 | return true; |
450 | case DBUS_TYPE_OBJECT_PATH: |
451 | append( qvariant_cast<QDBusObjectPath>(arg) ); |
452 | return true; |
453 | case DBUS_TYPE_SIGNATURE: |
454 | append( qvariant_cast<QDBusSignature>(arg) ); |
455 | return true; |
456 | |
457 | // compound types: |
458 | case DBUS_TYPE_VARIANT: |
459 | // nested QVariant |
460 | return append( qvariant_cast<QDBusVariant>(arg) ); |
461 | |
462 | case DBUS_TYPE_ARRAY: |
463 | // could be many things |
464 | // find out what kind of array it is |
465 | switch (arg.type()) { |
466 | case QVariant::StringList: |
467 | append( arg.toStringList() ); |
468 | return true; |
469 | |
470 | case QVariant::ByteArray: |
471 | append( arg.toByteArray() ); |
472 | return true; |
473 | |
474 | default: |
475 | ; // fall through |
476 | } |
477 | // fall through |
478 | |
479 | case DBUS_TYPE_STRUCT: |
480 | case DBUS_STRUCT_BEGIN_CHAR: |
481 | return appendRegisteredType( arg ); |
482 | |
483 | case DBUS_TYPE_DICT_ENTRY: |
484 | case DBUS_DICT_ENTRY_BEGIN_CHAR: |
485 | qFatal("QDBusMarshaller::appendVariantInternal got a DICT_ENTRY!" ); |
486 | return false; |
487 | |
488 | case DBUS_TYPE_UNIX_FD: |
489 | if (capabilities & QDBusConnection::UnixFileDescriptorPassing || ba) { |
490 | append(qvariant_cast<QDBusUnixFileDescriptor>(arg)); |
491 | return true; |
492 | } |
493 | // fall through |
494 | |
495 | default: |
496 | qWarning("QDBusMarshaller::appendVariantInternal: Found unknown D-BUS type '%s'" , |
497 | signature); |
498 | return false; |
499 | } |
500 | |
501 | return true; |
502 | } |
503 | |
504 | bool QDBusMarshaller::appendRegisteredType(const QVariant &arg) |
505 | { |
506 | ref.ref(); // reference up |
507 | QDBusArgument self(QDBusArgumentPrivate::create(this)); |
508 | return QDBusMetaType::marshall(self, arg.userType(), arg.constData()); |
509 | } |
510 | |
511 | bool QDBusMarshaller::appendCrossMarshalling(QDBusDemarshaller *demarshaller) |
512 | { |
513 | int code = q_dbus_message_iter_get_arg_type(&demarshaller->iterator); |
514 | if (QDBusUtil::isValidBasicType(code)) { |
515 | // easy: just append |
516 | // do exactly like the D-BUS docs suggest |
517 | // (see apidocs for q_dbus_message_iter_get_basic) |
518 | |
519 | qlonglong value; |
520 | q_dbus_message_iter_get_basic(&demarshaller->iterator, &value); |
521 | q_dbus_message_iter_next(&demarshaller->iterator); |
522 | q_dbus_message_iter_append_basic(&iterator, code, &value); |
523 | return true; |
524 | } |
525 | |
526 | if (code == DBUS_TYPE_ARRAY) { |
527 | int element = q_dbus_message_iter_get_element_type(&demarshaller->iterator); |
528 | if (QDBusUtil::isValidFixedType(element) && element != DBUS_TYPE_UNIX_FD) { |
529 | // another optimization: fixed size arrays |
530 | // code is exactly like QDBusDemarshaller::toByteArray |
531 | DBusMessageIter sub; |
532 | q_dbus_message_iter_recurse(&demarshaller->iterator, &sub); |
533 | q_dbus_message_iter_next(&demarshaller->iterator); |
534 | int len; |
535 | void* data; |
536 | q_dbus_message_iter_get_fixed_array(&sub,&data,&len); |
537 | |
538 | char signature[2] = { char(element), 0 }; |
539 | q_dbus_message_iter_open_container(&iterator, DBUS_TYPE_ARRAY, signature, &sub); |
540 | q_dbus_message_iter_append_fixed_array(&sub, element, &data, len); |
541 | q_dbus_message_iter_close_container(&iterator, &sub); |
542 | |
543 | return true; |
544 | } |
545 | } |
546 | |
547 | // We have to recurse |
548 | QDBusDemarshaller *drecursed = demarshaller->beginCommon(); |
549 | |
550 | QDBusMarshaller mrecursed(capabilities); // create on the stack makes it autoclose |
551 | QByteArray subSignature; |
552 | const char *sig = 0; |
553 | if (code == DBUS_TYPE_VARIANT || code == DBUS_TYPE_ARRAY) { |
554 | subSignature = drecursed->currentSignature().toLatin1(); |
555 | if (!subSignature.isEmpty()) |
556 | sig = subSignature.constData(); |
557 | } |
558 | open(mrecursed, code, sig); |
559 | |
560 | while (!drecursed->atEnd()) { |
561 | if (!mrecursed.appendCrossMarshalling(drecursed)) { |
562 | delete drecursed; |
563 | return false; |
564 | } |
565 | } |
566 | |
567 | delete drecursed; |
568 | return true; |
569 | } |
570 | |
571 | QT_END_NAMESPACE |
572 | |
573 | #endif // QT_NO_DBUS |
574 | |