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