This repository has been archived on 2026-05-31. You can view files and clone it, but cannot push or open issues or pull requests.
powersync-core/libguh/network/avahi/zconfservicebrowser.cpp
Simon Stürz ca34b7247d add missing license
prepare browsing for plugins
2019-04-01 20:48:17 +02:00

236 lines
9.6 KiB
C++

/*
* This file is part of qtzeroconf. (c) 2012 Johannes Hilden
* https://github.com/johanneshilden/qtzeroconf
*
* Modified: (C) 2016 Simon Stürz <stuerz.simon@gmail.com>
*
* qtzeroconf is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* qtzeroconf is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
* Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with qtzeroconf; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
#include <QHash>
#include <QStringBuilder>
#include <QDebug>
#include <avahi-common/error.h>
#include "zconfservicebrowser.h"
#include "zconfserviceclient.h"
class ZConfServiceBrowserPrivate
{
public:
ZConfServiceBrowserPrivate(ZConfServiceClient *client)
: client(client), browser(0)
{
}
static void callback(AvahiServiceBrowser *browser,
AvahiIfIndex interface,
AvahiProtocol protocol,
AvahiBrowserEvent event,
const char *name,
const char *type,
const char *domain,
AvahiLookupResultFlags flags,
void *userdata)
{
Q_UNUSED(browser);
Q_UNUSED(flags);
ZConfServiceBrowser *serviceBrowser = static_cast<ZConfServiceBrowser *>(userdata);
if (serviceBrowser) {
switch (event) {
case AVAHI_BROWSER_FAILURE:
qDebug() << ("Avahi browser error: " % QString(avahi_strerror(avahi_client_errno(serviceBrowser->d_ptr->client->client))));
emit serviceBrowser->serviceBrowsingFinished();
break;
case AVAHI_BROWSER_NEW:
qDebug() << ("New service '" % QString(name) % "' of type " % QString(type) % " in domain " % QString(domain) % ".");
// We ignore the returned resolver object. In the callback
// function we free it. If the server is terminated before
// the callback function is called the server will free
// the resolver for us.
if (!(avahi_service_resolver_new(serviceBrowser->d_ptr->client->client,
interface,
protocol,
name,
serviceBrowser->d_ptr->type.toLatin1().data(),
domain,
AVAHI_PROTO_UNSPEC,
(AvahiLookupFlags) 0,
ZConfServiceBrowserPrivate::resolve,
serviceBrowser)))
qDebug() << ("Failed to resolve service '" % QString(name) % "': " % avahi_strerror(avahi_client_errno(serviceBrowser->d_ptr->client->client)));
break;
case AVAHI_BROWSER_REMOVE:
serviceBrowser->d_ptr->entries.remove(name);
emit serviceBrowser->serviceEntryRemoved(name);
break;
case AVAHI_BROWSER_ALL_FOR_NOW:
emit serviceBrowser->serviceBrowsingFinished();
break;
case AVAHI_BROWSER_CACHE_EXHAUSTED:
break;
}
}
}
static void resolve(AvahiServiceResolver *resolver,
AvahiIfIndex interface,
AvahiProtocol protocol,
AvahiResolverEvent event,
const char *name,
const char *type,
const char *domain,
const char *host_name,
const AvahiAddress *address,
uint16_t port,
AvahiStringList *txt,
AvahiLookupResultFlags flags,
void *userdata)
{
Q_UNUSED(interface);
Q_UNUSED(type);
Q_UNUSED(txt);
ZConfServiceBrowser *serviceBrowser = static_cast<ZConfServiceBrowser *>(userdata);
if (serviceBrowser) {
switch (event) {
case AVAHI_RESOLVER_FAILURE:
qDebug() << ("Failed to resolve service '" % QString(name) % "': " % avahi_strerror(avahi_client_errno(serviceBrowser->d_ptr->client->client)));
break;
case AVAHI_RESOLVER_FOUND:
{
char a[AVAHI_ADDRESS_STR_MAX];
avahi_address_snprint(a, sizeof(a), address);
// convert protocol
QAbstractSocket::NetworkLayerProtocol p;
if (protocol == AVAHI_PROTO_INET) {
p = QAbstractSocket::IPv4Protocol;
} else if (protocol == AVAHI_PROTO_INET6) {
p = QAbstractSocket::IPv6Protocol;
} else {
p = QAbstractSocket::UnknownNetworkLayerProtocol;
}
QStringList txtList;
// TODO: get txt string list
// // get txt string list
// if (txt) {
// AvahiStringList *txtAtt = txt->text;
// txtAtt = (txt ? txt->next : 0);
// while (txtAtt) {
// qDebug() << txt;
// txtList.append(QString(txt->));
// }
// }
AvahiServiceEntry entry = AvahiServiceEntry(name, QHostAddress(QString(a)), QString(domain), QString(host_name), (quint16)port, p, txtList, flags);
serviceBrowser->d_ptr->entries.insert(name, entry);
emit serviceBrowser->serviceEntryAdded(name);
}
}
avahi_service_resolver_free(resolver);
}
}
typedef QHash<QString, AvahiServiceEntry> ZConfServiceEntryTable;
ZConfServiceClient *const client;
AvahiServiceBrowser *browser;
ZConfServiceEntryTable entries;
QString type;
};
/*!
\class ZConfServiceBrowser
\ingroup hardware
\inmodule libguh
\brief AvahiServiceBrowser wrapper that lets you browse for services
available on the local network. This class can be used to handle Zeroconf
service discovery in a Qt-based client application.
Instantiate a ZConfServiceBrowser object and call browse() with the desired
service type as argument (e.g., "_http._tcp" or "_ipp._tcp").
ZConfServiceBrowser will emit serviceEntryAdded() when a new service is
discovered and serviceEntryRemoved() when a service is removed from the
network.
*/
/*! \fn void ZConfServiceBrowser::serviceEntryAdded(const QString &name);
This signal will be emited when a new service entry with the given \a name was added in the network.
*/
/*! \fn void ZConfServiceBrowser::serviceEntryRemoved(const QString &name);
This signal will be emited when a new service entry with the given \a name was removed from the network.
*/
/*! Creates a Zeroconf service browser with the given \a parent. Call browse() to start browsing for services. */
ZConfServiceBrowser::ZConfServiceBrowser(QObject *parent)
: QObject(parent),
d_ptr(new ZConfServiceBrowserPrivate(new ZConfServiceClient(this)))
{
connect(d_ptr->client, SIGNAL(clientRunning()), this, SLOT(createServiceBrowser()));
}
/*! Destroys the browser object and releases all resources associated with it. */
ZConfServiceBrowser::~ZConfServiceBrowser()
{
if (d_ptr->browser)
avahi_service_browser_free(d_ptr->browser);
delete d_ptr;
}
/*!
Browses for Zeroconf services of the given \a serviceType on the LAN. This is a non-blocking call.
ZConfServiceBrowser will emit serviceEntryAdded() when a new service is
discovered and serviceEntryRemoved() when a service is removed from the
network.
*/
void ZConfServiceBrowser::browse(QString serviceType)
{
d_ptr->type = serviceType;
if(d_ptr->client)
d_ptr->client->run();
}
/*!
Returns a ZConfServiceEntry struct with detailed information about the
Zeroconf service associated with the \a name.
*/
AvahiServiceEntry ZConfServiceBrowser::serviceEntry(QString name)
{
return d_ptr->entries.value(name);
}
void ZConfServiceBrowser::createServiceBrowser()
{
if (d_ptr->browser)
return;
d_ptr->browser = avahi_service_browser_new(d_ptr->client->client,
AVAHI_IF_UNSPEC,
AVAHI_PROTO_UNSPEC,
d_ptr->type.toLatin1().data(),
NULL,
(AvahiLookupFlags) 0,
ZConfServiceBrowserPrivate::callback,
this);
}