KDNSSD

mdnsd-servicebrowser.cpp
1 /*
2  This file is part of the KDE project
3 
4  SPDX-FileCopyrightText: 2004 Jakub Stachowski <[email protected]>
5 
6  SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8 
9 #include "mdnsd-servicebrowser_p.h"
10 #include "domainbrowser.h"
11 #include "servicebrowser.h"
12 #include "mdnsd-responder.h"
13 #include "remoteservice.h"
14 #include "mdnsd-sdevent.h"
15 #include <dns_sd.h>
16 #include <QStringList>
17 #include <QHash>
18 #include <QCoreApplication>
19 #include <QTimer>
20 #include <QHostInfo>
21 
22 #define TIMEOUT_WAN 2000
23 #define TIMEOUT_LAN 200
24 
25 namespace KDNSSD
26 {
27 void query_callback(DNSServiceRef, DNSServiceFlags flags, uint32_t, DNSServiceErrorType errorCode,
28  const char *serviceName, const char *regtype, const char *replyDomain, void *context);
29 
30 ServiceBrowser::ServiceBrowser(const QString &type, bool autoResolve, const QString &domain, const QString &subtype)
31  : d(new ServiceBrowserPrivate(this))
32 {
33  d->m_type = type;
34  d->m_autoResolve = autoResolve;
35  d->m_domain = domain;
36  d->m_subtype = subtype;
37  d->timeout.setSingleShot(true);
38  connect(&d->timeout, SIGNAL(timeout()), d, SLOT(onTimeout()));
39 }
40 
42 {
43 // DNSServiceRef ref;
44 // bool ok (DNSServiceCreateConnection(&ref)==kDNSServiceErr_NoError);
45 // if (ok) DNSServiceRefDeallocate(ref);
46 // return (ok) ? Working : Stopped;
47  return Working;
48 }
49 ServiceBrowser::~ ServiceBrowser()
50 {
51  delete d;
52 }
53 
55 {
56  return d->m_autoResolve;
57 }
58 
59 void ServiceBrowserPrivate::serviceResolved(bool success)
60 {
61  QObject *sender_obj = const_cast<QObject *>(sender());
62  RemoteService *svr = static_cast<RemoteService *>(sender_obj);
63  disconnect(svr, SIGNAL(resolved(bool)), this, SLOT(serviceResolved(bool)));
64  QList<RemoteService::Ptr>::Iterator it = m_duringResolve.begin();
65  QList<RemoteService::Ptr>::Iterator itEnd = m_duringResolve.end();
66  while (it != itEnd && svr != (*it).data()) {
67  ++it;
68  }
69  if (it != itEnd) {
70  if (success) {
71  m_services += (*it);
72  emit m_parent->serviceAdded(RemoteService::Ptr(svr));
73  }
74  m_duringResolve.erase(it);
75  queryFinished();
76  }
77 }
78 
80 {
81  if (d->isRunning()) {
82  return;
83  }
84  d->m_finished = false;
85  DNSServiceRef ref;
86  QString fullType = d->m_type;
87  if (!d->m_subtype.isEmpty()) {
88  fullType = d->m_subtype + "._sub." + d->m_type;
89  }
90  if (DNSServiceBrowse(&ref, 0, 0, fullType.toLatin1().constData(),
91  domainToDNS(d->m_domain).constData(), query_callback, reinterpret_cast<void *>(d))
92  == kDNSServiceErr_NoError) {
93  d->setRef(ref);
94  }
95  if (!d->isRunning()) {
96  emit finished();
97  } else {
98  d->timeout.start(domainIsLocal(d->m_domain) ? TIMEOUT_LAN : TIMEOUT_WAN);
99  }
100 }
101 
102 void ServiceBrowserPrivate::queryFinished()
103 {
104  if (!m_duringResolve.count() && m_finished) {
105  emit m_parent->finished();
106  }
107 }
108 
110 {
111  return d->m_services;
112 }
113 
114 void ServiceBrowser::virtual_hook(int, void *)
115 {}
116 
117 RemoteService::Ptr ServiceBrowserPrivate::find(RemoteService::Ptr s, const QList<RemoteService::Ptr> &where) const
118 {
119  for (const RemoteService::Ptr &i : where) if (*s == *i) {
120  return i;
121  }
122  return RemoteService::Ptr();
123 }
124 
125 void ServiceBrowserPrivate::customEvent(QEvent *event)
126 {
127  if (event->type() == QEvent::User + SD_ERROR) {
128  stop();
129  m_finished = false;
130  queryFinished();
131  }
132  if (event->type() == QEvent::User + SD_ADDREMOVE) {
133  AddRemoveEvent *aev = static_cast<AddRemoveEvent *>(event);
134  // m_type has useless trailing dot
135  RemoteService::Ptr svr(new RemoteService(aev->m_name, aev->m_type.left(aev->m_type.length() - 1), aev->m_domain));
136  if (aev->m_op == AddRemoveEvent::Add) {
137  if (m_autoResolve) {
138  connect(svr.data(), SIGNAL(resolved(bool)), this, SLOT(serviceResolved(bool)));
139  m_duringResolve += svr;
140  svr->resolveAsync();
141  } else {
142  m_services += svr;
143  emit m_parent->serviceAdded(svr);
144  }
145  } else {
146 
147  RemoteService::Ptr found = find(svr, m_duringResolve);
148  if (found) {
149  m_duringResolve.removeAll(found);
150  } else {
151  found = find(svr, m_services);
152  if (found) {
153  emit m_parent->serviceRemoved(found);
154  m_services.removeAll(found);
155  }
156  }
157  }
158  m_finished = aev->m_last;
159  if (m_finished) {
160  queryFinished();
161  }
162  }
163 }
164 
165 void ServiceBrowserPrivate::onTimeout()
166 {
167  m_finished = true;
168  queryFinished();
169 }
170 
171 void query_callback(DNSServiceRef, DNSServiceFlags flags, uint32_t, DNSServiceErrorType errorCode,
172  const char *serviceName, const char *regtype, const char *replyDomain,
173  void *context)
174 {
175  QObject *obj = reinterpret_cast<QObject *>(context);
176  if (errorCode != kDNSServiceErr_NoError) {
177  ErrorEvent err;
178  QCoreApplication::sendEvent(obj, &err);
179  } else {
180  AddRemoveEvent arev((flags & kDNSServiceFlagsAdd) ? AddRemoveEvent::Add :
181  AddRemoveEvent::Remove, QString::fromUtf8(serviceName), regtype,
182  DNSToDomain(replyDomain), !(flags & kDNSServiceFlagsMoreComing));
183  QCoreApplication::sendEvent(obj, &arev);
184  }
185 }
186 
187 // TODO: Please Implement Me - Using a KResolver (if not natively)
189 {
190  return QHostAddress();
191 }
192 
194 {
195  return QHostInfo::localHostName();
196 }
197 
198 }
199 
200 #include "moc_servicebrowser.cpp"
ServiceBrowser(const QString &type, bool autoResolve=false, const QString &domain=QString(), const QString &subtype=QString())
Create a ServiceBrowser for a particular service type.
QEvent::Type type() const const
const QChar * constData() const const
virtual void startBrowse()
Starts browsing for services.
bool isAutoResolving() const
Whether discovered services are resolved before being reported.
QObject * sender() const const
static State isAvailable()
Checks availability of DNS-SD services.
QList::iterator erase(QList::iterator pos)
StandardShortcut find(const QKeySequence &keySeq)
State
Availability of DNS-SD services.
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
void ref()
virtual bool event(QEvent *e)
QString fromUtf8(const char *str, int size)
static QHostAddress resolveHostName(const QString &hostname)
Resolves an mDNS hostname into an IP address.
const char * constData() const const
bool sendEvent(QObject *receiver, QEvent *event)
QList::iterator end()
QList< RemoteService::Ptr > services() const
The currently known services of the specified type.
QByteArray toLatin1() const const
QString localHostName()
static QString getLocalHostName()
The mDNS hostname of the local machine.
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QList::iterator begin()
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Fri Aug 7 2020 22:38:33 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.