source: trunk/src/dbus/qdbusmessage.cpp@ 561

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

trunk: Merged in qt 4.6.1 sources.

File size: 24.3 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
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 [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qdbusmessage.h"
43
44#include <qdebug.h>
45#include <qstringlist.h>
46
47#include "qdbus_symbols_p.h"
48
49#include "qdbusargument_p.h"
50#include "qdbuserror.h"
51#include "qdbusmessage_p.h"
52#include "qdbusmetatype.h"
53#include "qdbusconnection_p.h"
54#include "qdbusutil_p.h"
55
56QT_BEGIN_NAMESPACE
57
58static inline const char *data(const QByteArray &arr)
59{
60 return arr.isEmpty() ? 0 : arr.constData();
61}
62
63QDBusMessagePrivate::QDBusMessagePrivate()
64 : msg(0), reply(0), type(DBUS_MESSAGE_TYPE_INVALID),
65 timeout(-1), localReply(0), ref(1), delayedReply(false), localMessage(false),
66 parametersValidated(false)
67{
68}
69
70QDBusMessagePrivate::~QDBusMessagePrivate()
71{
72 if (msg)
73 q_dbus_message_unref(msg);
74 if (reply)
75 q_dbus_message_unref(reply);
76 delete localReply;
77}
78
79/*!
80 \since 4.3
81 Returns the human-readable message associated with the error that was received.
82*/
83QString QDBusMessage::errorMessage() const
84{
85 if (d_ptr->type == ErrorMessage) {
86 if (!d_ptr->message.isEmpty())
87 return d_ptr->message;
88 if (!d_ptr->arguments.isEmpty())
89 return d_ptr->arguments.at(0).toString();
90 }
91 return QString();
92}
93
94/*!
95 \internal
96 Constructs a DBusMessage object from this object. The returned value must be de-referenced
97 with q_dbus_message_unref.
98
99 The \a error object is set to indicate the error if anything went wrong with the
100 marshalling. Usually, this error message will be placed in the reply, as if the call failed.
101 The \a error pointer must not be null.
102*/
103DBusMessage *QDBusMessagePrivate::toDBusMessage(const QDBusMessage &message, QDBusError *error)
104{
105 if (!qdbus_loadLibDBus()) {
106 *error = QDBusError(QDBusError::Failed, QLatin1String("Could not open lidbus-1 library"));
107 return 0;
108 }
109
110 DBusMessage *msg = 0;
111 const QDBusMessagePrivate *d_ptr = message.d_ptr;
112
113 switch (d_ptr->type) {
114 case DBUS_MESSAGE_TYPE_INVALID:
115 //qDebug() << "QDBusMessagePrivate::toDBusMessage" << "message is invalid";
116 break;
117 case DBUS_MESSAGE_TYPE_METHOD_CALL:
118 // only service and interface can be empty -> path and name must not be empty
119 if (!d_ptr->parametersValidated) {
120 if (!QDBusUtil::checkBusName(d_ptr->service, QDBusUtil::EmptyAllowed, error))
121 return 0;
122 if (!QDBusUtil::checkObjectPath(d_ptr->path, QDBusUtil::EmptyNotAllowed, error))
123 return 0;
124 if (!QDBusUtil::checkInterfaceName(d_ptr->interface, QDBusUtil::EmptyAllowed, error))
125 return 0;
126 if (!QDBusUtil::checkMemberName(d_ptr->name, QDBusUtil::EmptyNotAllowed, error, "method"))
127 return 0;
128 }
129
130 msg = q_dbus_message_new_method_call(data(d_ptr->service.toUtf8()), d_ptr->path.toUtf8(),
131 data(d_ptr->interface.toUtf8()), d_ptr->name.toUtf8());
132 break;
133 case DBUS_MESSAGE_TYPE_METHOD_RETURN:
134 msg = q_dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN);
135 if (!d_ptr->localMessage) {
136 q_dbus_message_set_destination(msg, q_dbus_message_get_sender(d_ptr->reply));
137 q_dbus_message_set_reply_serial(msg, q_dbus_message_get_serial(d_ptr->reply));
138 }
139 break;
140 case DBUS_MESSAGE_TYPE_ERROR:
141 // error name can't be empty
142 if (!d_ptr->parametersValidated
143 && !QDBusUtil::checkErrorName(d_ptr->name, QDBusUtil::EmptyNotAllowed, error))
144 return 0;
145
146 msg = q_dbus_message_new(DBUS_MESSAGE_TYPE_ERROR);
147 q_dbus_message_set_error_name(msg, d_ptr->name.toUtf8());
148 if (!d_ptr->localMessage) {
149 q_dbus_message_set_destination(msg, q_dbus_message_get_sender(d_ptr->reply));
150 q_dbus_message_set_reply_serial(msg, q_dbus_message_get_serial(d_ptr->reply));
151 }
152 break;
153 case DBUS_MESSAGE_TYPE_SIGNAL:
154 // nothing can be empty here
155 if (!d_ptr->parametersValidated) {
156 if (!QDBusUtil::checkObjectPath(d_ptr->path, QDBusUtil::EmptyNotAllowed, error))
157 return 0;
158 if (!QDBusUtil::checkInterfaceName(d_ptr->interface, QDBusUtil::EmptyAllowed, error))
159 return 0;
160 if (!QDBusUtil::checkMemberName(d_ptr->name, QDBusUtil::EmptyNotAllowed, error, "method"))
161 return 0;
162 }
163
164 msg = q_dbus_message_new_signal(d_ptr->path.toUtf8(), d_ptr->interface.toUtf8(),
165 d_ptr->name.toUtf8());
166 break;
167 default:
168 Q_ASSERT(false);
169 break;
170 }
171
172 // if we got here, the parameters validated
173 // and since the message parameters cannot be changed once the message is created
174 // we can record this fact
175 d_ptr->parametersValidated = true;
176
177 QDBusMarshaller marshaller;
178 QVariantList::ConstIterator it = d_ptr->arguments.constBegin();
179 QVariantList::ConstIterator cend = d_ptr->arguments.constEnd();
180 q_dbus_message_iter_init_append(msg, &marshaller.iterator);
181 if (!d_ptr->message.isEmpty())
182 // prepend the error message
183 marshaller.append(d_ptr->message);
184 for ( ; it != cend; ++it)
185 marshaller.appendVariantInternal(*it);
186
187 // check if everything is ok
188 if (marshaller.ok)
189 return msg;
190
191 // not ok;
192 q_dbus_message_unref(msg);
193 *error = QDBusError(QDBusError::Failed, QLatin1String("Marshalling failed: ") + marshaller.errorString);
194 return 0;
195}
196
197/*
198struct DBusMessage
199{
200 DBusAtomic refcount;
201 DBusHeader header;
202 DBusString body;
203 char byte_order;
204 unsigned int locked : 1;
205DBUS_DISABLE_CHECKS
206 unsigned int in_cache : 1;
207#endif
208 DBusList *size_counters;
209 long size_counter_delta;
210 dbus_uint32_t changed_stamp : CHANGED_STAMP_BITS;
211 DBusDataSlotList slot_list;
212#ifndef DBUS_DISABLE_CHECKS
213 int generation;
214#endif
215};
216*/
217
218/*!
219 \internal
220 Constructs a QDBusMessage by parsing the given DBusMessage object.
221*/
222QDBusMessage QDBusMessagePrivate::fromDBusMessage(DBusMessage *dmsg)
223{
224 QDBusMessage message;
225 if (!dmsg)
226 return message;
227
228 message.d_ptr->type = q_dbus_message_get_type(dmsg);
229 message.d_ptr->path = QString::fromUtf8(q_dbus_message_get_path(dmsg));
230 message.d_ptr->interface = QString::fromUtf8(q_dbus_message_get_interface(dmsg));
231 message.d_ptr->name = message.d_ptr->type == DBUS_MESSAGE_TYPE_ERROR ?
232 QString::fromUtf8(q_dbus_message_get_error_name(dmsg)) :
233 QString::fromUtf8(q_dbus_message_get_member(dmsg));
234 message.d_ptr->service = QString::fromUtf8(q_dbus_message_get_sender(dmsg));
235 message.d_ptr->signature = QString::fromUtf8(q_dbus_message_get_signature(dmsg));
236 message.d_ptr->msg = q_dbus_message_ref(dmsg);
237
238 QDBusDemarshaller demarshaller;
239 demarshaller.message = q_dbus_message_ref(dmsg);
240 if (q_dbus_message_iter_init(demarshaller.message, &demarshaller.iterator))
241 while (!demarshaller.atEnd())
242 message << demarshaller.toVariantInternal();
243 return message;
244}
245
246bool QDBusMessagePrivate::isLocal(const QDBusMessage &message)
247{
248 return message.d_ptr->localMessage;
249}
250
251QDBusMessage QDBusMessagePrivate::makeLocal(const QDBusConnectionPrivate &conn,
252 const QDBusMessage &asSent)
253{
254 // simulate the message being sent to the bus and then received back
255 // the only field that the bus sets when delivering the message
256 // (as opposed to the message as we send it), is the sender
257 // so we simply set the sender to our unique name
258
259 // determine if we are carrying any complex types
260 QString computedSignature;
261 QVariantList::ConstIterator it = asSent.d_ptr->arguments.constBegin();
262 QVariantList::ConstIterator end = asSent.d_ptr->arguments.constEnd();
263 for ( ; it != end; ++it) {
264 int id = it->userType();
265 const char *signature = QDBusMetaType::typeToSignature(id);
266 if ((id != QVariant::StringList && id != QVariant::ByteArray &&
267 qstrlen(signature) != 1) || id == qMetaTypeId<QDBusVariant>()) {
268 // yes, we are
269 // we must marshall and demarshall again so as to create QDBusArgument
270 // entries for the complex types
271 QDBusError error;
272 DBusMessage *message = toDBusMessage(asSent, &error);
273 if (!message) {
274 // failed to marshall, so it's a call error
275 return QDBusMessage::createError(error);
276 }
277
278 q_dbus_message_set_sender(message, conn.baseService.toUtf8());
279
280 QDBusMessage retval = fromDBusMessage(message);
281 retval.d_ptr->localMessage = true;
282 q_dbus_message_unref(message);
283 if (retval.d_ptr->service.isEmpty())
284 retval.d_ptr->service = conn.baseService;
285 return retval;
286 } else {
287 computedSignature += QLatin1String(signature);
288 }
289 }
290
291 // no complex types seen
292 // optimise by using the variant list itself
293 QDBusMessage retval;
294 QDBusMessagePrivate *d = retval.d_ptr;
295 d->arguments = asSent.d_ptr->arguments;
296 d->path = asSent.d_ptr->path;
297 d->interface = asSent.d_ptr->interface;
298 d->name = asSent.d_ptr->name;
299 d->message = asSent.d_ptr->message;
300 d->type = asSent.d_ptr->type;
301
302 d->service = conn.baseService;
303 d->signature = computedSignature;
304 d->localMessage = true;
305 return retval;
306}
307
308QDBusMessage QDBusMessagePrivate::makeLocalReply(const QDBusConnectionPrivate &conn,
309 const QDBusMessage &callMsg)
310{
311 // simulate the reply (return or error) message being sent to the bus and
312 // then received back.
313 if (callMsg.d_ptr->localReply)
314 return makeLocal(conn, *callMsg.d_ptr->localReply);
315 return QDBusMessage(); // failed
316}
317
318/*!
319 \class QDBusMessage
320 \inmodule QtDBus
321 \since 4.2
322
323 \brief The QDBusMessage class represents one message sent or
324 received over the D-Bus bus.
325
326 This object can represent any of the four different types of
327 messages (MessageType) that can occur on the bus:
328
329 \list
330 \o Method calls
331 \o Method return values
332 \o Signal emissions
333 \o Error codes
334 \endlist
335
336 Objects of this type are created with the static createError(),
337 createMethodCall() and createSignal() functions. Use the
338 QDBusConnection::send() function to send the messages.
339*/
340
341/*!
342 \enum QDBusMessage::MessageType
343 The possible message types:
344
345 \value MethodCallMessage a message representing an outgoing or incoming method call
346 \value SignalMessage a message representing an outgoing or incoming signal emission
347 \value ReplyMessage a message representing the return values of a method call
348 \value ErrorMessage a message representing an error condition in response to a method call
349 \value InvalidMessage an invalid message: this is never set on messages received from D-Bus
350*/
351
352/*!
353 Constructs a new DBus message with the given \a path, \a interface
354 and \a name, representing a signal emission.
355
356 A DBus signal is emitted from one application and is received by
357 all applications that are listening for that signal from that
358 interface.
359
360 The QDBusMessage object that is returned can be sent using the
361 QDBusConnection::send() function.
362*/
363QDBusMessage QDBusMessage::createSignal(const QString &path, const QString &interface,
364 const QString &name)
365{
366 QDBusMessage message;
367 message.d_ptr->type = DBUS_MESSAGE_TYPE_SIGNAL;
368 message.d_ptr->path = path;
369 message.d_ptr->interface = interface;
370 message.d_ptr->name = name;
371
372 return message;
373}
374
375/*!
376 Constructs a new DBus message representing a method call.
377 A method call always informs its destination address
378 (\a service, \a path, \a interface and \a method).
379
380 The DBus bus allows calling a method on a given remote object without specifying the
381 destination interface, if the method name is unique. However, if two interfaces on the
382 remote object export the same method name, the result is undefined (one of the two may be
383 called or an error may be returned).
384
385 When using DBus in a peer-to-peer context (i.e., not on a bus), the \a service parameter is
386 optional.
387
388 The QDBusObject and QDBusInterface classes provide a simpler abstraction to synchronous
389 method calling.
390
391 This function returns a QDBusMessage object that can be sent with
392 QDBusConnection::call().
393*/
394QDBusMessage QDBusMessage::createMethodCall(const QString &service, const QString &path,
395 const QString &interface, const QString &method)
396{
397 QDBusMessage message;
398 message.d_ptr->type = DBUS_MESSAGE_TYPE_METHOD_CALL;
399 message.d_ptr->service = service;
400 message.d_ptr->path = path;
401 message.d_ptr->interface = interface;
402 message.d_ptr->name = method;
403
404 return message;
405}
406
407/*!
408 Constructs a new DBus message representing an error,
409 with the given \a name and \a msg.
410*/
411QDBusMessage QDBusMessage::createError(const QString &name, const QString &msg)
412{
413 QDBusMessage error;
414 error.d_ptr->type = DBUS_MESSAGE_TYPE_ERROR;
415 error.d_ptr->name = name;
416 error.d_ptr->message = msg;
417
418 return error;
419}
420