switch to a different avahi lib which also works on android

This commit is contained in:
Michael Zanetti 2018-06-01 01:55:44 +02:00
parent 83a7814071
commit 348f15ac48
24 changed files with 84 additions and 1628 deletions

2
.gitignore vendored
View File

@ -71,3 +71,5 @@ Thumbs.db
*.dll
*.exe
# Android generated files
local.properties

6
.gitmodules vendored Normal file
View File

@ -0,0 +1,6 @@
[submodule "-f"]
path = -f
url = https://github.com/mzanetti/QtZeroConf.git
[submodule "QtZeroConf"]
path = QtZeroConf
url = https://github.com/jbagg/QtZeroConf.git

1
QtZeroConf Submodule

@ -0,0 +1 @@
Subproject commit 31119114cec29b890bc755bed0272a208b7130bf

View File

@ -7,6 +7,11 @@ Required packages:
It is recommended to install a complete Qt installation. Minimum required Version 5.7.
No extra modules are required for a basic desktop build.
After cloning the repository, run
$ git submodule init
$ git submodule update
To build a binary run
$ mkdir builddir
$ cd builddir
@ -16,8 +21,6 @@ $ make
Or open mea.pro in QtCreator and click the "Play" button.
Optional configuration flags to be passed to qmake:
- CONFIG+=withavahi
Enables avahi support. Requires libavahi-common and libavahi-client to be present on the system
- CONFIG+=withtests
Enables building the testrunner target

View File

@ -1,171 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2016 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of mea. *
* *
* This library 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. *
* *
* This library 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 this library; If not, see *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "avahiserviceentry.h"
/*! Constructs an empty invalid \l{AvahiServiceEntry}*/
AvahiServiceEntry::AvahiServiceEntry() :
m_port(0),
m_protocol(QAbstractSocket::UnknownNetworkLayerProtocol)
{
}
/*! Constructs a new \l{AvahiServiceEntry} with the given \a name, \a serviceType, \a hostAddress, \a domain, \a hostName, \a port, \a protocol, \a txt and \a flags.*/
AvahiServiceEntry::AvahiServiceEntry(QString name, QString serviceType, QHostAddress hostAddress, QString domain, QString hostName, quint16 port, QAbstractSocket::NetworkLayerProtocol protocol, QStringList txt, AvahiLookupResultFlags flags) :
m_name(name),
m_serviceType(serviceType),
m_hostAddress(hostAddress),
m_domain(domain),
m_hostName(hostName),
m_port(port),
m_protocol(protocol),
m_txt(txt),
m_flags(flags)
{
}
/*! Returns the name of this \l{AvahiServiceEntry}.*/
QString AvahiServiceEntry::name() const
{
return m_name;
}
/*! Returns the service type of this \l{AvahiServiceEntry}.*/
QString AvahiServiceEntry::serviceType() const
{
return m_serviceType;
}
/*! Returns the host address of this \l{AvahiServiceEntry}.*/
QHostAddress AvahiServiceEntry::hostAddress() const
{
return m_hostAddress;
}
/*! Returns the domain of this \l{AvahiServiceEntry}.*/
QString AvahiServiceEntry::domain() const
{
return m_domain;
}
/*! Returns the host name of this \l{AvahiServiceEntry}.*/
QString AvahiServiceEntry::hostName() const
{
return m_hostName;
}
/*! Returns the port of this \l{AvahiServiceEntry}.*/
quint16 AvahiServiceEntry::port() const
{
return m_port;
}
/*! Returns the network protocol of this \l{AvahiServiceEntry}.*/
QAbstractSocket::NetworkLayerProtocol AvahiServiceEntry::protocol() const
{
return m_protocol;
}
/*! Returns the avahi flags of this \l{AvahiServiceEntry}.*/
AvahiLookupResultFlags AvahiServiceEntry::flags() const
{
return m_flags;
}
/*! Returns the txt string list of this \l{AvahiServiceEntry}.*/
QStringList AvahiServiceEntry::txt() const
{
return m_txt;
}
/*! Returns true if this \l{AvahiServiceEntry} is valid.*/
bool AvahiServiceEntry::isValid() const
{
return !m_hostAddress.isNull() && !m_hostName.isEmpty() && m_port != 0 && m_protocol != QAbstractSocket::UnknownNetworkLayerProtocol;
}
/*! Returns true if this \l{AvahiServiceEntry} is cached.*/
bool AvahiServiceEntry::isChached() const
{
return m_flags & AVAHI_LOOKUP_RESULT_CACHED;
}
/*! Returns true if this \l{AvahiServiceEntry} was found in the wide area.*/
bool AvahiServiceEntry::isWideArea() const
{
return m_flags & AVAHI_LOOKUP_RESULT_WIDE_AREA;
}
/*! Returns true if this \l{AvahiServiceEntry} is a multicast service.*/
bool AvahiServiceEntry::isMulticast() const
{
return m_flags & AVAHI_LOOKUP_RESULT_MULTICAST;
}
/*! Returns true if this \l{AvahiServiceEntry} was found local.*/
bool AvahiServiceEntry::isLocal() const
{
return m_flags & AVAHI_LOOKUP_RESULT_LOCAL;
}
/*! Returns true if this \l{AvahiServiceEntry} is our own service.*/
bool AvahiServiceEntry::isOurOwn() const
{
return m_flags & AVAHI_LOOKUP_RESULT_OUR_OWN;
}
/*! Returns true if this \l{AvahiServiceEntry} is equal to \a other; otherwise returns false.*/
bool AvahiServiceEntry::operator ==(const AvahiServiceEntry &other) const
{
return other.name() == m_name &&
other.serviceType() == m_serviceType &&
other.hostAddress() == m_hostAddress &&
other.domain() == m_domain &&
other.hostName() == m_hostName &&
other.port() == m_port &&
other.protocol() == m_protocol &&
other.flags() == m_flags &&
other.txt() == m_txt;
}
/*! Returns true if this \l{AvahiServiceEntry} is not equal to \a other; otherwise returns false.*/
bool AvahiServiceEntry::operator !=(const AvahiServiceEntry &other) const
{
return !operator==(other);
}
/*! Writes the given \a entry to the specified \a dbg.*/
QDebug operator <<(QDebug dbg, const AvahiServiceEntry &entry)
{
dbg.nospace() << "AvahiServiceEntry(";
dbg << entry.name() << ")" << endl;
dbg << " location: " << entry.hostAddress().toString() << ":" << entry.port() << endl;
dbg << " hostname: " << entry.hostName() << endl;
dbg << " domain: " << entry.domain() << endl;
dbg << "service type: " << entry.serviceType() << endl;
dbg << " protocol: " << entry.protocol() << endl;
dbg << " txt: " << entry.txt() << endl;
return dbg;
}

View File

@ -1,75 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2016 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of mea. *
* *
* This library 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. *
* *
* This library 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 this library; If not, see *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef AVAHISERVICEENTRY_H
#define AVAHISERVICEENTRY_H
#include <QObject>
#include <QString>
#include <QHostAddress>
#include <QAbstractSocket>
#include <avahi-client/publish.h>
class AvahiServiceEntry
{
public:
AvahiServiceEntry();
AvahiServiceEntry(QString name, QString serviceType, QHostAddress hostAddress, QString domain, QString hostName, quint16 port, QAbstractSocket::NetworkLayerProtocol protocol, QStringList txt, AvahiLookupResultFlags flags);
QString name() const;
QString serviceType() const;
QHostAddress hostAddress() const;
QString domain() const;
QString hostName() const;
quint16 port() const;
QAbstractSocket::NetworkLayerProtocol protocol() const;
AvahiLookupResultFlags flags() const;
QStringList txt() const;
bool isValid() const;
bool isChached() const;
bool isWideArea() const;
bool isMulticast() const;
bool isLocal() const;
bool isOurOwn() const;
bool operator ==(const AvahiServiceEntry &other) const;
bool operator !=(const AvahiServiceEntry &other) const;
private:
QString m_name;
QString m_serviceType;
QHostAddress m_hostAddress;
QString m_domain;
QString m_hostName;
quint16 m_port;
QAbstractSocket::NetworkLayerProtocol m_protocol;
QStringList m_txt;
AvahiLookupResultFlags m_flags;
};
QDebug operator <<(QDebug dbg, const AvahiServiceEntry &entry);
Q_DECLARE_METATYPE(AvahiServiceEntry)
#endif // AVAHISERVICEENTRY_H

View File

@ -1,193 +0,0 @@
/***
This file is part of avahi.
avahi 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.
avahi 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 avahi; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
Modified: (C) 2016 Simon Stürz <stuerz.simon@gmail.com>
***/
#include <QObject>
#include <QTimer>
#include <QSocketNotifier>
#include <sys/time.h>
#include <avahi-common/timeval.h>
#include "qt-watch.h"
class AvahiWatch : public QObject
{
Q_OBJECT
public:
AvahiWatch(int fd, AvahiWatchEvent event, AvahiWatchCallback callback, void* userdata);
~AvahiWatch() { }
AvahiWatchEvent getEvents() const { return m_incallback ? m_lastEvent : (AvahiWatchEvent)0; }
void setWatchedEvents(AvahiWatchEvent event);
private slots:
void gotIn();
void gotOut();
private:
QSocketNotifier* m_in;
QSocketNotifier* m_out;
AvahiWatchCallback m_callback;
AvahiWatchEvent m_lastEvent;
int m_fd;
void* m_userdata;
bool m_incallback;
};
class AvahiTimeout : public QObject
{
Q_OBJECT
public:
AvahiTimeout(const struct timeval* tv, AvahiTimeoutCallback callback, void* userdata);
~AvahiTimeout() {}
void update(const struct timeval* tv);
private slots:
void timeout();
private:
QTimer m_timer;
AvahiTimeoutCallback m_callback;
void* m_userdata;
};
AvahiWatch::AvahiWatch(int fd, AvahiWatchEvent event, AvahiWatchCallback callback, void* userdata) :
m_in(0),
m_out(0),
m_callback(callback),
m_fd(fd),
m_userdata(userdata),
m_incallback(false)
{
setWatchedEvents(event);
}
void AvahiWatch::gotIn()
{
m_lastEvent = AVAHI_WATCH_IN;
m_incallback = true;
m_callback(this, m_fd, m_lastEvent, m_userdata);
m_incallback = false;
}
void AvahiWatch::gotOut()
{
m_lastEvent = AVAHI_WATCH_OUT;
m_incallback = true;
m_callback(this, m_fd, m_lastEvent, m_userdata);
m_incallback = false;
}
void AvahiWatch::setWatchedEvents(AvahiWatchEvent event)
{
if (!(event & AVAHI_WATCH_IN)) { delete m_in; m_in = 0; }
if (!(event & AVAHI_WATCH_OUT)) { delete m_out; m_out = 0; }
if (event & AVAHI_WATCH_IN) {
m_in = new QSocketNotifier(m_fd,QSocketNotifier::Read, this);
connect(m_in, SIGNAL(activated(int)), SLOT(gotIn()));
}
if (event & AVAHI_WATCH_OUT) {
m_out = new QSocketNotifier(m_fd, QSocketNotifier::Write, this);
connect(m_out, SIGNAL(activated(int)), SLOT(gotOut()));
}
}
AvahiTimeout::AvahiTimeout(const struct timeval* tv, AvahiTimeoutCallback callback, void *userdata) :
m_callback(callback),
m_userdata(userdata)
{
connect(&m_timer, SIGNAL(timeout()), this, SLOT(timeout()));
m_timer.setSingleShot(true);
update(tv);
}
void AvahiTimeout::update(const struct timeval *tv)
{
m_timer.stop();
if (tv) {
AvahiUsec u = avahi_age(tv)/1000;
m_timer.start( (u>0) ? 0 : -u);
}
}
void AvahiTimeout::timeout()
{
m_callback(this, m_userdata);
}
static AvahiWatch* q_watch_new(const AvahiPoll *api, int fd, AvahiWatchEvent event, AvahiWatchCallback callback, void *userdata)
{
Q_UNUSED(api)
return new AvahiWatch(fd, event, callback, userdata);
}
static void q_watch_update(AvahiWatch *w, AvahiWatchEvent events)
{
w->setWatchedEvents(events);
}
static AvahiWatchEvent q_watch_get_events(AvahiWatch *w)
{
return w->getEvents();
}
static void q_watch_free(AvahiWatch *w)
{
delete w;
}
static AvahiTimeout* q_timeout_new(const AvahiPoll *api, const struct timeval *tv, AvahiTimeoutCallback callback, void *userdata)
{
Q_UNUSED(api)
return new AvahiTimeout(tv, callback, userdata);
}
static void q_timeout_update(AvahiTimeout *t, const struct timeval *tv)
{
t->update(tv);
}
static void q_timeout_free(AvahiTimeout *t)
{
delete t;
}
const AvahiPoll* avahi_qt_poll_get(void)
{
static const AvahiPoll qt_poll = {
NULL,
q_watch_new,
q_watch_update,
q_watch_get_events,
q_watch_free,
q_timeout_new,
q_timeout_update,
q_timeout_free
};
return &qt_poll;
}
#include "qt-watch.moc"

View File

@ -1,35 +0,0 @@
#ifndef QAVAHI_H
#define QAVAHI_H
/***
This file is part of avahi.
avahi 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.
avahi 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 avahi; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
Modified: (C) 2016 Simon Stürz <stuerz.simon@gmail.com>
***/
/** \file qt-watch.h Qt main loop adapter */
#include <avahi-common/watch.h>
AVAHI_C_DECL_BEGIN
/** Setup abstract poll structure for integration with Qt main loop */
const AvahiPoll* avahi_qt_poll_get(void);
AVAHI_C_DECL_END
#endif // QAVAHI_H

View File

@ -1,121 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2016 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of mea. *
* *
* This library 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. *
* *
* This library 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 this library; If not, see *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "qt-watch.h"
#include "qtavahiclient.h"
#include <avahi-common/error.h>
QtAvahiClient::QtAvahiClient(QObject *parent) :
QObject(parent),
poll(avahi_qt_poll_get()),
client(0),
error(0),
m_state(QtAvahiClientStateNone)
{
connect(this, &QtAvahiClient::clientStateChangedInternal, this, &QtAvahiClient::onClientStateChanged);
}
QtAvahiClient::~QtAvahiClient()
{
if (client)
avahi_client_free(client);
}
QtAvahiClient::QtAvahiClientState QtAvahiClient::state() const
{
return m_state;
}
void QtAvahiClient::start()
{
if (client)
return;
avahi_client_new(poll, (AvahiClientFlags) 0, QtAvahiClient::callback, this, &error);
}
QString QtAvahiClient::errorString() const
{
return QString(avahi_strerror(error));
}
void QtAvahiClient::callback(AvahiClient *client, AvahiClientState state, void *userdata)
{
QtAvahiClient *serviceClient = static_cast<QtAvahiClient *>(userdata);
if (!serviceClient)
return;
serviceClient->client = client;
switch (state) {
case AVAHI_CLIENT_S_RUNNING:
emit serviceClient->clientStateChangedInternal(QtAvahiClientStateRunning);
break;
case AVAHI_CLIENT_FAILURE:
emit serviceClient->clientStateChangedInternal(QtAvahiClientStateFailure);
break;
case AVAHI_CLIENT_S_COLLISION:
emit serviceClient->clientStateChangedInternal(QtAvahiClientStateCollision);
break;
case AVAHI_CLIENT_S_REGISTERING:
emit serviceClient->clientStateChangedInternal(QtAvahiClientStateRegistering);
break;
case AVAHI_CLIENT_CONNECTING:
emit serviceClient->clientStateChangedInternal(QtAvahiClientStateConnecting);
break;
}
}
void QtAvahiClient::onClientStateChanged(const QtAvahiClient::QtAvahiClientState &state)
{
if (m_state == state)
return;
m_state = state;
// switch (m_state) {
// case QtAvahiClientStateNone:
// break;
// case QtAvahiClientStateRunning:
// qCDebug(dcAvahi()) << "Client running.";
// break;
// case QtAvahiClientStateFailure:
// qCWarning(dcAvahi()) << "Client failure:" << errorString();
// break;
// case QtAvahiClientStateCollision:
// qCWarning(dcAvahi()) << "Client collision:" << errorString();
// break;
// case QtAvahiClientStateRegistering:
// qCDebug(dcAvahi()) << "Client registering...";
// break;
// case QtAvahiClientStateConnecting:
// qCDebug(dcAvahi()) << "Client connecting...";
// break;
// default:
// break;
// }
emit clientStateChanged(m_state);
}

View File

@ -1,73 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2016 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of mea. *
* *
* This library 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. *
* *
* This library 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 this library; If not, see *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef QTAVAHICLIENT_H
#define QTAVAHICLIENT_H
#include <QObject>
#include <avahi-client/client.h>
class QtAvahiClient : public QObject
{
Q_OBJECT
Q_ENUMS(QtAvahiClientState)
public:
enum QtAvahiClientState {
QtAvahiClientStateNone,
QtAvahiClientStateRunning,
QtAvahiClientStateFailure,
QtAvahiClientStateCollision,
QtAvahiClientStateRegistering,
QtAvahiClientStateConnecting
};
explicit QtAvahiClient(QObject *parent = 0);
~QtAvahiClient();
QtAvahiClientState state() const;
private:
friend class QtAvahiService;
friend class QtAvahiServiceBrowser;
friend class QtAvahiServiceBrowserPrivate;
const AvahiPoll *poll;
AvahiClient *client;
int error;
QtAvahiClientState m_state;
void start();
QString errorString() const;
static void callback(AvahiClient *client, AvahiClientState state, void *userdata);
private slots:
void onClientStateChanged(const QtAvahiClientState &state);
signals:
void clientStateChanged(const QtAvahiClientState &state);
void clientStateChangedInternal(const QtAvahiClientState &state);
};
#endif // QTAVAHICLIENT_H

View File

@ -1,229 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2016 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of mea. *
* *
* This library 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. *
* *
* This library 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 this library; If not, see *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*! \enum QtAvahiService::QtAvahiServiceState
This enum type specifies the state of a \l{QtAvahiService}.
\value QtAvahiServiceStateUncomitted
The group has not yet been commited, the user must still call avahi_entry_group_commit().
\value QtAvahiServiceStateRegistering
The entries of the group are currently being registered.
\value QtAvahiServiceStateEstablished
The entries have successfully been established.
\value QtAvahiServiceStateCollision
A name collision for one of the entries in the group has been detected, the entries have been withdrawn.
\value QtAvahiServiceStateFailure
Some kind of failure happened, the entries have been withdrawn.
*/
/*! \fn void QtAvahiService::serviceStateChanged(const QtAvahiServiceState &state);
This signal will be emitted when the \a state of this \l{QtAvahiService} has changed.
*/
#include "qtavahiservice.h"
#include "qtavahiservice_p.h"
#include <QDebug>
/*! Constructs a new \l{QtAvahiService} with the given \a parent. */
QtAvahiService::QtAvahiService(QObject *parent) :
QObject(parent),
d_ptr(new QtAvahiServicePrivate),
m_state(QtAvahiServiceStateUncomitted)
{
connect(this, &QtAvahiService::serviceStateChanged, this, &QtAvahiService::onStateChanged);
d_ptr->client = new QtAvahiClient(this);
d_ptr->client->start();
}
/*! Destructs this \l{QtAvahiService}. */
QtAvahiService::~QtAvahiService()
{
if (d_ptr->group)
avahi_entry_group_free(d_ptr->group);
delete d_ptr;
}
/*! Returns the port of this \l{QtAvahiService}. */
quint16 QtAvahiService::port() const
{
return d_ptr->port;
}
/*! Returns the name of this \l{QtAvahiService}. */
QString QtAvahiService::name() const
{
return d_ptr->name;
}
/*! Returns the service type of this \l{QtAvahiService}. */
QString QtAvahiService::serviceType() const
{
return d_ptr->type;
}
QHash<QString, QString> QtAvahiService::txtRecords() const
{
return d_ptr->txtRecords;
}
QtAvahiService::QtAvahiServiceState QtAvahiService::state() const
{
return m_state;
}
/*! Register a new \l{QtAvahiService} with the given \a name and \a port. The service type can be specified with the \a serviceType string. The \a txtRecords records inform about additional information. Returns true if the service could be registered. */
bool QtAvahiService::registerService(const QString &name, const quint16 &port, const QString &serviceType, const QHash<QString, QString> &txtRecords)
{
// Check if the client is running
if (!d_ptr->client->client || AVAHI_CLIENT_S_RUNNING != avahi_client_get_state(d_ptr->client->client)) {
qWarning() << "Could not register service" << name << port << serviceType << ". The client is not available.";
return false;
}
d_ptr->name = name;
d_ptr->port = port;
d_ptr->type = serviceType;
d_ptr->txtRecords = txtRecords;
// If the group is not set yet, create it
if (!d_ptr->group)
d_ptr->group = avahi_entry_group_new(d_ptr->client->client, QtAvahiServicePrivate::callback, this);
// If the group is empty
if (avahi_entry_group_is_empty(d_ptr->group)) {
// Add the service
d_ptr->error = avahi_entry_group_add_service_strlst(d_ptr->group,
AVAHI_IF_UNSPEC,
AVAHI_PROTO_UNSPEC,
(AvahiPublishFlags) 0,
d_ptr->name.toLatin1().data(),
d_ptr->type.toLatin1().data(),
0,
0,
(uint16_t)d_ptr->port,
QtAvahiServicePrivate::createTxtList(txtRecords));
// Verify if the group has to be comitted
if (d_ptr->error) {
if (d_ptr->error == AVAHI_ERR_COLLISION) {
if (!handlCollision()) {
qWarning() << this << "error:" << avahi_strerror(d_ptr->error);
return false;
}
} else {
qWarning() << this << "error:" << avahi_strerror(d_ptr->error);
return false;
}
}
// Commit the service
d_ptr->error = avahi_entry_group_commit(d_ptr->group);
if (d_ptr->error) {
qWarning() << this << "error:" << avahi_strerror(d_ptr->error);
return false;
}
} else {
qWarning() << "Service already registered. Please reset the service before reusing it.";
return false;
}
return true;
}
/*! Remove this service from the local network. This \l{QtAvahiService} can be reused to register a new avahi service. */
void QtAvahiService::resetService()
{
if (!d_ptr->group)
return;
avahi_entry_group_reset(d_ptr->group);
}
/*! Returns true if the service group was added and commited to the network without errors. */
bool QtAvahiService::isValid() const
{
return (d_ptr->group && !d_ptr->error);
}
/*! Returns the error string of this \l{QtAvahiService}. */
QString QtAvahiService::errorString() const
{
if (!d_ptr->client->client)
return "Invalid client.";
return avahi_strerror(avahi_client_errno(d_ptr->client->client));
}
bool QtAvahiService::handlCollision()
{
QString alternativeServiceName = avahi_alternative_service_name(name().toStdString().data());
qDebug() << "Service name colision. Picking alternative service name" << alternativeServiceName;
resetService();
return registerService(alternativeServiceName, port(), serviceType(), txtRecords());
}
void QtAvahiService::onStateChanged(const QtAvahiServiceState &state)
{
if (m_state == state)
return;
m_state = state;
switch (m_state) {
case QtAvahiServiceStateUncomitted:
qDebug() << this << "state changed: uncomitted";
break;
case QtAvahiServiceStateRegistering:
qDebug() << this << "state changed: registering...";
break;
case QtAvahiServiceStateEstablished:
qDebug() << this << "state changed: established";
break;
case QtAvahiServiceStateCollision:
qDebug() << this << "state changed: collision";
handlCollision();
break;
case QtAvahiServiceStateFailure:
qWarning() << this << "failure: " << errorString();
break;
default:
break;
}
}
QDebug operator <<(QDebug dbg, QtAvahiService *service)
{
dbg.nospace() << "AvahiService(";
dbg << service->name() << ", " << service->serviceType() << ", " << service->port() << ") ";
return dbg;
}

View File

@ -1,80 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2016 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of mea. *
* *
* This library 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. *
* *
* This library 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 this library; If not, see *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef QTAVAHISERVICE_H
#define QTAVAHISERVICE_H
#include <QHash>
#include <QString>
#include <QObject>
class QtAvahiServicePrivate;
class QtAvahiService : public QObject
{
Q_OBJECT
Q_ENUMS(QtAvahiServiceState)
public:
enum QtAvahiServiceState {
QtAvahiServiceStateUncomitted = 0,
QtAvahiServiceStateRegistering = 1,
QtAvahiServiceStateEstablished = 2,
QtAvahiServiceStateCollision = 3,
QtAvahiServiceStateFailure = 4
};
explicit QtAvahiService(QObject *parent = 0);
~QtAvahiService();
quint16 port() const;
QString name() const;
QString serviceType() const;
QHash<QString, QString> txtRecords() const;
QtAvahiServiceState state() const;
bool registerService(const QString &name, const quint16 &port, const QString &serviceType = "_http._tcp", const QHash<QString, QString> &txtRecords = QHash<QString, QString>());
void resetService();
bool isValid() const;
QString errorString() const;
signals:
void serviceStateChanged(const QtAvahiServiceState &state);
protected:
QtAvahiServicePrivate *d_ptr;
private slots:
bool handlCollision();
void onStateChanged(const QtAvahiServiceState &state);
private:
QtAvahiServiceState m_state;
Q_DECLARE_PRIVATE(QtAvahiService)
};
QDebug operator <<(QDebug dbg, QtAvahiService *service);
#endif // QTAVAHISERVICE_H

View File

@ -1,81 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2016 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of mea. *
* *
* This library 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. *
* *
* This library 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 this library; If not, see *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "qtavahiservice_p.h"
#include <QString>
#include <QHash>
#include <QStringList>
QtAvahiServicePrivate::QtAvahiServicePrivate() :
client(0),
group(0),
error(0)
{
}
void QtAvahiServicePrivate::callback(AvahiEntryGroup *group, AvahiEntryGroupState state, void *userdata)
{
Q_UNUSED(group);
QtAvahiService *service = static_cast<QtAvahiService *>(userdata);
if (!service)
return;
if (service->state() == (QtAvahiService::QtAvahiServiceState)state)
return;
switch (state) {
case AVAHI_ENTRY_GROUP_UNCOMMITED:
emit service->serviceStateChanged(QtAvahiService::QtAvahiServiceStateUncomitted);
break;
case AVAHI_ENTRY_GROUP_REGISTERING:
emit service->serviceStateChanged(QtAvahiService::QtAvahiServiceStateRegistering);
break;
case AVAHI_ENTRY_GROUP_ESTABLISHED:
emit service->serviceStateChanged(QtAvahiService::QtAvahiServiceStateEstablished);
break;
case AVAHI_ENTRY_GROUP_COLLISION:
emit service->serviceStateChanged(QtAvahiService::QtAvahiServiceStateCollision);
break;
case AVAHI_ENTRY_GROUP_FAILURE:
emit service->serviceStateChanged(QtAvahiService::QtAvahiServiceStateFailure);
break;
}
}
AvahiStringList *QtAvahiServicePrivate::createTxtList(const QHash<QString, QString> &txt)
{
AvahiStringList *list = NULL;
if (txt.isEmpty())
return list;
const QStringList keys = txt.keys();
list = avahi_string_list_new((keys.first() + '=' + txt[keys.first()]).toLatin1().constData(), nullptr);
for (const QString &key : keys.mid(1)) {
list = avahi_string_list_add_pair(list, key.toLatin1().constData(), txt[key].toLatin1().constData());
}
return list;
}

View File

@ -1,56 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2016 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of mea. *
* *
* This library 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. *
* *
* This library 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 this library; If not, see *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef QTAVAHISERVICEPRIVATE_P
#define QTAVAHISERVICEPRIVATE_P
#include <QObject>
#include <QString>
#include "qtavahiservice.h"
#include "qtavahiclient.h"
#include <avahi-client/publish.h>
#include <avahi-common/error.h>
#include <avahi-common/alternative.h>
class QtAvahiServicePrivate
{
public:
QtAvahiServicePrivate();
static void callback(AvahiEntryGroup *group, AvahiEntryGroupState state, void *userdata);
QtAvahiClient *client;
AvahiEntryGroup *group;
QString name;
quint16 port;
QString type;
QHash<QString, QString> txtRecords;
int error;
static AvahiStringList *createTxtList(const QHash<QString, QString> &txt);
};
#endif // QTAVAHISERVICEPRIVATE_P

View File

@ -1,94 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2016 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of mea. *
* *
* This library 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. *
* *
* This library 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 this library; If not, see *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*! \fn void QtAvahiServiceBrowser::serviceEntryAdded(const AvahiServiceEntry &entry);
This signal will be emitted when a new \a entry was added to the current entry list.
*/
/*! \fn void QtAvahiServiceBrowser::serviceEntryRemoved(const AvahiServiceEntry &entry);
This signal will be emitted when a new \a entry was removed from the current entry list.
*/
#include "qtavahiservicebrowser.h"
#include "qtavahiservicebrowser_p.h"
#include <avahi-common/error.h>
/*! Constructs a new \l{QtAvahiServiceBrowser} with the given \a parent. */
QtAvahiServiceBrowser::QtAvahiServiceBrowser(QObject *parent) :
QObject(parent),
d_ptr(new QtAvahiServiceBrowserPrivate(new QtAvahiClient))
{
connect(d_ptr->client, &QtAvahiClient::clientStateChanged, this, &QtAvahiServiceBrowser::onClientStateChanged);
}
/*! Destructs this \l{QtAvahiServiceBrowser}. */
QtAvahiServiceBrowser::~QtAvahiServiceBrowser()
{
// Delete each service browser
foreach (const QString &serviceType, d_ptr->serviceBrowserTable.keys()) {
AvahiServiceBrowser *browser = d_ptr->serviceBrowserTable.take(serviceType);
if (browser) {
avahi_service_browser_free(browser);
}
}
// Delete the service type browser
if (d_ptr->serviceTypeBrowser)
avahi_service_type_browser_free(d_ptr->serviceTypeBrowser);
delete d_ptr;
}
/*! Enables this \l{QtAvahiServiceBrowser} and starts the service browsing. */
void QtAvahiServiceBrowser::enable()
{
d_ptr->client->start();
}
/*! Returns the current \l{AvahiServiceEntry} list of this \l{QtAvahiServiceBrowser}. */
QList<AvahiServiceEntry> QtAvahiServiceBrowser::serviceEntries() const
{
return m_serviceEntries;
}
void QtAvahiServiceBrowser::onClientStateChanged(const QtAvahiClient::QtAvahiClientState &state)
{
if (state == QtAvahiClient::QtAvahiClientStateRunning) {
qDebug() << "Service browser client connected.";
// Return if we already have a service type browser
if (d_ptr->serviceTypeBrowser)
return;
d_ptr->serviceTypeBrowser = avahi_service_type_browser_new(d_ptr->client->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, (AvahiLookupFlags) 0, QtAvahiServiceBrowserPrivate::callbackServiceTypeBrowser, this);
} else if (state == QtAvahiClient::QtAvahiClientStateFailure) {
qWarning() << "Service browser client failure:" << d_ptr->client->errorString();
}
}
void QtAvahiServiceBrowser::createServiceBrowser(const char *serviceType)
{
// create a new service browser for the given serviceType
AvahiServiceBrowser *browser = avahi_service_browser_new(d_ptr->client->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, serviceType, NULL, (AvahiLookupFlags) 0, QtAvahiServiceBrowserPrivate::callbackServiceBrowser, this);
d_ptr->serviceBrowserTable.insert(serviceType, browser);
}

View File

@ -1,63 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2016 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of mea. *
* *
* This library 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. *
* *
* This library 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 this library; If not, see *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef QTAVAHISERVICEBROWSER_H
#define QTAVAHISERVICEBROWSER_H
#include <QObject>
#include <avahi-client/lookup.h>
#include "qtavahiclient.h"
#include "avahiserviceentry.h"
class QtAvahiServiceBrowserPrivate;
class QtAvahiServiceBrowser : public QObject
{
Q_OBJECT
public:
explicit QtAvahiServiceBrowser(QObject *parent = 0);
~QtAvahiServiceBrowser();
void enable();
QList<AvahiServiceEntry> serviceEntries() const;
signals:
void serviceEntryAdded(const AvahiServiceEntry &entry);
void serviceEntryRemoved(const AvahiServiceEntry &entry);
private slots:
void onClientStateChanged(const QtAvahiClient::QtAvahiClientState &state);
private:
QtAvahiServiceBrowserPrivate *d_ptr;
QList<AvahiServiceEntry> m_serviceEntries;
QStringList m_serviceTypes;
void createServiceBrowser(const char* serviceType);
Q_DECLARE_PRIVATE(QtAvahiServiceBrowser)
};
#endif // QTAVAHISERVICEBROWSER_H

View File

@ -1,218 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2016 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of mea. *
* *
* This library 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. *
* *
* This library 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 this library; If not, see *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "qtavahiservicebrowser_p.h"
#include "qtavahiservicebrowser.h"
#include "avahiserviceentry.h"
#include <avahi-common/strlst.h>
#include <avahi-common/error.h>
#include <QPointer>
QtAvahiServiceBrowserPrivate::QtAvahiServiceBrowserPrivate(QtAvahiClient *client) :
client(client),
serviceTypeBrowser(NULL)
{
}
QtAvahiServiceBrowserPrivate::~QtAvahiServiceBrowserPrivate()
{
foreach (AvahiServiceResolver *resolver, m_serviceResolvers) {
avahi_service_resolver_free(resolver);
}
}
void QtAvahiServiceBrowserPrivate::callbackServiceTypeBrowser(AvahiServiceTypeBrowser *browser, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, const char *type, const char *domain, AvahiLookupResultFlags flags, void *userdata)
{
Q_UNUSED(browser)
Q_UNUSED(interface)
Q_UNUSED(protocol)
Q_UNUSED(domain)
Q_UNUSED(flags)
QtAvahiServiceBrowser *serviceBrowser = static_cast<QtAvahiServiceBrowser *>(userdata);
if (!serviceBrowser)
return;
switch (event) {
case AVAHI_BROWSER_NEW:
if (!serviceBrowser->m_serviceTypes.contains(type)) {
serviceBrowser->m_serviceTypes.append(type);
qDebug() << "[+] Service browser" << type;
serviceBrowser->createServiceBrowser(type);
}
break;
case AVAHI_BROWSER_REMOVE:
// Note: the browser for this serviceType will be deleted once all
// services from this type are removed
break;
case AVAHI_BROWSER_CACHE_EXHAUSTED:
break;
case AVAHI_BROWSER_ALL_FOR_NOW:
break;
case AVAHI_BROWSER_FAILURE:
qWarning() << "Service type browser error:" << QString(avahi_strerror(avahi_client_errno(serviceBrowser->d_ptr->client->client)));
break;
}
}
void QtAvahiServiceBrowserPrivate::callbackServiceBrowser(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);
QtAvahiServiceBrowser *serviceBrowser = static_cast<QtAvahiServiceBrowser *>(userdata);
if (!serviceBrowser)
return;
switch (event) {
case AVAHI_BROWSER_NEW: {
// Start resolving new service
AvahiServiceResolver *resolver = avahi_service_resolver_new(serviceBrowser->d_ptr->client->client,
interface,
protocol,
name,
type,
domain,
AVAHI_PROTO_UNSPEC,
(AvahiLookupFlags) 0,
QtAvahiServiceBrowserPrivate::callbackServiceResolver,
serviceBrowser);
if (resolver) {
serviceBrowser->d_ptr->m_serviceResolvers.append(resolver);
} else {
qWarning() << "Failed to resolve service" << QString(name) << ":" << avahi_strerror(avahi_client_errno(serviceBrowser->d_ptr->client->client));
}
break;
}
case AVAHI_BROWSER_REMOVE: {
// Remove the service
foreach (const AvahiServiceEntry &entry, serviceBrowser->m_serviceEntries) {
// Check not only the name, but also the protocol
if (entry.name() == name && entry.protocol() == QtAvahiServiceBrowserPrivate::convertProtocol(protocol)) {
serviceBrowser->m_serviceEntries.removeAll(entry);
emit serviceBrowser->serviceEntryRemoved(entry);
}
}
// Check if this was the last service for this serviceType
foreach (const AvahiServiceEntry &entry, serviceBrowser->m_serviceEntries) {
if (entry.serviceType() == type)
return;
}
// This was the last service for this serviceType, lets delete the corresponding browser
AvahiServiceBrowser *browser = serviceBrowser->d_ptr->serviceBrowserTable.take(type);
if (browser)
avahi_service_browser_free(browser);
serviceBrowser->m_serviceTypes.removeAll(type);
break;
}
case AVAHI_BROWSER_ALL_FOR_NOW:
break;
case AVAHI_BROWSER_CACHE_EXHAUSTED:
break;
case AVAHI_BROWSER_FAILURE:
qWarning() << "Service browser error:" << QString(avahi_strerror(avahi_client_errno(serviceBrowser->d_ptr->client->client)));
break;
}
}
void QtAvahiServiceBrowserPrivate::callbackServiceResolver(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);
QPointer<QtAvahiServiceBrowser> serviceBrowser = static_cast<QtAvahiServiceBrowser *>(userdata);
if (serviceBrowser.isNull())
return;
switch (event) {
case AVAHI_RESOLVER_FAILURE:
break;
case AVAHI_RESOLVER_FOUND: {
char a[AVAHI_ADDRESS_STR_MAX];
avahi_address_snprint(a, sizeof(a), address);
// convert protocol
QAbstractSocket::NetworkLayerProtocol networkProtocol = QtAvahiServiceBrowserPrivate::convertProtocol(protocol);
QStringList txtList = QtAvahiServiceBrowserPrivate::convertTxtList(txt);
// create the new resolved service entry
AvahiServiceEntry entry = AvahiServiceEntry(name,
type,
QHostAddress(QString(a)),
QString(domain),
QString(host_name),
(quint16)port,
networkProtocol,
txtList,
flags);
serviceBrowser->m_serviceEntries.append(entry);
emit serviceBrowser->serviceEntryAdded(entry);
}
}
serviceBrowser->d_ptr->m_serviceResolvers.removeAll(resolver);
avahi_service_resolver_free(resolver);
}
QStringList QtAvahiServiceBrowserPrivate::convertTxtList(AvahiStringList *txt)
{
if (!txt)
return QStringList();
QStringList txtList;
txtList.append(QString(reinterpret_cast<char *>(txt->text)));
while (txt->next) {
AvahiStringList *next = txt->next;
txtList.append(QString(reinterpret_cast<char *>(next->text)));
txt = next;
}
return txtList;
}
QAbstractSocket::NetworkLayerProtocol QtAvahiServiceBrowserPrivate::convertProtocol(const AvahiProtocol &protocol)
{
QAbstractSocket::NetworkLayerProtocol networkProtocol = QAbstractSocket::UnknownNetworkLayerProtocol;
switch (protocol) {
case AVAHI_PROTO_INET:
networkProtocol = QAbstractSocket::IPv4Protocol;
break;
case AVAHI_PROTO_INET6:
networkProtocol = QAbstractSocket::IPv6Protocol;
break;
case AVAHI_PROTO_UNSPEC:
networkProtocol = QAbstractSocket::UnknownNetworkLayerProtocol;
break;
}
return networkProtocol;
}

View File

@ -1,82 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2016 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of mea. *
* *
* This library 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. *
* *
* This library 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 this library; If not, see *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef QTAVAHISERVICEBROWSERPRIVATE_H
#define QTAVAHISERVICEBROWSERPRIVATE_H
#include <QStringList>
#include <QAbstractSocket>
#include <avahi-client/lookup.h>
#include "qtavahiclient.h"
#include "qtavahiservice.h"
class QtAvahiServiceBrowserPrivate
{
public:
QtAvahiServiceBrowserPrivate(QtAvahiClient *client);
~QtAvahiServiceBrowserPrivate();
// Callback members
static void callbackServiceTypeBrowser(AvahiServiceTypeBrowser *browser,
AvahiIfIndex interface,
AvahiProtocol protocol,
AvahiBrowserEvent event,
const char *type,
const char *domain,
AvahiLookupResultFlags flags,
void *userdata);
static void callbackServiceBrowser(AvahiServiceBrowser *browser,
AvahiIfIndex interface,
AvahiProtocol protocol,
AvahiBrowserEvent event,
const char *name,
const char *type,
const char *domain,
AvahiLookupResultFlags flags,
void *userdata);
static void callbackServiceResolver(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);
// Convert members
static QStringList convertTxtList(AvahiStringList *txt);
static QAbstractSocket::NetworkLayerProtocol convertProtocol(const AvahiProtocol &protocol);
QtAvahiClient *client;
AvahiServiceTypeBrowser *serviceTypeBrowser;
QHash<QString, AvahiServiceBrowser *> serviceBrowserTable;
QList<AvahiServiceResolver *> m_serviceResolvers;
};
#endif // QTAVAHISERVICEBROWSERPRIVATE_H

View File

@ -7,17 +7,27 @@ ZeroconfDiscovery::ZeroconfDiscovery(DiscoveryModel *discoveryModel, QObject *pa
QObject(parent),
m_discoveryModel(discoveryModel)
{
#ifdef WITH_AVAHI
m_serviceBrowser = new QtAvahiServiceBrowser(this);
connect(m_serviceBrowser, &QtAvahiServiceBrowser::serviceEntryAdded, this, &ZeroconfDiscovery::serviceEntryAdded);
m_serviceBrowser->enable();
#ifdef WITH_ZEROCONF
m_zeroconfJsonRPC = new QZeroConf(this);
connect(m_zeroconfJsonRPC, &QZeroConf::serviceAdded, this, &ZeroconfDiscovery::serviceEntryAdded);
connect(m_zeroconfJsonRPC, &QZeroConf::serviceUpdated, this, &ZeroconfDiscovery::serviceEntryAdded);
m_zeroconfJsonRPC->startBrowser("_jsonrpc._tcp");
qDebug() << "created service browser for _jsonrpc._tcp:" << m_zeroconfJsonRPC->browserExists();
m_zeroconfWebSocket = new QZeroConf(this);
connect(m_zeroconfWebSocket, &QZeroConf::serviceAdded, this, &ZeroconfDiscovery::serviceEntryAdded);
connect(m_zeroconfWebSocket, &QZeroConf::serviceUpdated, this, &ZeroconfDiscovery::serviceEntryAdded);
m_zeroconfWebSocket->startBrowser("_ws._tcp");
qDebug() << "created service browser for _ws._tcp:" << m_zeroconfWebSocket->browserExists();
#else
qDebug() << "Zeroconf support not compiled in. Zeroconf will not be available.";
#endif
}
bool ZeroconfDiscovery::available() const
{
#ifdef WITH_AVAHI
return true;
#ifdef WITH_ZEROCONF
return m_zeroconfJsonRPC->browserExists() || m_zeroconfWebSocket->browserExists();
#else
return false;
#endif
@ -25,22 +35,22 @@ bool ZeroconfDiscovery::available() const
bool ZeroconfDiscovery::discovering() const
{
return true;
return available();
}
#ifdef WITH_AVAHI
void ZeroconfDiscovery::serviceEntryAdded(const AvahiServiceEntry &entry)
#ifdef WITH_ZEROCONF
void ZeroconfDiscovery::serviceEntryAdded(const QZeroConfService &entry)
{
if (!entry.name().startsWith("nymea") || entry.serviceType() != "_jsonrpc._tcp" || entry.hostAddress().protocol() == QAbstractSocket::IPv6Protocol) {
if (!entry.name().startsWith("nymea") || entry.ip().isNull()) {
return;
}
qDebug() << "avahi service entry added" << entry.name() << entry.hostAddress() << entry.port() << entry.txt() << entry.serviceType();
qDebug() << "zeroconf service discovered" << entry << entry.txt() << entry.type();
QString uuid;
bool sslEnabled = false;
QString serverName;
foreach (const QString &txt, entry.txt()) {
QPair<QString, QString> txtRecord = qMakePair<QString, QString>(txt.split("=").first(), txt.split("=").at(1));
foreach (const QByteArray &key, entry.txt().keys()) {
QPair<QString, QString> txtRecord = qMakePair<QString, QString>(key, entry.txt().value(key));
if (!sslEnabled && txtRecord.first == "sslEnabled") {
sslEnabled = (txtRecord.second == "true");
}
@ -51,24 +61,30 @@ void ZeroconfDiscovery::serviceEntryAdded(const AvahiServiceEntry &entry)
serverName = txtRecord.second;
}
}
qDebug() << "avahi service entry added" << serverName << uuid << sslEnabled;
DiscoveryDevice dev = m_discoveryModel->find(entry.hostAddress());
DiscoveryDevice dev = m_discoveryModel->find(entry.ip());
if (dev.uuid() == uuid && dev.nymeaRpcUrl().startsWith("nymeas") && !sslEnabled) {
// We already have this host and with a more secure configuration... skip this one...
return;
}
qDebug() << "Adding new found entry:" << entry.name() << entry.ip();
dev.setUuid(uuid);
dev.setHostAddress(entry.hostAddress());
dev.setHostAddress(entry.ip());
dev.setPort(entry.port());
dev.setFriendlyName(serverName + " on " + entry.hostName());
QHostAddress address = entry.hostAddress();
dev.setFriendlyName(serverName + " on " + entry.ip().toString());
QHostAddress address = entry.ip();
QString addressString;
if (address.protocol() == QAbstractSocket::IPv6Protocol) {
addressString = "[" + address.toString() + "]";
} else {
addressString = address.toString();
}
dev.setNymeaRpcUrl(QString("%1://%2:%3").arg(sslEnabled ? "nymeas" : "nymea").arg(addressString).arg(entry.port()));
if (entry.type() == "_ws._tcp") {
dev.setWebSocketUrl(QString("%1://%2:%3").arg(sslEnabled ? "wss" : "ws").arg(addressString).arg(entry.port()));
} else {
dev.setNymeaRpcUrl(QString("%1://%2:%3").arg(sslEnabled ? "nymeas" : "nymea").arg(addressString).arg(entry.port()));
}
m_discoveryModel->addDevice(dev);
// DiscoveryDevice *dev = new DiscoveryDevice();

View File

@ -1,8 +1,8 @@
#ifndef ZEROCONFDISCOVERY_H
#define ZEROCONFDISCOVERY_H
#ifdef WITH_AVAHI
#include "avahi/qtavahiservicebrowser.h"
#ifdef WITH_ZEROCONF
#include "qzeroconf.h"
#endif
#include "discoverymodel.h"
@ -22,14 +22,13 @@ public:
private:
DiscoveryModel *m_discoveryModel;
#ifdef WITH_AVAHI
QtAvahiServiceBrowser *m_serviceBrowser;
#ifdef WITH_ZEROCONF
QZeroConf *m_zeroconfJsonRPC = nullptr;
QZeroConf *m_zeroconfWebSocket = nullptr;
private slots:
void serviceEntryAdded(const AvahiServiceEntry &entry);
void serviceEntryAdded(const QZeroConfService &entry);
#endif
};
#endif // ZEROCONFDISCOVERY_H

View File

@ -224,7 +224,7 @@ void JsonRpcClient::sendRequest(const QVariantMap &request)
{
QVariantMap newRequest = request;
newRequest.insert("token", m_token);
// qDebug() << "Sending request" << qUtf8Printable(QJsonDocument::fromVariant(newRequest).toJson());
qDebug() << "Sending request" << qUtf8Printable(QJsonDocument::fromVariant(newRequest).toJson());
m_connection->sendData(QJsonDocument::fromVariant(newRequest).toJson());
}
@ -237,6 +237,11 @@ void JsonRpcClient::onInterfaceConnectedChanged(bool connected)
m_connected = false;
emit connectedChanged(false);
}
} else {
QVariantMap request;
request.insert("id", 0);
request.insert("method", "JSONRPC.Hello");
sendRequest(request);
}
}
@ -254,7 +259,7 @@ void JsonRpcClient::dataReceived(const QByteArray &data)
// qWarning() << "Could not parse json data from mea" << data << error.errorString();
return;
}
// qDebug() << "received response" << m_receiveBuffer.left(splitIndex);
qDebug() << "received response" << m_receiveBuffer.left(splitIndex);
m_receiveBuffer = m_receiveBuffer.right(m_receiveBuffer.length() - splitIndex - 1);
if (!m_receiveBuffer.isEmpty()) {
staticMetaObject.invokeMethod(this, "dataReceived", Qt::QueuedConnection, Q_ARG(QByteArray, QByteArray()));
@ -264,7 +269,8 @@ void JsonRpcClient::dataReceived(const QByteArray &data)
// Check if this is the initial handshake
if (dataMap.value("id").toInt() == 0) {
if (dataMap.value("id").toInt() == 0 && dataMap.contains("params")) {
dataMap = dataMap.value("params").toMap();
m_initialSetupRequired = dataMap.value("initialSetupRequired").toBool();
m_authenticationRequired = dataMap.value("authenticationRequired").toBool();
m_pushButtonAuthAvailable = dataMap.value("pushButtonAuthAvailable").toBool();

View File

@ -3,11 +3,18 @@ TEMPLATE = lib
CONFIG += staticlib
include(../mea.pri)
!win32: {
# To enable this on Windows we'd need to install Bonjour
# https://support.apple.com/kb/DL999
DEFINES += QZEROCONF_STATIC
DEFINES += WITH_ZEROCONF
include(../QtZeroConf/qtzeroconf.pri)
}
QT -= gui
QT += websockets bluetooth
INCLUDEPATH += $$top_srcdir/libnymea-common
INCLUDEPATH += $$top_srcdir/libnymea-common $$top_srcdir/QtZeroConf
SOURCES += \
engine.cpp \
@ -92,29 +99,6 @@ HEADERS += \
wifisetup/networkmanagercontroler.h \
libmea-core.h
withavahi {
DEFINES += WITH_AVAHI
LIBS += -lavahi-client -lavahi-common
HEADERS += discovery/avahi/avahiserviceentry.h \
discovery/avahi/qt-watch.h \
discovery/avahi/qtavahiclient.h \
discovery/avahi/qtavahiservice_p.h \
discovery/avahi/qtavahiservice.h \
discovery/avahi/qtavahiservicebrowser_p.h \
discovery/avahi/qtavahiservicebrowser.h \
SOURCES += discovery/avahi/avahiserviceentry.cpp \
discovery/avahi/qt-watch.cpp \
discovery/avahi/qtavahiclient.cpp \
discovery/avahi/qtavahiservice_p.cpp \
discovery/avahi/qtavahiservice.cpp \
discovery/avahi/qtavahiservicebrowser_p.cpp \
discovery/avahi/qtavahiservicebrowser.cpp \
}
unix {
target.path = /usr/lib
INSTALLS += target

View File

@ -23,7 +23,10 @@ QStringList TcpSocketInterface::supportedSchemes() const
void TcpSocketInterface::sendData(const QByteArray &data)
{
m_socket.write(data);
quint64 ret = m_socket.write(data);
if (ret != data.length()) {
qWarning() << "Error writing data to socket.";
}
}
void TcpSocketInterface::ignoreSslErrors(const QList<QSslError> &errors)

View File

@ -135,8 +135,15 @@ Page {
}
}
onClicked: {
print("Should connect to", model.nymeaRpcUrl)
Engine.connection.connect(model.nymeaRpcUrl)
var url;
if (model.nymeaRpcUrl) {
url = model.nymeaRpcUrl;
} else {
url = model.webSocketUrl;
}
print("Should connect to", url)
Engine.connection.connect(url)
pageStack.push(connectingPage)
}
}