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

0001 /*
0002     This file is part of the KDE project
0003 
0004     SPDX-FileCopyrightText: 2004, 2005 Jakub Stachowski <qbast@go2.pl>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "mdnsd-responder.h"
0010 #include "mdnsd-sdevent.h"
0011 #include "publicservice.h"
0012 #include "servicebase_p.h"
0013 #include <QCoreApplication>
0014 #include <QStringList>
0015 #include <netinet/in.h>
0016 
0017 #define KDNSSD_D PublicServicePrivate *d = static_cast<PublicServicePrivate *>(this->d.operator->())
0018 
0019 namespace KDNSSD
0020 {
0021 void publish_callback(DNSServiceRef, DNSServiceFlags, DNSServiceErrorType errorCode, const char *name, const char *, const char *, void *context);
0022 class PublicServicePrivate : public Responder, public ServiceBasePrivate
0023 {
0024 public:
0025     PublicServicePrivate(PublicService *parent, const QString &name, const QString &type, unsigned int port, const QString &domain)
0026         : Responder()
0027         , ServiceBasePrivate(name, type, domain, QString(), port)
0028         , m_published(false)
0029         , m_parent(parent)
0030     {
0031     }
0032     bool m_published;
0033     PublicService *m_parent;
0034     QStringList m_subtypes;
0035     virtual void customEvent(QEvent *event);
0036 };
0037 
0038 PublicService::PublicService(const QString &name, const QString &type, unsigned int port, const QString &domain, const QStringList &subtypes)
0039     : QObject()
0040     , ServiceBase(new PublicServicePrivate(this, name, type, port, domain))
0041 {
0042     KDNSSD_D;
0043     if (domain.isNull()) {
0044         d->m_domain = "local.";
0045     }
0046     d->m_subtypes = subtypes;
0047 }
0048 
0049 PublicService::~PublicService()
0050 {
0051     stop();
0052 }
0053 
0054 void PublicService::setServiceName(const QString &serviceName)
0055 {
0056     KDNSSD_D;
0057     d->m_serviceName = serviceName;
0058     if (d->isRunning()) {
0059         stop();
0060         publishAsync();
0061     }
0062 }
0063 
0064 void PublicService::setDomain(const QString &domain)
0065 {
0066     KDNSSD_D;
0067     d->m_domain = domain;
0068     if (d->isRunning()) {
0069         stop();
0070         publishAsync();
0071     }
0072 }
0073 
0074 QStringList PublicService::subtypes() const
0075 {
0076     KDNSSD_D;
0077     return d->m_subtypes;
0078 }
0079 
0080 void PublicService::setType(const QString &type)
0081 {
0082     KDNSSD_D;
0083     d->m_type = type;
0084     if (d->isRunning()) {
0085         stop();
0086         publishAsync();
0087     }
0088 }
0089 
0090 void PublicService::setSubTypes(const QStringList &subtypes)
0091 {
0092     KDNSSD_D;
0093     d->m_subtypes = subtypes;
0094     if (d->isRunning()) {
0095         stop();
0096         publishAsync();
0097     }
0098 }
0099 
0100 void PublicService::setPort(unsigned short port)
0101 {
0102     KDNSSD_D;
0103     d->m_port = port;
0104     if (d->isRunning()) {
0105         stop();
0106         publishAsync();
0107     }
0108 }
0109 
0110 bool PublicService::isPublished() const
0111 {
0112     KDNSSD_D;
0113     return d->m_published;
0114 }
0115 
0116 void PublicService::setTextData(const QMap<QString, QByteArray> &textData)
0117 {
0118     KDNSSD_D;
0119     d->m_textData = textData;
0120     if (d->isRunning()) {
0121         stop();
0122         publishAsync();
0123     }
0124 }
0125 
0126 bool PublicService::publish()
0127 {
0128     KDNSSD_D;
0129     publishAsync();
0130     while (d->isRunning() && !d->m_published) {
0131         d->process();
0132     }
0133     return d->m_published;
0134 }
0135 
0136 void PublicService::stop()
0137 {
0138     KDNSSD_D;
0139     d->stop();
0140     d->m_published = false;
0141 }
0142 
0143 void PublicService::publishAsync()
0144 {
0145     KDNSSD_D;
0146     if (d->isRunning()) {
0147         stop();
0148     }
0149     TXTRecordRef txt;
0150     TXTRecordCreate(&txt, 0, 0);
0151     QMap<QString, QByteArray>::ConstIterator itEnd = d->m_textData.cend();
0152     for (QMap<QString, QByteArray>::ConstIterator it = d->m_textData.cbegin(); it != itEnd; ++it) {
0153         if (TXTRecordSetValue(&txt, it.key().toUtf8().constData(), it.value().length(), it.value().constData()) != kDNSServiceErr_NoError) {
0154             TXTRecordDeallocate(&txt);
0155             Q_EMIT published(false);
0156             return;
0157         }
0158     }
0159     DNSServiceRef ref;
0160     QString fullType = d->m_type;
0161     for (const QString &subtype : std::as_const(d->m_subtypes)) {
0162         fullType += ',' + subtype;
0163     }
0164     if (DNSServiceRegister(&ref,
0165                            0,
0166                            0,
0167                            d->m_serviceName.toUtf8().constData(),
0168                            fullType.toLatin1().constData(),
0169                            domainToDNS(d->m_domain).constData(),
0170                            NULL,
0171                            htons(d->m_port),
0172                            TXTRecordGetLength(&txt),
0173                            TXTRecordGetBytesPtr(&txt),
0174                            publish_callback,
0175                            reinterpret_cast<void *>(d))
0176         == kDNSServiceErr_NoError) {
0177         d->setRef(ref);
0178     }
0179     TXTRecordDeallocate(&txt);
0180     if (!d->isRunning()) {
0181         Q_EMIT published(false);
0182     }
0183 }
0184 
0185 void publish_callback(DNSServiceRef, DNSServiceFlags, DNSServiceErrorType errorCode, const char *name, const char *, const char *, void *context)
0186 {
0187     QObject *obj = reinterpret_cast<QObject *>(context);
0188     if (errorCode != kDNSServiceErr_NoError) {
0189         ErrorEvent err;
0190         QCoreApplication::sendEvent(obj, &err);
0191     } else {
0192         PublishEvent pev(QString::fromUtf8(name));
0193         QCoreApplication::sendEvent(obj, &pev);
0194     }
0195 }
0196 
0197 void PublicServicePrivate::customEvent(QEvent *event)
0198 {
0199     if (event->type() == QEvent::User + SD_ERROR) {
0200         m_parent->stop();
0201         Q_EMIT m_parent->published(false);
0202     }
0203     if (event->type() == QEvent::User + SD_PUBLISH) {
0204         m_published = true;
0205         Q_EMIT m_parent->published(true);
0206         m_serviceName = static_cast<PublishEvent *>(event)->m_name;
0207     }
0208 }
0209 
0210 void PublicService::virtual_hook(int, void *)
0211 {
0212 }
0213 
0214 }
0215 
0216 #include "moc_publicservice.cpp"