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

0001 /*
0002     This file is part of the KDE project
0003 
0004     SPDX-FileCopyrightText: 2004, 2007 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-servicetypebrowser_p.h"
0011 #include "avahi_server_interface.h"
0012 #include "avahi_servicetypebrowser_interface.h"
0013 #include "servicetypebrowser.h"
0014 #include <QSet>
0015 
0016 #define UNSPEC -1
0017 namespace KDNSSD
0018 {
0019 ServiceTypeBrowser::ServiceTypeBrowser(const QString &domain, QObject *parent)
0020     : QObject(parent)
0021     , d(new ServiceTypeBrowserPrivate(this))
0022 {
0023     Q_D(ServiceTypeBrowser);
0024     d->m_domain = domain;
0025     d->m_timer.setSingleShot(true);
0026 }
0027 
0028 ServiceTypeBrowser::~ServiceTypeBrowser() = default;
0029 
0030 void ServiceTypeBrowser::startBrowse()
0031 {
0032     Q_D(ServiceTypeBrowser);
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.ServiceTypeBrowser",
0054                                          "ItemNew",
0055                                          d,
0056                                          SLOT(gotGlobalItemNew(int, int, QString, QString, uint, QDBusMessage)));
0057     QDBusConnection::systemBus().connect("org.freedesktop.Avahi",
0058                                          "",
0059                                          "org.freedesktop.Avahi.ServiceTypeBrowser",
0060                                          "ItemRemove",
0061                                          d,
0062                                          SLOT(gotGlobalItemRemove(int, int, QString, QString, uint, QDBusMessage)));
0063     QDBusConnection::systemBus()
0064         .connect("org.freedesktop.Avahi", "", "org.freedesktop.Avahi.ServiceTypeBrowser", "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 
0069     QDBusReply<QDBusObjectPath> rep = s.ServiceTypeBrowserNew(-1, -1, d->m_domain, 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::ServiceTypeBrowser(s.service(), d->m_dbusObjectPath, s.connection());
0078 
0079     connect(&d->m_timer, SIGNAL(timeout()), d, SLOT(finished()));
0080     d->m_timer.start(domainIsLocal(d->m_domain) ? TIMEOUT_LAST_SERVICE : TIMEOUT_START_WAN);
0081 }
0082 
0083 void ServiceTypeBrowserPrivate::finished()
0084 {
0085     m_timer.stop();
0086     Q_EMIT m_parent->finished();
0087 }
0088 
0089 void ServiceTypeBrowserPrivate::gotGlobalItemNew(int interface, int protocol, const QString &type, const QString &domain, uint flags, QDBusMessage msg)
0090 {
0091     if (!isOurMsg(msg)) {
0092         return;
0093     }
0094     gotNewServiceType(interface, protocol, type, domain, flags);
0095 }
0096 
0097 void ServiceTypeBrowserPrivate::gotGlobalItemRemove(int interface, int protocol, const QString &type, const QString &domain, uint flags, QDBusMessage msg)
0098 {
0099     if (!isOurMsg(msg)) {
0100         return;
0101     }
0102     gotRemoveServiceType(interface, protocol, type, domain, flags);
0103 }
0104 
0105 void ServiceTypeBrowserPrivate::gotGlobalAllForNow(QDBusMessage msg)
0106 {
0107     if (!isOurMsg(msg)) {
0108         return;
0109     }
0110     finished();
0111 }
0112 
0113 void ServiceTypeBrowserPrivate::gotNewServiceType(int, int, const QString &type, const QString &, uint)
0114 {
0115     m_timer.start(TIMEOUT_LAST_SERVICE);
0116     m_servicetypes += type;
0117     Q_EMIT m_parent->serviceTypeAdded(type);
0118 }
0119 
0120 void ServiceTypeBrowserPrivate::gotRemoveServiceType(int, int, const QString &type, const QString &, uint)
0121 {
0122     m_timer.start(TIMEOUT_LAST_SERVICE);
0123     m_servicetypes.removeAll(type);
0124     Q_EMIT m_parent->serviceTypeRemoved(type);
0125 }
0126 
0127 QStringList ServiceTypeBrowser::serviceTypes() const
0128 {
0129     Q_D(const ServiceTypeBrowser);
0130     return d->m_servicetypes;
0131 }
0132 
0133 }
0134 #include "moc_avahi-servicetypebrowser_p.cpp"
0135 #include "moc_servicetypebrowser.cpp"