/src/zeroconf/qxtdiscoverableservice.cpp
C++ | 379 lines | 179 code | 27 blank | 173 comment | 35 complexity | c4564471ab2a677406dec1d849bfa370 MD5 | raw file
- #include "qxtdiscoverableservice.h"
- #include "qxtdiscoverableservice_p.h"
- #include <QSocketNotifier>
- #include <QList>
- #include <QPair>
- #include <QtDebug>
- #include <QtEndian>
- /*!
- \class QxtDiscoverableService
- \inmodule QxtZeroconf
- \brief The QxtDiscoverableService class registers a service that can be discovered with Zeroconf, or resolve such a service
- QxtDiscoverableService represents a service on the local network that can be discovered using Zeroconf.
- It can function to provide such a service so that other systems on the network can find it, or it can
- resolve the connection parameters for a service provided by another system on the network. Note that
- resolving a service requires that you already know the service name in addition to the service type for
- the remote service; to find all services on the network that offer a given service type, use
- QxtServiceBrowser.
- When registering a service, you may specify a set of service subtypes to indicate what kind of optional
- capabilities your service offers. Other applications browsing for discoverable services (for instance,
- using QxtServiceBrowser) can limit their search to only those services that provide a given subtype.
- Use the properties inherited from QxtDiscoverableServiceName to configure the other service parameters.
- For more information about Zeroconf, see the documentation available on www.zeroconf.org.
- \sa QxtDiscoverableServiceName
- \sa QxtServiceBrowser
- */
- void QxtDiscoverableServicePrivate::registerServiceCallback(DNSServiceRef service, DNSServiceFlags flags,
- DNSServiceErrorType errCode, const char* name, const char* regtype, const char* domain, void* context)
- {
- Q_UNUSED(service);
- Q_UNUSED(flags);
- Q_UNUSED(regtype);
- QxtDiscoverableServicePrivate* self = reinterpret_cast<QxtDiscoverableServicePrivate*>(context);
- QxtDiscoverableService* pub = &(self->qxt_p());
- if(errCode == kDNSServiceErr_NoError) {
- pub->setServiceName(name);
- pub->setDomain(domain);
- self->state = QxtDiscoverableService::Registered;
- emit self->qxt_p().registered();
- } else {
- self->state = QxtDiscoverableService::Unknown;
- emit self->qxt_p().registrationError((QxtDiscoverableService::ErrorCode)errCode);
- }
- }
- void QxtDiscoverableServicePrivate::resolveServiceCallback(DNSServiceRef service, DNSServiceFlags flags, quint32 iface,
- DNSServiceErrorType errCode, const char* fullname, const char* host, quint16 port, quint16 txtLen,
- const unsigned char* txt, void* context)
- {
- Q_UNUSED(service);
- Q_UNUSED(flags);
- QxtDiscoverableServicePrivate* self = reinterpret_cast<QxtDiscoverableServicePrivate*>(context);
- QxtDiscoverableService* pub = &self->qxt_p();
- if(errCode == kDNSServiceErr_NoError) {
- QxtDiscoverableServiceName name(fullname);
- pub->setServiceName(name.serviceName());
- pub->setDomain(name.domain());
- pub->setHost(host);
- pub->setPort(qFromBigEndian(port));
- pub->setTxtRecord(txtLen > 0 ? QString::fromUtf8((const char*)txt, txtLen) : QString());
- self->iface = iface;
- emit pub->resolved(fullname);
- } else {
- self->state = QxtDiscoverableService::Unknown;
- emit pub->resolveError((QxtDiscoverableService::ErrorCode)errCode);
- }
- }
- void QxtDiscoverableServicePrivate::socketData()
- {
- DNSServiceProcessResult(service);
- }
- /*!
- * Constructs a QxtDiscoverableService object using the specified service type.
- *
- * The service type may be a plain type name or it may be provided in the standard format
- * specified by the Zeroconf specification.
- *
- * The service name will be automatically generated based on the system's hostname.
- */
- QxtDiscoverableService::QxtDiscoverableService(const QString& serviceType, QObject* parent)
- : QObject(parent), QxtDiscoverableServiceName(QString(), serviceType, QString())
- {
- QXT_INIT_PRIVATE(QxtDiscoverableService);
- qxt_zeroconf_parse_subtypes(&qxt_d(), serviceType.toUtf8());
- }
- /*!
- * Constructs a QxtDiscoverableService object using the specified service type.
- *
- * The service type may be a plain type name or it may be provided in the standard format
- * specified by the Zeroconf specification.
- *
- * If the specified service name is already in use, it will be updated with a number to
- * make it unique.
- */
- QxtDiscoverableService::QxtDiscoverableService(const QString& serviceType, const QString& serviceName, QObject* parent)
- : QObject(parent), QxtDiscoverableServiceName(serviceName, serviceType, QString())
- {
- QXT_INIT_PRIVATE(QxtDiscoverableService);
- qxt_zeroconf_parse_subtypes(&qxt_d(), serviceType.toUtf8());
- }
- /*!
- * Destroys the QxtDiscoverableService.
- *
- * TIf registered, the service will be unregistered.
- */
- QxtDiscoverableService::~QxtDiscoverableService()
- {
- if(state() == Registered || state() == Resolved)
- releaseService();
- }
- /*!
- * Returns the current state of the service.
- */
- QxtDiscoverableService::State QxtDiscoverableService::state() const {
- return qxt_d().state;
- }
- /*!
- * Returns a list of all subtypes known for the service.
- *
- * When discovering a service, only subtypes that were included in the service
- * discovery request will be included in this list.
- *
- * \sa setServiceSubTypes
- * \sa addServiceSubType
- * \sa removeServiceSubType
- * \sa hasServiceSubType
- */
- QStringList QxtDiscoverableService::serviceSubTypes() const
- {
- return qxt_d().serviceSubTypes;
- }
- /*!
- * Sets the list of subtypes for this service.
- *
- * \sa serviceSubTypes
- * \sa addServiceSubType
- * \sa removeServiceSubType
- * \sa hasServiceSubType
- */
- void QxtDiscoverableService::setServiceSubTypes(const QStringList& subtypes)
- {
- if(state() != Unknown)
- qWarning() << "QxtDiscoverableService: Setting service subtypes while not in Unknown state has no effect";
- qxt_d().serviceSubTypes = subtypes;
- }
- /*!
- * Adds a subtype to the service.
- *
- * \sa serviceSubTypes
- * \sa setServiceSubTypes
- * \sa removeServiceSubType
- * \sa hasServiceSubType
- */
- void QxtDiscoverableService::addServiceSubType(const QString& subtype)
- {
- if(state() != Unknown)
- qWarning() << "QxtDiscoverableService: Setting service subtypes while not in Unknown state has no effect";
- qxt_d().serviceSubTypes << subtype;
- }
- /*!
- * Removes a subtype from the service.
- *
- * \sa serviceSubTypes
- * \sa setServiceSubTypes
- * \sa addServiceSubType
- * \sa hasServiceSubType
- */
- void QxtDiscoverableService::removeServiceSubType(const QString& subtype)
- {
- if(state() != Unknown)
- qWarning() << "QxtDiscoverableService: Setting service subtypes while not in Unknown state has no effect";
- qxt_d().serviceSubTypes.removeAll(subtype);
- }
- /*!
- * Tests to see if the specified service is available and known for the service.
- *
- * When discovering a service, only subtypes that were included in the service
- * discovery request will be available.
- *
- * \sa serviceSubTypes
- * \sa setServiceSubTypes
- * \sa addServiceSubType
- * \sa removeServiceSubType
- */
- bool QxtDiscoverableService::hasServiceSubType(const QString& subtype)
- {
- return qxt_d().serviceSubTypes.contains(subtype);
- }
- /*!
- * Returns the port number used for connecting to the service.
- *
- * \sa setPort
- */
- quint16 QxtDiscoverableService::port() const
- {
- return qFromBigEndian(qxt_d().port);
- }
- /*!
- * Sets the port number used for connecting to the service.
- *
- * When registering a service with a port number of 0 (the default), the service will not be found when browsing,
- * but the service name will be marked as reserved.
- *
- * Setting the port is only meaningful when registering a service.
- *
- * \sa port
- */
- void QxtDiscoverableService::setPort(quint16 port)
- {
- qxt_d().port = qToBigEndian(port);
- }
- /*!
- * Attempts to register the service on the local network.
- *
- * If noAutoRename is set to true, registration will fail if another service of the same service type
- * is already registered with the same service name. Otherwise, the service name will be updated with
- * a number to make it unique.
- *
- * When present, the given TXT record string will be registered along with the service.
- *
- * \sa registered
- * \sa registrationError
- */
- void QxtDiscoverableService::registerService(bool noAutoRename)
- {
- if(state() != Unknown) {
- qWarning() << "QxtDiscoverableService: Cannot register service while not in Unknown state";
- emit registrationError(0);
- return;
- }
- QString txtRecord = QxtDiscoverableServiceName::txtRecord();
- QByteArray txt;
- QStringList subtypes = qxt_d().serviceSubTypes;
- subtypes.prepend(fullServiceType());
- if (txtRecord.length()) {
- txt.append((char)txtRecord.length());
- txt.append(txtRecord.toUtf8());
- }
- else {
- txt.fill(0, 1);
- }
- DNSServiceErrorType err;
- err = DNSServiceRegister(&(qxt_d().service),
- noAutoRename ? kDNSServiceFlagsNoAutoRename : 0,
- qxt_d().iface,
- serviceName().isEmpty() ? 0 : serviceName().toUtf8().constData(),
- subtypes.join(",_").toUtf8().constData(),
- domain().isEmpty() ? 0 : domain().toUtf8().constData(),
- host().isEmpty() ? 0 : host().toUtf8().constData(),
- qxt_d().port,
- txt.length(),
- txt.constData(),
- QxtDiscoverableServicePrivate::registerServiceCallback,
- &qxt_d());
- if(err != kDNSServiceErr_NoError) {
- qxt_d().state = Unknown;
- emit registrationError(err);
- } else {
- qxt_d().state = Registering;
- qxt_d().notifier = new QSocketNotifier(DNSServiceRefSockFD(qxt_d().service), QSocketNotifier::Read, this);
- QObject::connect(qxt_d().notifier, SIGNAL(activated(int)), &qxt_d(), SLOT(socketData()));
- }
- }
- /*!
- * Attempts to resolve the service in order to determine the host and port necessary to establish a connection.
- *
- * If forceMulticast is set to true, QxtDiscoverableService will use a multicast request to resolve the service,
- * even if the host name appears to be a unicast address (that is, outside the local network).
- *
- * \sa resolved
- * \sa resolveError
- */
- void QxtDiscoverableService::resolve(bool forceMulticast)
- {
- if(state() != Unknown && state() != Found) {
- qWarning() << "QxtDiscoverableService: Cannot resolve service while not in Unknown or Found state";
- emit resolveError(0);
- return;
- }
- DNSServiceErrorType err;
- err = DNSServiceResolve(&(qxt_d().service),
- (forceMulticast ? kDNSServiceFlagsForceMulticast : 0),
- qxt_d().iface,
- serviceName().toUtf8().constData(),
- fullServiceType().constData(),
- domain().toUtf8().constData(),
- QxtDiscoverableServicePrivate::resolveServiceCallback,
- &qxt_d());
- if(err != kDNSServiceErr_NoError) {
- qxt_d().state = Unknown;
- emit resolveError(err);
- } else {
- qxt_d().state = Resolving;
- qxt_d().notifier = new QSocketNotifier(DNSServiceRefSockFD(qxt_d().service), QSocketNotifier::Read, this);
- QObject::connect(qxt_d().notifier, SIGNAL(activated(int)), &qxt_d(), SLOT(socketData()));
- }
- }
- /*!
- * Releases the service.
- *
- * If the service is registered, it will be unregistered. Any outstanding resolve attempt will be aborted.
- */
- void QxtDiscoverableService::releaseService()
- {
- if(state() != Registered && state() != Resolved) {
- qWarning() << "QxtDiscoverableService: Attempting to unregister an unresolved, unregistered service";
- } else {
- DNSServiceRefDeallocate(qxt_d().service);
- qxt_d().notifier->deleteLater();
- }
- }
- /*!
- * \fn registered()
- *
- * This signal is emitted after a call to registerService() when the service has been successfully registered
- * on the network. The service name may have been updated to ensure uniqueness.
- *
- * \sa registerService
- * \sa registrationError
- */
- /*!
- * \fn registrationError(int code)
- *
- * This signal is emitted after a call to registerService() when the service cannot be registered. This could
- * be because the service name is already in use and noAutoRename was requested, or because the mDNSResponder
- * or Avahi daemon on the local machine could not be contacted.
- *
- * \sa registerService
- * \sa registered
- */
- /*!
- * \fn resolved(const QByteArray& domainName)
- *
- * This signal is emitted after a call to resolve() when a service matching the requested parameters is found.
- * domainName contains the complete domain name of the resolved service. The host name, port, and socket type
- * will be updated to match the connection parameters announced by the service.
- *
- * \sa resolve
- * \sa resolveError
- */
- /*!
- * \fn resolveError(int code)
- *
- * This signal is emitted after a call to resolve() if an error occurs while attempting to resolve the service.
- *
- * \sa resolve
- * \sa resolveError
- */