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 
54 QT_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 
add(QDBusPendingCallWatcher * watcher)129 void QDBusPendingCallWatcherHelper::add(QDBusPendingCallWatcher *watcher)
130 {
131     connect(this, SIGNAL(finished()), watcher, SLOT(_q_finished()), Qt::QueuedConnection);
132 }
133 
~QDBusPendingCallPrivate()134 QDBusPendingCallPrivate::~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 
setReplyCallback(QObject * target,const char * member)143 bool 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 
setMetaTypes(int count,const int * types)188 void QDBusPendingCallPrivate::setMetaTypes(int count, const int *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",
201                    QMetaType::typeName(types[i]));
202         }
203         sig += typeSig;
204     }
205 
206     expectedReplySignature = QString::fromLatin1(sig);
207 }
208 
checkReceivedSignature()209 void QDBusPendingCallPrivate::checkReceivedSignature()
210 {
211     // MUST BE CALLED WITH A LOCKED MUTEX!
212 
213     if (replyMessage.type() == QDBusMessage::InvalidMessage)
214         return;                 // not yet finished - no message to
215                                 // validate against
216     if (replyMessage.type() == QDBusMessage::ErrorMessage)
217         return;                 // we don't have to check the signature of an error reply
218 
219     if (expectedReplySignature.isNull())
220         return;                 // no signature to validate against
221 
222     // can't use startsWith here because a null string doesn't start or end with an empty string
223     if (replyMessage.signature().indexOf(expectedReplySignature) != 0) {
224         const auto errorMsg = QLatin1String("Unexpected reply signature: got \"%1\", "
225                                             "expected \"%2\"");
226         replyMessage = QDBusMessage::createError(
227             QDBusError::InvalidSignature,
228             errorMsg.arg(replyMessage.signature(), expectedReplySignature));
229 
230     }
231 }
232 
waitForFinished()233 void QDBusPendingCallPrivate::waitForFinished()
234 {
235     const auto locker = qt_scoped_lock(mutex);
236 
237     if (replyMessage.type() != QDBusMessage::InvalidMessage)
238         return;                 // already finished
239 
240     waitForFinishedCondition.wait(&mutex);
241 }
242 
243 /*!
244     Creates a copy of the \a other pending asynchronous call. Note
245     that both objects will refer to the same pending call.
246 */
QDBusPendingCall(const QDBusPendingCall & other)247 QDBusPendingCall::QDBusPendingCall(const QDBusPendingCall &other)
248     : d(other.d)
249 {
250 }
251 
252 /*!
253     \internal
254 */
QDBusPendingCall(QDBusPendingCallPrivate * dd)255 QDBusPendingCall::QDBusPendingCall(QDBusPendingCallPrivate *dd)
256     : d(dd)
257 {
258     if (dd) {
259         bool r = dd->ref.deref();
260         Q_ASSERT(r);
261         Q_UNUSED(r);
262     }
263 }
264 
265 /*!
266     Destroys this copy of the QDBusPendingCall object. If this copy is
267     also the last copy of a pending asynchronous call, the call will
268     be canceled and no further notifications will be received. There
269     will be no way of accessing the reply's contents when it arrives.
270 */
~QDBusPendingCall()271 QDBusPendingCall::~QDBusPendingCall()
272 {
273     // d deleted by QExplicitlySharedDataPointer
274 }
275 
276 
277 /*!
278     Creates a copy of the \a other pending asynchronous call and drops
279     the reference to the previously-referenced call. Note that both
280     objects will refer to the same pending call after this function.
281 
282     If this object contained the last reference of a pending
283     asynchronous call, the call will be canceled and no further
284     notifications will be received. There will be no way of accessing
285     the reply's contents when it arrives.
286 */
operator =(const QDBusPendingCall & other)287 QDBusPendingCall &QDBusPendingCall::operator=(const QDBusPendingCall &other)
288 {
289     d = other.d;
290     return *this;
291 }
292 
293 /*!
294     \fn void QDBusPendingCall::swap(QDBusPendingCall &other)
295     \since 5.0
296 
297     Swaps this pending call instance with \a other. This function is
298     very fast and never fails.
299 */
300 
301 /*!
302     \fn bool QDBusPendingCallWatcher::isFinished() const
303 
304     Returns \c true if the pending call has finished processing and the
305     reply has been received.
306 
307     Note that this function only changes state if you call
308     waitForFinished() or if an external D-Bus event happens, which in
309     general only happens if you return to the event loop execution.
310 
311     \sa QDBusPendingReply::isFinished()
312 */
313 /*!
314     \fn template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8> bool QDBusPendingReply<T1, T2, T3, T4, T5, T6, T7, T8>::isFinished() const
315 
316     Returns \c true if the pending call has finished processing and the
317     reply has been received. If this function returns \c true, the
318     isError(), error() and reply() methods should return valid
319     information.
320 
321     Note that this function only changes state if you call
322     waitForFinished() or if an external D-Bus event happens, which in
323     general only happens if you return to the event loop execution.
324 
325     \sa QDBusPendingCallWatcher::isFinished()
326 */
327 
isFinished() const328 bool QDBusPendingCall::isFinished() const
329 {
330     if (!d)
331         return true; // considered finished
332 
333     const auto locker = qt_scoped_lock(d->mutex);
334     return d->replyMessage.type() != QDBusMessage::InvalidMessage;
335 }
336 
waitForFinished()337 void QDBusPendingCall::waitForFinished()
338 {
339     if (d) d->waitForFinished();
340 }
341 
342 /*!
343     \fn template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8> bool QDBusPendingReply<T1, T2, T3, T4, T5, T6, T7, T8>::isValid() const
344 
345     Returns \c true if the reply contains a normal reply message, false
346     if it contains anything else.
347 
348     If the pending call has not finished processing, this function
349     return false.
350 */
isValid() const351 bool QDBusPendingCall::isValid() const
352 {
353     if (!d)
354         return false;
355     const auto locker = qt_scoped_lock(d->mutex);
356     return d->replyMessage.type() == QDBusMessage::ReplyMessage;
357 }
358 
359 /*!
360     \fn template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8> bool QDBusPendingReply<T1, T2, T3, T4, T5, T6, T7, T8>::isError() const
361 
362     Returns \c true if the reply contains an error message, false if it
363     contains a normal method reply.
364 
365     If the pending call has not finished processing, this function
366     also returns \c true.
367 */
isError() const368 bool QDBusPendingCall::isError() const
369 {
370     if (!d)
371         return true; // considered finished and an error
372     const auto locker = qt_scoped_lock(d->mutex);
373     return d->replyMessage.type() == QDBusMessage::ErrorMessage;
374 }
375 
376 /*!
377     \fn template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8> QDBusError QDBusPendingReply<T1, T2, T3, T4, T5, T6, T7, T8>::error() const
378 
379     Retrieves the error content of the reply message, if it has
380     finished processing. If the reply message has not finished
381     processing or if it contains a normal reply message (non-error),
382     this function returns an invalid QDBusError.
383 */
error() const384 QDBusError QDBusPendingCall::error() const
385 {
386     if (d) {
387         const auto locker = qt_scoped_lock(d->mutex);
388         return QDBusError(d->replyMessage);
389     }
390 
391     // not connected, return an error
392     QDBusError err = QDBusError(QDBusError::Disconnected,
393                                 QDBusUtil::disconnectedErrorMessage());
394     return err;
395 }
396 
397 /*!
398     \fn template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8> QDBusMessage QDBusPendingReply<T1, T2, T3, T4, T5, T6, T7, T8>::reply() const
399 
400     Retrieves the reply message received for the asynchronous call
401     that was sent, if it has finished processing. If the pending call
402     is not finished, this function returns a QDBusMessage of type
403     QDBusMessage::InvalidMessage.
404 
405     After it has finished processing, the message type will either be
406     an error message or a normal method reply message.
407 */
reply() const408 QDBusMessage QDBusPendingCall::reply() const
409 {
410     if (!d)
411         return QDBusMessage::createError(error());
412     const auto locker = qt_scoped_lock(d->mutex);
413     return d->replyMessage;
414 }
415 
416 #if 0
417 /*
418     Sets the slot \a member in object \a target to be called when the
419     reply arrives. The slot's parameter list must match the reply
420     message's arguments for it to be called.
421 
422     It may, optionally, contain a QDBusMessage final parameter. If it
423     is present, the parameter will contain the reply message object.
424 
425     The callback will not be called if the reply is an error message.
426 
427     This function returns \c true if it could set the callback, false
428     otherwise. It is not a guarantee that the callback will be
429     called.
430 
431     \warning QDBusPendingCall only supports one callback per pending
432              asynchronous call, even if multiple QDBusPendingCall
433              objects are referencing the same pending call.
434 */
435 bool QDBusPendingCall::setReplyCallback(QObject *target, const char *member)
436 {
437     if (!d)
438         return false;
439 
440     return d->setReplyCallback(target, member);
441 }
442 #endif
443 
444 /*!
445     \since 4.6
446     Creates a QDBusPendingCall object based on the error condition
447     \a error. The resulting pending call object will be in the
448     "finished" state and QDBusPendingReply<T1, T2, T3, T4, T5, T6, T7, T8>::isError() will return true.
449 
450     \sa fromCompletedCall()
451 */
fromError(const QDBusError & error)452 QDBusPendingCall QDBusPendingCall::fromError(const QDBusError &error)
453 {
454     return fromCompletedCall(QDBusMessage::createError(error));
455 }
456 
457 /*!
458     \since 4.6
459     Creates a QDBusPendingCall object based on the message \a msg.
460     The message must be of type QDBusMessage::ErrorMessage or
461     QDBusMessage::ReplyMessage (that is, a message that is typical
462     of a completed call).
463 
464     This function is useful for code that requires simulating a pending
465     call, but that has already finished.
466 
467     \sa fromError()
468 */
fromCompletedCall(const QDBusMessage & msg)469 QDBusPendingCall QDBusPendingCall::fromCompletedCall(const QDBusMessage &msg)
470 {
471     QDBusPendingCallPrivate *d = nullptr;
472     if (msg.type() == QDBusMessage::ErrorMessage ||
473         msg.type() == QDBusMessage::ReplyMessage) {
474         d = new QDBusPendingCallPrivate(QDBusMessage(), nullptr);
475         d->replyMessage = msg;
476         d->ref.storeRelaxed(1);
477     }
478 
479     return QDBusPendingCall(d);
480 }
481 
482 
483 class QDBusPendingCallWatcherPrivate: public QObjectPrivate
484 {
485 public:
486     void _q_finished();
487 
488     Q_DECLARE_PUBLIC(QDBusPendingCallWatcher)
489 };
490 
_q_finished()491 inline void QDBusPendingCallWatcherPrivate::_q_finished()
492 {
493     Q_Q(QDBusPendingCallWatcher);
494     emit q->finished(q);
495 }
496 
497 /*!
498     Creates a QDBusPendingCallWatcher object to watch for replies on the
499     asynchronous pending call \a call and sets this object's parent
500     to \a parent.
501 */
QDBusPendingCallWatcher(const QDBusPendingCall & call,QObject * parent)502 QDBusPendingCallWatcher::QDBusPendingCallWatcher(const QDBusPendingCall &call, QObject *parent)
503     : QObject(*new QDBusPendingCallWatcherPrivate, parent), QDBusPendingCall(call)
504 {
505     if (d) {                    // QDBusPendingCall::d
506         const auto locker = qt_scoped_lock(d->mutex);
507         if (!d->watcherHelper) {
508             d->watcherHelper = new QDBusPendingCallWatcherHelper;
509             if (d->replyMessage.type() != QDBusMessage::InvalidMessage) {
510                 // cause a signal emission anyways
511                 QMetaObject::invokeMethod(d->watcherHelper, "finished", Qt::QueuedConnection);
512             }
513         }
514         d->watcherHelper->add(this);
515     }
516 }
517 
518 /*!
519     Destroys this object. If this QDBusPendingCallWatcher object was the
520     last reference to the unfinished pending call, the call will be
521     canceled.
522 */
~QDBusPendingCallWatcher()523 QDBusPendingCallWatcher::~QDBusPendingCallWatcher()
524 {
525 }
526 
527 /*!
528     \fn void QDBusPendingCallWatcher::waitForFinished()
529 
530     Suspends the execution of the calling thread until the reply is
531     received and processed. After this function returns, isFinished()
532     should return true, indicating the reply's contents are ready to
533     be processed.
534 
535     \sa QDBusPendingReply::waitForFinished()
536 */
waitForFinished()537 void QDBusPendingCallWatcher::waitForFinished()
538 {
539     if (d) {
540         d->waitForFinished();
541 
542         // our signals were queued, so deliver them
543         QCoreApplication::sendPostedEvents(d->watcherHelper, QEvent::MetaCall);
544         QCoreApplication::sendPostedEvents(this, QEvent::MetaCall);
545     }
546 }
547 QT_END_NAMESPACE
548 
549 #endif // QT_NO_DBUS
550 
551 #include "moc_qdbuspendingcall.cpp"
552