diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -18,6 +18,7 @@ avahi_server_interface.cpp avahi_serviceresolver_interface.cpp avahi_entrygroup_interface.cpp + avahi_listener.cpp ) qt5_add_dbus_interface (kdnssd_LIB_SRCS org.freedesktop.Avahi.DomainBrowser.xml avahi_domainbrowser_interface ) qt5_add_dbus_interface (kdnssd_LIB_SRCS org.freedesktop.Avahi.ServiceBrowser.xml avahi_servicebrowser_interface ) diff --git a/src/avahi-domainbrowser.cpp b/src/avahi-domainbrowser.cpp --- a/src/avahi-domainbrowser.cpp +++ b/src/avahi-domainbrowser.cpp @@ -1,6 +1,7 @@ /* This file is part of the KDE project * * Copyright (C) 2004 Jakub Stachowski + * Copyright (C) 2018 Harald Sitter * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -45,18 +46,58 @@ return; } d->m_started = true; + + // Do not race! + // https://github.com/lathiat/avahi/issues/9 + // Avahi's DBus API is incredibly racey with signals getting fired + // immediately after a request was made even though we may not yet be + // listening. In lieu of a proper upstream fix for this we'll unfortunately + // have to resort to this hack: + // We register to all signals regardless of path and then filter them once + // we know what "our" path is. This is much more fragile than a proper + // QDBusInterface assisted signal connection but unfortunately the only way + // we can reliably prevent signals getting lost in the race. + // This uses a fancy trick whereby using QDBusMessage as last argument will + // give us the correct signal argument types as well as the underlying + // message so that we may check the message path. + QDBusConnection::systemBus() + .connect("org.freedesktop.Avahi", + "", + "org.freedesktop.Avahi.DomainBrowser", + "ItemNew", + d, + SLOT(gotGlobalItemNew(int,int,QString,uint,QDBusMessage))); + QDBusConnection::systemBus() + .connect("org.freedesktop.Avahi", + "", + "org.freedesktop.Avahi.DomainBrowser", + "ItemRemove", + d, + SLOT(gotGlobalItemRemove(int,int,QString,uint,QDBusMessage))); + QDBusConnection::systemBus() + .connect("org.freedesktop.Avahi", + "", + "org.freedesktop.Avahi.DomainBrowser", + "AllForNow", + d, + SLOT(gotGlobalAllForNow(QDBusMessage))); + d->m_dbusObjectPath.clear(); + org::freedesktop::Avahi::Server s(QStringLiteral("org.freedesktop.Avahi"), QStringLiteral("/"), QDBusConnection::systemBus()); QDBusReply rep = s.DomainBrowserNew(-1, -1, QString(), (d->m_type == Browsing) ? AVAHI_DOMAIN_BROWSER_BROWSE : AVAHI_DOMAIN_BROWSER_REGISTER, 0); - if (!rep.isValid()) { return; } - org::freedesktop::Avahi::DomainBrowser *b = new org::freedesktop::Avahi::DomainBrowser(QStringLiteral("org.freedesktop.Avahi"), rep.value().path(), - QDBusConnection::systemBus()); - connect(b, SIGNAL(ItemNew(int,int,QString,uint)), d, SLOT(gotNewDomain(int,int,QString,uint))); - connect(b, SIGNAL(ItemRemove(int,int,QString,uint)), d, SLOT(gotRemoveDomain(int,int,QString,uint))); - d->m_browser = b; + + d->m_dbusObjectPath = rep.value().path(); + + // This is held because we need to explicitly Free it! + d->m_browser = new org::freedesktop::Avahi::DomainBrowser( + s.service(), + d->m_dbusObjectPath, + s.connection()); + if (d->m_type == Browsing) { QString domains_evar = QString::fromLocal8Bit(qgetenv("AVAHI_BROWSE_DOMAINS")); if (!domains_evar.isEmpty()) { @@ -72,9 +113,38 @@ while (!domains_cfg.atEnd()) { d->gotNewDomain(-1, -1, QString::fromUtf8(domains_cfg.readLine().data()).trimmed(), 0); } + } +} +void DomainBrowserPrivate::gotGlobalItemNew(int interface, + int protocol, + const QString &domain, + uint flags, + QDBusMessage msg) +{ + if (!isOurMsg(msg)) { + return; } + gotNewDomain(interface, protocol, domain, flags); +} +void DomainBrowserPrivate::gotGlobalItemRemove(int interface, + int protocol, + const QString &domain, + uint flags, + QDBusMessage msg) +{ + if (!isOurMsg(msg)) { + return; + } + gotRemoveDomain(interface, protocol, domain, flags); +} + +void DomainBrowserPrivate::gotGlobalAllForNow(QDBusMessage msg) +{ + if (!isOurMsg(msg)) { + return; + } } void DomainBrowserPrivate::gotNewDomain(int, int, const QString &domain, uint) diff --git a/src/avahi-domainbrowser_p.h b/src/avahi-domainbrowser_p.h --- a/src/avahi-domainbrowser_p.h +++ b/src/avahi-domainbrowser_p.h @@ -23,12 +23,13 @@ #include #include "domainbrowser.h" +#include "avahi_listener_p.h" #include "avahi_domainbrowser_interface.h" namespace KDNSSD { -class DomainBrowserPrivate : public QObject +class DomainBrowserPrivate : public QObject, public AvahiListener { Q_OBJECT public: @@ -45,10 +46,24 @@ DomainBrowser *m_parent = nullptr; bool m_started = false; QSet m_domains; + public Q_SLOTS: + // NB: The global slots are runtime connected! If their signature changes + // make sure the SLOT() signature gets updated! + void gotGlobalItemNew(int interface, + int protocol, + const QString &domain, + uint flags, + QDBusMessage msg); + void gotGlobalItemRemove(int interface, + int protocol, + const QString &domain, + uint flags, + QDBusMessage msg); + void gotGlobalAllForNow(QDBusMessage msg); + void gotNewDomain(int, int, const QString &, uint); void gotRemoveDomain(int, int, const QString &, uint); - }; } diff --git a/src/avahi-publicservice.cpp b/src/avahi-publicservice.cpp --- a/src/avahi-publicservice.cpp +++ b/src/avahi-publicservice.cpp @@ -62,6 +62,16 @@ } } +void PublicServicePrivate::gotGlobalStateChanged(int state, + const QString &error, + QDBusMessage msg) +{ + if (!isOurMsg(msg)) { + return; + } + groupStateChanged(state, error); +} + void PublicService::setServiceName(const QString &serviceName) { K_D; @@ -157,12 +167,38 @@ { registerTypes(); if (!m_group) { + // Do not race! + // https://github.com/lathiat/avahi/issues/9 + // Avahi's DBus API is incredibly racey with signals getting fired + // immediately after a request was made even though we may not yet be + // listening. In lieu of a proper upstream fix for this we'll unfortunately + // have to resort to this hack: + // We register to all signals regardless of path and then filter them once + // we know what "our" path is. This is much more fragile than a proper + // QDBusInterface assisted signal connection but unfortunately the only way + // we can reliably prevent signals getting lost in the race. + // This uses a fancy trick whereby using QDBusMessage as last argument will + // give us the correct signal argument types as well as the underlying + // message so that we may check the message path. + QDBusConnection::systemBus() + .connect("org.freedesktop.Avahi", + "", + "org.freedesktop.Avahi.EntryGroup", + "StateChanged", + this, + SLOT(gotGlobalStateChanged(int,QString,QDBusMessage))); + m_dbusObjectPath.clear(); + QDBusReply rep = m_server->EntryGroupNew(); if (!rep.isValid()) { return false; } - m_group = new org::freedesktop::Avahi::EntryGroup("org.freedesktop.Avahi", rep.value().path(), QDBusConnection::systemBus()); - connect(m_group, SIGNAL(StateChanged(int,QString)), this, SLOT(groupStateChanged(int,QString))); + + m_dbusObjectPath = rep.value().path(); + + m_group = new org::freedesktop::Avahi::EntryGroup("org.freedesktop.Avahi", + m_dbusObjectPath, + QDBusConnection::systemBus()); } if (m_serviceName.isNull()) { QDBusReply rep = m_server->GetHostName(); diff --git a/src/avahi-publicservice_p.h b/src/avahi-publicservice_p.h --- a/src/avahi-publicservice_p.h +++ b/src/avahi-publicservice_p.h @@ -25,15 +25,16 @@ #include "servicebase_p.h" #include #include "publicservice.h" +#include "avahi_listener_p.h" #include "avahi_server_interface.h" #include "avahi_entrygroup_interface.h" #define K_D PublicServicePrivate* d=static_cast(this->d) namespace KDNSSD { -class PublicServicePrivate : public QObject, public ServiceBasePrivate +class PublicServicePrivate : public QObject, public ServiceBasePrivate, public AvahiListener { Q_OBJECT public: @@ -69,6 +70,10 @@ void tryApply(); public Q_SLOTS: + // NB: The global slots are runtime connected! If their signature changes + // make sure the SLOT() signature gets updated! + void gotGlobalStateChanged(int state, const QString &error, QDBusMessage msg); + void serverStateChanged(int, const QString &); void groupStateChanged(int, const QString &); }; diff --git a/src/avahi-remoteservice.cpp b/src/avahi-remoteservice.cpp --- a/src/avahi-remoteservice.cpp +++ b/src/avahi-remoteservice.cpp @@ -1,6 +1,7 @@ /* This file is part of the KDE project * * Copyright (C) 2004, 2005 Jakub Stachowski + * Copyright (C) 2018 Harald Sitter * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -56,6 +57,38 @@ } d->m_resolved = false; registerTypes(); + + // Do not race! + // https://github.com/lathiat/avahi/issues/9 + // Avahi's DBus API is incredibly racey with signals getting fired + // immediately after a request was made even though we may not yet be + // listening. In lieu of a proper upstream fix for this we'll unfortunately + // have to resort to this hack: + // We register to all signals regardless of path and then filter them once + // we know what "our" path is. This is much more fragile than a proper + // QDBusInterface assisted signal connection but unfortunately the only way + // we can reliably prevent signals getting lost in the race. + // This uses a fancy trick whereby using QDBusMessage as last argument will + // give us the correct signal argument types as well as the underlying + // message so that we may check the message path. + QDBusConnection::systemBus() + .connect("org.freedesktop.Avahi", + "", + "org.freedesktop.Avahi.ServiceResolver", + "Found", + d, + SLOT(gotGlobalFound(int,int,QString,QString,QString,QString, + int,QString,ushort,QList, + uint,QDBusMessage))); + QDBusConnection::systemBus() + .connect("org.freedesktop.Avahi", + "", + "org.freedesktop.Avahi.ServiceResolver", + "Failure", + d, + SLOT(gotGlobalError(QDBusMessage))); + d->m_dbusObjectPath.clear(); + //qDebug() << this << ":Starting resolve of : " << d->m_serviceName << " " << d->m_type << " " << d->m_domain << "\n"; org::freedesktop::Avahi::Server s(QStringLiteral("org.freedesktop.Avahi"), QStringLiteral("/"), QDBusConnection::systemBus()); //FIXME: don't use LOOKUP_NO_ADDRESS if NSS unavailable @@ -66,12 +99,12 @@ return; } - org::freedesktop::Avahi::ServiceResolver *b = new org::freedesktop::Avahi::ServiceResolver("org.freedesktop.Avahi", rep.value().path(), - QDBusConnection::systemBus()); - connect(b, SIGNAL(Found(int, int, const QString &, const QString &, const QString &, const QString &, int, const QString &, ushort, - const QList &, uint)), d, SLOT(gotFound(int, int, const QString &, const QString &, const QString &, const QString &, - int, const QString &, ushort, const QList &, uint))); - connect(b, SIGNAL(Failure(QString)), d, SLOT(gotError())); + d->m_dbusObjectPath = rep.value().path(); + + new org::freedesktop::Avahi::ServiceResolver( + s.service(), + d->m_dbusObjectPath, + s.connection()); d->m_running = true; } @@ -89,6 +122,34 @@ emit m_parent->resolved(false); } +void RemoteServicePrivate::gotGlobalFound(int interface, + int protocol, + const QString &name, + const QString &type, + const QString &domain, + const QString &host, + int aprotocol, + const QString &address, + ushort port, + const QList &txt, + uint flags, + QDBusMessage msg) +{ + if (!isOurMsg(msg)) { + return; + } + gotFound(interface, protocol, name, type, domain, host, aprotocol, address, + port, txt, flags); +} + +void RemoteServicePrivate::gotGlobalError(QDBusMessage msg) +{ + if (!isOurMsg(msg)) { + return; + } + gotError(); +} + void RemoteServicePrivate::gotFound(int, int, const QString &name, const QString &, const QString &domain, const QString &host, int, const QString &, ushort port, const QList &txt, uint) { m_serviceName = name; diff --git a/src/avahi-remoteservice_p.h b/src/avahi-remoteservice_p.h --- a/src/avahi-remoteservice_p.h +++ b/src/avahi-remoteservice_p.h @@ -26,14 +26,15 @@ #include #include "servicebase_p.h" #include "remoteservice.h" +#include "avahi_listener_p.h" #include "avahi_serviceresolver_interface.h" #define K_D RemoteServicePrivate* d=static_cast(this->d) namespace KDNSSD { -class RemoteServicePrivate : public QObject, public ServiceBasePrivate +class RemoteServicePrivate : public QObject, public ServiceBasePrivate, public AvahiListener { Q_OBJECT public: @@ -53,7 +54,35 @@ void stop(); private Q_SLOTS: - void gotFound(int, int, const QString &name, const QString &type, const QString &domain, const QString &host, int aprotocol, const QString &address, ushort port, const QList &txt, uint flags); + // NB: The global slots are runtime connected! If their signature changes + // make sure the SLOT() signature gets updated! + void gotGlobalFound( + int interface, + int protocol, + const QString &name, + const QString &type, + const QString &domain, + const QString &host, + int aprotocol, + const QString &address, + ushort port, + const QList &txt, + uint flags, + QDBusMessage msg); + void gotGlobalError(QDBusMessage msg); + + void gotFound( + int interface, + int protocol, + const QString &name, + const QString &type, + const QString &domain, + const QString &host, + int aprotocol, + const QString &address, + ushort port, + const QList &txt, + uint flags); void gotError(); }; diff --git a/src/avahi-servicebrowser.cpp b/src/avahi-servicebrowser.cpp --- a/src/avahi-servicebrowser.cpp +++ b/src/avahi-servicebrowser.cpp @@ -1,6 +1,7 @@ /* This file is part of the KDE project * * Copyright (C) 2004 Jakub Stachowski + * Copyright (C) 2018 Harald Sitter * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -25,6 +26,7 @@ #include "avahi_server_interface.h" #include #include + namespace KDNSSD { @@ -59,28 +61,67 @@ if (d->m_running) { return; } + + // Do not race! + // https://github.com/lathiat/avahi/issues/9 + // Avahi's DBus API is incredibly racey with signals getting fired + // immediately after a request was made even though we may not yet be + // listening. In lieu of a proper upstream fix for this we'll unfortunately + // have to resort to this hack: + // We register to all signals regardless of path and then filter them once + // we know what "our" path is. This is much more fragile than a proper + // QDBusInterface assisted signal connection but unfortunately the only way + // we can reliably prevent signals getting lost in the race. + // This uses a fancy trick whereby using QDBusMessage as last argument will + // give us the correct signal argument types as well as the underlying + // message so that we may check the message path. + QDBusConnection::systemBus() + .connect("org.freedesktop.Avahi", + "", + "org.freedesktop.Avahi.ServiceBrowser", + "ItemNew", + d, + SLOT(gotGlobalItemNew(int,int,QString,QString,QString,uint,QDBusMessage))); + QDBusConnection::systemBus() + .connect("org.freedesktop.Avahi", + "", + "org.freedesktop.Avahi.ServiceBrowser", + "ItemRemove", + d, + SLOT(gotGlobalItemRemove(int,int,QString,QString,QString,uint,QDBusMessage))); + QDBusConnection::systemBus() + .connect("org.freedesktop.Avahi", + "", + "org.freedesktop.Avahi.ServiceBrowser", + "AllForNow", + d, + SLOT(gotGlobalAllForNow(QDBusMessage))); + d->m_dbusObjectPath.clear(); + org::freedesktop::Avahi::Server s(QStringLiteral("org.freedesktop.Avahi"), QStringLiteral("/"), QDBusConnection::systemBus()); + QString fullType = d->m_type; if (!d->m_subtype.isEmpty()) { fullType = d->m_subtype + QStringLiteral("._sub.") + d->m_type; } QDBusReply rep = s.ServiceBrowserNew(-1, -1, fullType, domainToDNS(d->m_domain), 0); - if (!rep.isValid()) { emit finished(); return; } + + d->m_dbusObjectPath = rep.value().path(); d->m_running = true; d->m_browserFinished = true; - org::freedesktop::Avahi::ServiceBrowser *b = new org::freedesktop::Avahi::ServiceBrowser(QStringLiteral("org.freedesktop.Avahi"), rep.value().path(), - QDBusConnection::systemBus()); - connect(b, SIGNAL(ItemNew(int,int,QString,QString,QString,uint)), d, - SLOT(gotNewService(int,int,QString,QString,QString,uint))); - connect(b, SIGNAL(ItemRemove(int,int,QString,QString,QString,uint)), d, - SLOT(gotRemoveService(int,int,QString,QString,QString,uint))); - connect(b, SIGNAL(AllForNow()), d, SLOT(browserFinished())); - d->m_browser = b; - connect(&d->m_timer, SIGNAL(timeout()), d, SLOT(browserFinished())); + + // This is held because we need to explicitly Free it! + d->m_browser = new org::freedesktop::Avahi::ServiceBrowser( + s.service(), + d->m_dbusObjectPath, + s.connection()); + + connect(&d->m_timer, &QTimer::timeout, + d, &ServiceBrowserPrivate::browserFinished); d->m_timer.start(domainIsLocal(d->m_domain) ? TIMEOUT_LAST_SERVICE : TIMEOUT_START_WAN); } @@ -104,6 +145,42 @@ } } +void ServiceBrowserPrivate::gotGlobalItemNew(int interface, + int protocol, + const QString &name, + const QString &type, + const QString &domain, + uint flags, + QDBusMessage msg) +{ + if (!isOurMsg(msg)) { + return; + } + gotNewService(interface, protocol, name, type, domain, flags); +} + +void ServiceBrowserPrivate::gotGlobalItemRemove(int interface, + int protocol, + const QString &name, + const QString &type, + const QString &domain, + uint flags, + QDBusMessage msg) +{ + if (!isOurMsg(msg)) { + return; + } + gotRemoveService(interface, protocol, name, type, domain, flags); +} + +void ServiceBrowserPrivate::gotGlobalAllForNow(QDBusMessage msg) +{ + if (!isOurMsg(msg)) { + return; + } + browserFinished(); +} + RemoteService::Ptr ServiceBrowserPrivate::find(RemoteService::Ptr s, const QList &where) const { Q_FOREACH (const RemoteService::Ptr &i, where) if (*s == *i) { diff --git a/src/avahi-servicebrowser_p.h b/src/avahi-servicebrowser_p.h --- a/src/avahi-servicebrowser_p.h +++ b/src/avahi-servicebrowser_p.h @@ -25,14 +25,16 @@ #include #include #include "servicebrowser.h" +#include "avahi_listener_p.h" #include "avahi_servicebrowser_interface.h" namespace KDNSSD { -class ServiceBrowserPrivate : public QObject +class ServiceBrowserPrivate : public QObject, public AvahiListener { Q_OBJECT + friend class ServiceBrowser; // So the public class may functor connect. public: ServiceBrowserPrivate(ServiceBrowser *parent) : QObject(), m_running(false), m_browser(nullptr), m_parent(parent) {} @@ -62,6 +64,25 @@ void browserFinished(); void queryFinished(); void serviceResolved(bool success); + + // NB: The global slots are runtime connected! If their signature changes + // make sure the SLOT() signature gets updated! + void gotGlobalItemNew(int interface, + int protocol, + const QString &name, + const QString &type, + const QString &domain, + uint flags, + QDBusMessage msg); + void gotGlobalItemRemove(int interface, + int protocol, + const QString &name, + const QString &type, + const QString &domain, + uint flags, + QDBusMessage msg); + void gotGlobalAllForNow(QDBusMessage msg); + void gotNewService(int, int, const QString &, const QString &, const QString &, uint); void gotRemoveService(int, int, const QString &, const QString &, const QString &, uint); }; diff --git a/src/avahi-servicetypebrowser.cpp b/src/avahi-servicetypebrowser.cpp --- a/src/avahi-servicetypebrowser.cpp +++ b/src/avahi-servicetypebrowser.cpp @@ -1,6 +1,7 @@ /* This file is part of the KDE project * * Copyright (C) 2004,2007 Jakub Stachowski + * Copyright (C) 2018 Harald Sitter * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -45,19 +46,59 @@ return; } d->m_started = true; + + // Do not race! + // https://github.com/lathiat/avahi/issues/9 + // Avahi's DBus API is incredibly racey with signals getting fired + // immediately after a request was made even though we may not yet be + // listening. In lieu of a proper upstream fix for this we'll unfortunately + // have to resort to this hack: + // We register to all signals regardless of path and then filter them once + // we know what "our" path is. This is much more fragile than a proper + // QDBusInterface assisted signal connection but unfortunately the only way + // we can reliably prevent signals getting lost in the race. + // This uses a fancy trick whereby using QDBusMessage as last argument will + // give us the correct signal argument types as well as the underlying + // message so that we may check the message path. + QDBusConnection::systemBus() + .connect("org.freedesktop.Avahi", + "", + "org.freedesktop.Avahi.ServiceTypeBrowser", + "ItemNew", + d, + SLOT(gotGlobalItemNew(int,int,QString,QString,uint,QDBusMessage))); + QDBusConnection::systemBus() + .connect("org.freedesktop.Avahi", + "", + "org.freedesktop.Avahi.ServiceTypeBrowser", + "ItemRemove", + d, + SLOT(gotGlobalItemRemove(int,int,QString,QString,uint,QDBusMessage))); + QDBusConnection::systemBus() + .connect("org.freedesktop.Avahi", + "", + "org.freedesktop.Avahi.ServiceTypeBrowser", + "AllForNow", + d, + SLOT(gotGlobalAllForNow(QDBusMessage))); + d->m_dbusObjectPath.clear(); + org::freedesktop::Avahi::Server s(QStringLiteral("org.freedesktop.Avahi"), QStringLiteral("/"), QDBusConnection::systemBus()); - QDBusReply rep = s.ServiceTypeBrowserNew(-1, -1, d->m_domain, 0); + QDBusReply rep = s.ServiceTypeBrowserNew(-1, -1, d->m_domain, 0); if (!rep.isValid()) { return; } - org::freedesktop::Avahi::ServiceTypeBrowser *b = new org::freedesktop::Avahi::ServiceTypeBrowser(QStringLiteral("org.freedesktop.Avahi"), rep.value().path(), - QDBusConnection::systemBus()); - connect(b, SIGNAL(ItemNew(int,int,QString,QString,uint)), d, SLOT(gotNewServiceType(int,int,QString,QString,uint))); - connect(b, SIGNAL(ItemRemove(int,int,QString,QString,uint)), d, SLOT(gotRemoveServiceType(int,int,QString,QString,uint))); - connect(b, SIGNAL(AllForNow()), d, SLOT(finished())); + + d->m_dbusObjectPath = rep.value().path(); + + // This is held because we need to explicitly Free it! + d->m_browser = new org::freedesktop::Avahi::ServiceTypeBrowser( + s.service(), + d->m_dbusObjectPath, + s.connection()); + connect(&d->m_timer, SIGNAL(timeout()), d, SLOT(finished())); - d->m_browser = b; d->m_timer.start(domainIsLocal(d->m_domain) ? TIMEOUT_LAST_SERVICE : TIMEOUT_START_WAN); } @@ -67,6 +108,40 @@ emit m_parent->finished(); } +void ServiceTypeBrowserPrivate::gotGlobalItemNew(int interface, + int protocol, + const QString &type, + const QString &domain, + uint flags, + QDBusMessage msg) +{ + if (!isOurMsg(msg)) { + return; + } + gotNewServiceType(interface, protocol, type, domain, flags); +} + +void ServiceTypeBrowserPrivate::gotGlobalItemRemove(int interface, + int protocol, + const QString &type, + const QString &domain, + uint flags, + QDBusMessage msg) +{ + if (!isOurMsg(msg)) { + return; + } + gotRemoveServiceType(interface, protocol, type, domain, flags); +} + +void ServiceTypeBrowserPrivate::gotGlobalAllForNow(QDBusMessage msg) +{ + if (!isOurMsg(msg)) { + return; + } + finished(); +} + void ServiceTypeBrowserPrivate::gotNewServiceType(int, int, const QString &type, const QString &, uint) { m_timer.start(TIMEOUT_LAST_SERVICE); diff --git a/src/avahi-servicetypebrowser_p.h b/src/avahi-servicetypebrowser_p.h --- a/src/avahi-servicetypebrowser_p.h +++ b/src/avahi-servicetypebrowser_p.h @@ -24,12 +24,13 @@ #include #include #include "servicetypebrowser.h" +#include "avahi_listener_p.h" #include "avahi_servicetypebrowser_interface.h" namespace KDNSSD { -class ServiceTypeBrowserPrivate : public QObject +class ServiceTypeBrowserPrivate : public QObject, public AvahiListener { Q_OBJECT public: @@ -47,11 +48,28 @@ QStringList m_servicetypes; QString m_domain; QTimer m_timer; + private Q_SLOTS: + // NB: The global slots are runtime connected! If their signature changes + // make sure the SLOT() signature gets updated! + void gotGlobalItemNew(int interface, + int protocol, + const QString &type, + const QString &domain, + uint flags, + QDBusMessage msg); + void gotGlobalItemRemove(int interface, + int protocol, + const QString &type, + const QString &domain, + uint flags, + QDBusMessage msg); + void gotGlobalAllForNow(QDBusMessage msg); + + void gotNewServiceType(int, int, const QString &, const QString &, uint); void gotRemoveServiceType(int, int, const QString &, const QString &, uint); void finished(); - }; } diff --git a/src/avahi_listener.cpp b/src/avahi_listener.cpp new file mode 100644 --- /dev/null +++ b/src/avahi_listener.cpp @@ -0,0 +1,35 @@ +/* This file is part of the KDE project + * + * Copyright (C) 2018 Harald Sitter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "avahi_listener_p.h" + +namespace KDNSSD { + +AvahiListener::AvahiListener() +{ +} + +AvahiListener::~AvahiListener() +{ +} + + + +} // namespace KDNSSD diff --git a/src/avahi_listener_p.h b/src/avahi_listener_p.h new file mode 100644 --- /dev/null +++ b/src/avahi_listener_p.h @@ -0,0 +1,54 @@ +/* This file is part of the KDE project + * + * Copyright (C) 2018 Harald Sitter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef AVAHILISTENER_H +#define AVAHILISTENER_H + +#include +#include + +namespace KDNSSD { + +// Assists with listening to Avahi for all signals and then checking if the +// a given dbus message is meant for us or not. +// Subclass and set the object path to object path you should be listening to. +// Messages may then be run through isOurMsg to determine if they target the +// object at hand. +class AvahiListener +{ +public: + explicit AvahiListener(); + virtual ~AvahiListener(); + + // This method gets called a lot but doesn't do much. Suggest inlining. + inline bool isOurMsg(const QDBusMessage &msg) const + { + if (m_dbusObjectPath.isEmpty() || m_dbusObjectPath != msg.path()) { + return false; + } + return true; + } + + QString m_dbusObjectPath; // public so !Private objects can access it +}; + +} // namespace KDNSSD + +#endif // AVAHILISTENER_H