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"