diff options
author | Ievgenii Meshcheriakov <ievgenii.meshcheriakov@qt.io> | 2023-06-19 16:09:30 +0200 |
---|---|---|
committer | Ievgenii Meshcheriakov <ievgenii.meshcheriakov@qt.io> | 2023-06-20 19:53:47 +0200 |
commit | 2509fc508a76600d9e090df1583711197e337339 (patch) | |
tree | c50989322997e5f85a1167514dec6ccd6d4dc0f4 | |
parent | 666ce51d4eb6b5dd312f98e2d7a18c54b59945e4 (diff) |
QDBus: Transform fallback interface names according to the spec
ACE-encode the domain names, replace dashes with underscores,
prepend underscore to domain name parts that start with a digit.
Add a regression test into tst_qdbusinterface.
Fixes: QTBUG-71674
Change-Id: I92e0c6889163c0eccc4c833f2058d759631f562c
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
-rw-r--r-- | src/dbus/qdbusmisc.cpp | 50 | ||||
-rw-r--r-- | tests/auto/dbus/qdbusinterface/tst_qdbusinterface.cpp | 60 |
2 files changed, 95 insertions, 15 deletions
diff --git a/src/dbus/qdbusmisc.cpp b/src/dbus/qdbusmisc.cpp index 502624cd66..5a9d8f548f 100644 --- a/src/dbus/qdbusmisc.cpp +++ b/src/dbus/qdbusmisc.cpp @@ -8,6 +8,7 @@ #include <QtCore/qlist.h> #include <QtCore/qmetaobject.h> #include <QtCore/qvariant.h> +#include <private/qurl_p.h> #include "qdbusutil_p.h" #include "qdbusconnection_p.h" @@ -58,23 +59,42 @@ QString qDBusInterfaceFromMetaObject(const QMetaObject *mo) } else if (!QCoreApplication::instance()|| QCoreApplication::instance()->applicationName().isEmpty()) { interface.prepend("local."_L1); - } else { - interface.prepend(u'.').prepend(QCoreApplication::instance()->applicationName()); + } else { + QString domainName = QCoreApplication::instance()->applicationName(); const QString organizationDomain = QCoreApplication::instance()->organizationDomain(); - const auto domainName = QStringView{organizationDomain}.split(u'.', Qt::SkipEmptyParts); - if (domainName.isEmpty()) { - interface.prepend("local."_L1); - } else { - QString composedDomain; - // + 1 for additional dot, e.g. organizationDomain equals "example.com", - // then composedDomain will be equal "com.example." - composedDomain.reserve(organizationDomain.size() + 1); - for (auto it = domainName.rbegin(), end = domainName.rend(); it != end; ++it) - composedDomain += *it + u'.'; - - interface.prepend(composedDomain); + if (organizationDomain.isEmpty()) + domainName.append(".local"_L1); + else + domainName.append(u'.').append(organizationDomain); + + // Domain names used to produce interface names should be IDN-encoded. + QString encodedDomainName = qt_ACE_do(domainName, ToAceOnly, ForbidLeadingDot); + if (encodedDomainName.isEmpty()) { + interface.prepend("local."_L1); + return interface; } - } + + // Hyphens are not allowed in interface names and should be replaced + // by underscores. + encodedDomainName.replace(u'-', u'_'); + + auto nameParts = QStringView{ encodedDomainName }.split(u'.', Qt::SkipEmptyParts); + + QString composedDomain; + // + 1 for additional dot, e.g. domainName equals "App.example.com", + // then composedDomain will be equal "com.example.App." + composedDomain.reserve(encodedDomainName.size() + nameParts.size() + 1); + for (auto it = nameParts.rbegin(), end = nameParts.rend(); it != end; ++it) { + // An interface name cannot start with a digit, and cannot + // contain digits immediately following a period. Prefix such + // digits with underscores. + if (it->first().isDigit()) + composedDomain += u'_'; + composedDomain += *it + u'.'; + } + + interface.prepend(composedDomain); + } } return interface; diff --git a/tests/auto/dbus/qdbusinterface/tst_qdbusinterface.cpp b/tests/auto/dbus/qdbusinterface/tst_qdbusinterface.cpp index d4b61d27dd..b5e2aa9599 100644 --- a/tests/auto/dbus/qdbusinterface/tst_qdbusinterface.cpp +++ b/tests/auto/dbus/qdbusinterface/tst_qdbusinterface.cpp @@ -20,6 +20,8 @@ #include "../qdbusmarshall/common.h" #include "myobject.h" +using namespace Qt::StringLiterals; + #define TEST_INTERFACE_NAME "org.qtproject.QtDBus.MyObject" #define TEST_SIGNAL_NAME "somethingHappened" @@ -198,6 +200,10 @@ private slots: void complexPropertyWritePeer(); void interactiveAuthorizationRequired(); + + void interfaceNameFallback_data(); + void interfaceNameFallback(); + private: QProcess proc; }; @@ -1147,6 +1153,60 @@ void tst_QDBusInterface::interactiveAuthorizationRequired() QVERIFY(reply.arguments().at(0).toBool()); } +class TestObject : public QObject +{ + Q_OBJECT +public Q_SLOTS: + void test() { } +}; + +void tst_QDBusInterface::interfaceNameFallback_data() +{ + QTest::addColumn<QString>("appName"); + QTest::addColumn<QString>("orgDomain"); + QTest::addColumn<QString>("interfaceName"); + + QTest::addRow("empty.empty") << "" << "" << "local.tst_qdbusinterface"; + QTest::addRow("with-domain") << "" << "qt-project.org" << "org.qt_project.tst_qdbusinterface"; + QTest::addRow("numbers") << "prog42" << "7-zip.org" << "org._7_zip.prog42"; + QTest::addRow("non-latin1") << u"\u00e6"_s << u"\u00e5"_s << "xn__5ca.xn__6ca"; +} + +void tst_QDBusInterface::interfaceNameFallback() +{ + QFETCH(QString, appName); + QFETCH(QString, orgDomain); + QFETCH(QString, interfaceName); + + auto app = QCoreApplication::instance(); + auto oldApplicationName = app->applicationName(); + auto oldOrganizationDomain = app->organizationDomain(); + + app->setApplicationName(appName); + app->setOrganizationDomain(orgDomain); + auto obj = new TestObject; + + auto cleanup = qScopeGuard([&] { + obj->deleteLater(); + app->setApplicationName(oldApplicationName); + app->setOrganizationDomain(oldOrganizationDomain); + }); + + auto con = QDBusConnection::sessionBus(); + const QString path = "/interfaceNameFallback"_L1; + + QVERIFY(con.registerObject(path, obj, QDBusConnection::ExportAllContents)); + + QDBusInterface interface(con.baseService(), path, "org.freedesktop.DBus.Introspectable"_L1, + con); + auto reply = interface.call("Introspect"); + + QCOMPARE(reply.type(), QDBusMessage::ReplyMessage); + QString tag = u"<interface name=\"%1.TestObject\">"_s.arg(interfaceName); + auto result = reply.arguments().at(0).toString(); + QVERIFY2(result.contains(tag), qUtf8Printable(u"Tag '%1' not found\n%2"_s.arg(tag, result))); +} + QTEST_MAIN(tst_QDBusInterface) #include "tst_qdbusinterface.moc" |