File indexing completed on 2024-04-28 15:22:04

0001 /*
0002     This file is part of the KDE project
0003 
0004     SPDX-FileCopyrightText: 2004 Jakub Stachowski <qbast@go2.pl>
0005     SPDX-FileCopyrightText: 2018 Harald Sitter <sitter@kde.org>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 #include "avahi-domainbrowser_p.h"
0011 #include "avahi_domainbrowser_interface.h"
0012 #include "avahi_server_interface.h"
0013 #include "domainbrowser.h"
0014 #include <QFile>
0015 #include <QIODevice>
0016 #include <QSet>
0017 #include <QStandardPaths>
0018 #include <avahi-common/defs.h>
0019 
0020 namespace KDNSSD
0021 {
0022 DomainBrowser::DomainBrowser(DomainType type, QObject *parent)
0023     : QObject(parent)
0024     , d(new DomainBrowserPrivate(type, this))
0025 {
0026 }
0027 
0028 DomainBrowser::~DomainBrowser() = default;
0029 
0030 void DomainBrowser::startBrowse()
0031 {
0032     Q_D(DomainBrowser);
0033     if (d->m_started) {
0034         return;
0035     }
0036     d->m_started = true;
0037 
0038     // Do not race!
0039     // https://github.com/lathiat/avahi/issues/9
0040     // Avahi's DBus API is incredibly racey with signals getting fired
0041     // immediately after a request was made even though we may not yet be
0042     // listening. In lieu of a proper upstream fix for this we'll unfortunately
0043     // have to resort to this hack:
0044     // We register to all signals regardless of path and then filter them once
0045     // we know what "our" path is. This is much more fragile than a proper
0046     // QDBusInterface assisted signal connection but unfortunately the only way
0047     // we can reliably prevent signals getting lost in the race.
0048     // This uses a fancy trick whereby using QDBusMessage as last argument will
0049     // give us the correct signal argument types as well as the underlying
0050     // message so that we may check the message path.
0051     QDBusConnection::systemBus().connect("org.freedesktop.Avahi",
0052                                          "",
0053                                          "org.freedesktop.Avahi.DomainBrowser",
0054                                          "ItemNew",
0055                                          d,
0056                                          SLOT(gotGlobalItemNew(int, int, QString, uint, QDBusMessage)));
0057     QDBusConnection::systemBus().connect("org.freedesktop.Avahi",
0058                                          "",
0059                                          "org.freedesktop.Avahi.DomainBrowser",
0060                                          "ItemRemove",
0061                                          d,
0062                                          SLOT(gotGlobalItemRemove(int, int, QString, uint, QDBusMessage)));
0063     QDBusConnection::systemBus()
0064         .connect("org.freedesktop.Avahi", "", "org.freedesktop.Avahi.DomainBrowser", "AllForNow", d, SLOT(gotGlobalAllForNow(QDBusMessage)));
0065     d->m_dbusObjectPath.clear();
0066 
0067     org::freedesktop::Avahi::Server s(QStringLiteral("org.freedesktop.Avahi"), QStringLiteral("/"), QDBusConnection::systemBus());
0068     QDBusReply<QDBusObjectPath> rep =
0069         s.DomainBrowserNew(-1, -1, QString(), (d->m_type == Browsing) ? AVAHI_DOMAIN_BROWSER_BROWSE : AVAHI_DOMAIN_BROWSER_REGISTER, 0);
0070     if (!rep.isValid()) {
0071         return;
0072     }
0073 
0074     d->m_dbusObjectPath = rep.value().path();
0075 
0076     // This is held because we need to explicitly Free it!
0077     d->m_browser = new org::freedesktop::Avahi::DomainBrowser(s.service(), d->m_dbusObjectPath, s.connection());
0078 
0079     if (d->m_type == Browsing) {
0080         QString domains_evar = QString::fromLocal8Bit(qgetenv("AVAHI_BROWSE_DOMAINS"));
0081         if (!domains_evar.isEmpty()) {
0082             const QStringList edomains = domains_evar.split(QLatin1Char(':'));
0083             for (const QString &s : edomains) {
0084                 d->gotNewDomain(-1, -1, s, 0);
0085             }
0086         }
0087         // FIXME: watch this file and restart browser if it changes
0088         QString confDir = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
0089         QFile domains_cfg(confDir + QStringLiteral("/avahi/browse-domains"));
0090         if (domains_cfg.open(QIODevice::ReadOnly | QIODevice::Text))
0091             while (!domains_cfg.atEnd()) {
0092                 d->gotNewDomain(-1, -1, QString::fromUtf8(domains_cfg.readLine().data()).trimmed(), 0);
0093             }
0094     }
0095 }
0096 
0097 void DomainBrowserPrivate::gotGlobalItemNew(int interface, int protocol, const QString &domain, uint flags, QDBusMessage msg)
0098 {
0099     if (!isOurMsg(msg)) {
0100         return;
0101     }
0102     gotNewDomain(interface, protocol, domain, flags);
0103 }
0104 
0105 void DomainBrowserPrivate::gotGlobalItemRemove(int interface, int protocol, const QString &domain, uint flags, QDBusMessage msg)
0106 {
0107     if (!isOurMsg(msg)) {
0108         return;
0109     }
0110     gotRemoveDomain(interface, protocol, domain, flags);
0111 }
0112 
0113 void DomainBrowserPrivate::gotGlobalAllForNow(QDBusMessage msg)
0114 {
0115     if (!isOurMsg(msg)) {
0116         return;
0117     }
0118 }
0119 
0120 void DomainBrowserPrivate::gotNewDomain(int, int, const QString &domain, uint)
0121 {
0122     QString decoded = DNSToDomain(domain);
0123     if (m_domains.contains(decoded)) {
0124         return;
0125     }
0126     m_domains += decoded;
0127     Q_EMIT m_parent->domainAdded(decoded);
0128 }
0129 
0130 void DomainBrowserPrivate::gotRemoveDomain(int, int, const QString &domain, uint)
0131 {
0132     QString decoded = DNSToDomain(domain);
0133     if (!m_domains.contains(decoded)) {
0134         return;
0135     }
0136     Q_EMIT m_parent->domainRemoved(decoded);
0137     m_domains.remove(decoded);
0138 }
0139 
0140 QStringList DomainBrowser::domains() const
0141 {
0142     Q_D(const DomainBrowser);
0143     return d->m_domains.values();
0144 }
0145 
0146 bool DomainBrowser::isRunning() const
0147 {
0148     Q_D(const DomainBrowser);
0149     return d->m_started;
0150 }
0151 
0152 }
0153 #include "moc_avahi-domainbrowser_p.cpp"
0154 #include "moc_domainbrowser.cpp"