diff --git a/CMakeLists.txt b/CMakeLists.txt index 37ec929..e3adb13 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,98 +1,98 @@ cmake_minimum_required(VERSION 3.5) set(KF5_VERSION "5.57.0") # handled by release scripts project(KDNSSD VERSION ${KF5_VERSION}) include(FeatureSummary) find_package(ECM 5.56.0 NO_MODULE) set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://projects.kde.org/projects/kdesupport/extra-cmake-modules") feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ${CMAKE_SOURCE_DIR}/cmake/modules) #TODO: remove cmake/modules when extra-cmake-modules is done include(KDEInstallDirs) include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) include(KDECMakeSettings) include(ECMPoQmTools) include(ECMAddQch) option(BUILD_QCH "Build API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)" OFF) add_feature_info(QCH ${BUILD_QCH} "API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)") set(REQUIRED_QT_VERSION 5.10.0) find_package(Qt5 ${REQUIRED_QT_VERSION} CONFIG REQUIRED Network) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) include(GenerateExportHeader) include(ECMSetupVersion) include(ECMGenerateHeaders) include(CMakePackageConfigHelpers) ecm_setup_version(PROJECT VARIABLE_PREFIX KDNSSD VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kdnssd_version.h" PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF5DNSSDConfigVersion.cmake" SOVERSION 5) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/kdnssd_version.h DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5} COMPONENT Devel) configure_file(config-kdnssd.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kdnssd.h ) find_package(Avahi) set_package_properties(Avahi PROPERTIES DESCRIPTION "Facilities for service discovery on a local network (DNSSD)" URL "http://avahi.org" TYPE OPTIONAL PURPOSE "Either Avahi or DNSSD is required for KDE applications to make use of multicast DNS/DNS-SD service discovery" ) if(NOT AVAHI_FOUND) find_package(DNSSD) set_package_properties(DNSSD PROPERTIES DESCRIPTION "Facilities for service discovery on a local network" URL "http://avahi.org" TYPE OPTIONAL PURPOSE "Either Avahi or DNSSD is required for KDE applications to make use of multicast DNS/DNS-SD service discovery" ) endif( ) if(AVAHI_FOUND OR DNSSD_FOUND) find_package(Qt5 ${REQUIRED_QT_VERSION} CONFIG REQUIRED DBus) endif() if (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/po") ecm_install_po_files_as_qm(po) endif() remove_definitions(-DQT_NO_CAST_FROM_ASCII) - +add_definitions(-DQT_NO_FOREACH) add_subdirectory(src) set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KF5DNSSD") if (BUILD_QCH) ecm_install_qch_export( TARGETS KF5DNSSD_QCH FILE KF5DNSSDQchTargets.cmake DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) set(PACKAGE_INCLUDE_QCHTARGETS "include(\"\${CMAKE_CURRENT_LIST_DIR}/KF5DNSSDQchTargets.cmake\")") endif() configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/KF5DNSSDConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/KF5DNSSDConfig.cmake" INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KF5DNSSDConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/KF5DNSSDConfigVersion.cmake" DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) install(EXPORT KF5DNSSDTargets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" FILE KF5DNSSDTargets.cmake NAMESPACE KF5:: COMPONENT Devel) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/src/avahi-domainbrowser.cpp b/src/avahi-domainbrowser.cpp index e67fc85..9f09a70 100644 --- a/src/avahi-domainbrowser.cpp +++ b/src/avahi-domainbrowser.cpp @@ -1,182 +1,182 @@ /* 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 * 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-domainbrowser_p.h" #include #include #include #include #include #include "avahi_server_interface.h" #include "domainbrowser.h" #include "avahi_domainbrowser_interface.h" namespace KDNSSD { DomainBrowser::DomainBrowser(DomainType type, QObject *parent) : QObject(parent), d(new DomainBrowserPrivate(type, this)) {} DomainBrowser::~DomainBrowser() { delete d; } void DomainBrowser::startBrowse() { if (d->m_started) { 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; } 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()) { - QStringList edomains = domains_evar.split(QLatin1Char(':')); - Q_FOREACH (const QString &s, edomains) { + const QStringList edomains = domains_evar.split(QLatin1Char(':')); + for (const QString &s : edomains) { d->gotNewDomain(-1, -1, s, 0); } } //FIXME: watch this file and restart browser if it changes QString confDir = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QFile domains_cfg(confDir + QStringLiteral("/avahi/browse-domains")); if (domains_cfg.open(QIODevice::ReadOnly | QIODevice::Text)) 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) { QString decoded = DNSToDomain(domain); if (m_domains.contains(decoded)) { return; } m_domains += decoded; emit m_parent->domainAdded(decoded); } void DomainBrowserPrivate::gotRemoveDomain(int, int, const QString &domain, uint) { QString decoded = DNSToDomain(domain); if (!m_domains.contains(decoded)) { return; } emit m_parent->domainRemoved(decoded); m_domains.remove(decoded); } QStringList DomainBrowser::domains() const { return d->m_domains.values(); } bool DomainBrowser::isRunning() const { return d->m_started; } } #include "moc_domainbrowser.cpp" #include "moc_avahi-domainbrowser_p.cpp" diff --git a/src/avahi-publicservice.cpp b/src/avahi-publicservice.cpp index 8cfad55..35118e7 100644 --- a/src/avahi-publicservice.cpp +++ b/src/avahi-publicservice.cpp @@ -1,324 +1,324 @@ /* This file is part of the KDE project * * Copyright (C) 2004, 2005 Jakub Stachowski * * 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-publicservice_p.h" #include #include #include "publicservice.h" #include #if HAVE_SYS_TYPES_H #include #endif #include "servicebrowser.h" #include "avahi_server_interface.h" #include "avahi_entrygroup_interface.h" namespace KDNSSD { PublicService::PublicService(const QString &name, const QString &type, unsigned int port, const QString &domain, const QStringList &subtypes) : QObject(), ServiceBase(new PublicServicePrivate(this, name, type, domain, port)) { K_D; if (domain.isNull()) { d->m_domain = "local."; } d->m_subtypes = subtypes; } PublicService::~PublicService() { stop(); } void PublicServicePrivate::tryApply() { if (fillEntryGroup()) { commit(); } else { m_parent->stop(); emit m_parent->published(false); } } 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; d->m_serviceName = serviceName; if (d->m_running) { d->m_group->Reset(); d->tryApply(); } } void PublicService::setDomain(const QString &domain) { K_D; d->m_domain = domain; if (d->m_running) { d->m_group->Reset(); d->tryApply(); } } void PublicService::setType(const QString &type) { K_D; d->m_type = type; if (d->m_running) { d->m_group->Reset(); d->tryApply(); } } void PublicService::setSubTypes(const QStringList &subtypes) { K_D; d->m_subtypes = subtypes; if (d->m_running) { d->m_group->Reset(); d->tryApply(); } } QStringList PublicService::subtypes() const { K_D; return d->m_subtypes; } void PublicService::setPort(unsigned short port) { K_D; d->m_port = port; if (d->m_running) { d->m_group->Reset(); d->tryApply(); } } void PublicService::setTextData(const QMap &textData) { K_D; d->m_textData = textData; if (d->m_running) { d->m_group->Reset(); d->tryApply(); } } bool PublicService::isPublished() const { K_D; return d->m_published; } bool PublicService::publish() { K_D; publishAsync(); while (d->m_running && !d->m_published) { QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); } return d->m_published; } void PublicService::stop() { K_D; if (d->m_group) { d->m_group->Reset(); } d->m_running = false; d->m_published = false; } bool PublicServicePrivate::fillEntryGroup() { 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_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(); if (!rep.isValid()) { return false; } m_serviceName = rep.value(); } QList txt; QMap::ConstIterator itEnd = m_textData.constEnd(); for (QMap::ConstIterator it = m_textData.constBegin(); it != itEnd; ++it) if (it.value().isNull()) { txt.append(it.key().toLatin1()); } else { txt.append(it.key().toLatin1() + '=' + it.value()); } for (;;) { QDBusReply ret = m_group->AddService(-1, -1, 0, m_serviceName, m_type, domainToDNS(m_domain), m_hostName, m_port, txt); if (ret.isValid()) { break; } // serious error, bail out if (ret.error().name() != QLatin1String("org.freedesktop.Avahi.CollisionError")) { return false; } // name collision, try another QDBusReply rep = m_server->GetAlternativeServiceName(m_serviceName); if (rep.isValid()) { m_serviceName = rep.value(); } else { return false; } } - Q_FOREACH (const QString &subtype, m_subtypes) { + for (const QString &subtype : qAsConst(m_subtypes)) { m_group->AddServiceSubtype(-1, -1, 0, m_serviceName, m_type, domainToDNS(m_domain), subtype); } return true; } void PublicServicePrivate::serverStateChanged(int s, const QString &) { if (!m_running) { return; } switch (s) { case AVAHI_SERVER_INVALID: m_parent->stop(); emit m_parent->published(false); break; case AVAHI_SERVER_REGISTERING: case AVAHI_SERVER_COLLISION: if (m_group) { m_group->Reset(); } m_collision = true; break; case AVAHI_SERVER_RUNNING: if (m_collision) { m_collision = false; tryApply(); } } } void PublicService::publishAsync() { K_D; if (d->m_running) { stop(); } if (!d->m_server) { d->m_server = new org::freedesktop::Avahi::Server(QStringLiteral("org.freedesktop.Avahi"), QStringLiteral("/"), QDBusConnection::systemBus()); connect(d->m_server, SIGNAL(StateChanged(int,QString)), d, SLOT(serverStateChanged(int,QString))); } int state = AVAHI_SERVER_INVALID; QDBusReply rep = d->m_server->GetState(); if (rep.isValid()) { state = rep.value(); } d->m_running = true; d->m_collision = true; // make it look like server is getting out of collision to force registering d->serverStateChanged(state, QString()); } void PublicServicePrivate::groupStateChanged(int s, const QString &reason) { switch (s) { case AVAHI_ENTRY_GROUP_COLLISION: { QDBusReply rep = m_server->GetAlternativeServiceName(m_serviceName); if (rep.isValid()) { m_parent->setServiceName(rep.value()); } else { serverStateChanged(AVAHI_SERVER_INVALID, reason); } break; } case AVAHI_ENTRY_GROUP_ESTABLISHED: m_published = true; emit m_parent->published(true); break; case AVAHI_ENTRY_GROUP_FAILURE: serverStateChanged(AVAHI_SERVER_INVALID, reason); break; } } void PublicService::virtual_hook(int, void *) { } } #include "moc_publicservice.cpp" #include "moc_avahi-publicservice_p.cpp" diff --git a/src/avahi-remoteservice.cpp b/src/avahi-remoteservice.cpp index b484292..f64a93f 100644 --- a/src/avahi-remoteservice.cpp +++ b/src/avahi-remoteservice.cpp @@ -1,190 +1,190 @@ /* 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 * 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-remoteservice_p.h" #include #include #include #include #include "remoteservice.h" #include "avahi_server_interface.h" #include "avahi_serviceresolver_interface.h" namespace KDNSSD { RemoteService::RemoteService(const QString &name, const QString &type, const QString &domain) : ServiceBase(new RemoteServicePrivate(this, name, type, domain)) { } RemoteService::~RemoteService() { } bool RemoteService::resolve() { K_D; resolveAsync(); while (d->m_running && !d->m_resolved) { QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); } return d->m_resolved; } void RemoteService::resolveAsync() { K_D; if (d->m_running) { return; } 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 QDBusReply rep = s.ServiceResolverNew(-1, -1, d->m_serviceName, d->m_type, domainToDNS(d->m_domain), -1, 8 /*AVAHI_LOOKUP_NO_ADDRESS*/); if (!rep.isValid()) { emit resolved(false); return; } d->m_dbusObjectPath = rep.value().path(); // This is held because we need to explicitly Free it! d->m_resolver = new org::freedesktop::Avahi::ServiceResolver( s.service(), d->m_dbusObjectPath, s.connection()); d->m_running = true; } bool RemoteService::isResolved() const { K_D; return d->m_resolved; } void RemoteServicePrivate::gotError() { m_resolved = false; stop(); 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; m_hostName = host; m_port = port; m_domain = DNSToDomain(domain); - Q_FOREACH (const QByteArray &x, txt) { + for (const QByteArray &x : txt) { int pos = x.indexOf("="); if (pos == -1) { m_textData[x] = QByteArray(); } else { m_textData[x.mid(0, pos)] = x.mid(pos + 1, x.size() - pos); } } m_resolved = true; emit m_parent->resolved(true); } void RemoteServicePrivate::stop() { if (m_resolver) { m_resolver->Free(); } delete m_resolver; m_resolver = nullptr; m_running = false; } void RemoteService::virtual_hook(int, void *) { // BASE::virtual_hook(int, void*); } } #include "moc_remoteservice.cpp" #include "moc_avahi-remoteservice_p.cpp" diff --git a/src/avahi-servicebrowser.cpp b/src/avahi-servicebrowser.cpp index 95b2a57..0439615 100644 --- a/src/avahi-servicebrowser.cpp +++ b/src/avahi-servicebrowser.cpp @@ -1,280 +1,280 @@ /* 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 * 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-servicebrowser_p.h" #include #include "servicebrowser.h" #include "avahi_servicebrowser_interface.h" #include "avahi_server_interface.h" #include #include namespace KDNSSD { ServiceBrowser::ServiceBrowser(const QString &type, bool autoResolve, const QString &domain, const QString &subtype) : d(new ServiceBrowserPrivate(this)) { d->m_type = type; d->m_subtype = subtype; d->m_autoResolve = autoResolve; d->m_domain = domain; d->m_timer.setSingleShot(true); } ServiceBrowser::State ServiceBrowser::isAvailable() { org::freedesktop::Avahi::Server s(QStringLiteral("org.freedesktop.Avahi"), QStringLiteral("/"), QDBusConnection::systemBus()); QDBusReply rep = s.GetState(); return (rep.isValid() && rep.value() == 2) ? Working : Stopped; } ServiceBrowser::~ServiceBrowser() { delete d; } bool ServiceBrowser::isAutoResolving() const { return d->m_autoResolve; } void ServiceBrowser::startBrowse() { 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; // 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); } void ServiceBrowserPrivate::serviceResolved(bool success) { QObject *sender_obj = const_cast(sender()); RemoteService *svr = static_cast(sender_obj); disconnect(svr, SIGNAL(resolved(bool)), this, SLOT(serviceResolved(bool))); QList::Iterator it = m_duringResolve.begin(); QList::Iterator itEnd = m_duringResolve.end(); while (it != itEnd && svr != (*it).data()) { ++it; } if (it != itEnd) { if (success) { m_services += (*it); emit m_parent->serviceAdded(RemoteService::Ptr(svr)); } m_duringResolve.erase(it); queryFinished(); } } 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) { + for (const RemoteService::Ptr &i : where) if (*s == *i) { return i; } return RemoteService::Ptr(); } void ServiceBrowserPrivate::gotNewService(int, int, const QString &name, const QString &type, const QString &domain, uint) { m_timer.start(TIMEOUT_LAST_SERVICE); RemoteService::Ptr svr(new RemoteService(name, type, domain)); if (m_autoResolve) { connect(svr.data(), SIGNAL(resolved(bool)), this, SLOT(serviceResolved(bool))); m_duringResolve += svr; svr->resolveAsync(); } else { m_services += svr; emit m_parent->serviceAdded(svr); } } void ServiceBrowserPrivate::gotRemoveService(int, int, const QString &name, const QString &type, const QString &domain, uint) { m_timer.start(TIMEOUT_LAST_SERVICE); RemoteService::Ptr tmpl(new RemoteService(name, type, domain)); RemoteService::Ptr found = find(tmpl, m_duringResolve); if (found) { m_duringResolve.removeAll(found); return; } found = find(tmpl, m_services); if (!found) { return; } emit m_parent->serviceRemoved(found); m_services.removeAll(found); } void ServiceBrowserPrivate::browserFinished() { m_timer.stop(); m_browserFinished = true; queryFinished(); } void ServiceBrowserPrivate::queryFinished() { if (!m_duringResolve.count() && m_browserFinished) { emit m_parent->finished(); } } QList ServiceBrowser::services() const { return d->m_services; } void ServiceBrowser::virtual_hook(int, void *) {} QHostAddress ServiceBrowser::resolveHostName(const QString &hostname) { org::freedesktop::Avahi::Server s(QStringLiteral("org.freedesktop.Avahi"), QStringLiteral("/"), QDBusConnection::systemBus()); int protocol = 0; QString name; int aprotocol = 0; QString address; uint flags = 0; QDBusReply reply = s.ResolveHostName(-1, -1, hostname, 0, (unsigned int) 0, protocol, name, aprotocol, address, flags); if (reply.isValid()) { return QHostAddress(address); } else { return QHostAddress(); } } QString ServiceBrowser::getLocalHostName() { org::freedesktop::Avahi::Server s(QStringLiteral("org.freedesktop.Avahi"), QStringLiteral("/"), QDBusConnection::systemBus()); QDBusReply reply = s.GetHostName(); if (reply.isValid()) { return reply.value(); } else { return QString(); } } } #include "moc_servicebrowser.cpp" #include "moc_avahi-servicebrowser_p.cpp" diff --git a/src/mdnsd-publicservice.cpp b/src/mdnsd-publicservice.cpp index b4142ca..570b3c5 100644 --- a/src/mdnsd-publicservice.cpp +++ b/src/mdnsd-publicservice.cpp @@ -1,217 +1,217 @@ /* This file is part of the KDE project * * Copyright (C) 2004, 2005 Jakub Stachowski * * 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 #include #include #include "publicservice.h" #include "servicebase_p.h" #include "mdnsd-sdevent.h" #include "mdnsd-responder.h" #define K_D PublicServicePrivate* d=static_cast(this->d) namespace KDNSSD { void publish_callback(DNSServiceRef, DNSServiceFlags, DNSServiceErrorType errorCode, const char *name, const char *, const char *, void *context); class PublicServicePrivate : public Responder, public ServiceBasePrivate { public: PublicServicePrivate(PublicService *parent, const QString &name, const QString &type, unsigned int port, const QString &domain) : Responder(), ServiceBasePrivate(name, type, domain, QString(), port), m_published(false), m_parent(parent) {} bool m_published; PublicService *m_parent; QStringList m_subtypes; virtual void customEvent(QEvent *event); }; PublicService::PublicService(const QString &name, const QString &type, unsigned int port, const QString &domain, const QStringList &subtypes) : QObject(), ServiceBase(new PublicServicePrivate(this, name, type, port, domain)) { K_D; if (domain.isNull()) { d->m_domain = "local."; } d->m_subtypes = subtypes; } PublicService::~PublicService() { stop(); } void PublicService::setServiceName(const QString &serviceName) { K_D; d->m_serviceName = serviceName; if (d->isRunning()) { stop(); publishAsync(); } } void PublicService::setDomain(const QString &domain) { K_D; d->m_domain = domain; if (d->isRunning()) { stop(); publishAsync(); } } QStringList PublicService::subtypes() const { K_D; return d->m_subtypes; } void PublicService::setType(const QString &type) { K_D; d->m_type = type; if (d->isRunning()) { stop(); publishAsync(); } } void PublicService::setSubTypes(const QStringList &subtypes) { K_D; d->m_subtypes = subtypes; if (d->isRunning()) { stop(); publishAsync(); } } void PublicService::setPort(unsigned short port) { K_D; d->m_port = port; if (d->isRunning()) { stop(); publishAsync(); } } bool PublicService::isPublished() const { K_D; return d->m_published; } void PublicService::setTextData(const QMap &textData) { K_D; d->m_textData = textData; if (d->isRunning()) { stop(); publishAsync(); } } bool PublicService::publish() { K_D; publishAsync(); while (d->isRunning() && !d->m_published) { d->process(); } return d->m_published; } void PublicService::stop() { K_D; d->stop(); d->m_published = false; } void PublicService::publishAsync() { K_D; if (d->isRunning()) { stop(); } TXTRecordRef txt; TXTRecordCreate(&txt, 0, 0); QMap::ConstIterator itEnd = d->m_textData.cend(); for (QMap::ConstIterator it = d->m_textData.cbegin(); it != itEnd; ++it) { if (TXTRecordSetValue(&txt, it.key().toUtf8().constData(), it.value().length(), it.value().constData()) != kDNSServiceErr_NoError) { TXTRecordDeallocate(&txt); emit published(false); return; } } DNSServiceRef ref; QString fullType = d->m_type; - Q_FOREACH (const QString &subtype, d->m_subtypes) { + for (const QString &subtype : qAsConst(d->m_subtypes)) { fullType += ',' + subtype; } if (DNSServiceRegister(&ref, 0, 0, d->m_serviceName.toUtf8().constData(), fullType.toLatin1().constData(), domainToDNS(d->m_domain).constData(), NULL, htons(d->m_port), TXTRecordGetLength(&txt), TXTRecordGetBytesPtr(&txt), publish_callback, reinterpret_cast(d)) == kDNSServiceErr_NoError) { d->setRef(ref); } TXTRecordDeallocate(&txt); if (!d->isRunning()) { emit published(false); } } void publish_callback(DNSServiceRef, DNSServiceFlags, DNSServiceErrorType errorCode, const char *name, const char *, const char *, void *context) { QObject *obj = reinterpret_cast(context); if (errorCode != kDNSServiceErr_NoError) { ErrorEvent err; QCoreApplication::sendEvent(obj, &err); } else { PublishEvent pev(QString::fromUtf8(name)); QCoreApplication::sendEvent(obj, &pev); } } void PublicServicePrivate::customEvent(QEvent *event) { if (event->type() == QEvent::User + SD_ERROR) { m_parent->stop(); emit m_parent->published(false); } if (event->type() == QEvent::User + SD_PUBLISH) { m_published = true; emit m_parent->published(true); m_serviceName = static_cast(event)->m_name; } } void PublicService::virtual_hook(int, void *) { } } #include "moc_publicservice.cpp" diff --git a/src/mdnsd-servicebrowser.cpp b/src/mdnsd-servicebrowser.cpp index 9b09c42..e5ada24 100644 --- a/src/mdnsd-servicebrowser.cpp +++ b/src/mdnsd-servicebrowser.cpp @@ -1,212 +1,212 @@ /* This file is part of the KDE project * * Copyright (C) 2004 Jakub Stachowski * * 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 "mdnsd-servicebrowser_p.h" #include "domainbrowser.h" #include "servicebrowser.h" #include "mdnsd-responder.h" #include "remoteservice.h" #include "mdnsd-sdevent.h" #include #include #include #include #include #include #define TIMEOUT_WAN 2000 #define TIMEOUT_LAN 200 namespace KDNSSD { void query_callback(DNSServiceRef, DNSServiceFlags flags, uint32_t, DNSServiceErrorType errorCode, const char *serviceName, const char *regtype, const char *replyDomain, void *context); ServiceBrowser::ServiceBrowser(const QString &type, bool autoResolve, const QString &domain, const QString &subtype) : d(new ServiceBrowserPrivate(this)) { d->m_type = type; d->m_autoResolve = autoResolve; d->m_domain = domain; d->m_subtype = subtype; d->timeout.setSingleShot(true); connect(&d->timeout, SIGNAL(timeout()), d, SLOT(onTimeout())); } ServiceBrowser::State ServiceBrowser::isAvailable() { // DNSServiceRef ref; // bool ok (DNSServiceCreateConnection(&ref)==kDNSServiceErr_NoError); // if (ok) DNSServiceRefDeallocate(ref); // return (ok) ? Working : Stopped; return Working; } ServiceBrowser::~ ServiceBrowser() { delete d; } bool ServiceBrowser::isAutoResolving() const { return d->m_autoResolve; } void ServiceBrowserPrivate::serviceResolved(bool success) { QObject *sender_obj = const_cast(sender()); RemoteService *svr = static_cast(sender_obj); disconnect(svr, SIGNAL(resolved(bool)), this, SLOT(serviceResolved(bool))); QList::Iterator it = m_duringResolve.begin(); QList::Iterator itEnd = m_duringResolve.end(); while (it != itEnd && svr != (*it).data()) { ++it; } if (it != itEnd) { if (success) { m_services += (*it); emit m_parent->serviceAdded(RemoteService::Ptr(svr)); } m_duringResolve.erase(it); queryFinished(); } } void ServiceBrowser::startBrowse() { if (d->isRunning()) { return; } d->m_finished = false; DNSServiceRef ref; QString fullType = d->m_type; if (!d->m_subtype.isEmpty()) { fullType = d->m_subtype + "._sub." + d->m_type; } if (DNSServiceBrowse(&ref, 0, 0, fullType.toLatin1().constData(), domainToDNS(d->m_domain).constData(), query_callback, reinterpret_cast(d)) == kDNSServiceErr_NoError) { d->setRef(ref); } if (!d->isRunning()) { emit finished(); } else { d->timeout.start(domainIsLocal(d->m_domain) ? TIMEOUT_LAN : TIMEOUT_WAN); } } void ServiceBrowserPrivate::queryFinished() { if (!m_duringResolve.count() && m_finished) { emit m_parent->finished(); } } QList ServiceBrowser::services() const { return d->m_services; } void ServiceBrowser::virtual_hook(int, void *) {} RemoteService::Ptr ServiceBrowserPrivate::find(RemoteService::Ptr s, const QList &where) const { - Q_FOREACH (const RemoteService::Ptr &i, where) if (*s == *i) { + for (const RemoteService::Ptr &i : where) if (*s == *i) { return i; } return RemoteService::Ptr(); } void ServiceBrowserPrivate::customEvent(QEvent *event) { if (event->type() == QEvent::User + SD_ERROR) { stop(); m_finished = false; queryFinished(); } if (event->type() == QEvent::User + SD_ADDREMOVE) { AddRemoveEvent *aev = static_cast(event); // m_type has useless trailing dot RemoteService::Ptr svr(new RemoteService(aev->m_name, aev->m_type.left(aev->m_type.length() - 1), aev->m_domain)); if (aev->m_op == AddRemoveEvent::Add) { if (m_autoResolve) { connect(svr.data(), SIGNAL(resolved(bool)), this, SLOT(serviceResolved(bool))); m_duringResolve += svr; svr->resolveAsync(); } else { m_services += svr; emit m_parent->serviceAdded(svr); } } else { RemoteService::Ptr found = find(svr, m_duringResolve); if (found) { m_duringResolve.removeAll(found); } else { found = find(svr, m_services); if (found) { emit m_parent->serviceRemoved(found); m_services.removeAll(found); } } } m_finished = aev->m_last; if (m_finished) { queryFinished(); } } } void ServiceBrowserPrivate::onTimeout() { m_finished = true; queryFinished(); } void query_callback(DNSServiceRef, DNSServiceFlags flags, uint32_t, DNSServiceErrorType errorCode, const char *serviceName, const char *regtype, const char *replyDomain, void *context) { QObject *obj = reinterpret_cast(context); if (errorCode != kDNSServiceErr_NoError) { ErrorEvent err; QCoreApplication::sendEvent(obj, &err); } else { AddRemoveEvent arev((flags & kDNSServiceFlagsAdd) ? AddRemoveEvent::Add : AddRemoveEvent::Remove, QString::fromUtf8(serviceName), regtype, DNSToDomain(replyDomain), !(flags & kDNSServiceFlagsMoreComing)); QCoreApplication::sendEvent(obj, &arev); } } // TODO: Please Implement Me - Using a KResolver (if not natively) QHostAddress ServiceBrowser::resolveHostName(const QString &hostname) { return QHostAddress(); } QString ServiceBrowser::getLocalHostName() { return QHostInfo::localHostName(); } } #include "moc_servicebrowser.cpp"