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 "qdbusabstractinterface.h"
42 #include "qdbusabstractinterface_p.h"
43 
44 #include <qcoreapplication.h>
45 #include <qthread.h>
46 
47 #include "qdbusargument.h"
48 #include "qdbuspendingcall.h"
49 #include "qdbusmessage_p.h"
50 #include "qdbusmetaobject_p.h"
51 #include "qdbusmetatype_p.h"
52 #include "qdbusservicewatcher.h"
53 #include "qdbusutil_p.h"
54 
55 #include <qdebug.h>
56 
57 #ifndef QT_NO_DBUS
58 
59 QT_BEGIN_NAMESPACE
60 
61 namespace {
62 // ### Qt6: change to a regular QEvent (customEvent)
63 // We need to use a QMetaCallEvent here because we can't override customEvent() in
64 // Qt 5. Since QDBusAbstractInterface is meant to be derived from, the vtables of
65 // classes in generated code will have a pointer to QObject::customEvent instead
66 // of to QDBusAbstractInterface::customEvent.
67 // See solution in Patch Set 1 of this change in the Qt Gerrit servers.
68 // (https://codereview.qt-project.org/#/c/126384/1)
69 class DisconnectRelayEvent : public QAbstractMetaCallEvent
70 {
71 public:
DisconnectRelayEvent(QObject * sender,const QMetaMethod & m)72     DisconnectRelayEvent(QObject *sender, const QMetaMethod &m)
73         : QAbstractMetaCallEvent(sender, m.methodIndex())
74     {}
75 
placeMetaCall(QObject * object)76     void placeMetaCall(QObject *object) override
77     {
78         QDBusAbstractInterface *iface = static_cast<QDBusAbstractInterface *>(object);
79         QDBusAbstractInterfacePrivate::finishDisconnectNotify(iface, signalId());
80     }
81 };
82 }
83 
checkIfValid(const QString & service,const QString & path,const QString & interface,bool isDynamic,bool isPeer)84 static QDBusError checkIfValid(const QString &service, const QString &path,
85                                const QString &interface, bool isDynamic, bool isPeer)
86 {
87     // We should be throwing exceptions here... oh well
88     QDBusError error;
89 
90     // dynamic interfaces (QDBusInterface) can have empty interfaces, but not service and object paths
91     // non-dynamic is the opposite: service and object paths can be empty, but not the interface
92     if (!isDynamic) {
93         // use assertion here because this should never happen, at all
94         Q_ASSERT_X(!interface.isEmpty(), "QDBusAbstractInterface", "Interface name cannot be empty");
95     }
96     if (!QDBusUtil::checkBusName(service, (isDynamic && !isPeer) ? QDBusUtil::EmptyNotAllowed : QDBusUtil::EmptyAllowed, &error))
97         return error;
98     if (!QDBusUtil::checkObjectPath(path, isDynamic ? QDBusUtil::EmptyNotAllowed : QDBusUtil::EmptyAllowed, &error))
99         return error;
100     if (!QDBusUtil::checkInterfaceName(interface, QDBusUtil::EmptyAllowed, &error))
101         return error;
102 
103     // no error
104     return QDBusError();
105 }
106 
QDBusAbstractInterfacePrivate(const QString & serv,const QString & p,const QString & iface,const QDBusConnection & con,bool isDynamic)107 QDBusAbstractInterfacePrivate::QDBusAbstractInterfacePrivate(const QString &serv,
108                                                              const QString &p,
109                                                              const QString &iface,
110                                                              const QDBusConnection& con,
111                                                              bool isDynamic)
112     : connection(con), service(serv), path(p), interface(iface),
113       lastError(checkIfValid(serv, p, iface, isDynamic, (connectionPrivate() &&
114                                                          connectionPrivate()->mode == QDBusConnectionPrivate::PeerMode))),
115       timeout(-1),
116       isValid(!lastError.isValid())
117 {
118     if (!isValid)
119         return;
120 
121     if (!connection.isConnected()) {
122         lastError = QDBusError(QDBusError::Disconnected,
123                                QDBusUtil::disconnectedErrorMessage());
124     }
125 }
126 
initOwnerTracking()127 void QDBusAbstractInterfacePrivate::initOwnerTracking()
128 {
129     if (!isValid || !connection.isConnected() || !connectionPrivate()->shouldWatchService(service))
130         return;
131 
132     QObject::connect(new QDBusServiceWatcher(service, connection, QDBusServiceWatcher::WatchForOwnerChange, q_func()),
133                      SIGNAL(serviceOwnerChanged(QString,QString,QString)),
134                      q_func(), SLOT(_q_serviceOwnerChanged(QString,QString,QString)));
135 
136     currentOwner = connectionPrivate()->getNameOwner(service);
137     if (currentOwner.isEmpty())
138         lastError = connectionPrivate()->lastError;
139 }
140 
canMakeCalls() const141 bool QDBusAbstractInterfacePrivate::canMakeCalls() const
142 {
143     // recheck only if we have a wildcard (i.e. empty) service or path
144     // if any are empty, set the error message according to QDBusUtil
145     if (service.isEmpty() && connectionPrivate()->mode != QDBusConnectionPrivate::PeerMode)
146         return QDBusUtil::checkBusName(service, QDBusUtil::EmptyNotAllowed, &lastError);
147     if (path.isEmpty())
148         return QDBusUtil::checkObjectPath(path, QDBusUtil::EmptyNotAllowed, &lastError);
149     return true;
150 }
151 
property(const QMetaProperty & mp,void * returnValuePtr) const152 bool QDBusAbstractInterfacePrivate::property(const QMetaProperty &mp, void *returnValuePtr) const
153 {
154     if (!isValid || !canMakeCalls())   // can't make calls
155         return false;
156 
157     const int type = mp.userType();
158     // is this metatype registered?
159     const char *expectedSignature = "";
160     if (int(mp.userType()) != QMetaType::QVariant) {
161         expectedSignature = QDBusMetaType::typeToSignature(type);
162         if (expectedSignature == nullptr) {
163             qWarning("QDBusAbstractInterface: type %s must be registered with Qt D-Bus before it can be "
164                      "used to read property %s.%s",
165                      mp.typeName(), qPrintable(interface), mp.name());
166             lastError = QDBusError(QDBusError::Failed,
167                                    QLatin1String("Unregistered type %1 cannot be handled")
168                                    .arg(QLatin1String(mp.typeName())));
169             return false;
170         }
171     }
172 
173     // try to read this property
174     QDBusMessage msg = QDBusMessage::createMethodCall(service, path,
175                                                       QDBusUtil::dbusInterfaceProperties(),
176                                                       QStringLiteral("Get"));
177     QDBusMessagePrivate::setParametersValidated(msg, true);
178     msg << interface << QString::fromUtf8(mp.name());
179     QDBusMessage reply = connection.call(msg, QDBus::Block, timeout);
180 
181     if (reply.type() != QDBusMessage::ReplyMessage) {
182         lastError = QDBusError(reply);
183         return false;
184     }
185     if (reply.signature() != QLatin1String("v")) {
186         QString errmsg = QLatin1String("Invalid signature `%1' in return from call to "
187                                        DBUS_INTERFACE_PROPERTIES);
188         lastError = QDBusError(QDBusError::InvalidSignature, std::move(errmsg).arg(reply.signature()));
189         return false;
190     }
191 
192     QByteArray foundSignature;
193     const char *foundType = nullptr;
194     QVariant value = qvariant_cast<QDBusVariant>(reply.arguments().at(0)).variant();
195 
196     if (value.userType() == type || type == QMetaType::QVariant
197         || (expectedSignature[0] == 'v' && expectedSignature[1] == '\0')) {
198         // simple match
199         if (type == QMetaType::QVariant) {
200             *reinterpret_cast<QVariant*>(returnValuePtr) = value;
201         } else {
202             QMetaType::destruct(type, returnValuePtr);
203             QMetaType::construct(type, returnValuePtr, value.constData());
204         }
205         return true;
206     }
207 
208     if (value.userType() == qMetaTypeId<QDBusArgument>()) {
209         QDBusArgument arg = qvariant_cast<QDBusArgument>(value);
210 
211         foundType = "user type";
212         foundSignature = arg.currentSignature().toLatin1();
213         if (foundSignature == expectedSignature) {
214             // signatures match, we can demarshall
215             return QDBusMetaType::demarshall(arg, type, returnValuePtr);
216         }
217     } else {
218         foundType = value.typeName();
219         foundSignature = QDBusMetaType::typeToSignature(value.userType());
220     }
221 
222     // there was an error...
223     const auto errmsg = QLatin1String("Unexpected `%1' (%2) when retrieving property `%3.%4' "
224                                       "(expected type `%5' (%6))");
225     lastError = QDBusError(QDBusError::InvalidSignature,
226                            errmsg.arg(QLatin1String(foundType),
227                                       QLatin1String(foundSignature),
228                                       interface,
229                                       QLatin1String(mp.name()),
230                                       QLatin1String(mp.typeName()),
231                                       QLatin1String(expectedSignature)));
232     return false;
233 }
234 
setProperty(const QMetaProperty & mp,const QVariant & value)235 bool QDBusAbstractInterfacePrivate::setProperty(const QMetaProperty &mp, const QVariant &value)
236 {
237     if (!isValid || !canMakeCalls())    // can't make calls
238         return false;
239 
240     // send the value
241     QDBusMessage msg = QDBusMessage::createMethodCall(service, path,
242                                                       QDBusUtil::dbusInterfaceProperties(),
243                                                       QStringLiteral("Set"));
244     QDBusMessagePrivate::setParametersValidated(msg, true);
245     msg << interface << QString::fromUtf8(mp.name()) << QVariant::fromValue(QDBusVariant(value));
246     QDBusMessage reply = connection.call(msg, QDBus::Block, timeout);
247 
248     if (reply.type() != QDBusMessage::ReplyMessage) {
249         lastError = QDBusError(reply);
250         return false;
251     }
252     return true;
253 }
254 
_q_serviceOwnerChanged(const QString & name,const QString & oldOwner,const QString & newOwner)255 void QDBusAbstractInterfacePrivate::_q_serviceOwnerChanged(const QString &name,
256                                                            const QString &oldOwner,
257                                                            const QString &newOwner)
258 {
259     Q_UNUSED(oldOwner);
260     Q_UNUSED(name);
261     //qDebug() << "QDBusAbstractInterfacePrivate serviceOwnerChanged" << name << oldOwner << newOwner;
262     Q_ASSERT(name == service);
263     currentOwner = newOwner;
264 }
265 
QDBusAbstractInterfaceBase(QDBusAbstractInterfacePrivate & d,QObject * parent)266 QDBusAbstractInterfaceBase::QDBusAbstractInterfaceBase(QDBusAbstractInterfacePrivate &d, QObject *parent)
267     : QObject(d, parent)
268 {
269 }
270 
qt_metacall(QMetaObject::Call _c,int _id,void ** _a)271 int QDBusAbstractInterfaceBase::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
272 {
273     int saved_id = _id;
274     _id = QObject::qt_metacall(_c, _id, _a);
275     if (_id < 0)
276         return _id;
277 
278     if (_c == QMetaObject::ReadProperty || _c == QMetaObject::WriteProperty) {
279         QMetaProperty mp = metaObject()->property(saved_id);
280         int &status = *reinterpret_cast<int *>(_a[2]);
281 
282         if (_c == QMetaObject::WriteProperty) {
283             QVariant value;
284             if (mp.userType() == qMetaTypeId<QDBusVariant>())
285                 value = reinterpret_cast<const QDBusVariant*>(_a[0])->variant();
286             else
287                 value = QVariant(mp.userType(), _a[0]);
288             status = d_func()->setProperty(mp, value) ? 1 : 0;
289         } else {
290             bool readStatus = d_func()->property(mp, _a[0]);
291             // Caller supports QVariant returns? Then we can also report errors
292             // by storing an invalid variant.
293             if (!readStatus && _a[1]) {
294                 status = 0;
295                 reinterpret_cast<QVariant*>(_a[1])->clear();
296             }
297         }
298         _id = -1;
299     }
300     return _id;
301 }
302 
303 /*!
304     \class QDBusAbstractInterface
305     \inmodule QtDBus
306     \since 4.2
307 
308     \brief The QDBusAbstractInterface class is the base class for all D-Bus interfaces in the Qt D-Bus binding, allowing access to remote interfaces.
309 
310     Generated-code classes also derive from QDBusAbstractInterface,
311     all methods described here are also valid for generated-code
312     classes. In addition to those described here, generated-code
313     classes provide member functions for the remote methods, which
314     allow for compile-time checking of the correct parameters and
315     return values, as well as property type-matching and signal
316     parameter-matching.
317 
318     \sa {qdbusxml2cpp.html}{The QDBus compiler}, QDBusInterface
319 */
320 
321 /*!
322     \internal
323     This is the constructor called from QDBusInterface::QDBusInterface.
324 */
QDBusAbstractInterface(QDBusAbstractInterfacePrivate & d,QObject * parent)325 QDBusAbstractInterface::QDBusAbstractInterface(QDBusAbstractInterfacePrivate &d, QObject *parent)
326     : QDBusAbstractInterfaceBase(d, parent)
327 {
328     d.initOwnerTracking();
329 }
330 
331 /*!
332     \internal
333     This is the constructor called from static classes derived from
334     QDBusAbstractInterface (i.e., those generated by dbusxml2cpp).
335 */
QDBusAbstractInterface(const QString & service,const QString & path,const char * interface,const QDBusConnection & con,QObject * parent)336 QDBusAbstractInterface::QDBusAbstractInterface(const QString &service, const QString &path,
337                                                const char *interface, const QDBusConnection &con,
338                                                QObject *parent)
339     : QDBusAbstractInterfaceBase(*new QDBusAbstractInterfacePrivate(service, path, QString::fromLatin1(interface),
340                                                  con, false), parent)
341 {
342     // keep track of the service owner
343     d_func()->initOwnerTracking();
344 }
345 
346 /*!
347     Releases this object's resources.
348 */
~QDBusAbstractInterface()349 QDBusAbstractInterface::~QDBusAbstractInterface()
350 {
351 }
352 
353 /*!
354     Returns \c true if this is a valid reference to a remote object. It returns \c false if
355     there was an error during the creation of this interface (for instance, if the remote
356     application does not exist).
357 
358     Note: when dealing with remote objects, it is not always possible to determine if it
359     exists when creating a QDBusInterface.
360 */
isValid() const361 bool QDBusAbstractInterface::isValid() const
362 {
363     Q_D(const QDBusAbstractInterface);
364     /* We don't retrieve the owner name for peer connections */
365     if (d->connectionPrivate() && d->connectionPrivate()->mode == QDBusConnectionPrivate::PeerMode) {
366         return d->isValid;
367     } else {
368         return !d->currentOwner.isEmpty();
369     }
370 }
371 
372 /*!
373     Returns the connection this interface is associated with.
374 */
connection() const375 QDBusConnection QDBusAbstractInterface::connection() const
376 {
377     return d_func()->connection;
378 }
379 
380 /*!
381     Returns the name of the service this interface is associated with.
382 */
service() const383 QString QDBusAbstractInterface::service() const
384 {
385     return d_func()->service;
386 }
387 
388 /*!
389     Returns the object path that this interface is associated with.
390 */
path() const391 QString QDBusAbstractInterface::path() const
392 {
393     return d_func()->path;
394 }
395 
396 /*!
397     Returns the name of this interface.
398 */
interface() const399 QString QDBusAbstractInterface::interface() const
400 {
401     return d_func()->interface;
402 }
403 
404 /*!
405     Returns the error the last operation produced, or an invalid error if the last operation did not
406     produce an error.
407 */
lastError() const408 QDBusError QDBusAbstractInterface::lastError() const
409 {
410     return d_func()->lastError;
411 }
412 
413 /*!
414     Sets the timeout in milliseconds for all future DBus calls to \a timeout.
415     -1 means the default DBus timeout (usually 25 seconds).
416 
417     \since 4.8
418 */
setTimeout(int timeout)419 void QDBusAbstractInterface::setTimeout(int timeout)
420 {
421     d_func()->timeout = timeout;
422 }
423 
424 /*!
425     Returns the current value of the timeout in milliseconds.
426     -1 means the default DBus timeout (usually 25 seconds).
427 
428     \since 4.8
429 */
timeout() const430 int QDBusAbstractInterface::timeout() const
431 {
432     return d_func()->timeout;
433 }
434 
435 /*!
436     Places a call to the remote method specified by \a method on this interface, using \a args as
437     arguments. This function returns the message that was received as a reply, which can be a normal
438     QDBusMessage::ReplyMessage (indicating success) or QDBusMessage::ErrorMessage (if the call
439     failed). The \a mode parameter specifies how this call should be placed.
440 
441     If the call succeeds, lastError() will be cleared; otherwise, it will contain the error this
442     call produced.
443 
444     Normally, you should place calls using call().
445 
446     \warning If you use \c UseEventLoop, your code must be prepared to deal with any reentrancy:
447              other method calls and signals may be delivered before this function returns, as well
448              as other Qt queued signals and events.
449 
450     \threadsafe
451 */
callWithArgumentList(QDBus::CallMode mode,const QString & method,const QList<QVariant> & args)452 QDBusMessage QDBusAbstractInterface::callWithArgumentList(QDBus::CallMode mode,
453                                                           const QString& method,
454                                                           const QList<QVariant>& args)
455 {
456     Q_D(QDBusAbstractInterface);
457 
458     if (!d->isValid || !d->canMakeCalls())
459         return QDBusMessage::createError(d->lastError);
460 
461     QString m = method;
462     // split out the signature from the method
463     int pos = method.indexOf(QLatin1Char('.'));
464     if (pos != -1)
465         m.truncate(pos);
466 
467     if (mode == QDBus::AutoDetect) {
468         // determine if this a sync or async call
469         mode = QDBus::Block;
470         const QMetaObject *mo = metaObject();
471         QByteArray match = m.toLatin1();
472 
473         for (int i = staticMetaObject.methodCount(); i < mo->methodCount(); ++i) {
474             QMetaMethod mm = mo->method(i);
475             if (mm.name() == match) {
476                 // found a method with the same name as what we're looking for
477                 // hopefully, nobody is overloading asynchronous and synchronous methods with
478                 // the same name
479 
480                 QList<QByteArray> tags = QByteArray(mm.tag()).split(' ');
481                 if (tags.contains("Q_NOREPLY"))
482                     mode = QDBus::NoBlock;
483 
484                 break;
485             }
486         }
487     }
488 
489 //    qDebug() << "QDBusAbstractInterface" << "Service" << service() << "Path:" << path();
490     QDBusMessage msg = QDBusMessage::createMethodCall(service(), path(), interface(), m);
491     QDBusMessagePrivate::setParametersValidated(msg, true);
492     msg.setArguments(args);
493 
494     QDBusMessage reply = d->connection.call(msg, mode, d->timeout);
495     if (thread() == QThread::currentThread())
496         d->lastError = QDBusError(reply);       // will clear if reply isn't an error
497 
498     // ensure that there is at least one element
499     if (reply.arguments().isEmpty())
500         reply << QVariant();
501 
502     return reply;
503 }
504 
505 /*!
506     \since 4.5
507     Places a call to the remote method specified by \a method on this
508     interface, using \a args as arguments. This function returns a
509     QDBusPendingCall object that can be used to track the status of the
510     reply and access its contents once it has arrived.
511 
512     Normally, you should place calls using asyncCall().
513 
514     \threadsafe
515 */
asyncCallWithArgumentList(const QString & method,const QList<QVariant> & args)516 QDBusPendingCall QDBusAbstractInterface::asyncCallWithArgumentList(const QString& method,
517                                                                    const QList<QVariant>& args)
518 {
519     Q_D(QDBusAbstractInterface);
520 
521     if (!d->isValid || !d->canMakeCalls())
522         return QDBusPendingCall::fromError(d->lastError);
523 
524     QDBusMessage msg = QDBusMessage::createMethodCall(service(), path(), interface(), method);
525     QDBusMessagePrivate::setParametersValidated(msg, true);
526     msg.setArguments(args);
527     return d->connection.asyncCall(msg, d->timeout);
528 }
529 
530 /*!
531     Places a call to the remote method specified by \a method
532     on this interface, using \a args as arguments. This function
533     returns immediately after queueing the call. The reply from
534     the remote function is delivered to the \a returnMethod on
535     object \a receiver. If an error occurs, the \a errorMethod
536     on object \a receiver is called instead.
537 
538     This function returns \c true if the queueing succeeds. It does
539     not indicate that the executed call succeeded. If it fails,
540     the \a errorMethod is called. If the queueing failed, this
541     function returns \c false and no slot will be called.
542 
543     The \a returnMethod must have as its parameters the types returned
544     by the function call. Optionally, it may have a QDBusMessage
545     parameter as its last or only parameter.  The \a errorMethod must
546     have a QDBusError as its only parameter.
547 
548     \since 4.3
549     \sa QDBusError, QDBusMessage
550  */
callWithCallback(const QString & method,const QList<QVariant> & args,QObject * receiver,const char * returnMethod,const char * errorMethod)551 bool QDBusAbstractInterface::callWithCallback(const QString &method,
552                                               const QList<QVariant> &args,
553                                               QObject *receiver,
554                                               const char *returnMethod,
555                                               const char *errorMethod)
556 {
557     Q_D(QDBusAbstractInterface);
558 
559     if (!d->isValid || !d->canMakeCalls())
560         return false;
561 
562     QDBusMessage msg = QDBusMessage::createMethodCall(service(),
563                                                       path(),
564                                                       interface(),
565                                                       method);
566     QDBusMessagePrivate::setParametersValidated(msg, true);
567     msg.setArguments(args);
568 
569     d->lastError = QDBusError();
570     return d->connection.callWithCallback(msg,
571                                           receiver,
572                                           returnMethod,
573                                           errorMethod,
574                                           d->timeout);
575 }
576 
577 /*!
578     \overload
579 
580     This function is deprecated. Please use the overloaded version.
581 
582     Places a call to the remote method specified by \a method
583     on this interface, using \a args as arguments. This function
584     returns immediately after queueing the call. The reply from
585     the remote function or any errors emitted by it are delivered
586     to the \a slot slot on object \a receiver.
587 
588     This function returns \c true if the queueing succeeded: it does
589     not indicate that the call succeeded. If it failed, the slot
590     will be called with an error message. lastError() will not be
591     set under those circumstances.
592 
593     \sa QDBusError, QDBusMessage
594 */
callWithCallback(const QString & method,const QList<QVariant> & args,QObject * receiver,const char * slot)595 bool QDBusAbstractInterface::callWithCallback(const QString &method,
596                                               const QList<QVariant> &args,
597                                               QObject *receiver,
598                                               const char *slot)
599 {
600     return callWithCallback(method, args, receiver, slot, nullptr);
601 }
602 
603 /*!
604     \internal
605     Catch signal connections.
606 */
connectNotify(const QMetaMethod & signal)607 void QDBusAbstractInterface::connectNotify(const QMetaMethod &signal)
608 {
609     // someone connecting to one of our signals
610     Q_D(QDBusAbstractInterface);
611     if (!d->isValid)
612         return;
613 
614     // we end up recursing here, so optimize away
615     static const QMetaMethod destroyedSignal = QMetaMethod::fromSignal(&QDBusAbstractInterface::destroyed);
616     if (signal == destroyedSignal)
617         return;
618 
619     QDBusConnectionPrivate *conn = d->connectionPrivate();
620     if (conn) {
621         conn->connectRelay(d->service, d->path, d->interface,
622                            this, signal);
623     }
624 }
625 
626 /*!
627     \internal
628     Catch signal disconnections.
629 */
disconnectNotify(const QMetaMethod & signal)630 void QDBusAbstractInterface::disconnectNotify(const QMetaMethod &signal)
631 {
632     // someone disconnecting from one of our signals
633     Q_D(QDBusAbstractInterface);
634     if (!d->isValid)
635         return;
636 
637     // disconnection is just resource freeing, so it can be delayed;
638     // let's do that later, after all the QObject mutexes have been unlocked.
639     QCoreApplication::postEvent(this, new DisconnectRelayEvent(this, signal));
640 }
641 
642 /*!
643     \internal
644     Continues the disconnect notification from above.
645 */
finishDisconnectNotify(QDBusAbstractInterface * ptr,int signalId)646 void QDBusAbstractInterfacePrivate::finishDisconnectNotify(QDBusAbstractInterface *ptr, int signalId)
647 {
648     QDBusAbstractInterfacePrivate *d = ptr->d_func();
649     QDBusConnectionPrivate *conn = d->connectionPrivate();
650     if (!conn)
651         return;
652 
653     const QMetaObject *mo = ptr->metaObject();
654     QMetaMethod signal = signalId >= 0 ? mo->method(signalId) : QMetaMethod();
655     if (signal.isValid()) {
656         if (!ptr->isSignalConnected(signal))
657             return conn->disconnectRelay(d->service, d->path, d->interface,
658                                          ptr, signal);
659     } else {
660         // wildcard disconnecting, we need to figure out which of our signals are
661         // no longer connected to anything
662         int midx = QObject::staticMetaObject.methodCount();
663         const int end = mo->methodCount();
664         for ( ; midx < end; ++midx) {
665             QMetaMethod mm = mo->method(midx);
666             if (mm.methodType() == QMetaMethod::Signal && !ptr->isSignalConnected(mm))
667                 conn->disconnectRelay(d->service, d->path, d->interface, ptr, mm);
668         }
669     }
670 }
671 
672 /*!
673     \internal
674     Get the value of the property \a propname.
675 */
internalPropGet(const char * propname) const676 QVariant QDBusAbstractInterface::internalPropGet(const char *propname) const
677 {
678     // assume this property exists and is readable
679     // we're only called from generated code anyways
680 
681     return property(propname);
682 }
683 
684 /*!
685     \internal
686     Set the value of the property \a propname to \a value.
687 */
internalPropSet(const char * propname,const QVariant & value)688 void QDBusAbstractInterface::internalPropSet(const char *propname, const QVariant &value)
689 {
690     setProperty(propname, value);
691 }
692 
693 /*!
694     \fn QDBusAbstractInterface::call(const QString &message)
695     \internal
696 */
697 
698 /*!
699     \fn template <typename...Args> QDBusMessage QDBusAbstractInterface::call(const QString &method, Args&&...args)
700 
701     Calls the method \a method on this interface and passes \a args to the method.
702     All \a args must be convertible to QVariant.
703 
704     The parameters to \c call are passed on to the remote function via D-Bus as input
705     arguments. Output arguments are returned in the QDBusMessage reply. If the reply is an error
706     reply, lastError() will also be set to the contents of the error message.
707 
708     It can be used the following way:
709 
710     \snippet code/src_qdbus_qdbusabstractinterface.cpp 0
711 
712     This example illustrates function calling with 0, 1 and 2 parameters and illustrates different
713     parameter types passed in each (the first call to \c "ProcessWorkUnicode" will contain one
714     Unicode string, the second call to \c "ProcessWork" will contain one string and one byte array).
715 
716     \note Before Qt 5.14, this function accepted a maximum of just eight (8) arguments.
717 
718     \sa callWithArgumentList()
719 */
720 
721 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
722 /*!
723     \internal
724 
725     This function exists for binary compatibility with Qt versions < 5.14.
726     Programs recompiled against Qt 5.14 will use the variadic template function
727     instead.
728 */
call(const QString & method,const QVariant & arg1,const QVariant & arg2,const QVariant & arg3,const QVariant & arg4,const QVariant & arg5,const QVariant & arg6,const QVariant & arg7,const QVariant & arg8)729 QDBusMessage QDBusAbstractInterface::call(const QString &method, const QVariant &arg1,
730                                           const QVariant &arg2,
731                                           const QVariant &arg3,
732                                           const QVariant &arg4,
733                                           const QVariant &arg5,
734                                           const QVariant &arg6,
735                                           const QVariant &arg7,
736                                           const QVariant &arg8)
737 {
738     return call(QDBus::AutoDetect, method, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
739 }
740 #endif
741 
742 /*!
743     \fn QDBusAbstractInterface::call(QDBus::CallMode mode, const QString &message)
744     \internal
745 */
746 
747 /*!
748     \fn template <typename...Args> QDBusMessage QDBusAbstractInterface::call(QDBus::CallMode mode, const QString &method, Args&&...args)
749 
750     \overload
751 
752     Calls the method \a method on this interface and passes \a args to the method.
753     All \a args must be convertible to QVariant.
754 
755     If \a mode is \c NoWaitForReply, then this function will return immediately after
756     placing the call, without waiting for a reply from the remote
757     method. Otherwise, \a mode indicates whether this function should
758     activate the Qt Event Loop while waiting for the reply to arrive.
759 
760     If this function reenters the Qt event loop in order to wait for the
761     reply, it will exclude user input. During the wait, it may deliver
762     signals and other method calls to your application. Therefore, it
763     must be prepared to handle a reentrancy whenever a call is placed
764     with call().
765 
766     \note Before Qt 5.14, this function accepted a maximum of just eight (8) arguments.
767 
768     \sa callWithArgumentList()
769 */
770 
771 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
772 /*!
773     \internal
774 
775     This function exists for binary compatibility with Qt versions < 5.14.
776     Programs recompiled against Qt 5.14 will use the variadic template function
777     instead.
778 */
call(QDBus::CallMode mode,const QString & method,const QVariant & arg1,const QVariant & arg2,const QVariant & arg3,const QVariant & arg4,const QVariant & arg5,const QVariant & arg6,const QVariant & arg7,const QVariant & arg8)779 QDBusMessage QDBusAbstractInterface::call(QDBus::CallMode mode, const QString &method,
780                                           const QVariant &arg1,
781                                           const QVariant &arg2,
782                                           const QVariant &arg3,
783                                           const QVariant &arg4,
784                                           const QVariant &arg5,
785                                           const QVariant &arg6,
786                                           const QVariant &arg7,
787                                           const QVariant &arg8)
788 {
789     QList<QVariant> argList;
790     int count = 0 + arg1.isValid() + arg2.isValid() + arg3.isValid() + arg4.isValid() +
791                 arg5.isValid() + arg6.isValid() + arg7.isValid() + arg8.isValid();
792 
793     switch (count) {
794     case 8:
795         argList.prepend(arg8);
796         Q_FALLTHROUGH();
797     case 7:
798         argList.prepend(arg7);
799         Q_FALLTHROUGH();
800     case 6:
801         argList.prepend(arg6);
802         Q_FALLTHROUGH();
803     case 5:
804         argList.prepend(arg5);
805         Q_FALLTHROUGH();
806     case 4:
807         argList.prepend(arg4);
808         Q_FALLTHROUGH();
809     case 3:
810         argList.prepend(arg3);
811         Q_FALLTHROUGH();
812     case 2:
813         argList.prepend(arg2);
814         Q_FALLTHROUGH();
815     case 1:
816         argList.prepend(arg1);
817         break;
818     }
819 
820     return callWithArgumentList(mode, method, argList);
821 }
822 #endif // Qt 5
823 
824 /*!
825     \fn QDBusAbstractInterface::asyncCall(const QString &message)
826     \internal
827 */
828 
829 /*!
830     \fn template <typename...Args> QDBusPendingCall QDBusAbstractInterface::asyncCall(const QString &method, Args&&...args)
831 
832     Calls the method \a method on this interface and passes \a args to the method.
833     All \a args must be convertible to QVariant.
834 
835     The parameters to \c call are passed on to the remote function via D-Bus as input
836     arguments. The returned QDBusPendingCall object can be used to find out information about
837     the reply.
838 
839     It can be used the following way:
840 
841     \snippet code/src_qdbus_qdbusabstractinterface.cpp 1
842 
843     This example illustrates function calling with 0, 1 and 2 parameters and illustrates different
844     parameter types passed in each (the first call to \c "ProcessWorkUnicode" will contain one
845     Unicode string, the second call to \c "ProcessWork" will contain one string and one byte array).
846 
847     \note Before Qt 5.14, this function accepted a maximum of just eight (8) arguments.
848 
849     \sa asyncCallWithArgumentList()
850 */
851 
852 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
853 /*!
854     \internal
855 
856     This function exists for binary compatibility with Qt versions < 5.14.
857     Programs recompiled against Qt 5.14 will use the variadic template function
858     instead.
859 */
asyncCall(const QString & method,const QVariant & arg1,const QVariant & arg2,const QVariant & arg3,const QVariant & arg4,const QVariant & arg5,const QVariant & arg6,const QVariant & arg7,const QVariant & arg8)860 QDBusPendingCall QDBusAbstractInterface::asyncCall(const QString &method, const QVariant &arg1,
861                                                    const QVariant &arg2,
862                                                    const QVariant &arg3,
863                                                    const QVariant &arg4,
864                                                    const QVariant &arg5,
865                                                    const QVariant &arg6,
866                                                    const QVariant &arg7,
867                                                    const QVariant &arg8)
868 {
869     QList<QVariant> argList;
870     int count = 0 + arg1.isValid() + arg2.isValid() + arg3.isValid() + arg4.isValid() +
871                 arg5.isValid() + arg6.isValid() + arg7.isValid() + arg8.isValid();
872 
873     switch (count) {
874     case 8:
875         argList.prepend(arg8);
876         Q_FALLTHROUGH();
877     case 7:
878         argList.prepend(arg7);
879         Q_FALLTHROUGH();
880     case 6:
881         argList.prepend(arg6);
882         Q_FALLTHROUGH();
883     case 5:
884         argList.prepend(arg5);
885         Q_FALLTHROUGH();
886     case 4:
887         argList.prepend(arg4);
888         Q_FALLTHROUGH();
889     case 3:
890         argList.prepend(arg3);
891         Q_FALLTHROUGH();
892     case 2:
893         argList.prepend(arg2);
894         Q_FALLTHROUGH();
895     case 1:
896         argList.prepend(arg1);
897         break;
898     }
899 
900     return asyncCallWithArgumentList(method, argList);
901 }
902 #endif // Qt 5
903 
904 /*!
905     \internal
906 */
internalConstCall(QDBus::CallMode mode,const QString & method,const QList<QVariant> & args) const907 QDBusMessage QDBusAbstractInterface::internalConstCall(QDBus::CallMode mode,
908                                                        const QString &method,
909                                                        const QList<QVariant> &args) const
910 {
911     // ### move the code here, and make the other functions call this
912     return const_cast<QDBusAbstractInterface*>(this)->callWithArgumentList(mode, method, args);
913 }
914 
doCall(QDBus::CallMode mode,const QString & method,const QVariant * args,size_t numArgs)915 QDBusMessage QDBusAbstractInterface::doCall(QDBus::CallMode mode, const QString &method, const QVariant *args, size_t numArgs)
916 {
917     QList<QVariant> list;
918     list.reserve(int(numArgs));
919     for (size_t i = 0; i < numArgs; ++i)
920         list.append(args[i]);
921     return callWithArgumentList(mode, method, list);
922 }
923 
doAsyncCall(const QString & method,const QVariant * args,size_t numArgs)924 QDBusPendingCall QDBusAbstractInterface::doAsyncCall(const QString &method, const QVariant *args, size_t numArgs)
925 {
926     QList<QVariant> list;
927     list.reserve(int(numArgs));
928     for (size_t i = 0; i < numArgs; ++i)
929         list.append(args[i]);
930     return asyncCallWithArgumentList(method, list);
931 }
932 
933 QT_END_NAMESPACE
934 
935 #endif // QT_NO_DBUS
936 
937 #include "moc_qdbusabstractinterface.cpp"
938