1 | /* |
2 | This file is part of the KDE project |
3 | |
4 | SPDX-FileCopyrightText: 2004 Jakub Stachowski <qbast@go2.pl> |
5 | |
6 | SPDX-License-Identifier: LGPL-2.0-or-later |
7 | */ |
8 | |
9 | #ifndef KDNSSDSERVICEBROWSER_H |
10 | #define KDNSSDSERVICEBROWSER_H |
11 | |
12 | #include "remoteservice.h" |
13 | #include <QHostAddress> |
14 | #include <QObject> |
15 | |
16 | #include <memory> |
17 | |
18 | namespace KDNSSD |
19 | { |
20 | class DomainBrowser; |
21 | class ServiceBrowserPrivate; |
22 | |
23 | /*! |
24 | * \class KDNSSD::ServiceBrowser |
25 | * \inmodule KDNSSD |
26 | * \inheaderfile KDNSSD/ServiceBrowser |
27 | * |
28 | * \brief Browses for network services advertised over DNS-SD. |
29 | * |
30 | * This is the central class in the KDNSSD library for applications |
31 | * that want to discover services on network. |
32 | * |
33 | * Suppose that you need list of web servers running. Then you |
34 | * might do something like: |
35 | * \code |
36 | * KDNSSD::ServiceBrowser *browser = new KDNSSD::ServiceBrowser(QStringLiteral("_http._tcp")); |
37 | * connect(browser, &KDNSSD::ServiceBrowser::serviceAdded, |
38 | * this, [](KDNSSD::RemoteService::Ptr service) { |
39 | * }); |
40 | * connect(browser, &KDNSSD::ServiceBrowser::serviceRemoved, |
41 | * this, [](KDNSSD::RemoteService::Ptr service) { |
42 | * }); |
43 | * browser->startBrowse(); |
44 | * \endcode |
45 | * |
46 | * In above example addService() will be called for every web server |
47 | * already running and for every web service that subsequently |
48 | * appears on the network and delService() will be called when |
49 | * a server previously advertised is stopped. |
50 | * |
51 | * Because no domain was passed to constructor, the default domain |
52 | * will be searched. To find other domains to browse for services on, |
53 | * use DomainBrowser. |
54 | */ |
55 | class KDNSSD_EXPORT ServiceBrowser : public QObject |
56 | { |
57 | Q_OBJECT |
58 | |
59 | public: |
60 | /*! |
61 | * \enum KDNSSD::ServiceBrowser::State |
62 | * \brief Availability of DNS-SD services. |
63 | * |
64 | * \value Working |
65 | * The DNS-SD services are available. |
66 | * \value Stopped |
67 | * Not available because mDnsd or Avahi daemon is not running. |
68 | * \value Unsupported |
69 | * Not available because KDE was compiled without DNS-SD support. |
70 | */ |
71 | enum State { |
72 | Working, |
73 | Stopped, |
74 | Unsupported, |
75 | }; |
76 | |
77 | /*! |
78 | * Creates a ServiceBrowser for a particular service type. |
79 | * |
80 | * DomainBrowser can be used to find other domains to browse on. |
81 | * If no domain is given, the default domain is used. |
82 | * |
83 | * The service type is the high-level protocol type, followed by a dot, |
84 | * followed by the transport protocol type (\c _tcp or \c _udp). |
85 | * The \l {http://www.dns-sd.org} {DNS-SD website} maintains |
86 | * \l {http://www.dns-sd.org/ServiceTypes.html} {a full list} |
87 | * of service types. |
88 | * |
89 | * The \a subtype parameter allows you to do more fine-grained filtering |
90 | * on the services you are interested in. So you might request only |
91 | * FTP servers that allow anonymous access by passing \c "_ftp._tcp" as the |
92 | * \a type and \c "_anon" as the \a subtype. Subtypes are particularly |
93 | * important for types like \c _soap and \c _upnp, which are far too generic |
94 | * for most applications. In these cases, the subtype can be used to |
95 | * specify the particular SOAP or UPnP protocol they want. |
96 | * |
97 | * \warning |
98 | * Enabling \a autoResolve will increase network usage by resolving |
99 | * all services, so this feature should be used only when necessary. |
100 | * |
101 | * \a type is the service type to browse for (example: "_http._tcp") |
102 | * |
103 | * \a autoResolve if true, discovered services will be resolved before being |
104 | * reported with the serviceAdded() signal |
105 | * |
106 | * \a domain is the domain to search on instead of the default one |
107 | * |
108 | * \a subtype if not an empty string, only browse for a specific subtype |
109 | * |
110 | * \sa startBrowse() and isAvailable() |
111 | */ |
112 | explicit ServiceBrowser(const QString &type, bool autoResolve = false, const QString &domain = QString(), const QString &subtype = QString()); |
113 | |
114 | ~ServiceBrowser() override; |
115 | |
116 | /*! |
117 | * The currently known services of the specified type. |
118 | * |
119 | * Returns a list of RemoteService pointers |
120 | * |
121 | * \sa serviceAdded() and serviceRemoved() |
122 | */ |
123 | QList<RemoteService::Ptr> services() const; |
124 | |
125 | /*! |
126 | * Starts browsing for services. |
127 | * |
128 | * \note Only the first call to this function will have any effect. |
129 | * |
130 | * Browsing stops when the ServiceBrowser object is destroyed. |
131 | * |
132 | * \warning The serviceAdded() signal may be emitted before this |
133 | * function returns. |
134 | * |
135 | * \sa serviceAdded(), serviceRemoved() and finished() |
136 | */ |
137 | virtual void startBrowse(); |
138 | |
139 | /*! |
140 | * Checks availability of DNS-SD services. |
141 | * |
142 | * \note Although this method is part of ServiceBrowser, none of the classes |
143 | * in this library will be able to perform their intended function |
144 | * if this method does not return \l Working. |
145 | * |
146 | * If this method does not return \l Working, it is still safe to call |
147 | * any of the methods in this library. However, no services will be |
148 | * found or published and no domains will be found. |
149 | * |
150 | * If you use this function to report an error to the user, below |
151 | * is a suggestion on how to word the errors when publishing a |
152 | * service. The first line of each error message can also be |
153 | * used for reporting errors when browsing for services. |
154 | * |
155 | * \code |
156 | * switch(KDNSSD::ServiceBrowser::isAvailable()) { |
157 | * case KDNSSD::ServiceBrowser::Working: |
158 | * return ""; |
159 | * case KDNSSD::ServiceBrowser::Stopped: |
160 | * return i18n("<p>The Zeroconf daemon is not running. See the Service" |
161 | * " Discovery Handbook for more information.<br/>" |
162 | * "Other users will not see the services provided by this |
163 | * " system when browsing the network via zeroconf, but " |
164 | * " normal access will still work.</p>"); |
165 | * case KDNSSD::ServiceBrowser::Unsupported: |
166 | * return i18n("<p>Zeroconf support is not available in this version of KDE." |
167 | * " See the Service Discovery Handbook for more information.<br/>" |
168 | * "Other users will not see the services provided by this |
169 | * " application when browsing the network via zeroconf, but " |
170 | * " normal access will still work.</p>"); |
171 | * default: |
172 | * return i18n("<p>Unknown error with Zeroconf.<br/>" |
173 | * "Other users will not see the services provided by this |
174 | * " application when browsing the network via zeroconf, but " |
175 | * " normal access will still work.</p>"); |
176 | * } |
177 | * \endcode |
178 | * |
179 | * Returns the mDNS-SD service state. |
180 | */ |
181 | static State isAvailable(); |
182 | |
183 | /*! |
184 | * Whether discovered services are resolved before being reported. |
185 | * |
186 | * Returns the value of the autoResolve parameter passed to the constructor. |
187 | * |
188 | * \since 4.1 |
189 | */ |
190 | bool isAutoResolving() const; |
191 | |
192 | /*! |
193 | * Resolves an mDNS hostname into an IP address. |
194 | * |
195 | * \note This function is very rarely useful, since a properly configured |
196 | * system is able to resolve an mDNS-based host name using the system |
197 | * resolver (ie: you can just pass the mDNS hostname to KIO or other |
198 | * library). |
199 | * |
200 | * \a hostname the hostname to be resolved |
201 | * |
202 | * Returns a QHostAddress containing the IP address, or QHostAddress() if |
203 | * resolution failed |
204 | * |
205 | * \since 4.2 |
206 | */ |
207 | static QHostAddress resolveHostName(const QString &hostname); |
208 | |
209 | /*! |
210 | * The mDNS hostname of the local machine. |
211 | * |
212 | * Usually this will return the same as QHostInfo::localHostName(), |
213 | * but it may be changed to something different |
214 | * in the Avahi configuration file (if using the Avahi backend). |
215 | * |
216 | * Returns the hostname, or an empty string on failure |
217 | * |
218 | * \since 4.2 |
219 | */ |
220 | static QString getLocalHostName(); |
221 | |
222 | Q_SIGNALS: |
223 | /*! |
224 | * Emitted when new service is discovered. |
225 | * |
226 | * If isAutoResolving() returns \c true, this will not be emitted |
227 | * until the service has been resolved. |
228 | * |
229 | * \a service is a RemoteService object describing the service |
230 | * |
231 | * \sa serviceRemoved() and finished() |
232 | */ |
233 | void serviceAdded(KDNSSD::RemoteService::Ptr service); |
234 | |
235 | /*! |
236 | * Emitted when a service is no longer published over DNS-SD. |
237 | * |
238 | * The RemoteService object is removed from the services() list |
239 | * and deleted immediately after this signal returns. |
240 | * |
241 | * \warning |
242 | * Do \b not use a delayed connection with this signal. |
243 | * |
244 | * \a service a RemoteService object describing the service |
245 | * |
246 | * \sa serviceAdded() and finished() |
247 | */ |
248 | void serviceRemoved(KDNSSD::RemoteService::Ptr service); |
249 | |
250 | /*! |
251 | * Emitted when the list of published services has settled. |
252 | * |
253 | * \note |
254 | * This signal is emitted once after startBrowse() is called |
255 | * when all the services of the requested type that are |
256 | * currently published have been reported (even if none |
257 | * are available or the DNS-SD service is not available). |
258 | * It is emitted again when a new batch of services become |
259 | * available or disappear. |
260 | * |
261 | * For example, if a new host is connected to network and |
262 | * announces some services watched for by this ServiceBrowser, |
263 | * they will be reported by one or more serviceAdded() signals |
264 | * and the whole batch will be concluded by finished(). |
265 | * |
266 | * This signal can be used by applications that just want to |
267 | * get a list of the currently available services |
268 | * (similar to a directory listing) and do not care about |
269 | * adding or removing services that appear or disappear later. |
270 | * |
271 | * \warning |
272 | * There is no guarantee any RemoteService |
273 | * pointers received by serviceAdded() will be valid |
274 | * by the time this signal is emitted, so you should either |
275 | * do all your work involving them in the slot receiving |
276 | * the serviceAdded() signal, or you should listen to |
277 | * serviceRemoved() as well. |
278 | * |
279 | * \sa serviceAdded() and serviceRemoved() |
280 | */ |
281 | void finished(); |
282 | |
283 | protected: |
284 | virtual void virtual_hook(int, void *); |
285 | |
286 | private: |
287 | friend class ServiceBrowserPrivate; |
288 | std::unique_ptr<ServiceBrowserPrivate> const d; |
289 | Q_DECLARE_PRIVATE_D(d, ServiceBrowser) |
290 | }; |
291 | |
292 | } |
293 | |
294 | #endif |
295 | |