forked from qt-creator/qt-creator
		
	Change-Id: I4b554d925102fcd7c503276c1e085e8f438b0e3c Reviewed-by: Friedemann Kleint <Friedemann.Kleint@nokia.com>
		
			
				
	
	
		
			2118 lines
		
	
	
		
			76 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			2118 lines
		
	
	
		
			76 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/**************************************************************************
 | 
						|
**
 | 
						|
** This file is part of Qt Creator
 | 
						|
**
 | 
						|
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
 | 
						|
**
 | 
						|
** Contact: http://www.qt-project.org/
 | 
						|
**
 | 
						|
**
 | 
						|
** GNU Lesser General Public License Usage
 | 
						|
**
 | 
						|
** This file may be used under the terms of the GNU Lesser General Public
 | 
						|
** License version 2.1 as published by the Free Software Foundation and
 | 
						|
** appearing in the file LICENSE.LGPL included in the packaging of this file.
 | 
						|
** Please review the following information to ensure the GNU Lesser General
 | 
						|
** Public License version 2.1 requirements will be met:
 | 
						|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 | 
						|
**
 | 
						|
** In addition, as a special exception, Nokia gives you certain additional
 | 
						|
** rights. These rights are described in the Nokia Qt LGPL Exception
 | 
						|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
 | 
						|
**
 | 
						|
** Other Usage
 | 
						|
**
 | 
						|
** Alternatively, this file may be used in accordance with the terms and
 | 
						|
** conditions contained in a signed written agreement between you and Nokia.
 | 
						|
**
 | 
						|
**
 | 
						|
**************************************************************************/
 | 
						|
#include "zeroconf_global.h"
 | 
						|
#ifdef Q_OS_WIN
 | 
						|
// Disable min max macros from windows headers,
 | 
						|
// because we want to use the template methods from std.
 | 
						|
#  ifndef NOMINMAX
 | 
						|
#    define NOMINMAX
 | 
						|
#  endif
 | 
						|
#  include <winsock2.h>
 | 
						|
#endif // Q_OS_WIN
 | 
						|
 | 
						|
#include "mdnsderived.h"
 | 
						|
#include "servicebrowser_p.h"
 | 
						|
 | 
						|
#include <algorithm>
 | 
						|
#include <errno.h>
 | 
						|
#include <limits>
 | 
						|
#include <signal.h>
 | 
						|
#ifdef Q_OS_UNIX
 | 
						|
// for select()
 | 
						|
#  include <unistd.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#include <QAtomicPointer>
 | 
						|
#include <QCoreApplication>
 | 
						|
#include <QDateTime>
 | 
						|
#include <QGlobalStatic>
 | 
						|
#include <QMetaType>
 | 
						|
#include <QMutexLocker>
 | 
						|
#include <QNetworkInterface>
 | 
						|
#include <QString>
 | 
						|
#include <QStringList>
 | 
						|
#include <QtEndian>
 | 
						|
#include <QHostInfo>
 | 
						|
 | 
						|
//the timeval struct under windows uses long instead of suseconds_t
 | 
						|
#ifdef Q_OS_WIN
 | 
						|
typedef long suseconds_t;
 | 
						|
#endif
 | 
						|
 | 
						|
/*!
 | 
						|
  \namespace ZeroConf
 | 
						|
 | 
						|
  \brief namespace for zeroconf (Bonjour/DNS-SD) functionality, currently mostly for browsing services.
 | 
						|
*/
 | 
						|
 | 
						|
namespace { // anonymous namespace for free functions
 | 
						|
// ----------------- free functions -----------------
 | 
						|
 | 
						|
using namespace ZeroConf;
 | 
						|
using namespace ZeroConf::Internal;
 | 
						|
 | 
						|
QString toFullNameC(const char * const service, const char * const regtype, const char * const domain)
 | 
						|
{
 | 
						|
    char fullName[kDNSServiceMaxDomainName];
 | 
						|
    myDNSServiceConstructFullName(fullName, service, regtype, domain);
 | 
						|
    fullName[kDNSServiceMaxDomainName - 1] = 0; // just to be sure
 | 
						|
    return QString::fromUtf8(fullName);
 | 
						|
}
 | 
						|
 | 
						|
int fromFullNameC(const char * const fullName, QString &service, QString ®type, QString &domain)
 | 
						|
{
 | 
						|
    char fullNameDecoded[kDNSServiceMaxDomainName];
 | 
						|
    int encodedI = 0;
 | 
						|
    int decodedI = 0;
 | 
						|
    int oldPos[4];
 | 
						|
    int iPos = 0;
 | 
						|
    while (fullName[encodedI] != 0 && encodedI <kDNSServiceMaxDomainName){
 | 
						|
        char c = fullName[encodedI++];
 | 
						|
        if (c == '\\'){
 | 
						|
            c = fullName[++encodedI];
 | 
						|
            if (c == 0 || encodedI == kDNSServiceMaxDomainName)
 | 
						|
                return 1;
 | 
						|
            if (c >= '0' && c <= '9'){
 | 
						|
                int val = (c - '0') * 100;
 | 
						|
                c = fullName[++encodedI];
 | 
						|
                if (c < '0' || c > '9' || encodedI == kDNSServiceMaxDomainName)
 | 
						|
                    return 2;
 | 
						|
                val += (c - '0') * 10;
 | 
						|
                c = fullName[++encodedI];
 | 
						|
                if (c < '0' || c > '9')
 | 
						|
                    return 3;
 | 
						|
                val += (c - '0');
 | 
						|
                fullNameDecoded[decodedI++] = static_cast<char>(static_cast<unsigned char>(val));
 | 
						|
            } else {
 | 
						|
                fullNameDecoded[decodedI++] = c;
 | 
						|
            }
 | 
						|
        } else if (c == '.') {
 | 
						|
            if (iPos < 4) {
 | 
						|
                oldPos[iPos++] = decodedI;
 | 
						|
            }
 | 
						|
            fullNameDecoded[decodedI++] = c;
 | 
						|
        } else {
 | 
						|
            fullNameDecoded[decodedI++] = c;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    if (iPos != 4) return 5;
 | 
						|
    service = QString::fromUtf8(&fullNameDecoded[0], oldPos[0]);
 | 
						|
    regtype = QString::fromUtf8(&fullNameDecoded[oldPos[0] + 1], oldPos[3] - oldPos[0] - 1);
 | 
						|
    domain = QString::fromUtf8(&fullNameDecoded[oldPos[3] + 1], decodedI - oldPos[3] - 1);
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/// singleton for lib setup
 | 
						|
class ZeroConfLib
 | 
						|
{
 | 
						|
public:
 | 
						|
    ZeroConfLib();
 | 
						|
    ZConfLib::Ptr defaultLib();
 | 
						|
    void setDefaultLib(LibUsage usage, const QString &avahiLibName, const QString &avahiVersion,
 | 
						|
                       const QString &dnsSdLibName, const QString &dnsSdDaemonPath);
 | 
						|
    int nFallbacksTot() const;
 | 
						|
 | 
						|
private:
 | 
						|
    static const char *defaultmDnsSdLibName;
 | 
						|
    static const char *defaultmDNSDaemonName;
 | 
						|
    int m_nFallbacksTot;
 | 
						|
    QMutex m_lock;
 | 
						|
    ZConfLib::Ptr m_defaultLib;
 | 
						|
};
 | 
						|
 | 
						|
Q_GLOBAL_STATIC(ZeroConfLib, zeroConfLibInstance)
 | 
						|
 | 
						|
#ifdef Q_OS_WIN
 | 
						|
    const char *ZeroConfLib::defaultmDnsSdLibName  = "dnssd";
 | 
						|
    const char *ZeroConfLib::defaultmDNSDaemonName = "mdnssd.exe";
 | 
						|
#else
 | 
						|
    const char *ZeroConfLib::defaultmDnsSdLibName  = "dns_sd";
 | 
						|
    const char *ZeroConfLib::defaultmDNSDaemonName = "mdnssd";
 | 
						|
#endif
 | 
						|
 | 
						|
ZeroConfLib::ZeroConfLib(): m_lock(QMutex::Recursive),
 | 
						|
    m_defaultLib(ZConfLib::createDnsSdLib(QLatin1String(defaultmDnsSdLibName),
 | 
						|
                 ZConfLib::createEmbeddedLib(QString(),
 | 
						|
                 ZConfLib::createAvahiLib(QLatin1String("avahi-client"),QLatin1String("3"),
 | 
						|
                 ZConfLib::createEmbeddedLib(QLatin1String(defaultmDNSDaemonName))))))
 | 
						|
{
 | 
						|
    qRegisterMetaType<ZeroConf::Service::ConstPtr>("ZeroConf::Service::ConstPtr");
 | 
						|
    qRegisterMetaType<ZeroConf::ErrorMessage::SeverityLevel>("ZeroConf::ErrorMessage::SeverityLevel");
 | 
						|
    qRegisterMetaType<ZeroConf::ErrorMessage>("ZeroConf::ErrorMessage");
 | 
						|
    qRegisterMetaType<QList<ZeroConf::ErrorMessage> >("QList<ZeroConf::ErrorMessage>");
 | 
						|
    m_nFallbacksTot = (m_defaultLib ? m_defaultLib->nFallbacks() : 0);
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
ZConfLib::Ptr ZeroConfLib::defaultLib(){
 | 
						|
    QMutexLocker l(&m_lock);
 | 
						|
    return m_defaultLib;
 | 
						|
}
 | 
						|
 | 
						|
void ZeroConfLib::setDefaultLib(LibUsage usage, const QString &avahiLibName,
 | 
						|
                                const QString &avahiVersion, const QString &dnsSdLibName,
 | 
						|
                                const QString &dnsSdDaemonPath){
 | 
						|
    QMutexLocker l(&m_lock);
 | 
						|
    switch (usage){
 | 
						|
    case (UseDnsSdOnly):
 | 
						|
        m_defaultLib = ZConfLib::createDnsSdLib(dnsSdLibName);
 | 
						|
        break;
 | 
						|
    case (UseEmbeddedOnly):
 | 
						|
        m_defaultLib = ZConfLib::createEmbeddedLib(dnsSdDaemonPath);
 | 
						|
        break;
 | 
						|
    case (UseAvahiOnly):
 | 
						|
        m_defaultLib = ZConfLib::createAvahiLib(avahiLibName, avahiVersion);
 | 
						|
        break;
 | 
						|
    case (UseAvahiOrDnsSd):
 | 
						|
        m_defaultLib = ZConfLib::createAvahiLib(avahiLibName, avahiVersion,
 | 
						|
                       ZConfLib::createDnsSdLib(dnsSdLibName));
 | 
						|
        break;
 | 
						|
    case (UseAvahiOrDnsSdOrEmbedded):
 | 
						|
        m_defaultLib = ZConfLib::createAvahiLib(avahiLibName, avahiVersion,
 | 
						|
                       ZConfLib::createDnsSdLib(dnsSdLibName,
 | 
						|
                       ZConfLib::createEmbeddedLib(dnsSdDaemonPath)));
 | 
						|
        break;
 | 
						|
    default:
 | 
						|
        qDebug() << "invalid usage " << usage;
 | 
						|
    }
 | 
						|
    int newNFallbacks = m_defaultLib->nFallbacks();
 | 
						|
    if (m_nFallbacksTot < newNFallbacks)
 | 
						|
        m_nFallbacksTot = newNFallbacks;
 | 
						|
}
 | 
						|
 | 
						|
int ZeroConfLib::nFallbacksTot() const
 | 
						|
{
 | 
						|
    return m_nFallbacksTot;
 | 
						|
}
 | 
						|
 | 
						|
} // end anonymous namespace
 | 
						|
 | 
						|
namespace ZeroConf {
 | 
						|
 | 
						|
int gQuickStop = 0;
 | 
						|
 | 
						|
// ----------------- ErrorMessage impl -----------------
 | 
						|
/*!
 | 
						|
  \class ZeroConf::ErrorMessage
 | 
						|
 | 
						|
  \brief class representing an error message
 | 
						|
 | 
						|
  very simple class representing an error message
 | 
						|
 */
 | 
						|
 | 
						|
/// empty constructor (required by qRegisterMetaType)
 | 
						|
ErrorMessage::ErrorMessage(): severity(FailureLevel) {}
 | 
						|
/// constructor
 | 
						|
ErrorMessage::ErrorMessage(SeverityLevel s, const QString &m): severity(s), msg(m) {}
 | 
						|
 | 
						|
/// returns a human readable string for each severity level
 | 
						|
QString ErrorMessage::severityLevelToString(ErrorMessage::SeverityLevel severity)
 | 
						|
{
 | 
						|
    const char *ctx = "Zeroconf::SeverityLevel";
 | 
						|
    switch (severity) {
 | 
						|
    case NoteLevel:
 | 
						|
        return QCoreApplication::translate(ctx, "NOTE");
 | 
						|
    case WarningLevel:
 | 
						|
        return QCoreApplication::translate(ctx, "WARNING");
 | 
						|
    case ErrorLevel:
 | 
						|
        return QCoreApplication::translate(ctx, "ERROR");
 | 
						|
    case FailureLevel:
 | 
						|
        return QCoreApplication::translate(ctx, "FATAL_ERROR");
 | 
						|
    default:
 | 
						|
        return QCoreApplication::translate(ctx, "UNKNOWN_LEVEL_%1").arg(severity);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
QDebug operator<<(QDebug dbg, const ErrorMessage &eMsg)
 | 
						|
{
 | 
						|
    dbg << ErrorMessage::severityLevelToString(eMsg.severity) << eMsg.msg;
 | 
						|
    return dbg;
 | 
						|
}
 | 
						|
 | 
						|
// ----------------- Service impl -----------------
 | 
						|
/*!
 | 
						|
  \class ZeroConf::Service
 | 
						|
 | 
						|
  \brief class representing a zeroconf service
 | 
						|
 | 
						|
  Instances of this class are basically constant, but can be outdated. They are normally accessed
 | 
						|
  through a Shared pointer.
 | 
						|
  This design avoids race conditions when used though multiple threads.
 | 
						|
 | 
						|
  \threadsafe
 | 
						|
 */
 | 
						|
 | 
						|
Service::Service(const Service &o) :
 | 
						|
    m_name(o.m_name),
 | 
						|
    m_type(o.m_type),
 | 
						|
    m_domain(o.m_domain),
 | 
						|
    m_fullName(o.m_fullName),
 | 
						|
    m_port(o.m_port),
 | 
						|
    m_txtRecord(o.m_txtRecord),
 | 
						|
    m_host(o.m_host ? new QHostInfo(*o.m_host) : 0),
 | 
						|
    m_interfaceNr(o.m_interfaceNr),
 | 
						|
    m_outdated(o.m_outdated)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
Service::Service() : m_host(0), m_interfaceNr(0), m_outdated(false)
 | 
						|
{ }
 | 
						|
 | 
						|
Service::~Service()
 | 
						|
{
 | 
						|
    delete m_host;
 | 
						|
}
 | 
						|
 | 
						|
/*!
 | 
						|
  \fn QString Service::networkInterface()
 | 
						|
 | 
						|
  Returns the interface on which the service is reachable.
 | 
						|
 */
 | 
						|
QNetworkInterface Service::networkInterface() const
 | 
						|
{
 | 
						|
    return QNetworkInterface::interfaceFromIndex(m_interfaceNr);
 | 
						|
}
 | 
						|
 | 
						|
QStringList Service::addresses(Service::AddressStyle style) const
 | 
						|
{
 | 
						|
    QStringList res;
 | 
						|
    if (host() == 0)
 | 
						|
        return res;
 | 
						|
    foreach (const QHostAddress &addr, host()->addresses()){
 | 
						|
        QString addrStr;
 | 
						|
        if (addr.protocol() == QAbstractSocket::IPv6Protocol) {
 | 
						|
            // Add the interface to use to the address <address>%<interface>
 | 
						|
            //
 | 
						|
            // This is required only for link local addresses (like fe80:*)
 | 
						|
            // but as we have it we do it (and most likely addresses here are
 | 
						|
            // link local).
 | 
						|
            //
 | 
						|
            // on windows only addresses like fe80::42%22 work
 | 
						|
            // on OSX 10.5 only things like fe80::42%en4 work
 | 
						|
            // on later OSX versions and linux both <address>%<interface number>
 | 
						|
            // and <address>%<interface name> work, we use the interface name as
 | 
						|
            // it looks better
 | 
						|
#ifdef Q_OS_WIN
 | 
						|
            QString interfaceStr = QString::number(networkInterface().index());
 | 
						|
#else
 | 
						|
            QString interfaceStr = networkInterface().name();
 | 
						|
#endif
 | 
						|
            addrStr = QString::fromLatin1("%1%%2").arg(addr.toString()).arg(interfaceStr);
 | 
						|
            if (style == QuoteIPv6Adresses)
 | 
						|
                addrStr = QString::fromLatin1("[%1]").arg(addrStr);
 | 
						|
        } else {
 | 
						|
            addrStr = addr.toString();
 | 
						|
        }
 | 
						|
        res.append(addrStr);
 | 
						|
    }
 | 
						|
    return res;
 | 
						|
}
 | 
						|
 | 
						|
bool Service::operator==(const Service &o) const {
 | 
						|
    bool eq = m_fullName == o.m_fullName
 | 
						|
            && m_name == o.m_name && m_type == o.m_type
 | 
						|
            && m_domain == o.m_domain && m_port == o.m_port
 | 
						|
            && m_txtRecord == o.m_txtRecord && m_interfaceNr == o.m_interfaceNr
 | 
						|
            && m_outdated == m_outdated;
 | 
						|
    if (eq) {
 | 
						|
        if (m_host != o.m_host) {
 | 
						|
            if (m_host == 0 || o.m_host == 0)
 | 
						|
                return false;
 | 
						|
            return m_host->hostName() == o.m_host->hostName()
 | 
						|
                    && m_host->addresses() == o.m_host->addresses();
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return eq;
 | 
						|
}
 | 
						|
 | 
						|
QDebug operator<<(QDebug dbg, const Service &service)
 | 
						|
{
 | 
						|
    dbg.maybeSpace() << "Service{ name:" << service.name() << ", "
 | 
						|
                     << "type:" << service.type() << ", domain:" << service.domain() << ", "
 | 
						|
                     << " fullName:" << service.fullName() << ", port:" << service.port()
 | 
						|
                     << ", txtRecord:{";
 | 
						|
    bool first = true;
 | 
						|
    QHashIterator<QString, QString> i(service.txtRecord());
 | 
						|
    while (i.hasNext()){
 | 
						|
        i.next();
 | 
						|
        if (first)
 | 
						|
            first = false;
 | 
						|
        else
 | 
						|
            dbg << ", ";
 | 
						|
        dbg << i.key() << ":" << i.value();
 | 
						|
    }
 | 
						|
    dbg << "}, ";
 | 
						|
    if (const QHostInfo *host = service.host()){
 | 
						|
        dbg << "host:{" << host->hostName() << ", addresses[";
 | 
						|
        first = true;
 | 
						|
        foreach (const QHostAddress &addr, host->addresses()){
 | 
						|
            if (first)
 | 
						|
                first = false;
 | 
						|
            else
 | 
						|
                dbg << ", ";
 | 
						|
            dbg << addr.toString();
 | 
						|
        }
 | 
						|
        dbg << "], },";
 | 
						|
    } else {
 | 
						|
        dbg << " host:*null*,";
 | 
						|
    }
 | 
						|
    dbg << " interfaceNr:" << service.interfaceNr() << ", outdated:" << service.outdated() << " }";
 | 
						|
    return dbg.space();
 | 
						|
}
 | 
						|
 | 
						|
QDebug operator<<(QDebug dbg, const Service::ConstPtr &service){
 | 
						|
    if (service.data() == 0){
 | 
						|
        dbg << "Service{*NULL*}";
 | 
						|
    } else {
 | 
						|
        dbg << *service.data();
 | 
						|
    }
 | 
						|
    return dbg;
 | 
						|
}
 | 
						|
 | 
						|
// inline methods
 | 
						|
/*!
 | 
						|
  \fn bool Service::outdated() const
 | 
						|
 | 
						|
  Returns if the service data is outdated, its value might change even on
 | 
						|
  the (otherwise constant) objects returned by a ServiceBrowser.
 | 
						|
*/
 | 
						|
/*!
 | 
						|
  \fn QString Service::name() const
 | 
						|
 | 
						|
  Returns the name of the service (non escaped).
 | 
						|
*/
 | 
						|
/*!
 | 
						|
  \fn QString Service::type() const
 | 
						|
 | 
						|
  Returns the name of the service type (non escaped).
 | 
						|
*/
 | 
						|
/*!
 | 
						|
  \fn QString Service::domain() const
 | 
						|
 | 
						|
  Returns the name of the domain (non escaped).
 | 
						|
*/
 | 
						|
/*!
 | 
						|
  \fn QString Service::fullName() const
 | 
						|
 | 
						|
  Returns the full name (service.type.domain) with each component correctly escaped.
 | 
						|
*/
 | 
						|
/*!
 | 
						|
  \fn QString Service::port() const
 | 
						|
 | 
						|
  Return the port of the service (as a string, not as number).
 | 
						|
*/
 | 
						|
/*!
 | 
						|
  \fn const Service::ServiceTxtRecord &txtRecord() const
 | 
						|
 | 
						|
  Returns the extra information on this service.
 | 
						|
*/
 | 
						|
/*!
 | 
						|
  \fn const Service::QHostInfo *host() const
 | 
						|
 | 
						|
  Returns the host through which this service is reachable.
 | 
						|
*/
 | 
						|
/*!
 | 
						|
  \fn int Service::interfaceNr() const
 | 
						|
 | 
						|
  returns the interface on which the service is reachable, 1 based, 0 means to try all interfaces
 | 
						|
*/
 | 
						|
/*!
 | 
						|
  \fn bool Service::invalidate()
 | 
						|
 | 
						|
  Marks this service as outdated.
 | 
						|
*/
 | 
						|
 | 
						|
// ----------------- ServiceBrowser impl -----------------
 | 
						|
/*!
 | 
						|
  \class ZeroConf::ServiceBrowser
 | 
						|
 | 
						|
  \brief class that browses (searches) for a given zeronconf service
 | 
						|
 | 
						|
  The actual browsing starts only when startBrowsing() is called. If you want to receive all service
 | 
						|
  changes connect before starting browsing.
 | 
						|
 | 
						|
  The current list of services can be gathered with the services() method.
 | 
						|
 | 
						|
  \threadsafe
 | 
						|
 */
 | 
						|
 | 
						|
/// starts the browsing, return true if successfull
 | 
						|
void ServiceBrowser::startBrowsing(qint32 interfaceIndex)
 | 
						|
{
 | 
						|
    d->startBrowsing(interfaceIndex);
 | 
						|
}
 | 
						|
 | 
						|
/// create a new brower for the given service type
 | 
						|
ServiceBrowser::ServiceBrowser(const QString &serviceType, const QString &domain,
 | 
						|
        AddressesSetting addressesSetting, QObject *parent)
 | 
						|
    : QObject(parent), timer(0),
 | 
						|
      d(new ServiceBrowserPrivate(serviceType, domain, addressesSetting == RequireAddresses,
 | 
						|
          MainConnectionPtr()))
 | 
						|
{
 | 
						|
    connect(this,SIGNAL(activateAutoRefresh()),this,SLOT(autoRefresh()));
 | 
						|
    d->q = this;
 | 
						|
}
 | 
						|
 | 
						|
ServiceBrowser::ServiceBrowser(const MainConnectionPtr &mainConnection, const QString &serviceType,
 | 
						|
        const QString &domain, AddressesSetting addressesSetting, QObject *parent)
 | 
						|
    : QObject(parent), timer(0),
 | 
						|
      d(new ServiceBrowserPrivate(serviceType, domain, addressesSetting == RequireAddresses,
 | 
						|
          mainConnection))
 | 
						|
{
 | 
						|
    d->q = this;
 | 
						|
}
 | 
						|
 | 
						|
ServiceBrowser::~ServiceBrowser()
 | 
						|
{
 | 
						|
    delete d;
 | 
						|
}
 | 
						|
 | 
						|
/// returns the main connection used by this ServiceBrowser
 | 
						|
MainConnectionPtr ServiceBrowser::mainConnection() const
 | 
						|
{
 | 
						|
    return d->mainConnection;
 | 
						|
}
 | 
						|
 | 
						|
/// stops browsing, but does not delete all services found
 | 
						|
void ServiceBrowser::stopBrowsing()
 | 
						|
{
 | 
						|
    if (timer) {
 | 
						|
        timer->stop();
 | 
						|
        delete timer;
 | 
						|
        timer = 0;
 | 
						|
    }
 | 
						|
    d->stopBrowsing();
 | 
						|
}
 | 
						|
 | 
						|
/// starts an explicit browse (important especially with avahi)
 | 
						|
void ServiceBrowser::triggerRefresh()
 | 
						|
{
 | 
						|
    d->triggerRefresh();
 | 
						|
}
 | 
						|
 | 
						|
/// if the service is currently active
 | 
						|
bool ServiceBrowser::isBrowsing() const
 | 
						|
{
 | 
						|
    return d->browsing;
 | 
						|
}
 | 
						|
 | 
						|
/// type of the service browsed (non escaped)
 | 
						|
const QString& ServiceBrowser::serviceType() const
 | 
						|
{
 | 
						|
    return d->serviceType;
 | 
						|
}
 | 
						|
 | 
						|
/// domain that is browser (non escaped)
 | 
						|
const QString& ServiceBrowser::domain() const
 | 
						|
{
 | 
						|
    return d->domain;
 | 
						|
}
 | 
						|
 | 
						|
/// if addresses should be resolved automatically for each service found
 | 
						|
bool ServiceBrowser::adressesAutoResolved() const
 | 
						|
{
 | 
						|
    return d->autoResolveAddresses;
 | 
						|
}
 | 
						|
 | 
						|
/// if addresses are required to add the service to the list of available services
 | 
						|
bool ServiceBrowser::addressesRequired() const
 | 
						|
{
 | 
						|
    return d->requireAddresses;
 | 
						|
}
 | 
						|
 | 
						|
/// list of current services (by copy on purpose)
 | 
						|
QList<Service::ConstPtr> ServiceBrowser::services() const
 | 
						|
{
 | 
						|
    QMutexLocker l(d->mainConnection->lock());
 | 
						|
    return d->activeServices;
 | 
						|
}
 | 
						|
 | 
						|
/// forces a full update of a service (call this after a failure to connect to the service)
 | 
						|
/// this is an expensive call, use only when needed
 | 
						|
void ServiceBrowser::reconfirmService(Service::ConstPtr service)
 | 
						|
{
 | 
						|
    d->reconfirmService(service);
 | 
						|
}
 | 
						|
 | 
						|
void ServiceBrowser::autoRefresh()
 | 
						|
{
 | 
						|
    QMutexLocker l(d->mainConnection->lock());
 | 
						|
    if (!timer) {
 | 
						|
        timer = new QTimer(this);
 | 
						|
        connect(timer,SIGNAL(timeout()),this,SLOT(triggerRefresh()));
 | 
						|
        timer->setSingleShot(true);
 | 
						|
    }
 | 
						|
    timer->start(5000);
 | 
						|
}
 | 
						|
 | 
						|
// signals
 | 
						|
/*!
 | 
						|
  \fn void ServiceBrowser::serviceChanged(
 | 
						|
       Service::ConstPtr oldService, Service::ConstPtr newService, ServiceBrowser *browser)
 | 
						|
 | 
						|
   This signal is called when a service is added removed or changes.
 | 
						|
   Both oldService or newService might be null (covers both add and remove).
 | 
						|
   The services list might not be synchronized with respect to this signal.
 | 
						|
*/
 | 
						|
/*!
 | 
						|
  \fn void ServiceBrowser::serviceAdded(Service::ConstPtr service, ServiceBrowser *browser)
 | 
						|
 | 
						|
   This signal is called when a service is added (convenience method)
 | 
						|
   the services list might not be synchronized with respect to this signal
 | 
						|
 | 
						|
   \sa serviceChanged()
 | 
						|
*/
 | 
						|
/*!
 | 
						|
  \fn void ServiceBrowser::serviceRemoved(Service::ConstPtr service, ServiceBrowser *browser)
 | 
						|
 | 
						|
  This signal is called when a service is removed (convenience method)
 | 
						|
  the services list might not be synchronized with respect to this signal
 | 
						|
 | 
						|
   \sa serviceChanged()
 | 
						|
*/
 | 
						|
/*!
 | 
						|
  \fn void ServiceBrowser::servicesUpdated(ServiceBrowser *browser)
 | 
						|
 | 
						|
  This signal is called when the list is updated.
 | 
						|
  It might collect several serviceChanged signals together, if you use the list returned by
 | 
						|
  services(), use this signal, not serviceChanged(), serviceAdded() or serviceRemoved() to know
 | 
						|
  about changes to the list.
 | 
						|
*/
 | 
						|
/*!
 | 
						|
  \fn void errorMessage(ZeroConf::ErrorMessage::SeverityLevel severity, const QString &msg, ZeroConf::ServiceBrowser *browser)
 | 
						|
 | 
						|
  This signal is called every time a warning or error is emitted (for example when a library 
 | 
						|
  cannot be used and another one has to be used). Zeroconf will still work if severity < FailureLevel.
 | 
						|
*/
 | 
						|
/*!
 | 
						|
  \fn void hadFailure(const QList<ZeroConf::ErrorMessage> &messages, ZeroConf::ServiceBrowser *browser)
 | 
						|
 | 
						|
  This signal is emitted only when a full failure has happened, and all the previous errors/attempts to set up zeroconf
 | 
						|
  are passed in messages.
 | 
						|
*/
 | 
						|
/*!
 | 
						|
  \fn void startedBrowsing(ZeroConf::ServiceBrowser *browser)
 | 
						|
 | 
						|
  This signal is emitted when browsing has actually started.
 | 
						|
  One can rely on either startedBrowsing or hadFailure to be emitted.
 | 
						|
*/
 | 
						|
 | 
						|
// ----------------- library initialization impl -----------------
 | 
						|
/*!
 | 
						|
  Sets the library used by future Service Browsers to preform the mdns queries.
 | 
						|
  This changes the default library used by the next MainConnection, it does not change the already
 | 
						|
  instantiated connections.
 | 
						|
  \a usage can decide which libraries are tried,
 | 
						|
  \a libName should be the name (or path) to the libdns library,
 | 
						|
  \a daemonPath is the path to the daemon executable which should be started by the embedded library
 | 
						|
                if no daemon is found.
 | 
						|
 | 
						|
  \threadsafe
 | 
						|
*/
 | 
						|
void setDefaultZConfLib(LibUsage usage, const QString &avahiLibName, const QString &version,
 | 
						|
                        const QString &dnsSdLibName,const QString &dnsSdDaemonPath)
 | 
						|
{
 | 
						|
    zeroConfLibInstance()->setDefaultLib(usage, avahiLibName, version, dnsSdLibName, dnsSdDaemonPath);
 | 
						|
}
 | 
						|
 | 
						|
namespace Internal {
 | 
						|
// ----------------- dns-sd C callbacks -----------------
 | 
						|
 | 
						|
extern "C" void DNSSD_API cServiceResolveReply(DNSServiceRef                       sdRef,
 | 
						|
                                               DNSServiceFlags                     flags,
 | 
						|
                                               uint32_t                            interfaceIndex,
 | 
						|
                                               DNSServiceErrorType                 errorCode,
 | 
						|
                                               const char                          *fullname,
 | 
						|
                                               const char                          *hosttarget,
 | 
						|
                                               uint16_t                            port, /* In network byte order */
 | 
						|
                                               uint16_t                            txtLen,
 | 
						|
                                               const unsigned char                 *txtRecord,
 | 
						|
                                               void                                *context)
 | 
						|
{
 | 
						|
    if (DEBUG_ZEROCONF)
 | 
						|
        qDebug() << "cServiceResolveReply(" << ((size_t)sdRef) << ", " << ((quint32)flags)
 | 
						|
                 << ", " << interfaceIndex << ", " << ((int)errorCode) << ", " << fullname
 | 
						|
                 << ", " << hosttarget << ", " << qFromBigEndian(port) << ", " << txtLen << ", '"
 | 
						|
                 << QString::fromUtf8((const char *)txtRecord, txtLen) << "', "
 | 
						|
                 << ((size_t)context);
 | 
						|
    ServiceGatherer *ctxGatherer = reinterpret_cast<ServiceGatherer *>(context);
 | 
						|
    if (ctxGatherer){
 | 
						|
        if (ctxGatherer->currentService->fullName() != fullname){
 | 
						|
            qDebug() << "ServiceBrowser " << ctxGatherer->serviceBrowser->serviceType
 | 
						|
                     << " for service " << ctxGatherer->currentService->name()
 | 
						|
                     << " ignoring resolve reply for " << fullname << " vs. "
 | 
						|
                     << ctxGatherer->currentService->fullName();
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        ctxGatherer->serviceResolveReply(flags, interfaceIndex, errorCode, hosttarget,
 | 
						|
                                         QString::number(qFromBigEndian(port)), txtLen, txtRecord);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
extern "C" void DNSSD_API cTxtRecordReply(DNSServiceRef                       sdRef,
 | 
						|
                                          DNSServiceFlags                     flags,
 | 
						|
                                          uint32_t                            interfaceIndex,
 | 
						|
                                          DNSServiceErrorType                 errorCode,
 | 
						|
                                          const char                          *fullname,
 | 
						|
                                          uint16_t                            rrtype,
 | 
						|
                                          uint16_t                            rrclass,
 | 
						|
                                          uint16_t                            rdlen,
 | 
						|
                                          const void                          *rdata,
 | 
						|
                                          uint32_t                            ttl,
 | 
						|
                                          void                                *context)
 | 
						|
{
 | 
						|
    if (DEBUG_ZEROCONF)
 | 
						|
        qDebug() << "cTxtRecordReply(" << ((size_t)sdRef) << ", " << ((int)flags) << ", "
 | 
						|
                 << interfaceIndex << ", " << ((int)errorCode) << ", " << fullname << ", "
 | 
						|
                 << rrtype << ", " << rrclass << ", " << ", " << rdlen
 | 
						|
                 << QString::fromUtf8((const char *)rdata, rdlen) << "', " << ttl << ", "
 | 
						|
                 << ((size_t)context);
 | 
						|
    ServiceGatherer *ctxGatherer = reinterpret_cast<ServiceGatherer *>(context);
 | 
						|
    if (ctxGatherer){
 | 
						|
        if (rrtype != kDNSServiceType_TXT || rrclass != kDNSServiceClass_IN) {
 | 
						|
            qDebug() << "ServiceBrowser " << ctxGatherer->serviceBrowser->serviceType
 | 
						|
                     << " for service " << ctxGatherer->currentService->fullName()
 | 
						|
                     << " received an unexpected rrtype/class:" << rrtype << "/" << rrclass;
 | 
						|
        }
 | 
						|
        ctxGatherer->txtRecordReply(flags, errorCode, rdlen, rdata, ttl);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
extern "C" void DNSSD_API cAddrReply(DNSServiceRef                    sdRef,
 | 
						|
                                     DNSServiceFlags                  flags,
 | 
						|
                                     uint32_t                         interfaceIndex,
 | 
						|
                                     DNSServiceErrorType              errorCode,
 | 
						|
                                     const char                       *hostname,
 | 
						|
                                     const struct sockaddr            *address,
 | 
						|
                                     uint32_t                         ttl,
 | 
						|
                                     void                             *context)
 | 
						|
{
 | 
						|
    if (DEBUG_ZEROCONF)
 | 
						|
        qDebug() << "cAddrReply(" << ((size_t)sdRef) << ", " << ((int)flags) << ", "
 | 
						|
                 << interfaceIndex << ", " << ((int)errorCode) << ", " << hostname << ", "
 | 
						|
                 << QHostAddress(address).toString() << ", " << ttl << ", " << ((size_t)context);
 | 
						|
    ServiceGatherer *ctxGatherer = reinterpret_cast<ServiceGatherer *>(context);
 | 
						|
    if (ctxGatherer){
 | 
						|
        ctxGatherer->addrReply(flags, errorCode, hostname, address, ttl);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/// callback for service browsing
 | 
						|
extern "C" void DNSSD_API cBrowseReply(DNSServiceRef       sdRef,
 | 
						|
                                       DNSServiceFlags     flags,
 | 
						|
                                       uint32_t            interfaceIndex,
 | 
						|
                                       DNSServiceErrorType errorCode,
 | 
						|
                                       const char          *serviceName,
 | 
						|
                                       const char          *regtype,
 | 
						|
                                       const char          *replyDomain,
 | 
						|
                                       void                *context)
 | 
						|
{
 | 
						|
    if (DEBUG_ZEROCONF)
 | 
						|
        qDebug() << "cBrowseReply(" << ((size_t)sdRef) << ", " << flags << ", " << interfaceIndex
 | 
						|
                 << ", " << ((int)errorCode) << ", " << serviceName << ", " << regtype << ", "
 | 
						|
                 << replyDomain << ", " << ((size_t)context);
 | 
						|
    ServiceBrowserPrivate *sb = (ServiceBrowserPrivate *)(context);
 | 
						|
    if (sb == 0){
 | 
						|
        qDebug() << "ServiceBrowser ignoring reply because context was null ";
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    sb->browseReply(flags, interfaceIndex, ZK_PROTO_IPv4_OR_IPv6,
 | 
						|
                    errorCode, serviceName, regtype, replyDomain);
 | 
						|
}
 | 
						|
 | 
						|
// ----------------- ConnectionThread impl -----------------
 | 
						|
 | 
						|
void ConnectionThread::run()
 | 
						|
{
 | 
						|
#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN)
 | 
						|
    struct sigaction act;
 | 
						|
    // ignore SIGPIPE
 | 
						|
    act.sa_handler=SIG_IGN;
 | 
						|
    sigemptyset(&act.sa_mask);
 | 
						|
    act.sa_flags=0;
 | 
						|
    sigaction(SIGPIPE, &act, NULL);
 | 
						|
#endif
 | 
						|
    connection.handleEvents();
 | 
						|
}
 | 
						|
 | 
						|
ConnectionThread::ConnectionThread(MainConnection &mc, QObject *parent):
 | 
						|
    QThread(parent), connection(mc)
 | 
						|
{ }
 | 
						|
 | 
						|
// ----------------- ServiceGatherer impl -----------------
 | 
						|
 | 
						|
ZConfLib::Ptr ServiceGatherer::lib()
 | 
						|
{
 | 
						|
    return serviceBrowser->mainConnection->lib;
 | 
						|
}
 | 
						|
 | 
						|
QString ServiceGatherer::fullName(){
 | 
						|
    return currentService->fullName();
 | 
						|
}
 | 
						|
 | 
						|
bool ServiceGatherer::enactServiceChange()
 | 
						|
{
 | 
						|
    if (DEBUG_ZEROCONF)
 | 
						|
        qDebug() << "ServiceGatherer::enactServiceChange() for service "
 | 
						|
                 << currentService->fullName();
 | 
						|
    if (currentServiceCanBePublished()) {
 | 
						|
        if ((publishedService.data() == 0 && currentService == 0)
 | 
						|
                || (publishedService.data() != 0 && currentService != 0
 | 
						|
                    && *publishedService == *currentService))
 | 
						|
            return false;
 | 
						|
        Service::Ptr nService = Service::Ptr(currentService);
 | 
						|
        if (publishedService) {
 | 
						|
            publishedService->invalidate();
 | 
						|
            serviceBrowser->nextActiveServices.removeOne(publishedService);
 | 
						|
            serviceBrowser->serviceRemoved(publishedService, serviceBrowser->q);
 | 
						|
        }
 | 
						|
        serviceBrowser->serviceChanged(publishedService, nService, serviceBrowser->q);
 | 
						|
        publishedService = nService;
 | 
						|
        if (nService) {
 | 
						|
            serviceBrowser->nextActiveServices.append(nService);
 | 
						|
            serviceBrowser->serviceAdded(nService, serviceBrowser->q);
 | 
						|
            currentService = new Service(*currentService);
 | 
						|
        }
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
void ServiceGatherer::retireService()
 | 
						|
{
 | 
						|
    if (publishedService) {
 | 
						|
        if (DEBUG_ZEROCONF)
 | 
						|
            qDebug() << "ServiceGatherer::retireService() for service "
 | 
						|
                     << currentService->fullName();
 | 
						|
        Service::Ptr nService;
 | 
						|
        serviceBrowser->nextActiveServices.removeOne(publishedService);
 | 
						|
        publishedService->invalidate();
 | 
						|
        serviceBrowser->serviceRemoved(publishedService, serviceBrowser->q);
 | 
						|
        serviceBrowser->serviceChanged(publishedService, nService, serviceBrowser->q);
 | 
						|
        publishedService = nService;
 | 
						|
    } else if (DEBUG_ZEROCONF){
 | 
						|
        qDebug() << "ServiceGatherer::retireService() for non published service "
 | 
						|
                 << currentService->fullName();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void ServiceGatherer::stopResolve(ZK_IP_Protocol protocol)
 | 
						|
{
 | 
						|
    if ((protocol == ZK_PROTO_IPv4_OR_IPv6 || protocol == ZK_PROTO_IPv4)
 | 
						|
            && (status & ResolveConnectionActive) != 0) {
 | 
						|
        QMutexLocker l(serviceBrowser->mainConnection->lock());
 | 
						|
        if (serviceBrowser->mainConnection->status() < MainConnection::Stopping)
 | 
						|
            lib()->refDeallocate(resolveConnection);
 | 
						|
        status &= ~ResolveConnectionActive;
 | 
						|
        serviceBrowser->updateFlowStatusForCancel();
 | 
						|
    }
 | 
						|
    if ((protocol == ZK_PROTO_IPv4_OR_IPv6 || protocol == ZK_PROTO_IPv6)
 | 
						|
            && (status & ResolveConnectionV6Active) != 0) {
 | 
						|
        QMutexLocker l(serviceBrowser->mainConnection->lock());
 | 
						|
        if (serviceBrowser->mainConnection->status() < MainConnection::Stopping)
 | 
						|
            lib()->refDeallocate(resolveConnectionV6);
 | 
						|
        status &= ~ResolveConnectionV6Active;
 | 
						|
        serviceBrowser->updateFlowStatusForCancel();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void ServiceGatherer::restartResolve(ZK_IP_Protocol protocol)
 | 
						|
{
 | 
						|
    stopResolve(protocol);
 | 
						|
    if (protocol == ZK_PROTO_IPv6) {
 | 
						|
        if (currentService->host()) {
 | 
						|
            QList<QHostAddress> addrNow = currentService->host()->addresses();
 | 
						|
            QMutableListIterator<QHostAddress> addr(addrNow);
 | 
						|
            bool changed = false;
 | 
						|
            while (addr.hasNext()) {
 | 
						|
                if (addr.next().protocol() == QAbstractSocket::IPv6Protocol) {
 | 
						|
                    addr.remove();
 | 
						|
                    changed = true;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            if (changed) {
 | 
						|
                currentService->m_host->setAddresses(addrNow);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        DNSServiceErrorType err = lib()->resolve(
 | 
						|
                    serviceBrowser->mainRef(),
 | 
						|
                    &resolveConnectionV6,
 | 
						|
                    currentService->interfaceNr(), protocol,
 | 
						|
                    currentService->name().toUtf8().constData(),
 | 
						|
                    currentService->type().toUtf8().constData(),
 | 
						|
                    currentService->domain().toUtf8().constData(), this);
 | 
						|
        if (err != kDNSServiceErr_NoError) {
 | 
						|
            qDebug() << "ServiceBrowser " << serviceBrowser->serviceType
 | 
						|
                     << " failed IPv6 discovery of service " << currentService->fullName()
 | 
						|
                     << " due to error " << err;
 | 
						|
            status = status | ResolveConnectionV6Failed;
 | 
						|
        } else {
 | 
						|
            status = ((status & ~ResolveConnectionV6Failed) | ResolveConnectionV6Active);
 | 
						|
        }
 | 
						|
    } else {
 | 
						|
        if (currentService->host()) {
 | 
						|
            if (protocol == ZK_PROTO_IPv4_OR_IPv6) {
 | 
						|
                currentService->m_host->setAddresses(QList<QHostAddress>());
 | 
						|
            } else {
 | 
						|
                QList<QHostAddress> addrNow = currentService->host()->addresses();
 | 
						|
                QMutableListIterator<QHostAddress> addr(addrNow);
 | 
						|
                bool changed = false;
 | 
						|
                while (addr.hasNext()) {
 | 
						|
                    if (addr.next().protocol() == QAbstractSocket::IPv4Protocol) {
 | 
						|
                        addr.remove();
 | 
						|
                        changed = true;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                if (changed) {
 | 
						|
                    currentService->m_host->setAddresses(addrNow);
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        DNSServiceErrorType err = lib()->resolve(
 | 
						|
                    serviceBrowser->mainRef(),
 | 
						|
                    &resolveConnection,
 | 
						|
                    currentService->interfaceNr(), protocol,
 | 
						|
                    currentService->name().toUtf8().constData(),
 | 
						|
                    currentService->type().toUtf8().constData(),
 | 
						|
                    currentService->domain().toUtf8().constData(), this);
 | 
						|
        if (err != kDNSServiceErr_NoError) {
 | 
						|
            qDebug() << "ServiceBrowser " << serviceBrowser->serviceType
 | 
						|
                     << " failed discovery of service " << currentService->fullName()
 | 
						|
                     << " due to error " << err;
 | 
						|
            status = status | ResolveConnectionFailed;
 | 
						|
        } else {
 | 
						|
            status = ((status & ~ResolveConnectionFailed) | ResolveConnectionActive);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void ServiceGatherer::stopTxt()
 | 
						|
{
 | 
						|
    if ((status & TxtConnectionActive) == 0) return;
 | 
						|
    QMutexLocker l(serviceBrowser->mainConnection->lock());
 | 
						|
    if (serviceBrowser->mainConnection->status() < MainConnection::Stopping)
 | 
						|
        lib()->refDeallocate(txtConnection);
 | 
						|
    status &= ~TxtConnectionActive;
 | 
						|
    serviceBrowser->updateFlowStatusForCancel();
 | 
						|
}
 | 
						|
 | 
						|
void ServiceGatherer::restartTxt()
 | 
						|
{
 | 
						|
    stopTxt();
 | 
						|
    DNSServiceErrorType err = lib()->queryRecord(serviceBrowser->mainRef(), &txtConnection,
 | 
						|
                                                 currentService->interfaceNr(),
 | 
						|
                                                 currentService->fullName().toUtf8().constData(),
 | 
						|
                                                 this);
 | 
						|
 | 
						|
    if (err != kDNSServiceErr_NoError) {
 | 
						|
        qDebug() << "ServiceBrowser " << serviceBrowser->serviceType
 | 
						|
                 << " failed query of TXT record of service " << currentService->fullName()
 | 
						|
                 << " due to error " << err;
 | 
						|
        status = status | TxtConnectionFailed;
 | 
						|
    } else {
 | 
						|
        status = ((status & ~TxtConnectionFailed) | TxtConnectionActive);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void ServiceGatherer::stopHostResolution()
 | 
						|
{
 | 
						|
    if ((status & AddrConnectionActive) == 0) return;
 | 
						|
    QMutexLocker l(serviceBrowser->mainConnection->lock());
 | 
						|
    if (serviceBrowser->mainConnection->status() < MainConnection::Stopping)
 | 
						|
        lib()->refDeallocate(addrConnection);
 | 
						|
    status &= ~AddrConnectionActive;
 | 
						|
    serviceBrowser->updateFlowStatusForCancel();
 | 
						|
}
 | 
						|
 | 
						|
void ServiceGatherer::restartHostResolution()
 | 
						|
{
 | 
						|
    stopHostResolution();
 | 
						|
    if (DEBUG_ZEROCONF)
 | 
						|
        qDebug() << "ServiceGatherer::restartHostResolution for host " << hostName << " service "
 | 
						|
                 << currentService->fullName();
 | 
						|
    if (hostName.isEmpty()){
 | 
						|
        qDebug() << "ServiceBrowser " << serviceBrowser->serviceType
 | 
						|
                 << " cannot start host resolution without hostname for service "
 | 
						|
                 << currentService->fullName();
 | 
						|
    }
 | 
						|
    DNSServiceErrorType err = lib()->getAddrInfo(serviceBrowser->mainRef(), &addrConnection,
 | 
						|
                                                 currentService->interfaceNr(),
 | 
						|
                                                 0 /* kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6 */,
 | 
						|
                                                 hostName.toUtf8().constData(), this);
 | 
						|
 | 
						|
    if (err != kDNSServiceErr_NoError) {
 | 
						|
        qDebug() << "ServiceBrowser " << serviceBrowser->serviceType
 | 
						|
                 << " failed starting resolution of host " << hostName << " for service "
 | 
						|
                 << currentService->fullName() << " due to error " << err;
 | 
						|
        status = status | AddrConnectionFailed;
 | 
						|
    } else {
 | 
						|
        status = ((status & ~AddrConnectionFailed) | AddrConnectionActive);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/// if the current service can be added
 | 
						|
bool ServiceGatherer::currentServiceCanBePublished()
 | 
						|
{
 | 
						|
    return (currentService->host() && !currentService->host()->addresses().isEmpty())
 | 
						|
            || !serviceBrowser->requireAddresses;
 | 
						|
}
 | 
						|
 | 
						|
ServiceGatherer::ServiceGatherer(const QString &newServiceName, const QString &newType,
 | 
						|
                                 const QString &newDomain, const QString &fullName,
 | 
						|
                                 uint32_t interfaceIndex, ZK_IP_Protocol protocol,
 | 
						|
                                 ServiceBrowserPrivate *serviceBrowser):
 | 
						|
    serviceBrowser(serviceBrowser), publishedService(0), currentService(new Service()), status(0)
 | 
						|
{
 | 
						|
    if (DEBUG_ZEROCONF)
 | 
						|
        qDebug() << " creating ServiceGatherer(" << newServiceName << ", " << newType << ", "
 | 
						|
                 << newDomain << ", " << fullName << ", " << interfaceIndex << ", "
 | 
						|
                 << ((size_t) serviceBrowser);
 | 
						|
    currentService->m_name = newServiceName;
 | 
						|
    currentService->m_type = newType;
 | 
						|
    currentService->m_domain = newDomain;
 | 
						|
    currentService->m_fullName = fullName;
 | 
						|
    currentService->m_interfaceNr = interfaceIndex;
 | 
						|
    if (fullName.isEmpty())
 | 
						|
        currentService->m_fullName = toFullNameC(currentService->name().toUtf8().data(),
 | 
						|
                                                 currentService->type().toUtf8().data(),
 | 
						|
                                                 currentService->domain().toUtf8().data());
 | 
						|
    restartResolve(protocol);
 | 
						|
    restartTxt();
 | 
						|
}
 | 
						|
 | 
						|
ServiceGatherer::~ServiceGatherer()
 | 
						|
{
 | 
						|
    stopHostResolution();
 | 
						|
    stopResolve();
 | 
						|
    stopTxt();
 | 
						|
    delete currentService;
 | 
						|
}
 | 
						|
 | 
						|
ServiceGatherer::Ptr ServiceGatherer::createGatherer(
 | 
						|
        const QString &newServiceName, const QString &newType, const QString &newDomain,
 | 
						|
        const QString &fullName, uint32_t interfaceIndex, ZK_IP_Protocol protocol,
 | 
						|
        ServiceBrowserPrivate *serviceBrowser)
 | 
						|
{
 | 
						|
    Ptr res(new ServiceGatherer(newServiceName, newType, newDomain, fullName, interfaceIndex,
 | 
						|
                                protocol, serviceBrowser));
 | 
						|
    res->self = res.toWeakRef();
 | 
						|
    return res;
 | 
						|
}
 | 
						|
 | 
						|
ServiceGatherer::Ptr ServiceGatherer::gatherer() {
 | 
						|
    return self.toStrongRef();
 | 
						|
}
 | 
						|
 | 
						|
void ServiceGatherer::serviceResolveReply(DNSServiceFlags                     flags,
 | 
						|
                                          uint32_t                            interfaceIndex,
 | 
						|
                                          DNSServiceErrorType                 errorCode,
 | 
						|
                                          const char                          *hosttarget,
 | 
						|
                                          const QString                       &port,
 | 
						|
                                          uint16_t                            txtLen,
 | 
						|
                                          const unsigned char                 *rawTxtRecord)
 | 
						|
{
 | 
						|
    if (errorCode != kDNSServiceErr_NoError){
 | 
						|
        if (errorCode == kDNSServiceErr_Timeout){
 | 
						|
            if ((status & ResolveConnectionSuccess) == 0){
 | 
						|
                qDebug() << "ServiceBrowser " << serviceBrowser->serviceType
 | 
						|
                         << " failed service resolution for service "
 | 
						|
                         << currentService->fullName() << " as it did timeout";
 | 
						|
                status |= ResolveConnectionFailed;
 | 
						|
            }
 | 
						|
        } else {
 | 
						|
            qDebug() << "ServiceBrowser " << serviceBrowser->serviceType
 | 
						|
                     << " failed service resolution for service " << currentService->fullName()
 | 
						|
                     << " with error " << errorCode;
 | 
						|
            status |= ResolveConnectionFailed;
 | 
						|
        }
 | 
						|
        if (status & ResolveConnectionActive) {
 | 
						|
            status &= ~ResolveConnectionActive;
 | 
						|
            lib()->refDeallocate(resolveConnection);
 | 
						|
            serviceBrowser->updateFlowStatusForCancel();
 | 
						|
        }
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    serviceBrowser->updateFlowStatusForFlags(flags);
 | 
						|
    uint16_t nKeys = txtRecordGetCount(txtLen, rawTxtRecord);
 | 
						|
    for (uint16_t i = 0; i < nKeys; ++i){
 | 
						|
        enum { maxTxtLen = 256 };
 | 
						|
        char keyBuf[maxTxtLen];
 | 
						|
        uint8_t valLen;
 | 
						|
        const char *valueCStr;
 | 
						|
        DNSServiceErrorType txtErr = txtRecordGetItemAtIndex(
 | 
						|
                    txtLen, rawTxtRecord, i, maxTxtLen, keyBuf, &valLen, (const void **)&valueCStr);
 | 
						|
        if (txtErr != kDNSServiceErr_NoError){
 | 
						|
            qDebug() << "ServiceBrowser " << serviceBrowser->serviceType << " error " << txtErr
 | 
						|
                     << " decoding txt record of service " << currentService->fullName();
 | 
						|
            break;
 | 
						|
        }
 | 
						|
        keyBuf[maxTxtLen-1] = 0; // just to be sure
 | 
						|
        currentService->m_txtRecord.insert(QString::fromUtf8(keyBuf),
 | 
						|
                                           QString::fromUtf8(valueCStr, valLen));
 | 
						|
    }
 | 
						|
    currentService->m_interfaceNr = interfaceIndex;
 | 
						|
    currentService->m_port = port;
 | 
						|
    if (hostName != hosttarget) {
 | 
						|
        hostName = QString::fromUtf8(hosttarget);
 | 
						|
        if (!currentService->host())
 | 
						|
            currentService->m_host = new QHostInfo();
 | 
						|
        else
 | 
						|
            currentService->m_host->setAddresses(QList<QHostAddress>());
 | 
						|
        currentService->m_host->setHostName(hostName);
 | 
						|
        if (serviceBrowser->autoResolveAddresses){
 | 
						|
            restartHostResolution();
 | 
						|
        }
 | 
						|
    }
 | 
						|
    if (currentServiceCanBePublished())
 | 
						|
        serviceBrowser->pendingGathererAdd(gatherer());
 | 
						|
}
 | 
						|
 | 
						|
void ServiceGatherer::txtRecordReply(DNSServiceFlags                     flags,
 | 
						|
                                     DNSServiceErrorType                 errorCode,
 | 
						|
                                     uint16_t                            txtLen,
 | 
						|
                                     const void                          *rawTxtRecord,
 | 
						|
                                     uint32_t                            /*ttl*/)
 | 
						|
{
 | 
						|
    if (errorCode != kDNSServiceErr_NoError){
 | 
						|
        if (errorCode == kDNSServiceErr_Timeout){
 | 
						|
            if ((status & TxtConnectionSuccess) == 0){
 | 
						|
                qDebug() << "ServiceBrowser " << serviceBrowser->serviceType
 | 
						|
                         << " failed txt gathering for service " << currentService->fullName()
 | 
						|
                         << " as it did timeout";
 | 
						|
                status |= TxtConnectionFailed;
 | 
						|
            }
 | 
						|
        } else {
 | 
						|
            qDebug() << "ServiceBrowser " << serviceBrowser->serviceType
 | 
						|
                     << " failed txt gathering for service " << currentService->fullName()
 | 
						|
                     << " with error " << errorCode;
 | 
						|
            status |= TxtConnectionFailed;
 | 
						|
        }
 | 
						|
        if (status & TxtConnectionActive) {
 | 
						|
            status &= ~TxtConnectionActive;
 | 
						|
            lib()->refDeallocate(txtConnection);
 | 
						|
            serviceBrowser->updateFlowStatusForCancel();
 | 
						|
        }
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    serviceBrowser->updateFlowStatusForFlags(flags);
 | 
						|
 | 
						|
    uint16_t nKeys = txtRecordGetCount(txtLen, rawTxtRecord);
 | 
						|
    for (uint16_t i = 0; i < nKeys; ++i){
 | 
						|
        char keyBuf[256];
 | 
						|
        uint8_t valLen;
 | 
						|
        const char *valueCStr;
 | 
						|
        DNSServiceErrorType txtErr = txtRecordGetItemAtIndex(txtLen, rawTxtRecord, i, 256, keyBuf,
 | 
						|
                                                             &valLen, (const void **)&valueCStr);
 | 
						|
        if (txtErr != kDNSServiceErr_NoError){
 | 
						|
            qDebug() << "ServiceBrowser " << serviceBrowser->serviceType << " error " << txtErr
 | 
						|
                     << " decoding txt record of service " << currentService->fullName();
 | 
						|
            if ((flags & kDNSServiceFlagsAdd) == 0)
 | 
						|
                currentService->m_txtRecord.clear();
 | 
						|
            break;
 | 
						|
        }
 | 
						|
        keyBuf[255] = 0; // just to be sure
 | 
						|
        if (flags & kDNSServiceFlagsAdd) {
 | 
						|
            currentService->m_txtRecord.insert(QString::fromUtf8(keyBuf),
 | 
						|
                                               QString::fromUtf8(valueCStr, valLen));
 | 
						|
        } else {
 | 
						|
            currentService->m_txtRecord.remove(QString::fromUtf8(keyBuf)); // check value???
 | 
						|
        }
 | 
						|
    }
 | 
						|
    if ((flags & kDNSServiceFlagsAdd) != 0) {
 | 
						|
        status |= TxtConnectionSuccess;
 | 
						|
    }
 | 
						|
    if (currentService->m_txtRecord.count() != 0 && currentServiceCanBePublished())
 | 
						|
        serviceBrowser->pendingGathererAdd(gatherer());
 | 
						|
}
 | 
						|
 | 
						|
void ServiceGatherer::txtFieldReply(DNSServiceFlags                     flags,
 | 
						|
                                    DNSServiceErrorType                 errorCode,
 | 
						|
                                    uint16_t                            txtLen,
 | 
						|
                                    const void                          *rawTxtRecord,
 | 
						|
                                    uint32_t                            /*ttl*/){
 | 
						|
    if (errorCode != kDNSServiceErr_NoError){
 | 
						|
        if (errorCode == kDNSServiceErr_Timeout){
 | 
						|
            if ((status & TxtConnectionSuccess) == 0){
 | 
						|
                qDebug() << "ServiceBrowser " << serviceBrowser->serviceType
 | 
						|
                         << " failed txt gathering for service " << currentService->fullName()
 | 
						|
                         << " as it did timeout";
 | 
						|
                status |= TxtConnectionFailed;
 | 
						|
            }
 | 
						|
        } else {
 | 
						|
            qDebug() << "ServiceBrowser " << serviceBrowser->serviceType
 | 
						|
                     << " failed txt gathering for service " << currentService->fullName()
 | 
						|
                     << " with error " << errorCode;
 | 
						|
            status |= TxtConnectionFailed;
 | 
						|
        }
 | 
						|
        if (status & TxtConnectionActive) {
 | 
						|
            status &= ~TxtConnectionActive;
 | 
						|
            lib()->refDeallocate(txtConnection);
 | 
						|
            serviceBrowser->updateFlowStatusForCancel();
 | 
						|
        }
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    serviceBrowser->updateFlowStatusForFlags(flags);
 | 
						|
    uint16_t keyLen = 0;
 | 
						|
    const char *txt = reinterpret_cast<const char *>(rawTxtRecord);
 | 
						|
    while (keyLen < txtLen) {
 | 
						|
        if (txt[keyLen]=='=')
 | 
						|
            break;
 | 
						|
        ++keyLen;
 | 
						|
    }
 | 
						|
    if (flags & kDNSServiceFlagsAdd) {
 | 
						|
        currentService->m_txtRecord.insert(
 | 
						|
                    QString::fromUtf8(txt, keyLen),
 | 
						|
                    QString::fromUtf8(txt + keyLen + 1, ((txtLen > keyLen)?txtLen - keyLen - 1:0)));
 | 
						|
    } else {
 | 
						|
        currentService->m_txtRecord.remove(QString::fromUtf8(txt, keyLen)); // check value???
 | 
						|
    }
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
void ServiceGatherer::addrReply(DNSServiceFlags                  flags,
 | 
						|
                                DNSServiceErrorType              errorCode,
 | 
						|
                                const char                       *hostname,
 | 
						|
                                const struct sockaddr            *address,
 | 
						|
                                uint32_t                         /*ttl*/) // should we use this???
 | 
						|
{
 | 
						|
    if (errorCode != kDNSServiceErr_NoError) {
 | 
						|
        if (errorCode == kDNSServiceErr_Timeout){
 | 
						|
            if ((status & AddrConnectionSuccess) == 0){
 | 
						|
                qDebug() << "ServiceBrowser " << serviceBrowser->serviceType
 | 
						|
                         << " failed address resolve for service " << currentService->fullName()
 | 
						|
                         << " as it did timeout";
 | 
						|
                status |= AddrConnectionFailed;
 | 
						|
            }
 | 
						|
            qDebug() << "ServiceBrowser " << serviceBrowser->serviceType
 | 
						|
                     << " failed addr resolve for service " << currentService->fullName()
 | 
						|
                     << " with error " << errorCode;
 | 
						|
            status |= AddrConnectionFailed;
 | 
						|
        }
 | 
						|
        if (status & AddrConnectionActive){
 | 
						|
            status &= ~AddrConnectionActive;
 | 
						|
            lib()->refDeallocate(addrConnection);
 | 
						|
            serviceBrowser->updateFlowStatusForCancel();
 | 
						|
        }
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    serviceBrowser->updateFlowStatusForFlags(flags);
 | 
						|
    if (!currentService->host())
 | 
						|
        currentService->m_host = new QHostInfo();
 | 
						|
    if (currentService->host()->hostName() != hostname) {
 | 
						|
        if ((flags & kDNSServiceFlagsAdd) == 1)
 | 
						|
            currentService->m_host->setHostName(QString::fromUtf8(hostname));
 | 
						|
        if (currentService->host()->addresses().isEmpty()) {
 | 
						|
            qDebug() << "ServiceBrowser " << serviceBrowser->serviceType << " for service "
 | 
						|
                     << currentService->fullName() << " add with name " << hostname
 | 
						|
                     << " while old name " << currentService->host()->hostName()
 | 
						|
                     << " has still adresses, removing them";
 | 
						|
            currentService->m_host->setAddresses(QList<QHostAddress>());
 | 
						|
        } else {
 | 
						|
            qDebug() << "ServiceBrowser " << serviceBrowser->serviceType << " for service "
 | 
						|
                     << currentService->fullName() << " ignoring remove for " << hostname
 | 
						|
                     << " as current hostname is " << currentService->host()->hostName();
 | 
						|
            return;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    QHostAddress newAddr(address);
 | 
						|
    QList<QHostAddress> addrNow = currentService->host()->addresses();
 | 
						|
    if ((flags & kDNSServiceFlagsAdd) == 0) {
 | 
						|
        if (addrNow.removeOne(newAddr))
 | 
						|
            currentService->m_host->setAddresses(addrNow);
 | 
						|
    } else {
 | 
						|
        if (!addrNow.contains(newAddr)){
 | 
						|
            switch (newAddr.protocol()){
 | 
						|
#ifdef Q_OS_WIN
 | 
						|
            case QAbstractSocket::IPv4Protocol: // prefers IPv4 addresses
 | 
						|
#else
 | 
						|
            case QAbstractSocket::IPv6Protocol: // prefers IPv6 addresses
 | 
						|
#endif
 | 
						|
                addrNow.insert(0, newAddr);
 | 
						|
                break;
 | 
						|
            default:
 | 
						|
                addrNow.append(newAddr);
 | 
						|
                break;
 | 
						|
            }
 | 
						|
            currentService->m_host->setAddresses(addrNow);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    serviceBrowser->pendingGathererAdd(gatherer());
 | 
						|
}
 | 
						|
 | 
						|
void ServiceGatherer::maybeRemove()
 | 
						|
{
 | 
						|
    // could trigger an update, but for now we just ignore it (less chatty)
 | 
						|
}
 | 
						|
 | 
						|
void ServiceGatherer::stop(ZK_IP_Protocol protocol)
 | 
						|
{
 | 
						|
    if ((protocol == ZK_PROTO_IPv4_OR_IPv6 || protocol == ZK_PROTO_IPv4)
 | 
						|
            && (status & ResolveConnectionActive) != 0){
 | 
						|
        status &= ~ResolveConnectionActive;
 | 
						|
        lib()->refDeallocate(resolveConnection);
 | 
						|
        serviceBrowser->updateFlowStatusForCancel();
 | 
						|
    }
 | 
						|
    if ((protocol == ZK_PROTO_IPv4_OR_IPv6 || protocol == ZK_PROTO_IPv6)
 | 
						|
            && (status & ResolveConnectionV6Active) != 0){
 | 
						|
        status &= ~ResolveConnectionV6Active;
 | 
						|
        lib()->refDeallocate(resolveConnectionV6);
 | 
						|
        serviceBrowser->updateFlowStatusForCancel();
 | 
						|
    }
 | 
						|
    if (status & TxtConnectionActive){
 | 
						|
        status &= ~TxtConnectionActive;
 | 
						|
        lib()->refDeallocate(txtConnection);
 | 
						|
        serviceBrowser->updateFlowStatusForCancel();
 | 
						|
    }
 | 
						|
    if (status & AddrConnectionActive) {
 | 
						|
        status &= ~AddrConnectionActive;
 | 
						|
        lib()->refDeallocate(addrConnection);
 | 
						|
        serviceBrowser->updateFlowStatusForCancel();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void ServiceGatherer::reload(qint32 interfaceIndex, ZK_IP_Protocol protocol)
 | 
						|
{
 | 
						|
    this->interfaceIndex = interfaceIndex;
 | 
						|
    stop(protocol);
 | 
						|
    restartResolve(protocol);
 | 
						|
    restartTxt();
 | 
						|
    if (currentServiceCanBePublished()) // avoid???
 | 
						|
        restartHostResolution();
 | 
						|
}
 | 
						|
 | 
						|
void ServiceGatherer::remove()
 | 
						|
{
 | 
						|
    stop();
 | 
						|
    if (serviceBrowser->gatherers.contains(currentService->fullName())
 | 
						|
            && serviceBrowser->gatherers[currentService->fullName()] == this)
 | 
						|
    {
 | 
						|
        serviceBrowser->gatherers.remove(currentService->fullName());
 | 
						|
    }
 | 
						|
}
 | 
						|
/// forces a full reload of the record
 | 
						|
void ServiceGatherer::reconfirm()
 | 
						|
{
 | 
						|
    stop();
 | 
						|
    /*        DNSServiceErrorType err = DNSServiceReconfirmRecord(kDNSServiceFlagsShareConnection,
 | 
						|
                                                        interfaceIndex, fullName.toUtf8().constData(),
 | 
						|
                                                        kDNSServiceType_PTR, // kDNSServiceType_SRV could be another possibility
 | 
						|
                                                        kDNSServiceClass_IN //,
 | 
						|
                                                        // uint16_t                           rdlen, // not cached... what is the best solution???
 | 
						|
                                                        // const void                         *rdata
 | 
						|
                                                        ); */
 | 
						|
}
 | 
						|
 | 
						|
// ----------------- ServiceBrowserPrivate impl -----------------
 | 
						|
 | 
						|
ZConfLib::ConnectionRef ServiceBrowserPrivate::mainRef()
 | 
						|
{
 | 
						|
    return mainConnection->mainRef();
 | 
						|
}
 | 
						|
 | 
						|
void ServiceBrowserPrivate::updateFlowStatusForCancel()
 | 
						|
{
 | 
						|
    mainConnection->updateFlowStatusForCancel();
 | 
						|
}
 | 
						|
 | 
						|
void ServiceBrowserPrivate::updateFlowStatusForFlags(DNSServiceFlags flags)
 | 
						|
{
 | 
						|
    mainConnection->updateFlowStatusForFlags(flags);
 | 
						|
}
 | 
						|
 | 
						|
void ServiceBrowserPrivate::pendingGathererAdd(ServiceGatherer::Ptr gatherer)
 | 
						|
{
 | 
						|
    int ng = pendingGatherers.count();
 | 
						|
    for (int i = 0; i < ng; ++i){
 | 
						|
        const ServiceGatherer::Ptr &g = pendingGatherers.at(i);
 | 
						|
        if (g->fullName() == gatherer->fullName()){
 | 
						|
            if (g != gatherer){
 | 
						|
                gatherer->publishedService = g->publishedService;
 | 
						|
                pendingGatherers[i] = gatherer;
 | 
						|
            }
 | 
						|
            return;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    pendingGatherers.append(gatherer);
 | 
						|
}
 | 
						|
 | 
						|
ServiceBrowserPrivate::ServiceBrowserPrivate(const QString &serviceType, const QString &domain,
 | 
						|
                                             bool requireAddresses, MainConnectionPtr mconn):
 | 
						|
    q(0), serviceType(serviceType), domain(domain), mainConnection(mconn), serviceConnection(0), flags(0), interfaceIndex(0),
 | 
						|
    delayDeletesUntil(std::numeric_limits<qint64>::min()), failed(false), browsing(false),
 | 
						|
    autoResolveAddresses(requireAddresses), requireAddresses(requireAddresses), shouldRefresh(false)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
ServiceBrowserPrivate::~ServiceBrowserPrivate()
 | 
						|
{
 | 
						|
    if (DEBUG_ZEROCONF)
 | 
						|
        qDebug() << "destroying ServiceBrowserPrivate " << serviceType;
 | 
						|
    if (browsing){
 | 
						|
        stopBrowsing();
 | 
						|
    }
 | 
						|
    if (mainConnection){
 | 
						|
        mainConnection->removeBrowser(this);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void ServiceBrowserPrivate::insertGatherer(const QString &fullName)
 | 
						|
{
 | 
						|
    if (!gatherers.contains(fullName)){
 | 
						|
        QString newServiceName, newType, newDomain;
 | 
						|
        if (fromFullNameC(fullName.toUtf8().data(), *&newServiceName, *&newType, *&newDomain)){
 | 
						|
            qDebug() << "Error unescaping fullname " << fullName;
 | 
						|
        } else {
 | 
						|
            ServiceGatherer::Ptr serviceGatherer = ServiceGatherer::createGatherer(
 | 
						|
                        newServiceName, newType, newDomain, fullName, 0, ZK_PROTO_IPv4_OR_IPv6, this);
 | 
						|
            gatherers[fullName] = serviceGatherer;
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void ServiceBrowserPrivate::maybeUpdateLists()
 | 
						|
{
 | 
						|
    if (mainConnection->flowStatus != MainConnection::MoreComingRFS
 | 
						|
            || pendingGatherers.count() > 50 || shouldRefresh)
 | 
						|
    {
 | 
						|
        qint64 now = QDateTime::currentMSecsSinceEpoch();
 | 
						|
        QList<QString>::iterator i = knownServices.begin(), endi = knownServices.end();
 | 
						|
        QMap<QString, ServiceGatherer::Ptr>::iterator j = gatherers.begin();
 | 
						|
        bool hasServicesChanges = false;
 | 
						|
        while (i != endi && j != gatherers.end()) {
 | 
						|
            const QString vi = *i;
 | 
						|
            QString vj = j.value()->fullName();
 | 
						|
            if (vi == vj){
 | 
						|
                ++i;
 | 
						|
                ++j;
 | 
						|
            } else if (vi < vj) {
 | 
						|
                qDebug() << "ServiceBrowser " << serviceType << ", missing gatherer for " << vi;
 | 
						|
                insertGatherer(vi);
 | 
						|
                ++i;
 | 
						|
            } else if (delayDeletesUntil <= now) {
 | 
						|
                pendingGatherers.removeAll(j.value());
 | 
						|
                j.value()->retireService();
 | 
						|
                j = gatherers.erase(j);
 | 
						|
                hasServicesChanges = true;
 | 
						|
            } else {
 | 
						|
                ++j;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        while (i != endi) {
 | 
						|
            qDebug() << "ServiceBrowser " << serviceType << ", missing gatherer for " << *i;
 | 
						|
            insertGatherer(*i);
 | 
						|
        }
 | 
						|
        while (j != gatherers.end()) {
 | 
						|
            if (delayDeletesUntil <= now) {
 | 
						|
                pendingGatherers.removeAll(j.value());
 | 
						|
                j.value()->retireService();
 | 
						|
                j = gatherers.erase(j);
 | 
						|
                hasServicesChanges = true;
 | 
						|
            } else {
 | 
						|
                ++j;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        foreach (const ServiceGatherer::Ptr &g, pendingGatherers)
 | 
						|
            if (delayDeletesUntil <= now || ! g->publishedService)
 | 
						|
                hasServicesChanges = g->enactServiceChange() || hasServicesChanges;
 | 
						|
        if (hasServicesChanges) {
 | 
						|
            {
 | 
						|
                QMutexLocker l(mainConnection->lock());
 | 
						|
                activeServices = nextActiveServices;
 | 
						|
            }
 | 
						|
            emit q->servicesUpdated(q);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    {
 | 
						|
        QMutexLocker l(mainConnection->lock());
 | 
						|
        if (shouldRefresh)
 | 
						|
            refresh();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/// callback announcing
 | 
						|
void ServiceBrowserPrivate::browseReply(DNSServiceFlags                     flags,
 | 
						|
                                        uint32_t                            interfaceIndex,
 | 
						|
                                        ZK_IP_Protocol                             protocol,
 | 
						|
                                        DNSServiceErrorType                 errorCode,
 | 
						|
                                        const char                          *serviceName,
 | 
						|
                                        const char                          *regtype,
 | 
						|
                                        const char                          *replyDomain)
 | 
						|
{
 | 
						|
    if (DEBUG_ZEROCONF)
 | 
						|
        qDebug() << "browseReply(" << ((int)flags) << ", " << interfaceIndex << ", "
 | 
						|
                 << ((int)errorCode) << ", " << serviceName << ", " << regtype << ", "
 | 
						|
                 << replyDomain << ")";
 | 
						|
    if (errorCode != kDNSServiceErr_NoError){
 | 
						|
        qDebug() << "ServiceBrowser " << serviceType << " ignoring reply due to error " << errorCode;
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    QString newServiceName = QString::fromUtf8(serviceName);
 | 
						|
    QString newType = serviceType;
 | 
						|
    QString newDomain = domain;
 | 
						|
    if (serviceType != regtype) // discard? should not happen...
 | 
						|
        newType = QString::fromUtf8(regtype);
 | 
						|
    if (domain != replyDomain)
 | 
						|
        domain = QString::fromUtf8(replyDomain);
 | 
						|
    QString fullName = toFullNameC(serviceName, regtype, replyDomain);
 | 
						|
    updateFlowStatusForFlags(flags);
 | 
						|
    if (flags & kDNSServiceFlagsAdd){
 | 
						|
        ServiceGatherer::Ptr serviceGatherer;
 | 
						|
        if (!gatherers.contains(fullName)){
 | 
						|
            serviceGatherer = ServiceGatherer::createGatherer(newServiceName, newType, newDomain,
 | 
						|
                                                              fullName, interfaceIndex, protocol,
 | 
						|
                                                              this);
 | 
						|
            gatherers[fullName] = serviceGatherer;
 | 
						|
        } else {
 | 
						|
            serviceGatherer = gatherers[fullName];
 | 
						|
            serviceGatherer->reload(interfaceIndex, protocol);
 | 
						|
        }
 | 
						|
        QList<QString>::iterator pos = std::lower_bound(knownServices.begin(), knownServices.end(),
 | 
						|
                                                        fullName);
 | 
						|
        // could order later (more efficient, but then we have to handle eventual duplicates)
 | 
						|
        if (pos == knownServices.end() || *pos != fullName)
 | 
						|
            knownServices.insert(pos, fullName);
 | 
						|
    } else {
 | 
						|
        if (gatherers.contains(fullName)){
 | 
						|
            gatherers[fullName]->maybeRemove();
 | 
						|
        }
 | 
						|
        knownServices.removeOne(fullName);
 | 
						|
    }
 | 
						|
    maybeUpdateLists(); // avoid?
 | 
						|
}
 | 
						|
 | 
						|
void ServiceBrowserPrivate::activateAutoRefresh()
 | 
						|
{
 | 
						|
    emit q->activateAutoRefresh();
 | 
						|
}
 | 
						|
 | 
						|
void ServiceBrowserPrivate::startBrowsing(quint32 interfaceIndex)
 | 
						|
{
 | 
						|
    this->interfaceIndex = interfaceIndex;
 | 
						|
    if (failed || browsing)
 | 
						|
        return;
 | 
						|
    if (mainConnection.isNull()) {
 | 
						|
        startupPhase(1, ServiceBrowser::tr("Starting Zeroconf Browsing"));
 | 
						|
        mainConnection = MainConnectionPtr(new MainConnection(this));
 | 
						|
    } else {
 | 
						|
        mainConnection->addBrowser(this);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
bool ServiceBrowserPrivate::internalStartBrowsing()
 | 
						|
{
 | 
						|
    mainConnection->updateFlowStatusForFlags(0);
 | 
						|
    if (failed || browsing)
 | 
						|
        return false;
 | 
						|
    DNSServiceErrorType err;
 | 
						|
    err = mainConnection->lib->browse(mainRef(), &serviceConnection, interfaceIndex,
 | 
						|
                                      serviceType.toUtf8().constData(),
 | 
						|
                                      ((domain.isEmpty()) ? 0 : (domain.toUtf8().constData())), this);
 | 
						|
    if (err != kDNSServiceErr_NoError){
 | 
						|
        qDebug() << "ServiceBrowser " << serviceType << " failed initializing serviceConnection";
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
    browsing = true;
 | 
						|
    if (DEBUG_ZEROCONF)
 | 
						|
        qDebug() << "startBrowsing(" << interfaceIndex << ") for serviceType:" << serviceType
 | 
						|
                 << " domain:" << domain;
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
void ServiceBrowserPrivate::triggerRefresh()
 | 
						|
{
 | 
						|
    {
 | 
						|
        QMutexLocker l(mainConnection->lock());
 | 
						|
        const qint64 msecDelay = 5100;
 | 
						|
        delayDeletesUntil = QDateTime::currentMSecsSinceEpoch() + msecDelay;
 | 
						|
        stopBrowsing();
 | 
						|
        shouldRefresh = true;
 | 
						|
    }
 | 
						|
    {
 | 
						|
        QMutexLocker l(mainConnection->mainThreadLock());
 | 
						|
        if (!browsing)
 | 
						|
            refresh();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void ServiceBrowserPrivate::refresh()
 | 
						|
{
 | 
						|
    const qint64 msecDelay = 500;
 | 
						|
    delayDeletesUntil = QDateTime::currentMSecsSinceEpoch() + msecDelay;
 | 
						|
    shouldRefresh = false;
 | 
						|
    internalStartBrowsing();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void ServiceBrowserPrivate::stopBrowsing()
 | 
						|
{
 | 
						|
    QMutexLocker l(mainConnection->lock());
 | 
						|
    if (browsing){
 | 
						|
        if (serviceConnection) {
 | 
						|
            mainConnection->lib->browserDeallocate(&serviceConnection);
 | 
						|
            updateFlowStatusForCancel();
 | 
						|
            serviceConnection = 0;
 | 
						|
        }
 | 
						|
        knownServices.clear();
 | 
						|
        browsing = false;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void ServiceBrowserPrivate::reconfirmService(Service::ConstPtr s)
 | 
						|
{
 | 
						|
    if (!s->outdated())
 | 
						|
        mainConnection->lib->reconfirmRecord(
 | 
						|
                    mainRef(), s->interfaceNr(), s->name().toUtf8().data(),
 | 
						|
                    s->type().toUtf8().data(), s->domain().toUtf8().data(),
 | 
						|
                    s->fullName().toUtf8().data());
 | 
						|
}
 | 
						|
 | 
						|
/// called when a service is added removed or changes. oldService or newService might be null
 | 
						|
/// (covers both add and remove)
 | 
						|
void ServiceBrowserPrivate::serviceChanged(const Service::ConstPtr &oldService,
 | 
						|
                                           const Service::ConstPtr &newService,
 | 
						|
                                           ServiceBrowser *browser)
 | 
						|
{
 | 
						|
    emit q->serviceChanged(oldService, newService, browser);
 | 
						|
}
 | 
						|
 | 
						|
/// called when a service is added (utility method)
 | 
						|
void ServiceBrowserPrivate::serviceAdded(const Service::ConstPtr &service, ServiceBrowser *browser)
 | 
						|
{
 | 
						|
    emit q->serviceAdded(service, browser);
 | 
						|
}
 | 
						|
 | 
						|
/// called when a service is removed (utility method)
 | 
						|
void ServiceBrowserPrivate::serviceRemoved(const Service::ConstPtr &service, ServiceBrowser *browser)
 | 
						|
{
 | 
						|
    emit q->serviceRemoved(service, browser);
 | 
						|
}
 | 
						|
 | 
						|
/// called when the list is updated (this might collect several serviceChanged signals together)
 | 
						|
void ServiceBrowserPrivate::servicesUpdated(ServiceBrowser *browser)
 | 
						|
{
 | 
						|
    emit q->servicesUpdated(browser);
 | 
						|
}
 | 
						|
 | 
						|
/// called to describe the current startup phase
 | 
						|
void ServiceBrowserPrivate::startupPhase(int progress, const QString &description)
 | 
						|
{
 | 
						|
    emit q->startupPhase(progress, description, q);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/// called when there is an error message
 | 
						|
void ServiceBrowserPrivate::errorMessage(ErrorMessage::SeverityLevel severity, const QString &msg)
 | 
						|
{
 | 
						|
    if (severity == ErrorMessage::FailureLevel)
 | 
						|
        this->failed = true;
 | 
						|
    emit q->errorMessage(severity, msg, q);
 | 
						|
}
 | 
						|
 | 
						|
/// called when the browsing fails
 | 
						|
void ServiceBrowserPrivate::hadFailure(const QList<ErrorMessage> &messages)
 | 
						|
{
 | 
						|
    emit q->hadFailure(messages, q);
 | 
						|
}
 | 
						|
 | 
						|
void ServiceBrowserPrivate::startedBrowsing()
 | 
						|
{
 | 
						|
    emit q->startedBrowsing(q);
 | 
						|
}
 | 
						|
 | 
						|
// ----------------- MainConnection impl -----------------
 | 
						|
 | 
						|
void MainConnection::stop(bool wait)
 | 
						|
{
 | 
						|
    {
 | 
						|
        if (QThread::currentThread() == m_thread)
 | 
						|
            qCritical() << "ERROR ZerocConf::MainConnection::stop called from m_thread";
 | 
						|
        // This will most likely lock if called from the connection thread (as mainThreadLock is non recusive)
 | 
						|
        // Making it recursive would open a hole during the startup of the thread.
 | 
						|
        // As of now this should always be called during the destruction of a browser,
 | 
						|
        // so from another thread.
 | 
						|
        increaseStatusTo(Stopping);
 | 
						|
        QMutexLocker l(mainThreadLock());
 | 
						|
        QMutexLocker l2(lock());
 | 
						|
    }
 | 
						|
    if (m_mainRef) {
 | 
						|
        lib->stopConnection(m_mainRef);
 | 
						|
        m_mainRef = 0;
 | 
						|
    }
 | 
						|
    if (!m_thread)
 | 
						|
        increaseStatusTo(Stopped);
 | 
						|
    else if (wait && QThread::currentThread() != m_thread)
 | 
						|
        m_thread->wait();
 | 
						|
}
 | 
						|
 | 
						|
MainConnection::MainConnection(ServiceBrowserPrivate *initialBrowser):
 | 
						|
    flowStatus(NormalRFS),
 | 
						|
    lib(zeroConfLibInstance()->defaultLib()), m_lock(QMutex::Recursive),
 | 
						|
    m_mainThreadLock(QMutex::NonRecursive), m_mainRef(0), m_failed(false), m_status(Starting), m_nErrs(0)
 | 
						|
{
 | 
						|
    if (initialBrowser)
 | 
						|
        addBrowser(initialBrowser);
 | 
						|
    if (lib.isNull()){
 | 
						|
        appendError(ErrorMessage::FailureLevel, tr("Zeroconf could not load a valid library, failing."));
 | 
						|
    } else {
 | 
						|
        m_thread = new ConnectionThread(*this);
 | 
						|
        m_mainThreadLock.lock();
 | 
						|
        m_thread->start(); // delay startup??
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
MainConnection::~MainConnection()
 | 
						|
{
 | 
						|
    gQuickStop = 1;
 | 
						|
    stop(true);
 | 
						|
    gQuickStop = 0;
 | 
						|
    delete m_thread;
 | 
						|
}
 | 
						|
 | 
						|
bool MainConnection::increaseStatusTo(int s)
 | 
						|
{
 | 
						|
    int sAtt = status();
 | 
						|
    while (sAtt < s){
 | 
						|
        if (m_status.testAndSetRelaxed(sAtt, s))
 | 
						|
            return true;
 | 
						|
        sAtt = status();
 | 
						|
    }
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
QMutex *MainConnection::lock()
 | 
						|
{
 | 
						|
    return &m_lock;
 | 
						|
}
 | 
						|
 | 
						|
QMutex *MainConnection::mainThreadLock()
 | 
						|
{
 | 
						|
    return &m_mainThreadLock;
 | 
						|
}
 | 
						|
 | 
						|
void MainConnection::waitStartup()
 | 
						|
{
 | 
						|
    int sAtt;
 | 
						|
    while (true){
 | 
						|
        {
 | 
						|
            QMutexLocker l(lock());
 | 
						|
            sAtt = status();
 | 
						|
            if (sAtt >= Running)
 | 
						|
                return;
 | 
						|
        }
 | 
						|
        QThread::yieldCurrentThread();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void MainConnection::addBrowser(ServiceBrowserPrivate *browser)
 | 
						|
{
 | 
						|
    int actualStatus;
 | 
						|
    QList<ErrorMessage> errs;
 | 
						|
    {
 | 
						|
        QMutexLocker l(lock());
 | 
						|
        actualStatus = status();
 | 
						|
        m_browsers.append(browser);
 | 
						|
        errs = m_errors;
 | 
						|
    }
 | 
						|
    if (actualStatus == Running && browser->internalStartBrowsing())
 | 
						|
        browser->startedBrowsing();
 | 
						|
    bool didFail = false;
 | 
						|
    foreach (const ErrorMessage &msg, errs) {
 | 
						|
        browser->errorMessage(msg.severity, msg.msg);
 | 
						|
        didFail = didFail || msg.severity == ErrorMessage::FailureLevel;
 | 
						|
    }
 | 
						|
    if (didFail)
 | 
						|
        browser->hadFailure(errs);
 | 
						|
}
 | 
						|
 | 
						|
void MainConnection::removeBrowser(ServiceBrowserPrivate *browser)
 | 
						|
{
 | 
						|
    QMutexLocker l(lock());
 | 
						|
    m_browsers.removeOne(browser);
 | 
						|
}
 | 
						|
 | 
						|
void MainConnection::updateFlowStatusForCancel(){
 | 
						|
    flowStatus = ForceUpdateRFS;
 | 
						|
}
 | 
						|
void MainConnection::updateFlowStatusForFlags(DNSServiceFlags flags)
 | 
						|
{
 | 
						|
    if (flags & kDNSServiceFlagsMoreComing) {
 | 
						|
        if (flowStatus == NormalRFS)
 | 
						|
            flowStatus = MoreComingRFS;
 | 
						|
    } else {
 | 
						|
        flowStatus = NormalRFS;
 | 
						|
    }
 | 
						|
}
 | 
						|
void MainConnection::maybeUpdateLists()
 | 
						|
{
 | 
						|
    foreach (ServiceBrowserPrivate *sb, m_browsers) {
 | 
						|
        sb->maybeUpdateLists();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void MainConnection::gotoValidLib(){
 | 
						|
    while (lib){
 | 
						|
        if (lib->isOk()) break;
 | 
						|
        appendError(ErrorMessage::WarningLevel, tr("Zeroconf giving up on non working %1 (%2).")
 | 
						|
                                .arg(lib->name()).arg(lib->errorMsg()));
 | 
						|
        lib = lib->fallbackLib;
 | 
						|
    }
 | 
						|
    if (!lib) {
 | 
						|
        appendError(ErrorMessage::FailureLevel, tr("Zeroconf has no valid library, aborting connection."));
 | 
						|
        increaseStatusTo(Stopping);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void MainConnection::abortLib(){
 | 
						|
    if (!lib){
 | 
						|
        appendError(ErrorMessage::FailureLevel, tr("Zeroconf has no valid library, aborting connection."));
 | 
						|
        increaseStatusTo(Stopping);
 | 
						|
    } else if (lib->fallbackLib){
 | 
						|
        appendError(ErrorMessage::WarningLevel, tr("Zeroconf giving up on %1, switching to %2.")
 | 
						|
                                .arg(lib->name()).arg(lib->fallbackLib->name()));
 | 
						|
        lib = lib->fallbackLib;
 | 
						|
        m_nErrs = 0;
 | 
						|
        gotoValidLib();
 | 
						|
    } else {
 | 
						|
        appendError(ErrorMessage::FailureLevel, tr("Zeroconf giving up on %1, no fallback provided, aborting connection.")
 | 
						|
                                .arg(lib->name()));
 | 
						|
        increaseStatusTo(Stopping);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void MainConnection::createConnection()
 | 
						|
{
 | 
						|
    gotoValidLib();
 | 
						|
    while (status() <= Running) {
 | 
						|
        if (!lib) {
 | 
						|
            increaseStatusTo(Stopped);
 | 
						|
            break;
 | 
						|
        }
 | 
						|
        startupPhase(m_nErrs + zeroConfLibInstance()->nFallbacksTot() - lib->nFallbacks() + 2,
 | 
						|
                     tr("Trying %1...").arg(lib->name()));
 | 
						|
        uint32_t version;
 | 
						|
        uint32_t size = (uint32_t)sizeof(uint32_t);
 | 
						|
        DNSServiceErrorType err = lib->getProperty(kDNSServiceProperty_DaemonVersion, &version, &size);
 | 
						|
        if (err == kDNSServiceErr_NoError){
 | 
						|
            DNSServiceErrorType error = lib->createConnection(this, &m_mainRef);
 | 
						|
            if (error != kDNSServiceErr_NoError){
 | 
						|
                appendError(ErrorMessage::WarningLevel, tr("Zeroconf using %1 failed the initialization of the main library connection with error %2.")
 | 
						|
                                        .arg(lib->name()).arg(error));
 | 
						|
                ++m_nErrs;
 | 
						|
                if (m_nErrs > lib->nFallbacks() || !lib->isOk())
 | 
						|
                    abortLib();
 | 
						|
            } else {
 | 
						|
                QList<ServiceBrowserPrivate *> waitingBrowsers;
 | 
						|
                {
 | 
						|
                    QMutexLocker l(lock());
 | 
						|
                    waitingBrowsers = m_browsers;
 | 
						|
                    increaseStatusTo(Running);
 | 
						|
                }
 | 
						|
                for (int i = waitingBrowsers.count(); i-- != 0; ){
 | 
						|
                    ServiceBrowserPrivate *actualBrowser = waitingBrowsers[i];
 | 
						|
                    if (actualBrowser && !actualBrowser->browsing
 | 
						|
                            && actualBrowser->internalStartBrowsing())
 | 
						|
                        actualBrowser->startedBrowsing();
 | 
						|
                }
 | 
						|
                break;
 | 
						|
            }
 | 
						|
        } else if (err == kDNSServiceErr_ServiceNotRunning) {
 | 
						|
            appendError(ErrorMessage::WarningLevel, tr("Zeroconf using %1 failed because no daemon is running.")
 | 
						|
                                    .arg(lib->name()));
 | 
						|
            ++m_nErrs;
 | 
						|
            if (m_nErrs > lib->maxErrors() || !lib->isOk()) {
 | 
						|
                abortLib();
 | 
						|
            } else if (lib->tryStartDaemon(this)) {
 | 
						|
                appendError(ErrorMessage::WarningLevel, tr("Zeroconf using %1 daemon starting seem successful, continuing.")
 | 
						|
                                        .arg(lib->name()));
 | 
						|
            } else {
 | 
						|
                appendError(ErrorMessage::WarningLevel, tr("Zeroconf using %1 failed because no daemon is running.")
 | 
						|
                                        .arg(lib->name()));
 | 
						|
                abortLib();
 | 
						|
            }
 | 
						|
        } else {
 | 
						|
            appendError(ErrorMessage::WarningLevel, tr("Zeroconf using %1 failed getProperty call with error %2.")
 | 
						|
                                    .arg(lib->name()).arg(err));
 | 
						|
            abortLib();
 | 
						|
        }
 | 
						|
    }
 | 
						|
    if (status() < Stopping) {
 | 
						|
        startupPhase(zeroConfLibInstance()->nFallbacksTot() + 3, tr("Succeded using %1.").arg(lib->name()));
 | 
						|
        appendError(ErrorMessage::NoteLevel,
 | 
						|
                    tr("MainConnection could successfully create a connection using %1.")
 | 
						|
                    .arg(lib->name()));
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
ZConfLib::RunLoopStatus MainConnection::handleEvent()
 | 
						|
{
 | 
						|
    qint64 now = QDateTime::currentMSecsSinceEpoch();
 | 
						|
    qint64 nextEvent = now; // worth keeping a heap to quickly calculate this?
 | 
						|
    foreach (ServiceBrowserPrivate *bAtt, m_browsers) {
 | 
						|
        if (nextEvent < bAtt->delayDeletesUntil)
 | 
						|
            nextEvent = bAtt->delayDeletesUntil;
 | 
						|
    }
 | 
						|
    if (nextEvent <= now)
 | 
						|
        nextEvent = -1;
 | 
						|
    else
 | 
						|
        nextEvent -= now;
 | 
						|
    maybeUpdateLists();
 | 
						|
    ZConfLib::RunLoopStatus err = lib->processOneEvent(this, m_mainRef, nextEvent);
 | 
						|
    if (err != ZConfLib::ProcessedOk && err != ZConfLib::ProcessedIdle) {
 | 
						|
        qDebug() << "processOneEvent returned " << err;
 | 
						|
        ++m_nErrs;
 | 
						|
    } else {
 | 
						|
        m_nErrs = 0;
 | 
						|
    }
 | 
						|
    return err;
 | 
						|
}
 | 
						|
 | 
						|
void MainConnection::destroyConnection()
 | 
						|
{
 | 
						|
    // multithreading issues if we support multiple cycles of createConnection/destroyConnection
 | 
						|
    for (int i = m_browsers.count(); i-- != 0;){
 | 
						|
        ServiceBrowserPrivate *bAtt = m_browsers[i];
 | 
						|
        if (bAtt->browsing)
 | 
						|
            bAtt->stopBrowsing();
 | 
						|
    }
 | 
						|
    if (m_mainRef != 0)
 | 
						|
        lib->destroyConnection(&m_mainRef);
 | 
						|
    m_mainRef = 0;
 | 
						|
}
 | 
						|
 | 
						|
void  MainConnection::handleEvents()
 | 
						|
{
 | 
						|
    try {
 | 
						|
        if (!m_status.testAndSetAcquire(Starting, Started)){
 | 
						|
            appendError(ErrorMessage::WarningLevel, tr("Zeroconf, unexpected start status, aborting."));
 | 
						|
            increaseStatusTo(Stopped);
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        m_nErrs = 0;
 | 
						|
        createConnection();
 | 
						|
    } catch(...) {
 | 
						|
        mainThreadLock()->unlock();
 | 
						|
        throw;
 | 
						|
    }
 | 
						|
    mainThreadLock()->unlock();
 | 
						|
    increaseStatusTo(Running);
 | 
						|
    while (status() < Stopping) {
 | 
						|
        QMutexLocker l(mainThreadLock());
 | 
						|
        if (m_nErrs > 10)
 | 
						|
            increaseStatusTo(Stopping);
 | 
						|
        switch (handleEvent()) {
 | 
						|
        case ZConfLib::ProcessedOk :
 | 
						|
        case ZConfLib::ProcessedIdle :
 | 
						|
            break;
 | 
						|
        case ZConfLib::ProcessedError :
 | 
						|
            ++m_nErrs;
 | 
						|
            break;
 | 
						|
        case ZConfLib::ProcessedFailure :
 | 
						|
            increaseStatusTo(Stopping);
 | 
						|
            ++m_nErrs;
 | 
						|
            break;
 | 
						|
        case ZConfLib::ProcessedQuit :
 | 
						|
            increaseStatusTo(Stopping);
 | 
						|
            break;
 | 
						|
        default:
 | 
						|
            appendError(ErrorMessage::FailureLevel, tr("Zeroconf detected an unexpected return status of handleEvent."));
 | 
						|
            increaseStatusTo(Stopping);
 | 
						|
            break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    destroyConnection();
 | 
						|
    if (m_nErrs > 0){
 | 
						|
        QString browsersNames = (m_browsers.isEmpty() ? QString() : m_browsers.at(0)->serviceType)
 | 
						|
                + ((m_browsers.count() > 1) ? QString::fromLatin1(",...") : QString());
 | 
						|
        if (isOk())
 | 
						|
            appendError(ErrorMessage::FailureLevel,
 | 
						|
                        tr("Zeroconf for [%1] accumulated %n consecutive errors, aborting.", 0, m_nErrs)
 | 
						|
                            .arg(browsersNames));
 | 
						|
    }
 | 
						|
    increaseStatusTo(Stopped);
 | 
						|
}
 | 
						|
 | 
						|
ZConfLib::ConnectionRef MainConnection::mainRef()
 | 
						|
{
 | 
						|
    while (status() < Running){
 | 
						|
        QThread::yieldCurrentThread();
 | 
						|
    }
 | 
						|
    return m_mainRef;
 | 
						|
}
 | 
						|
 | 
						|
MainConnection::Status MainConnection::status()
 | 
						|
{
 | 
						|
#if QT_VERSION >= 0x050000
 | 
						|
    return static_cast<MainConnection::Status>(m_status.load());
 | 
						|
#else
 | 
						|
    int val = m_status;
 | 
						|
    return static_cast<MainConnection::Status>(val);
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
QList<ErrorMessage> MainConnection::errors()
 | 
						|
{
 | 
						|
    QMutexLocker l(lock());
 | 
						|
    return m_errors;
 | 
						|
}
 | 
						|
 | 
						|
void MainConnection::appendError(ErrorMessage::SeverityLevel severity, const QString &msg)
 | 
						|
{
 | 
						|
    QList<ServiceBrowserPrivate *> browsersAtt;
 | 
						|
    QList<ErrorMessage> errs;
 | 
						|
    {
 | 
						|
        QMutexLocker l(lock());
 | 
						|
        m_errors.append(ErrorMessage(severity, msg));
 | 
						|
        errs = m_errors;
 | 
						|
        browsersAtt = m_browsers;
 | 
						|
        m_failed = severity == ErrorMessage::FailureLevel || m_failed;
 | 
						|
    }
 | 
						|
    foreach (ServiceBrowserPrivate *b, browsersAtt) {
 | 
						|
        b->errorMessage(severity, msg);
 | 
						|
        if (severity == ErrorMessage::FailureLevel)
 | 
						|
            b->hadFailure(errs);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void MainConnection::startupPhase(int progress, const QString &description)
 | 
						|
{
 | 
						|
    QList<ServiceBrowserPrivate *> browsersAtt;
 | 
						|
    {
 | 
						|
        QMutexLocker l(lock());
 | 
						|
        browsersAtt = m_browsers;
 | 
						|
    }
 | 
						|
    foreach (ServiceBrowserPrivate *b, browsersAtt)
 | 
						|
        b->startupPhase(progress, description);
 | 
						|
}
 | 
						|
 | 
						|
bool MainConnection::isOk()
 | 
						|
{
 | 
						|
    return !m_failed;
 | 
						|
}
 | 
						|
 | 
						|
// ----------------- ZConfLib impl -----------------
 | 
						|
 | 
						|
bool ZConfLib::tryStartDaemon(ErrorMessage::ErrorLogger * /* logger */)
 | 
						|
{
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
QString ZConfLib::name(){
 | 
						|
    return QString::fromLatin1("ZeroConfLib@%1").arg(size_t(this), 0, 16);
 | 
						|
}
 | 
						|
 | 
						|
ZConfLib::ZConfLib(ZConfLib::Ptr f) : fallbackLib(f), m_isOk(true), m_maxErrors(4)
 | 
						|
{ }
 | 
						|
 | 
						|
ZConfLib::~ZConfLib()
 | 
						|
{ }
 | 
						|
 | 
						|
bool ZConfLib::isOk()
 | 
						|
{
 | 
						|
    return m_isOk;
 | 
						|
}
 | 
						|
 | 
						|
QString ZConfLib::errorMsg()
 | 
						|
{
 | 
						|
    return m_errorMsg;
 | 
						|
}
 | 
						|
 | 
						|
void ZConfLib::setError(bool failure, const QString &eMsg)
 | 
						|
{
 | 
						|
    m_errorMsg = eMsg;
 | 
						|
    m_isOk = !failure;
 | 
						|
}
 | 
						|
 | 
						|
int ZConfLib::maxErrors() const
 | 
						|
{
 | 
						|
    return m_maxErrors;
 | 
						|
}
 | 
						|
 | 
						|
int ZConfLib::nFallbacks() const
 | 
						|
{
 | 
						|
    return (m_isOk ? ((m_maxErrors > 0) ? m_maxErrors : 1) : 0)
 | 
						|
            + (fallbackLib ? fallbackLib->nFallbacks() : 0);
 | 
						|
}
 | 
						|
 | 
						|
ZConfLib::RunLoopStatus ZConfLib::processOneEvent(MainConnection *mainConnection,
 | 
						|
                                                  ConnectionRef cRef, qint64 maxMsBlock)
 | 
						|
{
 | 
						|
    if (maxMsBlock < 0) { // just block
 | 
						|
        maxMsBlock = MAX_SEC_FOR_READ * static_cast<qint64>(1000);
 | 
						|
    }
 | 
						|
    // some stuff could be extracted for maximal performance
 | 
						|
    int dns_sd_fd  = (cRef ? refSockFD(cRef) : -1);
 | 
						|
    int nfds = dns_sd_fd + 1;
 | 
						|
    fd_set readfds;
 | 
						|
    struct timeval tv;
 | 
						|
    int result;
 | 
						|
    dns_sd_fd  = (cRef ? refSockFD(cRef) : -1);
 | 
						|
    if (dns_sd_fd < 0)
 | 
						|
        return ProcessedError;
 | 
						|
    nfds = dns_sd_fd + 1;
 | 
						|
    FD_ZERO(&readfds);
 | 
						|
    FD_SET(dns_sd_fd, &readfds);
 | 
						|
 | 
						|
    if (maxMsBlock > MAX_SEC_FOR_READ * static_cast<qint64>(1000)) {
 | 
						|
        tv.tv_sec = MAX_SEC_FOR_READ;
 | 
						|
        tv.tv_usec = 0;
 | 
						|
    } else {
 | 
						|
        tv.tv_sec = static_cast<time_t>(maxMsBlock / 1000);
 | 
						|
        tv.tv_usec = static_cast<suseconds_t>((maxMsBlock % 1000) * 1000);
 | 
						|
    }
 | 
						|
    QMutex *lock = (mainConnection ? mainConnection->mainThreadLock() : 0);
 | 
						|
    if (lock)
 | 
						|
        lock->unlock();
 | 
						|
    result = select(nfds, &readfds, (fd_set *)NULL, (fd_set *)NULL, &tv);
 | 
						|
    if (lock)
 | 
						|
        lock->lock();
 | 
						|
    if (result > 0) {
 | 
						|
        if (FD_ISSET(dns_sd_fd, &readfds))
 | 
						|
            return processOneEventBlock(cRef);
 | 
						|
    } else if (result == 0) {
 | 
						|
        return ProcessedIdle;
 | 
						|
    } else if (errno != EINTR) {
 | 
						|
        if (DEBUG_ZEROCONF)
 | 
						|
            qDebug() << "select() returned " << result << " errno " << errno
 | 
						|
                     << strerror(errno);
 | 
						|
        return ProcessedError;
 | 
						|
    }
 | 
						|
    return ProcessedIdle; // change? should never happen anyway
 | 
						|
}
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
int ServiceBrowser::maxProgress() const
 | 
						|
{
 | 
						|
    return zeroConfLibInstance()->nFallbacksTot() + 3;
 | 
						|
}
 | 
						|
 | 
						|
// namespace Internal
 | 
						|
} // namespace ZeroConf
 |