source: trunk/src/dbus/qdbusmarshaller.cpp@ 651

Last change on this file since 651 was 651, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.2 sources.

File size: 17.3 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the QtDBus module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qdbusargument_p.h"
43#include "qdbusutil_p.h"
44
45QT_BEGIN_NAMESPACE
46
47static void qIterAppend(DBusMessageIter *it, QByteArray *ba, int type, const void *arg)
48{
49 if (ba)
50 *ba += char(type);
51 else
52 q_dbus_message_iter_append_basic(it, type, arg);
53}
54
55QDBusMarshaller::~QDBusMarshaller()
56{
57 close();
58}
59
60inline QString QDBusMarshaller::currentSignature()
61{
62 if (message)
63 return QString::fromUtf8(q_dbus_message_get_signature(message));
64 return QString();
65}
66
67inline void QDBusMarshaller::append(uchar arg)
68{
69 qIterAppend(&iterator, ba, DBUS_TYPE_BYTE, &arg);
70}
71
72inline void QDBusMarshaller::append(bool arg)
73{
74 dbus_bool_t cast = arg;
75 qIterAppend(&iterator, ba, DBUS_TYPE_BOOLEAN, &cast);
76}
77
78inline void QDBusMarshaller::append(short arg)
79{
80 qIterAppend(&iterator, ba, DBUS_TYPE_INT16, &arg);
81}
82
83inline void QDBusMarshaller::append(ushort arg)
84{
85 qIterAppend(&iterator, ba, DBUS_TYPE_UINT16, &arg);
86}
87
88inline void QDBusMarshaller::append(int arg)
89{
90 qIterAppend(&iterator, ba, DBUS_TYPE_INT32, &arg);
91}
92
93inline void QDBusMarshaller::append(uint arg)
94{
95 qIterAppend(&iterator, ba, DBUS_TYPE_UINT32, &arg);
96}
97
98inline void QDBusMarshaller::append(qlonglong arg)
99{
100 qIterAppend(&iterator, ba, DBUS_TYPE_INT64, &arg);
101}
102
103inline void QDBusMarshaller::append(qulonglong arg)
104{
105 qIterAppend(&iterator, ba, DBUS_TYPE_UINT64, &arg);
106}
107
108inline void QDBusMarshaller::append(double arg)
109{
110 qIterAppend(&iterator, ba, DBUS_TYPE_DOUBLE, &arg);
111}
112
113void QDBusMarshaller::append(const QString &arg)
114{
115 QByteArray data = arg.toUtf8();
116 const char *cdata = data.constData();
117 qIterAppend(&iterator, ba, DBUS_TYPE_STRING, &cdata);
118}
119
120inline void QDBusMarshaller::append(const QDBusObjectPath &arg)
121{
122 QByteArray data = arg.path().toUtf8();
123 if (!ba && data.isEmpty())
124 error(QLatin1String("Invalid object path passed in arguments"));
125 const char *cdata = data.constData();
126 qIterAppend(&iterator, ba, DBUS_TYPE_OBJECT_PATH, &cdata);
127}
128
129inline void QDBusMarshaller::append(const QDBusSignature &arg)
130{
131 QByteArray data = arg.signature().toUtf8();
132 if (!ba && data.isEmpty())
133 error(QLatin1String("Invalid signature passed in arguments"));
134 const char *cdata = data.constData();
135 qIterAppend(&iterator, ba, DBUS_TYPE_SIGNATURE, &cdata);
136}
137
138inline void QDBusMarshaller::append(const QByteArray &arg)
139{
140 if (ba) {
141 *ba += DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING;
142 return;
143 }
144
145 const char* cdata = arg.constData();
146 DBusMessageIter subiterator;
147 q_dbus_message_iter_open_container(&iterator, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING,
148 &subiterator);
149 q_dbus_message_iter_append_fixed_array(&subiterator, DBUS_TYPE_BYTE, &cdata, arg.length());
150 q_dbus_message_iter_close_container(&iterator, &subiterator);
151}
152
153inline bool QDBusMarshaller::append(const QDBusVariant &arg)
154{
155 if (ba) {
156 *ba += DBUS_TYPE_VARIANT_AS_STRING;
157 return true;
158 }
159
160 const QVariant &value = arg.variant();
161 QVariant::Type id = QVariant::Type(value.userType());
162 if (id == QVariant::Invalid) {
163 qWarning("QDBusMarshaller: cannot add a null QDBusVariant");
164 error(QLatin1String("Variant containing QVariant::Invalid passed in arguments"));
165 return false;
166 }
167
168 QByteArray tmpSignature;
169 const char *signature = 0;
170 if (int(id) == qMetaTypeId<QDBusArgument>()) {
171 // take the signature from the QDBusArgument object we're marshalling
172 tmpSignature =
173 qvariant_cast<QDBusArgument>(value).currentSignature().toLatin1();
174 signature = tmpSignature.constData();
175 } else {
176 // take the signatuer from the metatype we're marshalling
177 signature = QDBusMetaType::typeToSignature(id);
178 }
179 if (!signature) {
180 qWarning("QDBusMarshaller: type `%s' (%d) is not registered with D-BUS. "
181 "Use qDBusRegisterMetaType to register it",
182 QVariant::typeToName( id ), id);
183 error(QString::fromLatin1("Unregistered type %1 passed in arguments")
184 .arg(QLatin1String(QVariant::typeToName(id))));
185 return false;
186 }
187
188 QDBusMarshaller sub;
189 open(sub, DBUS_TYPE_VARIANT, signature);
190 bool isOk = sub.appendVariantInternal(value);
191 // don't call sub.close(): it auto-closes
192
193 return isOk;
194}
195
196inline void QDBusMarshaller::append(const QStringList &arg)
197{
198 if (ba) {
199 *ba += DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING;
200 return;
201 }
202
203 QDBusMarshaller sub;
204 open(sub, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING);
205 QStringList::ConstIterator it = arg.constBegin();
206 QStringList::ConstIterator end = arg.constEnd();
207 for ( ; it != end; ++it)
208 sub.append(*it);
209 // don't call sub.close(): it auto-closes
210}
211
212inline QDBusMarshaller *QDBusMarshaller::beginStructure()
213{
214 return beginCommon(DBUS_TYPE_STRUCT, 0);
215}
216
217inline QDBusMarshaller *QDBusMarshaller::beginArray(int id)
218{
219 const char *signature = QDBusMetaType::typeToSignature( QVariant::Type(id) );
220 if (!signature) {
221 qWarning("QDBusMarshaller: type `%s' (%d) is not registered with D-BUS. "
222 "Use qDBusRegisterMetaType to register it",
223 QVariant::typeToName( QVariant::Type(id) ), id);
224 error(QString::fromLatin1("Unregistered type %1 passed in arguments")
225 .arg(QLatin1String(QVariant::typeToName(QVariant::Type(id)))));
226 return this;
227 }
228
229 return beginCommon(DBUS_TYPE_ARRAY, signature);
230}
231
232inline QDBusMarshaller *QDBusMarshaller::beginMap(int kid, int vid)
233{
234 const char *ksignature = QDBusMetaType::typeToSignature( QVariant::Type(kid) );
235 if (!ksignature) {
236 qWarning("QDBusMarshaller: type `%s' (%d) is not registered with D-BUS. "
237 "Use qDBusRegisterMetaType to register it",
238 QVariant::typeToName( QVariant::Type(kid) ), kid);
239 error(QString::fromLatin1("Unregistered type %1 passed in arguments")
240 .arg(QLatin1String(QVariant::typeToName(QVariant::Type(kid)))));
241 return this;
242 }
243 if (ksignature[1] != 0 || !q_dbus_type_is_basic(*ksignature)) {
244 qWarning("QDBusMarshaller: type '%s' (%d) cannot be used as the key type in a D-BUS map.",
245 QVariant::typeToName( QVariant::Type(kid) ), kid);
246 error(QString::fromLatin1("Type %1 passed in arguments cannot be used as a key in a map")
247 .arg(QLatin1String(QVariant::typeToName(QVariant::Type(kid)))));
248 return this;
249 }
250
251 const char *vsignature = QDBusMetaType::typeToSignature( QVariant::Type(vid) );
252 if (!vsignature) {
253 const char *typeName = QVariant::typeToName(QVariant::Type(vid));
254 qWarning("QDBusMarshaller: type `%s' (%d) is not registered with D-BUS. "
255 "Use qDBusRegisterMetaType to register it",
256 typeName, vid);
257 error(QString::fromLatin1("Unregistered type %1 passed in arguments")
258 .arg(QLatin1String(typeName)));
259 return this;
260 }
261
262 QByteArray signature;
263 signature = DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING;
264 signature += ksignature;
265 signature += vsignature;
266 signature += DBUS_DICT_ENTRY_END_CHAR_AS_STRING;
267 return beginCommon(DBUS_TYPE_ARRAY, signature);
268}
269
270inline QDBusMarshaller *QDBusMarshaller::beginMapEntry()
271{
272 return beginCommon(DBUS_TYPE_DICT_ENTRY, 0);
273}
274
275void QDBusMarshaller::open(QDBusMarshaller &sub, int code, const char *signature)
276{
277 sub.parent = this;
278 sub.ba = ba;
279 sub.ok = true;
280
281 if (ba)
282 switch (code) {
283 case DBUS_TYPE_ARRAY:
284 *ba += char(code);
285 *ba += signature;
286 // fall through
287
288 case DBUS_TYPE_DICT_ENTRY:
289 sub.closeCode = 0;
290 break;
291
292 case DBUS_TYPE_STRUCT:
293 *ba += DBUS_STRUCT_BEGIN_CHAR;
294 sub.closeCode = DBUS_STRUCT_END_CHAR;
295 break;
296 }
297 else
298 q_dbus_message_iter_open_container(&iterator, code, signature, &sub.iterator);
299}
300
301QDBusMarshaller *QDBusMarshaller::beginCommon(int code, const char *signature)
302{
303 QDBusMarshaller *d = new QDBusMarshaller;
304 open(*d, code, signature);
305 return d;
306}
307
308inline QDBusMarshaller *QDBusMarshaller::endStructure()
309{ return endCommon(); }
310
311inline QDBusMarshaller *QDBusMarshaller::endArray()
312{ return endCommon(); }
313
314inline QDBusMarshaller *QDBusMarshaller::endMap()
315{ return endCommon(); }
316
317inline QDBusMarshaller *QDBusMarshaller::endMapEntry()
318{ return endCommon(); }
319
320QDBusMarshaller *QDBusMarshaller::endCommon()
321{
322 QDBusMarshaller *retval = parent;
323 delete this;
324 return retval;
325}
326
327void QDBusMarshaller::close()
328{
329 if (ba) {
330 if (closeCode)
331 *ba += closeCode;
332 } else if (parent) {
333 q_dbus_message_iter_close_container(&parent->iterator, &iterator);
334 }
335}
336
337void QDBusMarshaller::error(const QString &msg)
338{
339 ok = false;
340 if (parent)
341 parent->error(msg);
342 else
343 errorString = msg;
344}
345
346bool QDBusMarshaller::appendVariantInternal(const QVariant &arg)
347{
348 int id = arg.userType();
349 if (id == QVariant::Invalid) {
350 qWarning("QDBusMarshaller: cannot add an invalid QVariant");
351 error(QLatin1String("Variant containing QVariant::Invalid passed in arguments"));
352 return false;
353 }
354
355 // intercept QDBusArgument parameters here
356 if (id == qMetaTypeId<QDBusArgument>()) {
357 QDBusArgument dbusargument = qvariant_cast<QDBusArgument>(arg);
358 QDBusArgumentPrivate *d = QDBusArgumentPrivate::d(dbusargument);
359 if (!d->message)
360 return false; // can't append this one...
361
362 QDBusDemarshaller demarshaller;
363 demarshaller.message = q_dbus_message_ref(d->message);
364
365 if (d->direction == Demarshalling) {
366 // it's demarshalling; just copy
367 demarshaller.iterator = static_cast<QDBusDemarshaller *>(d)->iterator;
368 } else {
369 // it's marshalling; start over
370 if (!q_dbus_message_iter_init(demarshaller.message, &demarshaller.iterator))
371 return false; // error!
372 }
373
374 return appendCrossMarshalling(&demarshaller);
375 }
376
377 const char *signature = QDBusMetaType::typeToSignature( QVariant::Type(id) );
378 if (!signature) {
379 qWarning("QDBusMarshaller: type `%s' (%d) is not registered with D-BUS. "
380 "Use qDBusRegisterMetaType to register it",
381 QVariant::typeToName( QVariant::Type(id) ), id);
382 error(QString::fromLatin1("Unregistered type %1 passed in arguments")
383 .arg(QLatin1String(QVariant::typeToName(QVariant::Type(id)))));
384 return false;
385 }
386
387 switch (*signature) {
388#ifdef __OPTIMIZE__
389 case DBUS_TYPE_BYTE:
390 case DBUS_TYPE_INT16:
391 case DBUS_TYPE_UINT16:
392 case DBUS_TYPE_INT32:
393 case DBUS_TYPE_UINT32:
394 case DBUS_TYPE_INT64:
395 case DBUS_TYPE_UINT64:
396 case DBUS_TYPE_DOUBLE:
397 qIterAppend(&iterator, ba, *signature, arg.constData());
398 return true;
399 case DBUS_TYPE_BOOLEAN:
400 append( arg.toBool() );
401 return true;
402#else
403 case DBUS_TYPE_BYTE:
404 append( qvariant_cast<uchar>(arg) );
405 return true;
406 case DBUS_TYPE_BOOLEAN:
407 append( arg.toBool() );
408 return true;
409 case DBUS_TYPE_INT16:
410 append( qvariant_cast<short>(arg) );
411 return true;
412 case DBUS_TYPE_UINT16:
413 append( qvariant_cast<ushort>(arg) );
414 return true;
415 case DBUS_TYPE_INT32:
416 append( static_cast<dbus_int32_t>(arg.toInt()) );
417 return true;
418 case DBUS_TYPE_UINT32:
419 append( static_cast<dbus_uint32_t>(arg.toUInt()) );
420 return true;
421 case DBUS_TYPE_INT64:
422 append( arg.toLongLong() );
423 return true;
424 case DBUS_TYPE_UINT64:
425 append( arg.toULongLong() );
426 return true;
427 case DBUS_TYPE_DOUBLE:
428 append( arg.toDouble() );
429 return true;
430#endif
431
432 case DBUS_TYPE_STRING:
433 append( arg.toString() );
434 return true;
435 case DBUS_TYPE_OBJECT_PATH:
436 append( qvariant_cast<QDBusObjectPath>(arg) );
437 return true;
438 case DBUS_TYPE_SIGNATURE:
439 append( qvariant_cast<QDBusSignature>(arg) );
440 return true;
441
442 // compound types:
443 case DBUS_TYPE_VARIANT:
444 // nested QVariant
445 return append( qvariant_cast<QDBusVariant>(arg) );
446
447 case DBUS_TYPE_ARRAY:
448 // could be many things
449 // find out what kind of array it is
450 switch (arg.type()) {
451 case QVariant::StringList:
452 append( arg.toStringList() );
453 return true;
454
455 case QVariant::ByteArray:
456 append( arg.toByteArray() );
457 return true;
458
459 default:
460 ; // fall through
461 }
462 // fall through
463
464 case DBUS_TYPE_STRUCT:
465 case DBUS_STRUCT_BEGIN_CHAR:
466 return appendRegisteredType( arg );
467
468 case DBUS_TYPE_DICT_ENTRY:
469 case DBUS_DICT_ENTRY_BEGIN_CHAR:
470 qFatal("QDBusMarshaller::appendVariantInternal got a DICT_ENTRY!");
471 return false;
472
473 default:
474 qWarning("QDBusMarshaller::appendVariantInternal: Found unknown D-BUS type '%s'",
475 signature);
476 return false;
477 }
478
479 return true;
480}
481
482bool QDBusMarshaller::appendRegisteredType(const QVariant &arg)
483{
484 ref.ref(); // reference up
485 QDBusArgument self(QDBusArgumentPrivate::create(this));
486 return QDBusMetaType::marshall(self, arg.userType(), arg.constData());
487}
488
489bool QDBusMarshaller::appendCrossMarshalling(QDBusDemarshaller *demarshaller)
490{
491 int code = q_dbus_message_iter_get_arg_type(&demarshaller->iterator);
492 if (q_dbus_type_is_basic(code)) {
493 // easy: just append
494 // do exactly like the D-BUS docs suggest
495 // (see apidocs for q_dbus_message_iter_get_basic)
496
497 qlonglong value;
498 q_dbus_message_iter_get_basic(&demarshaller->iterator, &value);
499 q_dbus_message_iter_next(&demarshaller->iterator);
500 q_dbus_message_iter_append_basic(&iterator, code, &value);
501 return true;
502 }
503
504 if (code == DBUS_TYPE_ARRAY) {
505 int element = q_dbus_message_iter_get_element_type(&demarshaller->iterator);
506 if (q_dbus_type_is_fixed(element)) {
507 // another optimisation: fixed size arrays
508 // code is exactly like QDBusDemarshaller::toByteArray
509 DBusMessageIter sub;
510 q_dbus_message_iter_recurse(&demarshaller->iterator, &sub);
511 q_dbus_message_iter_next(&demarshaller->iterator);
512 int len;
513 void* data;
514 q_dbus_message_iter_get_fixed_array(&sub,&data,&len);
515
516 char signature[2] = { element, 0 };
517 q_dbus_message_iter_open_container(&iterator, DBUS_TYPE_ARRAY, signature, &sub);
518 q_dbus_message_iter_append_fixed_array(&sub, element, &data, len);
519 q_dbus_message_iter_close_container(&iterator, &sub);
520
521 return true;
522 }
523 }
524
525 // We have to recurse
526 QDBusDemarshaller *drecursed = demarshaller->beginCommon();
527
528 QDBusMarshaller mrecursed; // create on the stack makes it autoclose
529 QByteArray subSignature;
530 const char *sig = 0;
531 if (code == DBUS_TYPE_VARIANT || code == DBUS_TYPE_ARRAY) {
532 subSignature = drecursed->currentSignature().toLatin1();
533 if (!subSignature.isEmpty())
534 sig = subSignature.constData();
535 }
536 open(mrecursed, code, sig);
537
538 while (!drecursed->atEnd()) {
539 if (!mrecursed.appendCrossMarshalling(drecursed)) {
540 delete drecursed;
541 return false;
542 }
543 }
544
545 delete drecursed;
546 return true;
547}
548
549QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.