KDNSSD

avahi-servicebrowser.cpp
1 /*
2  This file is part of the KDE project
3 
4  SPDX-FileCopyrightText: 2004 Jakub Stachowski <[email protected]>
5  SPDX-FileCopyrightText: 2018 Harald Sitter <[email protected]>
6 
7  SPDX-License-Identifier: LGPL-2.0-or-later
8 */
9 
10 #include "avahi-servicebrowser_p.h"
11 #include <QStringList>
12 #include "servicebrowser.h"
13 #include "avahi_servicebrowser_interface.h"
14 #include "avahi_server_interface.h"
15 #include <QHash>
16 #include <QHostAddress>
17 
18 namespace KDNSSD
19 {
20 
21 ServiceBrowser::ServiceBrowser(const QString &type, bool autoResolve, const QString &domain, const QString &subtype)
22  : d(new ServiceBrowserPrivate(this))
23 {
24  d->m_type = type;
25  d->m_subtype = subtype;
26  d->m_autoResolve = autoResolve;
27  d->m_domain = domain;
28  d->m_timer.setSingleShot(true);
29 }
30 
32 {
33  org::freedesktop::Avahi::Server s(QStringLiteral("org.freedesktop.Avahi"), QStringLiteral("/"), QDBusConnection::systemBus());
34  QDBusReply<int> rep = s.GetState();
35  return (rep.isValid() && rep.value() == 2) ? Working : Stopped;
36 }
37 ServiceBrowser::~ServiceBrowser()
38 {
39  delete d;
40 }
41 
43 {
44  return d->m_autoResolve;
45 }
46 
48 {
49  if (d->m_running) {
50  return;
51  }
52 
53  // Do not race!
54  // https://github.com/lathiat/avahi/issues/9
55  // Avahi's DBus API is incredibly racey with signals getting fired
56  // immediately after a request was made even though we may not yet be
57  // listening. In lieu of a proper upstream fix for this we'll unfortunately
58  // have to resort to this hack:
59  // We register to all signals regardless of path and then filter them once
60  // we know what "our" path is. This is much more fragile than a proper
61  // QDBusInterface assisted signal connection but unfortunately the only way
62  // we can reliably prevent signals getting lost in the race.
63  // This uses a fancy trick whereby using QDBusMessage as last argument will
64  // give us the correct signal argument types as well as the underlying
65  // message so that we may check the message path.
67  .connect("org.freedesktop.Avahi",
68  "",
69  "org.freedesktop.Avahi.ServiceBrowser",
70  "ItemNew",
71  d,
72  SLOT(gotGlobalItemNew(int,int,QString,QString,QString,uint,QDBusMessage)));
74  .connect("org.freedesktop.Avahi",
75  "",
76  "org.freedesktop.Avahi.ServiceBrowser",
77  "ItemRemove",
78  d,
79  SLOT(gotGlobalItemRemove(int,int,QString,QString,QString,uint,QDBusMessage)));
81  .connect("org.freedesktop.Avahi",
82  "",
83  "org.freedesktop.Avahi.ServiceBrowser",
84  "AllForNow",
85  d,
86  SLOT(gotGlobalAllForNow(QDBusMessage)));
87  d->m_dbusObjectPath.clear();
88 
89  org::freedesktop::Avahi::Server s(QStringLiteral("org.freedesktop.Avahi"), QStringLiteral("/"), QDBusConnection::systemBus());
90 
91  QString fullType = d->m_type;
92  if (!d->m_subtype.isEmpty()) {
93  fullType = d->m_subtype + QStringLiteral("._sub.") + d->m_type;
94  }
95  QDBusReply<QDBusObjectPath> rep = s.ServiceBrowserNew(-1, -1, fullType, domainToDNS(d->m_domain), 0);
96  if (!rep.isValid()) {
97  emit finished();
98  return;
99  }
100 
101  d->m_dbusObjectPath = rep.value().path();
102  d->m_running = true;
103  d->m_browserFinished = true;
104 
105  // This is held because we need to explicitly Free it!
106  d->m_browser = new org::freedesktop::Avahi::ServiceBrowser(
107  s.service(),
108  d->m_dbusObjectPath,
109  s.connection());
110 
111  connect(&d->m_timer, &QTimer::timeout,
112  d, &ServiceBrowserPrivate::browserFinished);
113  d->m_timer.start(domainIsLocal(d->m_domain) ? TIMEOUT_LAST_SERVICE : TIMEOUT_START_WAN);
114 }
115 
116 void ServiceBrowserPrivate::serviceResolved(bool success)
117 {
118  QObject *sender_obj = const_cast<QObject *>(sender());
119  RemoteService *svr = static_cast<RemoteService *>(sender_obj);
120  disconnect(svr, SIGNAL(resolved(bool)), this, SLOT(serviceResolved(bool)));
121  QList<RemoteService::Ptr>::Iterator it = m_duringResolve.begin();
122  QList<RemoteService::Ptr>::Iterator itEnd = m_duringResolve.end();
123  while (it != itEnd && svr != (*it).data()) {
124  ++it;
125  }
126  if (it != itEnd) {
127  if (success) {
128  m_services += (*it);
129  emit m_parent->serviceAdded(RemoteService::Ptr(svr));
130  }
131  m_duringResolve.erase(it);
132  queryFinished();
133  }
134 }
135 
136 void ServiceBrowserPrivate::gotGlobalItemNew(int interface,
137  int protocol,
138  const QString &name,
139  const QString &type,
140  const QString &domain,
141  uint flags,
142  QDBusMessage msg)
143 {
144  if (!isOurMsg(msg)) {
145  return;
146  }
147  gotNewService(interface, protocol, name, type, domain, flags);
148 }
149 
150 void ServiceBrowserPrivate::gotGlobalItemRemove(int interface,
151  int protocol,
152  const QString &name,
153  const QString &type,
154  const QString &domain,
155  uint flags,
156  QDBusMessage msg)
157 {
158  if (!isOurMsg(msg)) {
159  return;
160  }
161  gotRemoveService(interface, protocol, name, type, domain, flags);
162 }
163 
164 void ServiceBrowserPrivate::gotGlobalAllForNow(QDBusMessage msg)
165 {
166  if (!isOurMsg(msg)) {
167  return;
168  }
169  browserFinished();
170 }
171 
172 RemoteService::Ptr ServiceBrowserPrivate::find(RemoteService::Ptr s, const QList<RemoteService::Ptr> &where) const
173 {
174  for (const RemoteService::Ptr &i : where) if (*s == *i) {
175  return i;
176  }
177  return RemoteService::Ptr();
178 }
179 
180 void ServiceBrowserPrivate::gotNewService(int, int, const QString &name, const QString &type, const QString &domain, uint)
181 {
182  m_timer.start(TIMEOUT_LAST_SERVICE);
183  RemoteService::Ptr svr(new RemoteService(name, type, domain));
184  if (m_autoResolve) {
185  connect(svr.data(), SIGNAL(resolved(bool)), this, SLOT(serviceResolved(bool)));
186  m_duringResolve += svr;
187  svr->resolveAsync();
188  } else {
189  m_services += svr;
190  emit m_parent->serviceAdded(svr);
191  }
192 }
193 
194 void ServiceBrowserPrivate::gotRemoveService(int, int, const QString &name, const QString &type, const QString &domain, uint)
195 {
196  m_timer.start(TIMEOUT_LAST_SERVICE);
197  RemoteService::Ptr tmpl(new RemoteService(name, type, domain));
198  RemoteService::Ptr found = find(tmpl, m_duringResolve);
199  if (found) {
200  m_duringResolve.removeAll(found);
201  return;
202  }
203  found = find(tmpl, m_services);
204  if (!found) {
205  return;
206  }
207 
208  emit m_parent->serviceRemoved(found);
209  m_services.removeAll(found);
210 }
211 void ServiceBrowserPrivate::browserFinished()
212 {
213  m_timer.stop();
214  m_browserFinished = true;
215  queryFinished();
216 }
217 
218 void ServiceBrowserPrivate::queryFinished()
219 {
220  if (!m_duringResolve.count() && m_browserFinished) {
221  emit m_parent->finished();
222  }
223 }
224 
226 {
227  return d->m_services;
228 }
229 
230 void ServiceBrowser::virtual_hook(int, void *)
231 {}
232 
234 {
235  org::freedesktop::Avahi::Server s(QStringLiteral("org.freedesktop.Avahi"), QStringLiteral("/"), QDBusConnection::systemBus());
236 
237  int protocol = 0;
238  QString name;
239  int aprotocol = 0;
240  QString address;
241  uint flags = 0;
242 
243  QDBusReply<int> reply = s.ResolveHostName(-1, -1, hostname, 0, (unsigned int) 0, protocol, name, aprotocol, address, flags);
244 
245  if (reply.isValid()) {
246  return QHostAddress(address);
247  } else {
248  return QHostAddress();
249  }
250 }
251 
253 {
254  org::freedesktop::Avahi::Server s(QStringLiteral("org.freedesktop.Avahi"), QStringLiteral("/"), QDBusConnection::systemBus());
255 
256  QDBusReply<QString> reply = s.GetHostName();
257 
258  if (reply.isValid()) {
259  return reply.value();
260  } else {
261  return QString();
262  }
263 }
264 
265 }
266 
267 #include "moc_servicebrowser.cpp"
268 #include "moc_avahi-servicebrowser_p.cpp"
ServiceBrowser(const QString &type, bool autoResolve=false, const QString &domain=QString(), const QString &subtype=QString())
Create a ServiceBrowser for a particular service type.
virtual void startBrowse()
Starts browsing for services.
not available because mDnsd or Avahi daemon is not running
bool isAutoResolving() const
Whether discovered services are resolved before being reported.
Describes a service published over DNS-SD, typically on a remote machine.
Definition: remoteservice.h:38
QObject * sender() const const
static State isAvailable()
Checks availability of DNS-SD services.
QDBusConnection systemBus()
bool isValid() const const
QList::iterator erase(QList::iterator pos)
State
Availability of DNS-SD services.
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
void timeout()
static QHostAddress resolveHostName(const QString &hostname)
Resolves an mDNS hostname into an IP address.
QDBusReply::Type value() const const
void finished()
Emitted when the list of published services has settled.
QList::iterator end()
QList< RemoteService::Ptr > services() const
The currently known services of the specified type.
static QString getLocalHostName()
The mDNS hostname of the local machine.
bool connect(const QString &service, const QString &path, const QString &interface, const QString &name, QObject *receiver, const char *slot)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QList::iterator begin()
the service is available
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Nov 30 2020 22:40:49 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.