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