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 "qdbuspendingcall.h"
43#include "qdbuspendingcall_p.h"
44
45#include "qdbusconnection_p.h"
46#include "qdbusmetatype_p.h"
47#include "qcoreapplication.h"
48#include "qcoreevent.h"
49#include <private/qobject_p.h>
50
51#ifndef QT_NO_DBUS
52
53QT_BEGIN_NAMESPACE
54
55/*!
56 \class QDBusPendingCall
57 \inmodule QtDBus
58 \since 4.5
59
60 \brief The QDBusPendingCall class refers to one pending asynchronous call
61
62 A QDBusPendingCall object is a reference to a method call that was
63 sent over D-Bus without waiting for a reply. QDBusPendingCall is an
64 opaque type, meant to be used as a handle for a pending reply.
65
66 In most programs, the QDBusPendingCall class will not be used
67 directly. It can be safely replaced with the template-based
68 QDBusPendingReply, in order to access the contents of the reply or
69 wait for it to be complete.
70
71 The QDBusPendingCallWatcher class allows one to connect to a signal
72 that will indicate when the reply has arrived or if the call has
73 timed out. It also provides the
74 QDBusPendingCallWatcher::waitForFinished() method which will suspend
75 the execution of the program until the reply has arrived.
76
77 \note If you create a copy of a QDBusPendingCall object, all
78 information will be shared among the many copies. Therefore,
79 QDBusPendingCall is an explicitly-shared object and does not
80 provide a method of detaching the copies (since they refer
81 to the same pending call)
82
83 \sa QDBusPendingReply, QDBusPendingCallWatcher,
84 QDBusAbstractInterface::asyncCall()
85*/
86
87/*!
88 \class QDBusPendingCallWatcher
89 \inmodule QtDBus
90 \since 4.5
91
92 \brief The QDBusPendingCallWatcher class provides a convenient way for
93 waiting for asynchronous replies
94
95 The QDBusPendingCallWatcher provides the finished() signal that will be
96 emitted when a reply arrives.
97
98 It is usually used like the following example:
99
100 \snippet doc/src/snippets/code/src.qdbus.qdbuspendingcall.cpp 0
101
102 Note that it is not necessary to keep the original QDBusPendingCall
103 object around since QDBusPendingCallWatcher inherits from that class
104 too.
105
106 The slot connected to by the above code could be something similar
107 to the following:
108
109 \snippet doc/src/snippets/code/src.qdbus.qdbuspendingcall.cpp 1
110
111 Note the use of QDBusPendingReply to validate the argument types in
112 the reply. If the reply did not contain exactly two arguments
113 (one string and one QByteArray), QDBusPendingReply::isError() will
114 return true.
115
116 \sa QDBusPendingReply, QDBusAbstractInterface::asyncCall()
117*/
118
119/*!
120 \fn void QDBusPendingCallWatcher::finished(QDBusPendingCallWatcher *self)
121
122 This signal is emitted when the pending call has finished and its
123 reply is available. The \a self parameter is a pointer to the
124 object itself, passed for convenience so that the slot can access
125 the properties and determine the contents of the reply.
126*/
127
128void QDBusPendingCallWatcherHelper::add(QDBusPendingCallWatcher *watcher)
129{
130 connect(this, SIGNAL(finished()), watcher, SLOT(_q_finished()), Qt::QueuedConnection);
131}
132
133QDBusPendingCallPrivate::~QDBusPendingCallPrivate()
134{
135 if (pending) {
136 q_dbus_pending_call_cancel(pending);
137 q_dbus_pending_call_unref(pending);
138 }
139 delete watcherHelper;
140}
141
142bool QDBusPendingCallPrivate::setReplyCallback(QObject *target, const char *member)
143{
144 receiver = target;
145 metaTypes.clear();
146 methodIdx = -1;
147 if (!target)
148 return true;; // unsetting
149
150 if (!member || !*member) {
151 // would not be able to deliver a reply
152 qWarning("QDBusPendingCall::setReplyCallback: error: cannot deliver a reply to %s::%s (%s)",
153 target ? target->metaObject()->className() : "(null)",
154 member ? member + 1 : "(null)",
155 target ? qPrintable(target->objectName()) : "no name");
156 return false;
157 }
158
159 methodIdx = QDBusConnectionPrivate::findSlot(target, member + 1, metaTypes);
160 if (methodIdx == -1) {
161 QByteArray normalizedName = QMetaObject::normalizedSignature(member + 1);
162 methodIdx = QDBusConnectionPrivate::findSlot(target, normalizedName, metaTypes);
163 }
164 if (methodIdx == -1) {
165 // would not be able to deliver a reply
166 qWarning("QDBusPendingCall::setReplyCallback: error: cannot deliver a reply to %s::%s (%s)",
167 target->metaObject()->className(),
168 member + 1, qPrintable(target->objectName()));
169 return false;
170 }
171
172 // success
173 // construct the expected signature
174 int count = metaTypes.count() - 1;
175 if (count == 1 && metaTypes.at(1) == QDBusMetaTypeId::message) {
176 // wildcard slot, can receive anything, so don't set the signature
177 return true;
178 }
179
180 if (metaTypes.at(count) == QDBusMetaTypeId::message)
181 --count;
182
183 if (count == 0) {
184 setMetaTypes(count, 0);
185 } else {
186 QVector<int> types = QVector<int>::fromList(metaTypes);
187 setMetaTypes(count, types.constData() + 1);
188 }
189 return true;
190}
191
192void QDBusPendingCallPrivate::setMetaTypes(int count, const int *types)
193{
194 expectedReplyCount = count;
195 if (count == 0) {
196 expectedReplySignature = QLatin1String(""); // not null
197 return;
198 }
199
200 QByteArray sig;
201 sig.reserve(count + count / 2);
202 for (int i = 0; i < count; ++i) {
203 const char *typeSig = QDBusMetaType::typeToSignature(types[i]);
204 if (!typeSig) {
205 qFatal("QDBusPendingReply: type %s is not registered with QtDBus",
206 QMetaType::typeName(types[i]));
207 }
208 sig += typeSig;
209 }
210
211 expectedReplySignature = QString::fromLatin1(sig);
212}
213
214void QDBusPendingCallPrivate::checkReceivedSignature()
215{
216 // MUST BE CALLED WITH A LOCKED MUTEX!
217
218 if (replyMessage.type() == QDBusMessage::InvalidMessage)
219 return; // not yet finished - no message to
220 // validate against
221 if (replyMessage.type() == QDBusMessage::ErrorMessage)
222 return; // we don't have to check the signature of an error reply
223
224 if (expectedReplySignature.isNull())
225 return; // no signature to validate against
226
227 // can't use startsWith here because a null string doesn't start or end with an empty string
228 if (replyMessage.signature().indexOf(expectedReplySignature) != 0) {
229 QString errorMsg = QLatin1String("Unexpected reply signature: got \"%1\", "
230 "expected \"%2\"");
231 replyMessage = QDBusMessage::createError(
232 QDBusError::InvalidSignature,
233 errorMsg.arg(replyMessage.signature(), expectedReplySignature));
234
235 }
236}
237
238void QDBusPendingCallPrivate::waitForFinished()
239{
240 QMutexLocker locker(&mutex);
241
242 if (replyMessage.type() != QDBusMessage::InvalidMessage)
243 return; // already finished
244
245 connection->waitForFinished(this);
246}
247
248/*!
249 Creates a copy of the \a other pending asynchronous call. Note
250 that both objects will refer to the same pending call.
251*/
252QDBusPendingCall::QDBusPendingCall(const QDBusPendingCall &other)
253 : d(other.d)
254{
255}
256
257/*!
258 \internal
259*/
260QDBusPendingCall::QDBusPendingCall(QDBusPendingCallPrivate *dd)
261 : d(dd)
262{
263 if (dd) {
264 bool r = dd->ref.deref();
265 Q_ASSERT(r);
266 Q_UNUSED(r);
267 }
268}
269
270/*!
271 Destroys this copy of the QDBusPendingCall object. If this copy is
272 also the last copy of a pending asynchronous call, the call will
273 be canceled and no further notifications will be received. There
274 will be no way of accessing the reply's contents when it arrives.
275*/
276QDBusPendingCall::~QDBusPendingCall()
277{
278 // d deleted by QExplicitlySharedDataPointer
279}
280
281
282/*!
283 Creates a copy of the \a other pending asynchronous call and drops
284 the reference to the previously-referenced call. Note that both
285 objects will refer to the same pending call after this function.
286
287 If this object contained the last reference of a pending
288 asynchronous call, the call will be canceled and no further
289 notifications will be received. There will be no way of accessing
290 the reply's contents when it arrives.
291*/
292QDBusPendingCall &QDBusPendingCall::operator=(const QDBusPendingCall &other)
293{
294 d = other.d;
295 return *this;
296}
297
298/*!
299 \fn bool QDBusPendingCallWatcher::isFinished() const
300
301 Returns true if the pending call has finished processing and the
302 reply has been received.
303
304 Note that this function only changes state if you call
305 waitForFinished() or if an external D-Bus event happens, which in
306 general only happens if you return to the event loop execution.
307
308 \sa QDBusPendingReply::isFinished()
309*/
310/*!
311 \fn bool QDBusPendingReply::isFinished() const
312
313 Returns true if the pending call has finished processing and the
314 reply has been received. If this function returns true, the
315 isError(), error() and reply() methods should return valid
316 information.
317
318 Note that this function only changes state if you call
319 waitForFinished() or if an external D-Bus event happens, which in
320 general only happens if you return to the event loop execution.
321
322 \sa QDBusPendingCallWatcher::isFinished()
323*/
324
325bool QDBusPendingCall::isFinished() const
326{
327 if (!d)
328 return true; // considered finished
329
330 QMutexLocker locker(&d->mutex);
331 return d->replyMessage.type() != QDBusMessage::InvalidMessage;
332}
333
334void QDBusPendingCall::waitForFinished()
335{
336 if (d) d->waitForFinished();
337}
338
339/*!
340 \fn bool QDBusPendingReply::isValid() const
341
342 Returns true if the reply contains a normal reply message, false
343 if it contains anything else.
344
345 If the pending call has not finished processing, this function
346 return false.
347*/
348bool QDBusPendingCall::isValid() const
349{
350 if (!d)
351 return false;
352 QMutexLocker locker(&d->mutex);
353 return d->replyMessage.type() == QDBusMessage::ReplyMessage;
354}
355
356/*!
357 \fn bool QDBusPendingReply::isError() const
358
359 Returns true if the reply contains an error message, false if it
360 contains a normal method reply.
361
362 If the pending call has not finished processing, this function
363 also returns true.
364*/
365bool QDBusPendingCall::isError() const
366{
367 if (!d)
368 return true; // considered finished and an error
369 QMutexLocker locker(&d->mutex);
370 return d->replyMessage.type() == QDBusMessage::ErrorMessage;
371}
372
373/*!
374 \fn QDBusError QDBusPendingReply::error() const
375
376 Retrieves the error content of the reply message, if it has
377 finished processing. If the reply message has not finished
378 processing or if it contains a normal reply message (non-error),
379 this function returns an invalid QDBusError.
380*/
381QDBusError QDBusPendingCall::error() const
382{
383 if (d) {
384 QMutexLocker locker(&d->mutex);
385 return d->replyMessage;
386 }
387
388 // not connected, return an error
389 QDBusError err = QDBusError(QDBusError::Disconnected,
390 QLatin1String("Not connected to D-Bus server"));
391 return err;
392}
393
394/*!
395 \fn QDBusMessage QDBusPendingReply::reply() const
396
397 Retrieves the reply message received for the asynchronous call
398 that was sent, if it has finished processing. If the pending call
399 is not finished, this function returns a QDBusMessage of type
400 QDBusMessage::InvalidMessage.
401
402 After it has finished processing, the message type will either be
403 an error message or a normal method reply message.
404*/
405QDBusMessage QDBusPendingCall::reply() const
406{
407 if (!d)
408 return QDBusMessage::createError(error());
409 QMutexLocker locker(&d->mutex);
410 return d->replyMessage;
411}
412
413#if 0
414/*!
415 Sets the slot \a member in object \a target to be called when the
416 reply arrives. The slot's parameter list must match the reply
417 message's arguments for it to be called.
418
419 It may, optionally, contain a QDBusMessage final parameter. If it
420 is present, the parameter will contain the reply message object.
421
422 The callback will not be called if the reply is an error message.
423
424 This function returns true if it could set the callback, false
425 otherwise. It is not a guarantee that the callback will be
426 called.
427
428 \warning QDBusPendingCall only supports one callback per pending
429 asynchronous call, even if multiple QDBusPendingCall
430 objects are referencing the same pending call.
431*/
432bool QDBusPendingCall::setReplyCallback(QObject *target, const char *member)
433{
434 if (!d)
435 return false;
436
437 return d->setReplyCallback(target, member);
438}
439#endif
440
441/*!
442 \since 4.6
443 Creates a QDBusPendingCall object based on the error condition
444 \a error. The resulting pending call object will be in the
445 "finished" state and QDBusPendingReply::isError() will return true.
446
447 \sa fromCompletedCall()
448*/
449QDBusPendingCall QDBusPendingCall::fromError(const QDBusError &error)
450{
451 return fromCompletedCall(QDBusMessage::createError(error));
452}
453
454/*!
455 \since 4.6
456 Creates a QDBusPendingCall object based on the message \a msg.
457 The message must be of type QDBusMessage::ErrorMessage or
458 QDBusMessage::ReplyMessage (that is, a message that is typical
459 of a completed call).
460
461 This function is useful for code that requires simulating a pending
462 call, but that has already finished.
463
464 \sa fromError()
465*/
466QDBusPendingCall QDBusPendingCall::fromCompletedCall(const QDBusMessage &msg)
467{
468 QDBusPendingCallPrivate *d = 0;
469 if (msg.type() == QDBusMessage::ErrorMessage ||
470 msg.type() == QDBusMessage::ReplyMessage) {
471 d = new QDBusPendingCallPrivate(QDBusMessage(), 0);
472 d->replyMessage = msg;
473 d->ref = 1;
474 }
475
476 return QDBusPendingCall(d);
477}
478
479
480class QDBusPendingCallWatcherPrivate: public QObjectPrivate
481{
482public:
483 void _q_finished();
484
485 Q_DECLARE_PUBLIC(QDBusPendingCallWatcher)
486};
487
488inline void QDBusPendingCallWatcherPrivate::_q_finished()
489{
490 Q_Q(QDBusPendingCallWatcher);
491 emit q->finished(q);
492}
493
494/*!
495 Creates a QDBusPendingCallWatcher object to watch for replies on the
496 asynchronous pending call \a call and sets this object's parent
497 to \a parent.
498*/
499QDBusPendingCallWatcher::QDBusPendingCallWatcher(const QDBusPendingCall &call, QObject *parent)
500 : QObject(*new QDBusPendingCallWatcherPrivate, parent), QDBusPendingCall(call)
501{
502 if (d) { // QDBusPendingCall::d
503 QMutexLocker locker(&d->mutex);
504 if (!d->watcherHelper) {
505 d->watcherHelper = new QDBusPendingCallWatcherHelper;
506 if (d->replyMessage.type() != QDBusMessage::InvalidMessage) {
507 // cause a signal emission anyways
508 QMetaObject::invokeMethod(d->watcherHelper, "finished", Qt::QueuedConnection);
509 }
510 }
511 d->watcherHelper->add(this);
512 }
513}
514
515/*!
516 Destroys this object. If this QDBusPendingCallWatcher object was the
517 last reference to the unfinished pending call, the call will be
518 canceled.
519*/
520QDBusPendingCallWatcher::~QDBusPendingCallWatcher()
521{
522}
523
524/*!
525 \fn void QDBusPendingCallWatcher::waitForFinished()
526
527 Suspends the execution of the calling thread until the reply is
528 received and processed. After this function returns, isFinished()
529 should return true, indicating the reply's contents are ready to
530 be processed.
531
532 \sa QDBusPendingReply::waitForFinished()
533*/
534void QDBusPendingCallWatcher::waitForFinished()
535{
536 if (d) {
537 d->waitForFinished();
538
539 // our signals were queued, so deliver them
540 QCoreApplication::sendPostedEvents(d->watcherHelper, QEvent::MetaCall);
541 QCoreApplication::sendPostedEvents(this, QEvent::MetaCall);
542 }
543}
544QT_END_NAMESPACE
545
546#endif // QT_NO_DBUS
547
548#include "moc_qdbuspendingcall.cpp"
549