File indexing completed on 2024-12-22 04:33:56

0001 //SPDX-FileCopyrightText: 2015 Jolla Ltd. <valerio.valerio@jolla.com>
0002 //SPDX-FileContributor: Andres Gomez
0003 //
0004 //SPDX-License-Identifier: LGPL-2.1-or-later
0005 
0006 
0007 #include "dbusextendedpendingcallwatcher_p.h"
0008 
0009 #include <dbusextendedabstractinterface.h>
0010 
0011 #include <QtDBus/QDBusMessage>
0012 #include <QtDBus/QDBusPendingCall>
0013 #include <QtDBus/QDBusPendingCallWatcher>
0014 #include <QtDBus/QDBusPendingReply>
0015 
0016 #include <QtCore/QDebug>
0017 #include <QtCore/QMetaProperty>
0018 
0019 
0020 Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, dBusPropertiesInterface, ("org.freedesktop.DBus.Properties"))
0021 Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, dBusPropertiesChangedSignal, ("PropertiesChanged"))
0022 Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, propertyChangedSignature, ("propertyChanged(QString,QVariant)"))
0023 Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, propertyInvalidatedSignature, ("propertyInvalidated(QString)"))
0024 
0025 
0026 DBusExtendedAbstractInterface::DBusExtendedAbstractInterface(const QString &service, const QString &path, const char *interface, const QDBusConnection &connection, QObject *parent)
0027     : QDBusAbstractInterface(service, path, interface, connection, parent)
0028     , m_sync(false)
0029     , m_useCache(false)
0030     , m_getAllPendingCallWatcher(0)
0031     , m_propertiesChangedConnected(false)
0032 {
0033 }
0034 
0035 DBusExtendedAbstractInterface::~DBusExtendedAbstractInterface()
0036 {
0037 }
0038 
0039 void DBusExtendedAbstractInterface::getAllProperties()
0040 {
0041     m_lastExtendedError = QDBusError();
0042 
0043     if (!isValid()) {
0044         QString errorMessage = QStringLiteral("This Extended DBus interface is not valid yet.");
0045         m_lastExtendedError = QDBusMessage::createError(QDBusError::Failed, errorMessage);
0046         qDebug() << Q_FUNC_INFO << errorMessage;
0047         return;
0048     }
0049 
0050     if (!m_sync && m_getAllPendingCallWatcher) {
0051         // Call already in place, not repeating ...
0052         return;
0053     }
0054 
0055     QDBusMessage msg = QDBusMessage::createMethodCall(service(), path(), *dBusPropertiesInterface(), QStringLiteral("GetAll"));
0056     msg << interface();
0057 
0058     if (m_sync) {
0059         QDBusMessage reply = connection().call(msg);
0060 
0061         if (reply.type() != QDBusMessage::ReplyMessage) {
0062             m_lastExtendedError = QDBusError(reply);
0063             qWarning() << Q_FUNC_INFO << m_lastExtendedError.message();
0064             return;
0065         }
0066 
0067         if (reply.signature() != QLatin1String("a{sv}")) {
0068             QString errorMessage = QStringLiteral("Invalid signature \"%1\" in return from call to %2")
0069                 .arg(reply.signature(),
0070                      QString(*dBusPropertiesInterface()));
0071             qWarning() << Q_FUNC_INFO << errorMessage;
0072             m_lastExtendedError = QDBusError(QDBusError::InvalidSignature, errorMessage);
0073             return;
0074         }
0075 
0076         QVariantMap value = reply.arguments().at(0).toMap();
0077         onPropertiesChanged(interface(), value, QStringList());
0078     } else {
0079         QDBusPendingReply<QVariantMap> async = connection().asyncCall(msg);
0080         m_getAllPendingCallWatcher = new QDBusPendingCallWatcher(async, this);
0081 
0082         connect(m_getAllPendingCallWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(onAsyncGetAllPropertiesFinished(QDBusPendingCallWatcher*)));
0083         return;
0084     }
0085 }
0086 
0087 void DBusExtendedAbstractInterface::connectNotify(const QMetaMethod &signal)
0088 {
0089     if (signal.methodType() == QMetaMethod::Signal
0090         && (signal.methodSignature() == *propertyChangedSignature()
0091             || signal.methodSignature() == *propertyInvalidatedSignature())) {
0092         if (!m_propertiesChangedConnected) {
0093             QStringList argumentMatch;
0094             argumentMatch << interface();
0095             connection().connect(service(), path(), *dBusPropertiesInterface(), *dBusPropertiesChangedSignal(),
0096                                  argumentMatch, QString(),
0097                                  this, SLOT(onPropertiesChanged(QString, QVariantMap, QStringList)));
0098 
0099             m_propertiesChangedConnected = true;
0100             return;
0101         }
0102     } else {
0103         QDBusAbstractInterface::connectNotify(signal);
0104     }
0105 }
0106 
0107 void DBusExtendedAbstractInterface::disconnectNotify(const QMetaMethod &signal)
0108 {
0109     if (signal.methodType() == QMetaMethod::Signal
0110         && (signal.methodSignature() == *propertyChangedSignature()
0111             || signal.methodSignature() == *propertyInvalidatedSignature())) {
0112         if (m_propertiesChangedConnected
0113             && 0 == receivers(propertyChangedSignature()->constData())
0114             && 0 == receivers(propertyInvalidatedSignature()->constData())) {
0115             QStringList argumentMatch;
0116             argumentMatch << interface();
0117             connection().disconnect(service(), path(), *dBusPropertiesInterface(), *dBusPropertiesChangedSignal(),
0118                                  argumentMatch, QString(),
0119                                  this, SLOT(onPropertiesChanged(QString, QVariantMap, QStringList)));
0120 
0121             m_propertiesChangedConnected = false;
0122             return;
0123         }
0124     } else {
0125         QDBusAbstractInterface::disconnectNotify(signal);
0126     }
0127 }
0128 
0129 QVariant DBusExtendedAbstractInterface::internalPropGet(const char *propname, void *propertyPtr)
0130 {
0131     m_lastExtendedError = QDBusError();
0132 
0133     if (m_useCache) {
0134         int propertyIndex = metaObject()->indexOfProperty(propname);
0135         QMetaProperty metaProperty = metaObject()->property(propertyIndex);
0136         return QVariant(metaProperty.metaType(), propertyPtr);
0137     }
0138 
0139     if (m_sync) {
0140         return property(propname);
0141     } else {
0142         if (!isValid()) {
0143             QString errorMessage = QStringLiteral("This Extended DBus interface is not valid yet.");
0144             m_lastExtendedError = QDBusMessage::createError(QDBusError::Failed, errorMessage);
0145             qDebug() << Q_FUNC_INFO << errorMessage;
0146             return QVariant();
0147         }
0148 
0149         int propertyIndex = metaObject()->indexOfProperty(propname);
0150 
0151         if (-1 == propertyIndex) {
0152             QString errorMessage = QStringLiteral("Got unknown property \"%1\" to read")
0153                     .arg(QString::fromLatin1(propname));
0154             m_lastExtendedError = QDBusMessage::createError(QDBusError::Failed, errorMessage);
0155             qWarning() << Q_FUNC_INFO << errorMessage;
0156             return QVariant();
0157         }
0158 
0159         QMetaProperty metaProperty = metaObject()->property(propertyIndex);
0160 
0161         if (!metaProperty.isReadable()) {
0162             QString errorMessage = QStringLiteral("Property \"%1\" is NOT readable")
0163                     .arg(QString::fromLatin1(propname));
0164             m_lastExtendedError = QDBusMessage::createError(QDBusError::Failed, errorMessage);
0165             qWarning() << Q_FUNC_INFO << errorMessage;
0166             return QVariant();
0167         }
0168 
0169         // is this metatype registered?
0170         const char *expectedSignature = "";
0171         if (metaProperty.metaType() != QMetaType(QMetaType::QVariant)) {
0172             expectedSignature = QDBusMetaType::typeToSignature(metaProperty.metaType());
0173             if (0 == expectedSignature) {
0174                 QString errorMessage =
0175                         QStringLiteral("Type %1 must be registered with Qt D-Bus "
0176                                        "before it can be used to read property "
0177                                        "%2.%3")
0178                         .arg(metaProperty.typeName(),
0179                              interface(),
0180                              propname);
0181                 m_lastExtendedError = QDBusMessage::createError(QDBusError::Failed, errorMessage);
0182                 qWarning() << Q_FUNC_INFO << errorMessage;
0183                 return QVariant();
0184             }
0185         }
0186 
0187         asyncProperty(propname);
0188         return QVariant(metaProperty.metaType(), propertyPtr);
0189     }
0190 }
0191 
0192 void DBusExtendedAbstractInterface::internalPropSet(const char *propname, const QVariant &value, void *propertyPtr)
0193 {
0194     m_lastExtendedError = QDBusError();
0195 
0196     if (m_sync) {
0197         setProperty(propname, value);
0198     } else {
0199         if (!isValid()) {
0200             QString errorMessage = QStringLiteral("This interface is not yet valid");
0201             m_lastExtendedError = QDBusMessage::createError(QDBusError::Failed, errorMessage);
0202             qDebug() << Q_FUNC_INFO << errorMessage;
0203             return;
0204         }
0205 
0206         int propertyIndex = metaObject()->indexOfProperty(propname);
0207 
0208         if (-1 == propertyIndex) {
0209             QString errorMessage = QStringLiteral("Got unknown property \"%1\" to write")
0210                     .arg(QString::fromLatin1(propname));
0211             m_lastExtendedError = QDBusMessage::createError(QDBusError::Failed, errorMessage);
0212             qWarning() << Q_FUNC_INFO << errorMessage;
0213             return;
0214         }
0215 
0216         QMetaProperty metaProperty = metaObject()->property(propertyIndex);
0217 
0218         if (!metaProperty.isWritable()) {
0219             QString errorMessage = QStringLiteral("Property \"%1\" is NOT writable")
0220                     .arg(QString::fromLatin1(propname));
0221             m_lastExtendedError = QDBusMessage::createError(QDBusError::Failed, errorMessage);
0222             qWarning() << Q_FUNC_INFO << errorMessage;
0223             return;
0224         }
0225 
0226         asyncSetProperty(propname, QVariant(metaProperty.metaType(), propertyPtr));
0227     }
0228 }
0229 
0230 QVariant DBusExtendedAbstractInterface::asyncProperty(const QString &propertyName)
0231 {
0232     QDBusMessage msg = QDBusMessage::createMethodCall(service(), path(), *dBusPropertiesInterface(), QStringLiteral("Get"));
0233     msg << interface() << propertyName;
0234     QDBusPendingReply<QVariant> async = connection().asyncCall(msg);
0235     DBusExtendedPendingCallWatcher *watcher = new DBusExtendedPendingCallWatcher(async, propertyName, QVariant(), this);
0236 
0237     connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(onAsyncPropertyFinished(QDBusPendingCallWatcher*)));
0238 
0239     return QVariant();
0240 }
0241 
0242 void DBusExtendedAbstractInterface::asyncSetProperty(const QString &propertyName, const QVariant &value)
0243 {
0244     QDBusMessage msg = QDBusMessage::createMethodCall(service(), path(), *dBusPropertiesInterface(), QStringLiteral("Set"));
0245     msg << interface() << propertyName << value;
0246     QDBusPendingReply<QVariant> async = connection().asyncCall(msg);
0247     DBusExtendedPendingCallWatcher *watcher = new DBusExtendedPendingCallWatcher(async, propertyName, value, this);
0248 
0249     connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(onAsyncSetPropertyFinished(QDBusPendingCallWatcher*)));
0250 }
0251 
0252 
0253 void DBusExtendedAbstractInterface::onAsyncPropertyFinished(DBusExtendedPendingCallWatcher *watcher)
0254 {
0255     QDBusPendingReply<QVariant> reply = *watcher;
0256 
0257     if (reply.isError()) {
0258         m_lastExtendedError = reply.error();
0259     } else {
0260         int propertyIndex = metaObject()->indexOfProperty(watcher->asyncProperty().toLatin1().constData());
0261         QVariant value = demarshall(interface(),
0262                                     metaObject()->property(propertyIndex),
0263                                     reply.value(),
0264                                     &m_lastExtendedError);
0265 
0266         if (m_lastExtendedError.isValid()) {
0267             emit propertyInvalidated(watcher->asyncProperty());
0268         } else {
0269             emit propertyChanged(watcher->asyncProperty(), value);
0270         }
0271     }
0272 
0273     emit asyncPropertyFinished(watcher->asyncProperty());
0274     watcher->deleteLater();
0275 }
0276 
0277 void DBusExtendedAbstractInterface::onAsyncSetPropertyFinished(DBusExtendedPendingCallWatcher *watcher)
0278 {
0279     QDBusPendingReply<QVariant> reply = *watcher;
0280 
0281     if (reply.isError()) {
0282         m_lastExtendedError = reply.error();
0283     } else {
0284         m_lastExtendedError = QDBusError();
0285     }
0286 
0287     emit asyncSetPropertyFinished(watcher->asyncProperty());
0288 
0289     // Resetting the property to its previous value after sending the
0290     // finished signal
0291     if (reply.isError()) {
0292         m_lastExtendedError = QDBusError();
0293         emit propertyChanged(watcher->asyncProperty(), watcher->previousValue());
0294     }
0295 
0296     watcher->deleteLater();
0297 }
0298 
0299 void DBusExtendedAbstractInterface::onAsyncGetAllPropertiesFinished(QDBusPendingCallWatcher *watcher)
0300 {
0301     m_getAllPendingCallWatcher = 0;
0302 
0303     QDBusPendingReply<QVariantMap> reply = *watcher;
0304 
0305     if (reply.isError()) {
0306         m_lastExtendedError = reply.error();
0307     } else {
0308         m_lastExtendedError = QDBusError();
0309     }
0310 
0311     emit asyncGetAllPropertiesFinished();
0312 
0313     if (!reply.isError()) {
0314         onPropertiesChanged(interface(), reply.value(), QStringList());
0315     }
0316 
0317     watcher->deleteLater();
0318 }
0319 
0320 void DBusExtendedAbstractInterface::onPropertiesChanged(const QString& interfaceName,
0321                                                         const QVariantMap& changedProperties,
0322                                                         const QStringList& invalidatedProperties)
0323 {
0324     if (interfaceName == interface()) {
0325         QVariantMap::const_iterator i = changedProperties.constBegin();
0326         while (i != changedProperties.constEnd()) {
0327             int propertyIndex = metaObject()->indexOfProperty(i.key().toLatin1().constData());
0328 
0329             if (-1 == propertyIndex) {
0330                 qDebug() << Q_FUNC_INFO << "Got unknown changed property" <<  i.key();
0331             } else {
0332                 QVariant value = demarshall(interface(), metaObject()->property(propertyIndex), i.value(), &m_lastExtendedError);
0333 
0334                 if (m_lastExtendedError.isValid()) {
0335                     emit propertyInvalidated(i.key());
0336                 } else {
0337                     emit propertyChanged(i.key(), value);
0338                 }
0339             }
0340 
0341             ++i;
0342         }
0343 
0344         QStringList::const_iterator j = invalidatedProperties.constBegin();
0345         while (j != invalidatedProperties.constEnd()) {
0346             if (-1 == metaObject()->indexOfProperty(j->toLatin1().constData())) {
0347                 qDebug() << Q_FUNC_INFO << "Got unknown invalidated property" <<  *j;
0348             } else {
0349                 m_lastExtendedError = QDBusError();
0350                 emit propertyInvalidated(*j);
0351             }
0352 
0353             ++j;
0354         }
0355     }
0356 }
0357 
0358 QVariant DBusExtendedAbstractInterface::demarshall(const QString &interface, const QMetaProperty &metaProperty, const QVariant &value, QDBusError *error)
0359 {
0360     Q_ASSERT(metaProperty.isValid());
0361     Q_ASSERT(error != 0);
0362 
0363     if (value.userType() == metaProperty.userType()) {
0364         // No need demarshalling. Passing back straight away ...
0365         *error = QDBusError();
0366         return value;
0367     }
0368 
0369     QVariant result = QVariant(metaProperty.metaType(), (void *)nullptr);
0370     QString errorMessage;
0371     const char *expectedSignature = QDBusMetaType::typeToSignature(metaProperty.metaType());
0372 
0373     if (value.userType() == qMetaTypeId<QDBusArgument>()) {
0374         // demarshalling a DBus argument ...
0375         QDBusArgument dbusArg = value.value<QDBusArgument>();
0376 
0377         if (expectedSignature == dbusArg.currentSignature().toLatin1()) {
0378             QDBusMetaType::demarshall(dbusArg, metaProperty.metaType(), result.data());
0379             if (!result.isValid()) {
0380                 errorMessage = QStringLiteral("Unexpected failure demarshalling "
0381                                               "upon PropertiesChanged signal arrival "
0382                                               "for property `%3.%4' (expected type `%5' (%6))")
0383                                    .arg(interface,
0384                                         QString::fromLatin1(metaProperty.name()),
0385                                         QString::fromLatin1(metaProperty.typeName()),
0386                                         expectedSignature);
0387             }
0388         } else {
0389             errorMessage = QStringLiteral("Unexpected `user type' (%2) "
0390                                           "upon PropertiesChanged signal arrival "
0391                                           "for property `%3.%4' (expected type `%5' (%6))")
0392                                .arg(dbusArg.currentSignature(),
0393                                     interface,
0394                                     QString::fromLatin1(metaProperty.name()),
0395                                     QString::fromLatin1(metaProperty.typeName()),
0396                                     QString::fromLatin1(expectedSignature));
0397         }
0398     } else {
0399         const char *actualSignature = QDBusMetaType::typeToSignature(value.metaType());
0400 
0401         errorMessage = QStringLiteral("Unexpected `%1' (%2) "
0402                                       "upon PropertiesChanged signal arrival "
0403                                       "for property `%3.%4' (expected type `%5' (%6))")
0404                            .arg(QString::fromLatin1(value.typeName()),
0405                                 QString::fromLatin1(actualSignature),
0406                                 interface,
0407                                 QString::fromLatin1(metaProperty.name()),
0408                                 QString::fromLatin1(metaProperty.typeName()),
0409                                 QString::fromLatin1(expectedSignature));
0410     }
0411 
0412     if (errorMessage.isEmpty()) {
0413         *error = QDBusError();
0414     } else {
0415         *error = QDBusMessage::createError(QDBusError::InvalidSignature, errorMessage);
0416         qDebug() << Q_FUNC_INFO << errorMessage;
0417     }
0418 
0419     return result;
0420 }