File indexing completed on 2024-04-28 15:22:07

0001 /*
0002     This file is part of the KDE project
0003 
0004     SPDX-FileCopyrightText: 2004 Jakub Stachowski <qbast@go2.pl>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #ifndef KDNSSDSERVICEBROWSER_H
0010 #define KDNSSDSERVICEBROWSER_H
0011 
0012 #include "remoteservice.h"
0013 #include <QHostAddress>
0014 #include <QObject>
0015 
0016 #include <memory>
0017 
0018 namespace KDNSSD
0019 {
0020 class DomainBrowser;
0021 class ServiceBrowserPrivate;
0022 
0023 /**
0024  * @class ServiceBrowser servicebrowser.h KDNSSD/ServiceBrowser
0025  * @short Browses for network services advertised over DNS-SD
0026  *
0027  * This is the central class in the KDNSSD library for applications
0028  * that want to discover services on network.
0029  *
0030  * Suppose that you need list of web servers running.  Then you
0031  * might do something like
0032  * @code
0033  * KDNSSD::ServiceBrowser *browser = new KDNSSD::ServiceBrowser(QStringLiteral("_http._tcp"));
0034  * connect(browser, &KDNSSD::ServiceBrowser::serviceAdded,
0035  *         this, [](KDNSSD::RemoteService::Ptr service) {
0036  *         });
0037  * connect(browser, &KDNSSD::ServiceBrowser::serviceRemoved,
0038  *         this, [](KDNSSD::RemoteService::Ptr service) {
0039  *         });
0040  * browser->startBrowse();
0041  * @endcode
0042  *
0043  * In above example addService() will be called for every web server
0044  * already running and for every web service that subsequently
0045  * appears on the network and delService() will be called when
0046  * a server previously advertised is stopped.
0047  *
0048  * Because no domain was passed to constructor, the default domain
0049  * will be searched.  To find other domains to browse for services on,
0050  * use DomainBrowser.
0051  *
0052  * @author Jakub Stachowski
0053  */
0054 class KDNSSD_EXPORT ServiceBrowser : public QObject
0055 {
0056     Q_OBJECT
0057 
0058 public:
0059     /**
0060      * Availability of DNS-SD services
0061      */
0062     enum State {
0063         /** the service is available */
0064         Working,
0065         /** not available because mDnsd or Avahi daemon is not running */
0066         Stopped,
0067         /** not available because KDE was compiled without DNS-SD support */
0068         Unsupported,
0069     };
0070 
0071     /**
0072      * Create a ServiceBrowser for a particular service type
0073      *
0074      * DomainBrowser can be used to find other domains to browse on.
0075      * If no domain is given, the default domain is used.
0076      *
0077      * The service type is the high-level protocol type, followed by a dot,
0078      * followed by the transport protocol type (@c _tcp or @c _udp).
0079      * The <a href="http://www.dns-sd.org">DNS-SD website</a> maintains
0080      * <a href="http://www.dns-sd.org/ServiceTypes.html">a full list</a>
0081      * of service types.
0082      *
0083      * The @p subtype parameter allows you to do more fine-grained filtering
0084      * on the services you are interested in.  So you might request only
0085      * FTP servers that allow anonymous access by passing "_ftp._tcp" as the
0086      * @p type and "_anon" as the @p subtype.  Subtypes are particularly
0087      * important for types like _soap and _upnp, which are far too generic
0088      * for most applications.  In these cases, the subtype can be used to
0089      * specify the particular SOAP or UPnP protocol they want.
0090      *
0091      * @warning
0092      * Enabling @p autoResolve will increase network usage by resolving
0093      * all services, so this feature should be used only when necessary.
0094      *
0095      * @param type        service types to browse for (example: "_http._tcp")
0096      * @param autoResolve discovered services will be resolved before being
0097      *                    reported with the serviceAdded() signal
0098      * @param domain      a domain to search on instead of the default one
0099      * @param subtype     only browse for a specific subtype
0100      *
0101      * @see startBrowse() and isAvailable()
0102      */
0103     explicit ServiceBrowser(const QString &type, bool autoResolve = false, const QString &domain = QString(), const QString &subtype = QString());
0104 
0105     ~ServiceBrowser() override;
0106 
0107     /**
0108      * The currently known services of the specified type
0109      *
0110      * @returns a list of RemoteService pointers
0111      *
0112      * @see serviceAdded() and serviceRemoved()
0113      */
0114     QList<RemoteService::Ptr> services() const;
0115 
0116     /**
0117      * Starts browsing for services
0118      *
0119      * Only the first call to this function will have any effect.
0120      *
0121      * Browsing stops when the ServiceBrowser object is destroyed.
0122      *
0123      * @warning The serviceAdded() signal may be emitted before this
0124      *          function returns.
0125      *
0126      * @see serviceAdded(), serviceRemoved() and finished()
0127      */
0128     virtual void startBrowse();
0129 
0130     /**
0131      * Checks availability of DNS-SD services
0132      *
0133      * Although this method is part of ServiceBrowser, none of the classes
0134      * in this library will be able to perform their intended function
0135      * if this method does not return Working.
0136      *
0137      * If this method does not return Working, it is still safe to call
0138      * any of the methods in this library.  However, no services will be
0139      * found or published and no domains will be found.
0140      *
0141      * If you use this function to report an error to the user, below
0142      * is a suggestion on how to word the errors when publishing a
0143      * service.  The first line of each error message can also be
0144      * used for reporting errors when browsing for services.
0145      *
0146      * @code
0147      * switch(KDNSSD::ServiceBrowser::isAvailable()) {
0148      * case KDNSSD::ServiceBrowser::Working:
0149      *     return "";
0150      * case KDNSSD::ServiceBrowser::Stopped:
0151      *     return i18n("<p>The Zeroconf daemon is not running. See the Service"
0152      *                 " Discovery Handbook for more information.<br/>"
0153      *                 "Other users will not see the services provided by this
0154      *                 " system when browsing the network via zeroconf, but "
0155      *                 " normal access will still work.</p>");
0156      * case KDNSSD::ServiceBrowser::Unsupported:
0157      *     return i18n("<p>Zeroconf support is not available in this version of KDE."
0158      *                 " See the Service Discovery Handbook for more information.<br/>"
0159      *                 "Other users will not see the services provided by this
0160      *                 " application when browsing the network via zeroconf, but "
0161      *                 " normal access will still work.</p>");
0162      * default:
0163      *     return i18n("<p>Unknown error with Zeroconf.<br/>"
0164      *                 "Other users will not see the services provided by this
0165      *                 " application when browsing the network via zeroconf, but "
0166      *                 " normal access will still work.</p>");
0167      * }
0168      * @endcode
0169      *
0170      */
0171     static State isAvailable();
0172 
0173     /**
0174      * Whether discovered services are resolved before being reported
0175      *
0176      * @return the value of the @p autoResolve parameter passed to the constructor
0177      *
0178      * @since 4.1
0179      */
0180     bool isAutoResolving() const;
0181 
0182     /**
0183      * Resolves an mDNS hostname into an IP address
0184      *
0185      * This function is very rarely useful, since a properly configured
0186      * system is able to resolve an mDNS-based host name using the system
0187      * resolver (ie: you can just pass the mDNS hostname to KIO or other
0188      * library).
0189      *
0190      * @param hostname the hostname to be resolved
0191      * @return a QHostAddress containing the IP address, or QHostAddress() if
0192      *         resolution failed
0193      * @since 4.2
0194      */
0195     static QHostAddress resolveHostName(const QString &hostname);
0196 
0197     /**
0198      * The mDNS hostname of the local machine
0199      *
0200      * Usually this will return the same as QHostInfo::localHostName(),
0201      * but it may be changed to something different
0202      * in the Avahi configuration file (if using the Avahi backend).
0203      *
0204      * @return the hostname, or an empty string on failure
0205      * @since 4.2
0206      */
0207     static QString getLocalHostName();
0208 
0209 Q_SIGNALS:
0210     /**
0211      * Emitted when new service is discovered
0212      *
0213      * If isAutoResolving() returns @c true, this will not be emitted
0214      * until the service has been resolved.
0215      *
0216      * @param service a RemoteService object describing the service
0217      *
0218      * @see serviceRemoved() and finished()
0219      */
0220     void serviceAdded(KDNSSD::RemoteService::Ptr service);
0221 
0222     /**
0223      * Emitted when a service is no longer published over DNS-SD
0224      *
0225      * The RemoteService object is removed from the services() list
0226      * and deleted immediately after this signal returns.
0227      *
0228      * @warning
0229      * Do @b not use a delayed connection with this signal
0230      *
0231      * @param service a RemoteService object describing the service
0232      *
0233      * @see serviceAdded() and finished()
0234      */
0235     void serviceRemoved(KDNSSD::RemoteService::Ptr service);
0236 
0237     /**
0238      * Emitted when the list of published services has settled
0239      *
0240      * This signal is emitted once after startBrowse() is called
0241      * when all the services of the requested type that are
0242      * currently published have been reported (even if none
0243      * are available or the DNS-SD service is not available).
0244      * It is emitted again when a new batch of services become
0245      * available or disappear.
0246      *
0247      * For example, if a new host is connected to network and
0248      * announces some services watched for by this ServiceBrowser,
0249      * they will be reported by one or more serviceAdded() signals
0250      * and the whole batch will be concluded by finished().
0251      *
0252      * This signal can be used by applications that just want to
0253      * get a list of the currently available services
0254      * (similar to a directory listing) and do not care about
0255      * adding or removing services that appear or disappear later.
0256      *
0257      * @warning
0258      * There is no guarantee any RemoteService
0259      * pointers received by serviceAdded() will be valid
0260      * by the time this signal is emitted, so you should either
0261      * do all your work involving them in the slot receiving
0262      * the serviceAdded() signal, or you should listen to
0263      * serviceRemoved() as well.
0264      *
0265      * @see serviceAdded() and serviceRemoved()
0266      */
0267     void finished();
0268 
0269 protected:
0270     virtual void virtual_hook(int, void *);
0271 
0272 private:
0273     friend class ServiceBrowserPrivate;
0274     std::unique_ptr<ServiceBrowserPrivate> const d;
0275     Q_DECLARE_PRIVATE_D(d, ServiceBrowser)
0276 };
0277 
0278 }
0279 
0280 #endif