added zeroconf discovery

This commit is contained in:
Michael Zanetti 2017-10-22 22:28:03 +02:00
parent 5047fbe631
commit cde21a4770
27 changed files with 2004 additions and 262 deletions

View File

@ -0,0 +1,180 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2016 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of guh. *
* *
* 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/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*!
\class AvahiServiceEntry
\brief Holds information about an avahi service entry.
\ingroup types
\inmodule libguh
*/
#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

@ -0,0 +1,75 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2016 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of guh. *
* *
* 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

@ -0,0 +1,193 @@
/***
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

@ -0,0 +1,35 @@
#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

@ -0,0 +1,121 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2016 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of guh. *
* *
* 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

@ -0,0 +1,73 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2016 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of guh. *
* *
* 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

@ -0,0 +1,235 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2016 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of guh. *
* *
* 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/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*!
\class QtAvahiService
\brief Allows to publish an avahi service to the network.
\inmodule libguh
*/
/*! \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

@ -0,0 +1,80 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2016 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of guh. *
* *
* 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

@ -0,0 +1,81 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2016 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of guh. *
* *
* 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

@ -0,0 +1,56 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2016 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of guh. *
* *
* 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

@ -0,0 +1,102 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2016 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of guh. *
* *
* 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/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*!
\class QtAvahiServiceBrowser
\brief Allows to browse avahi services in the local network.
\ingroup hardware
\inmodule libguh
*/
/*! \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

@ -0,0 +1,63 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2016 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of guh. *
* *
* 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

@ -0,0 +1,218 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2016 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of guh. *
* *
* 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

@ -0,0 +1,82 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2016 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of guh. *
* *
* 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

@ -0,0 +1,176 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stuerz <stuerz.simon@gmail.com> *
* *
* This file is part of guh-ubuntu. *
* *
* guh-ubuntu is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 3 of the License. *
* *
* guh-ubuntu 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh-ubuntu. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "discoverydevice.h"
#include <QUrl>
DiscoveryDevice::DiscoveryDevice()
{
}
QUrl DiscoveryDevice::location() const
{
return m_location;
}
void DiscoveryDevice::setLocation(const QUrl &location)
{
m_location = location;
}
QString DiscoveryDevice::webSocketUrl() const
{
return m_webSocketUrl;
}
void DiscoveryDevice::setWebSocketUrl(const QString &webSocketUrl)
{
m_webSocketUrl = webSocketUrl;
}
QString DiscoveryDevice::guhRpcUrl() const
{
return m_guhRpcUrl;
}
void DiscoveryDevice::setGuhRpcUrl(const QString &guhRpcUrl)
{
m_guhRpcUrl = guhRpcUrl;
}
QHostAddress DiscoveryDevice::hostAddress() const
{
return m_hostAddress;
}
void DiscoveryDevice::setHostAddress(const QHostAddress &hostAddress)
{
m_hostAddress = hostAddress;
}
int DiscoveryDevice::port() const
{
return m_port;
}
void DiscoveryDevice::setPort(const int &port)
{
m_port = port;
}
QString DiscoveryDevice::friendlyName() const
{
return m_friendlyName;
}
void DiscoveryDevice::setFriendlyName(const QString &friendlyName)
{
m_friendlyName = friendlyName;
}
QString DiscoveryDevice::manufacturer() const
{
return m_manufacturer;
}
void DiscoveryDevice::setManufacturer(const QString &manufacturer)
{
m_manufacturer = manufacturer;
}
QUrl DiscoveryDevice::manufacturerURL() const
{
return m_manufacturerURL;
}
void DiscoveryDevice::setManufacturerURL(const QUrl &manufacturerURL)
{
m_manufacturerURL = manufacturerURL;
}
QString DiscoveryDevice::modelDescription() const
{
return m_modelDescription;
}
void DiscoveryDevice::setModelDescription(const QString &modelDescription)
{
m_modelDescription = modelDescription;
}
QString DiscoveryDevice::modelName() const
{
return m_modelName;
}
void DiscoveryDevice::setModelName(const QString &modelName)
{
m_modelName = modelName;
}
QString DiscoveryDevice::modelNumber() const
{
return m_modelNumber;
}
void DiscoveryDevice::setModelNumber(const QString &modelNumber)
{
m_modelNumber = modelNumber;
}
QUrl DiscoveryDevice::modelURL() const
{
return m_modelURL;
}
void DiscoveryDevice::setModelURL(const QUrl &modelURL)
{
m_modelURL = modelURL;
}
QString DiscoveryDevice::uuid() const
{
return m_uuid;
}
void DiscoveryDevice::setUuid(const QString &uuid)
{
m_uuid = uuid;
}
QDebug operator<<(QDebug debug, const DiscoveryDevice &DiscoveryDevice)
{
debug << "----------------------------------------------\n";
debug << "UPnP device on " << QString("%1:%2").arg(DiscoveryDevice.hostAddress().toString()).arg(DiscoveryDevice.port()) << "\n";
debug << "location | " << DiscoveryDevice.location().toString() << "\n";
debug << "websocket | " << DiscoveryDevice.webSocketUrl() << "\n";
debug << "guhrpc | " << DiscoveryDevice.guhRpcUrl() << "\n";
debug << "friendly name | " << DiscoveryDevice.friendlyName() << "\n";
debug << "manufacturer | " << DiscoveryDevice.manufacturer() << "\n";
debug << "manufacturer URL | " << DiscoveryDevice.manufacturerURL().toString() << "\n";
debug << "model name | " << DiscoveryDevice.modelName() << "\n";
debug << "model number | " << DiscoveryDevice.modelNumber() << "\n";
debug << "model description | " << DiscoveryDevice.modelDescription() << "\n";
debug << "model URL | " << DiscoveryDevice.modelURL().toString() << "\n";
debug << "UUID | " << DiscoveryDevice.uuid() << "\n";
return debug;
}

View File

@ -18,17 +18,17 @@
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef UPNPDEVICE_H
#define UPNPDEVICE_H
#ifndef DISCOVERYDEVICE_H
#define DISCOVERYDEVICE_H
#include <QObject>
#include <QUrl>
#include <QHostAddress>
class UpnpDevice
class DiscoveryDevice
{
public:
explicit UpnpDevice();
explicit DiscoveryDevice();
QUrl location() const;
void setLocation(const QUrl &location);
@ -88,6 +88,6 @@ private:
QString m_uuid;
};
QDebug operator<< (QDebug debug, const UpnpDevice &upnpDevice);
QDebug operator<< (QDebug debug, const DiscoveryDevice &discoveryDevice);
#endif // UPNPDEVICE_H
#endif // DISCOVERYDEVICE_H

View File

@ -18,30 +18,30 @@
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "upnpdiscoverymodel.h"
#include "discoverymodel.h"
UpnpDiscoveryModel::UpnpDiscoveryModel(QObject *parent) :
DiscoveryModel::DiscoveryModel(QObject *parent) :
QAbstractListModel(parent)
{
}
QList<UpnpDevice> UpnpDiscoveryModel::devices()
QList<DiscoveryDevice> DiscoveryModel::devices()
{
return m_devices;
}
int UpnpDiscoveryModel::rowCount(const QModelIndex &parent) const
int DiscoveryModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return m_devices.count();
}
QVariant UpnpDiscoveryModel::data(const QModelIndex &index, int role) const
QVariant DiscoveryModel::data(const QModelIndex &index, int role) const
{
if (index.row() < 0 || index.row() >= m_devices.count())
return QVariant();
UpnpDevice device = m_devices.at(index.row());
DiscoveryDevice device = m_devices.at(index.row());
if (role == NameRole) {
return device.friendlyName();
} else if (role == HostAddressRole) {
@ -58,22 +58,29 @@ QVariant UpnpDiscoveryModel::data(const QModelIndex &index, int role) const
return QVariant();
}
void UpnpDiscoveryModel::addDevice(UpnpDevice device)
void DiscoveryModel::addDevice(const DiscoveryDevice &device)
{
for (int i = 0; i < m_devices.count(); i++) {
if (m_devices.at(i).uuid() == device.uuid()) {
m_devices[i] = device;
emit dataChanged(index(i), index(i));
return;
}
}
beginInsertRows(QModelIndex(), m_devices.count(), m_devices.count());
m_devices.append(device);
endInsertRows();
emit countChanged();
}
QString UpnpDiscoveryModel::get(int index, const QByteArray &role) const
QString DiscoveryModel::get(int index, const QByteArray &role) const
{
return data(this->index(index), roleNames().key(role)).toString();
}
bool UpnpDiscoveryModel::contains(const QString &uuid) const
bool DiscoveryModel::contains(const QString &uuid) const
{
foreach (const UpnpDevice &dev, m_devices) {
foreach (const DiscoveryDevice &dev, m_devices) {
if (dev.uuid() == uuid) {
return true;
}
@ -81,7 +88,17 @@ bool UpnpDiscoveryModel::contains(const QString &uuid) const
return false;
}
void UpnpDiscoveryModel::clearModel()
DiscoveryDevice DiscoveryModel::find(const QHostAddress &address) const
{
foreach (const DiscoveryDevice &dev, m_devices) {
if (dev.hostAddress() == address) {
return dev;
}
}
return DiscoveryDevice();
}
void DiscoveryModel::clearModel()
{
beginResetModel();
m_devices.clear();
@ -89,7 +106,7 @@ void UpnpDiscoveryModel::clearModel()
emit countChanged();
}
QHash<int, QByteArray> UpnpDiscoveryModel::roleNames() const
QHash<int, QByteArray> DiscoveryModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[NameRole] = "name";

View File

@ -18,15 +18,15 @@
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef UPNPDISCOVERYMODEL_H
#define UPNPDISCOVERYMODEL_H
#ifndef DISCOVERYMODEL_H
#define DISCOVERYMODEL_H
#include <QAbstractListModel>
#include <QList>
#include "upnpdevice.h"
#include "discoverydevice.h"
class UpnpDiscoveryModel : public QAbstractListModel
class DiscoveryModel : public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
@ -40,17 +40,18 @@ public:
VersionRole
};
explicit UpnpDiscoveryModel(QObject *parent = 0);
explicit DiscoveryModel(QObject *parent = 0);
QList<UpnpDevice> devices();
QList<DiscoveryDevice> devices();
int rowCount(const QModelIndex & parent = QModelIndex()) const;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
void addDevice(UpnpDevice device);
void addDevice(const DiscoveryDevice &device);
Q_INVOKABLE QString get(int index, const QByteArray &role) const;
bool contains(const QString &uuid) const;
DiscoveryDevice find(const QHostAddress &address) const;
void clearModel();
@ -61,9 +62,7 @@ protected:
QHash<int, QByteArray> roleNames() const;
private:
QList<UpnpDevice> m_devices;
QList<DiscoveryDevice> m_devices;
};
#endif // UPNPDISCOVERYMODEL_H
#endif // DISCOVERYMODEL_H

View File

@ -1,176 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stuerz <stuerz.simon@gmail.com> *
* *
* This file is part of guh-ubuntu. *
* *
* guh-ubuntu is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 3 of the License. *
* *
* guh-ubuntu 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh-ubuntu. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "upnpdevice.h"
#include <QUrl>
UpnpDevice::UpnpDevice()
{
}
QUrl UpnpDevice::location() const
{
return m_location;
}
void UpnpDevice::setLocation(const QUrl &location)
{
m_location = location;
}
QString UpnpDevice::webSocketUrl() const
{
return m_webSocketUrl;
}
void UpnpDevice::setWebSocketUrl(const QString &webSocketUrl)
{
m_webSocketUrl = webSocketUrl;
}
QString UpnpDevice::guhRpcUrl() const
{
return m_guhRpcUrl;
}
void UpnpDevice::setGuhRpcUrl(const QString &guhRpcUrl)
{
m_guhRpcUrl = guhRpcUrl;
}
QHostAddress UpnpDevice::hostAddress() const
{
return m_hostAddress;
}
void UpnpDevice::setHostAddress(const QHostAddress &hostAddress)
{
m_hostAddress = hostAddress;
}
int UpnpDevice::port() const
{
return m_port;
}
void UpnpDevice::setPort(const int &port)
{
m_port = port;
}
QString UpnpDevice::friendlyName() const
{
return m_friendlyName;
}
void UpnpDevice::setFriendlyName(const QString &friendlyName)
{
m_friendlyName = friendlyName;
}
QString UpnpDevice::manufacturer() const
{
return m_manufacturer;
}
void UpnpDevice::setManufacturer(const QString &manufacturer)
{
m_manufacturer = manufacturer;
}
QUrl UpnpDevice::manufacturerURL() const
{
return m_manufacturerURL;
}
void UpnpDevice::setManufacturerURL(const QUrl &manufacturerURL)
{
m_manufacturerURL = manufacturerURL;
}
QString UpnpDevice::modelDescription() const
{
return m_modelDescription;
}
void UpnpDevice::setModelDescription(const QString &modelDescription)
{
m_modelDescription = modelDescription;
}
QString UpnpDevice::modelName() const
{
return m_modelName;
}
void UpnpDevice::setModelName(const QString &modelName)
{
m_modelName = modelName;
}
QString UpnpDevice::modelNumber() const
{
return m_modelNumber;
}
void UpnpDevice::setModelNumber(const QString &modelNumber)
{
m_modelNumber = modelNumber;
}
QUrl UpnpDevice::modelURL() const
{
return m_modelURL;
}
void UpnpDevice::setModelURL(const QUrl &modelURL)
{
m_modelURL = modelURL;
}
QString UpnpDevice::uuid() const
{
return m_uuid;
}
void UpnpDevice::setUuid(const QString &uuid)
{
m_uuid = uuid;
}
QDebug operator<<(QDebug debug, const UpnpDevice &upnpDevice)
{
debug << "----------------------------------------------\n";
debug << "UPnP device on " << QString("%1:%2").arg(upnpDevice.hostAddress().toString()).arg(upnpDevice.port()) << "\n";
debug << "location | " << upnpDevice.location().toString() << "\n";
debug << "websocket | " << upnpDevice.webSocketUrl() << "\n";
debug << "guhrpc | " << upnpDevice.guhRpcUrl() << "\n";
debug << "friendly name | " << upnpDevice.friendlyName() << "\n";
debug << "manufacturer | " << upnpDevice.manufacturer() << "\n";
debug << "manufacturer URL | " << upnpDevice.manufacturerURL().toString() << "\n";
debug << "model name | " << upnpDevice.modelName() << "\n";
debug << "model number | " << upnpDevice.modelNumber() << "\n";
debug << "model description | " << upnpDevice.modelDescription() << "\n";
debug << "model URL | " << upnpDevice.modelURL().toString() << "\n";
debug << "UUID | " << upnpDevice.uuid() << "\n";
return debug;
}

View File

@ -32,12 +32,13 @@ UpnpDiscovery::UpnpDiscovery(QObject *parent) :
m_networkAccessManager = new QNetworkAccessManager(this);
connect(m_networkAccessManager, &QNetworkAccessManager::finished, this, &UpnpDiscovery::networkReplyFinished);
m_discoveryModel = new UpnpDiscoveryModel(this);
m_discoveryModel = new DiscoveryModel(this);
m_timer = new QTimer(this);
m_timer->setSingleShot(true);
m_timer->setInterval(5000);
connect(m_timer, &QTimer::timeout, this, &UpnpDiscovery::onTimeout);
m_timer.setSingleShot(true);
m_timer.setInterval(5000);
connect(&m_timer, &QTimer::timeout, this, &UpnpDiscovery::onTimeout);
m_repeatTimer.setInterval(500);
connect(&m_repeatTimer, &QTimer::timeout, this, &UpnpDiscovery::writeDiscoveryPacket);
// bind udp socket and join multicast group
m_port = 1900;
@ -68,7 +69,7 @@ bool UpnpDiscovery::discovering() const
return m_discovering;
}
UpnpDiscoveryModel *UpnpDiscovery::discoveryModel()
DiscoveryModel *UpnpDiscovery::discoveryModel()
{
return m_discoveryModel;
}
@ -86,25 +87,21 @@ void UpnpDiscovery::discover()
}
qDebug() << "start discovering...";
m_timer->start();
m_timer.start();
m_repeatTimer.start();
// m_discoveryModel->clearModel();
m_foundDevices.clear();
setDiscovering(true);
QByteArray ssdpSearchMessage = QByteArray("M-SEARCH * HTTP/1.1\r\n"
"HOST:239.255.255.250:1900\r\n"
"MAN:\"ssdp:discover\"\r\n"
"MX:2\r\n"
"ST: ssdp:all\r\n\r\n");
writeDatagram(ssdpSearchMessage, m_host, m_port);
writeDiscoveryPacket();
}
void UpnpDiscovery::stopDiscovery()
{
qDebug() << "stop discovering";
m_timer->stop();
m_timer.stop();
m_repeatTimer.stop();
m_discoveryModel->clearModel();
setDiscovering(false);
}
@ -121,6 +118,17 @@ void UpnpDiscovery::setAvailable(const bool &available)
emit availableChanged();
}
void UpnpDiscovery::writeDiscoveryPacket()
{
QByteArray ssdpSearchMessage = QByteArray("M-SEARCH * HTTP/1.1\r\n"
"HOST:239.255.255.250:1900\r\n"
"MAN:\"ssdp:discover\"\r\n"
"MX:2\r\n"
"ST: ssdp:all\r\n\r\n");
writeDatagram(ssdpSearchMessage, m_host, m_port);
}
void UpnpDiscovery::error(QAbstractSocket::SocketError error)
{
qWarning() << "UPnP socket error:" << error << errorString();
@ -165,17 +173,17 @@ void UpnpDiscovery::readData()
if (!m_foundDevices.contains(location) && isGuh) {
m_foundDevices.append(location);
UpnpDevice upnpDevice;
upnpDevice.setHostAddress(hostAddress);
upnpDevice.setPort(port);
upnpDevice.setLocation(location.toString());
DiscoveryDevice discoveryDevice;
discoveryDevice.setHostAddress(hostAddress);
discoveryDevice.setPort(port);
discoveryDevice.setLocation(location.toString());
qDebug() << "Getting server data from:" << location;
QNetworkReply *reply = m_networkAccessManager->get(QNetworkRequest(location));
connect(reply, &QNetworkReply::sslErrors, [this, reply](const QList<QSslError> &errors){
reply->ignoreSslErrors(errors);
});
m_runningReplies.insert(reply, upnpDevice);
m_runningReplies.insert(reply, discoveryDevice);
}
}
}
@ -187,7 +195,7 @@ void UpnpDiscovery::networkReplyFinished(QNetworkReply *reply)
switch (status) {
case(200):{
QByteArray data = reply->readAll();
UpnpDevice upnpDevice = m_runningReplies.take(reply);
DiscoveryDevice discoveryDevice = m_runningReplies.take(reply);
// parse XML data
QXmlStreamReader xml(data);
@ -199,13 +207,13 @@ void UpnpDiscovery::networkReplyFinished(QNetworkReply *reply)
if (xml.isStartElement()) {
if (xml.name().toString() == "websocketURL") {
upnpDevice.setWebSocketUrl(xml.readElementText());
discoveryDevice.setWebSocketUrl(xml.readElementText());
}
}
if (xml.isStartElement()) {
if (xml.name().toString() == "guhRpcURL") {
upnpDevice.setGuhRpcUrl(xml.readElementText());
discoveryDevice.setGuhRpcUrl(xml.readElementText());
}
}
@ -213,28 +221,28 @@ void UpnpDiscovery::networkReplyFinished(QNetworkReply *reply)
if (xml.name().toString() == "device") {
while (!xml.atEnd()) {
if (xml.name() == "friendlyName" && xml.isStartElement()) {
upnpDevice.setFriendlyName(xml.readElementText());
discoveryDevice.setFriendlyName(xml.readElementText());
}
if (xml.name() == "manufacturer" && xml.isStartElement()) {
upnpDevice.setManufacturer(xml.readElementText());
discoveryDevice.setManufacturer(xml.readElementText());
}
if (xml.name() == "manufacturerURL" && xml.isStartElement()) {
upnpDevice.setManufacturerURL(QUrl(xml.readElementText()));
discoveryDevice.setManufacturerURL(QUrl(xml.readElementText()));
}
if (xml.name() == "modelDescription" && xml.isStartElement()) {
upnpDevice.setModelDescription(xml.readElementText());
discoveryDevice.setModelDescription(xml.readElementText());
}
if (xml.name() == "modelName" && xml.isStartElement()) {
upnpDevice.setModelName(xml.readElementText());
discoveryDevice.setModelName(xml.readElementText());
}
if (xml.name() == "modelNumber" && xml.isStartElement()) {
upnpDevice.setModelNumber(xml.readElementText());
discoveryDevice.setModelNumber(xml.readElementText());
}
if (xml.name() == "modelURL" && xml.isStartElement()) {
upnpDevice.setModelURL(QUrl(xml.readElementText()));
discoveryDevice.setModelURL(QUrl(xml.readElementText()));
}
if (xml.name() == "UDN" && xml.isStartElement()) {
upnpDevice.setUuid(xml.readElementText());
discoveryDevice.setUuid(xml.readElementText());
}
xml.readNext();
}
@ -244,10 +252,10 @@ void UpnpDiscovery::networkReplyFinished(QNetworkReply *reply)
}
if (upnpDevice.friendlyName().contains("guh")) {
qDebug() << upnpDevice;
if (!m_discoveryModel->contains(upnpDevice.uuid())) {
m_discoveryModel->addDevice(upnpDevice);
if (discoveryDevice.friendlyName().contains("guh")) {
qDebug() << discoveryDevice;
if (!m_discoveryModel->contains(discoveryDevice.uuid())) {
m_discoveryModel->addDevice(discoveryDevice);
}
}
@ -264,5 +272,6 @@ void UpnpDiscovery::networkReplyFinished(QNetworkReply *reply)
void UpnpDiscovery::onTimeout()
{
qDebug() << "discovery timeout";
m_repeatTimer.stop();
setDiscovering(false);
}

View File

@ -27,21 +27,21 @@
#include <QNetworkAccessManager>
#include <QTimer>
#include "upnpdevice.h"
#include "upnpdiscoverymodel.h"
#include "discoverydevice.h"
#include "discoverymodel.h"
class UpnpDiscovery : public QUdpSocket
{
Q_OBJECT
Q_PROPERTY(bool discovering READ discovering NOTIFY discoveringChanged)
Q_PROPERTY(bool available READ available NOTIFY availableChanged)
Q_PROPERTY(UpnpDiscoveryModel *discoveryModel READ discoveryModel NOTIFY discoveryModelChanged)
Q_PROPERTY(DiscoveryModel *discoveryModel READ discoveryModel NOTIFY discoveryModelChanged)
public:
explicit UpnpDiscovery(QObject *parent = 0);
bool discovering() const;
UpnpDiscoveryModel *discoveryModel();
DiscoveryModel *discoveryModel();
bool available() const;
@ -51,16 +51,17 @@ public:
private:
QNetworkAccessManager *m_networkAccessManager;
QTimer *m_timer;
QTimer m_timer;
QTimer m_repeatTimer;
QHostAddress m_host;
qint16 m_port;
UpnpDiscoveryModel *m_discoveryModel;
DiscoveryModel *m_discoveryModel;
bool m_discovering;
bool m_available;
QHash<QNetworkReply *, UpnpDevice> m_runningReplies;
QHash<QNetworkReply *, DiscoveryDevice> m_runningReplies;
QList<QUrl> m_foundDevices;
void setDiscovering(const bool &discovering);
@ -72,6 +73,7 @@ signals:
void discoveryModelChanged();
private slots:
void writeDiscoveryPacket();
void error(QAbstractSocket::SocketError error);
void readData();
void networkReplyFinished(QNetworkReply *reply);

View File

@ -0,0 +1,70 @@
#include "zeroconfdiscovery.h"
#include <QUuid>
ZeroconfDiscovery::ZeroconfDiscovery(QObject *parent) : QObject(parent)
{
m_discoveryModel = new DiscoveryModel(this);
#ifdef WITH_AVAHI
m_serviceBrowser = new QtAvahiServiceBrowser(this);
connect(m_serviceBrowser, &QtAvahiServiceBrowser::serviceEntryAdded, this, &ZeroconfDiscovery::serviceEntryAdded);
m_serviceBrowser->enable();
#endif
}
bool ZeroconfDiscovery::available() const
{
#ifdef WITH_AVAHI
return true;
#else
return false;
#endif
}
bool ZeroconfDiscovery::discovering() const
{
return true;
}
DiscoveryModel *ZeroconfDiscovery::discoveryModel() const
{
return m_discoveryModel;
}
#ifdef WITH_AVAHI
void ZeroconfDiscovery::serviceEntryAdded(const AvahiServiceEntry &entry)
{
if (!entry.name().startsWith("guhIO") || entry.serviceType() != "_jsonrpc._tcp") {
return;
}
qDebug() << "avahi service entry added" << entry.name() << entry.hostAddress() << entry.port() << entry.txt() << entry.serviceType();
QString uuid;
bool sslEnabled = false;
foreach (const QString &txt, entry.txt()) {
QPair<QString, QString> txtRecord = qMakePair<QString, QString>(txt.split("=").first(), txt.split("=").at(1));
if (!sslEnabled && txtRecord.first == "sslEnabled") {
sslEnabled = (txtRecord.second == "true");
}
if (txtRecord.first == "uuid") {
uuid = txtRecord.second;
}
}
DiscoveryDevice dev = m_discoveryModel->find(entry.hostAddress());
if (dev.uuid() == uuid && dev.guhRpcUrl().startsWith("guhs") && !sslEnabled) {
// We already have this host and with a more secure configuration... skip this one...
return;
}
dev.setUuid(uuid);
dev.setHostAddress(entry.hostAddress());
dev.setPort(entry.port());
dev.setFriendlyName(entry.hostName());
dev.setGuhRpcUrl(QString("%1://%2:%3").arg(sslEnabled ? "guhs" : "guh").arg(entry.hostAddress().toString()).arg(entry.port()));
m_discoveryModel->addDevice(dev);
// DiscoveryDevice *dev = new DiscoveryDevice();
// dev->setFriendlyName(entry.hostName());
}
#endif

View File

@ -0,0 +1,45 @@
#ifndef ZEROCONFDISCOVERY_H
#define ZEROCONFDISCOVERY_H
#ifdef WITH_AVAHI
#include "avahi/qtavahiservicebrowser.h"
#endif
#include "discoverymodel.h"
#include <QObject>
class ZeroconfDiscovery : public QObject
{
Q_OBJECT
Q_PROPERTY(bool discovering READ discovering NOTIFY discoveringChanged)
Q_PROPERTY(bool available READ available CONSTANT)
Q_PROPERTY(DiscoveryModel* discoveryModel READ discoveryModel CONSTANT)
public:
explicit ZeroconfDiscovery(QObject *parent = nullptr);
bool available() const;
bool discovering() const;
DiscoveryModel* discoveryModel() const;
signals:
void discoveringChanged();
public slots:
private:
DiscoveryModel *m_discoveryModel;
#ifdef WITH_AVAHI
QtAvahiServiceBrowser *m_serviceBrowser;
private slots:
void serviceEntryAdded(const AvahiServiceEntry &entry);
#endif
};
#endif // ZEROCONFDISCOVERY_H

View File

@ -31,6 +31,7 @@
#include "pluginsproxy.h"
#include "devicediscovery.h"
#include "discovery/upnpdiscovery.h"
#include "discovery/zeroconfdiscovery.h"
#include "interfacesmodel.h"
int main(int argc, char *argv[])
@ -85,6 +86,7 @@ int main(int argc, char *argv[])
qmlRegisterType<PluginsProxy>(uri, 1, 0, "PluginsProxy");
qmlRegisterType<UpnpDiscovery>(uri, 1, 0, "UpnpDiscovery");
qmlRegisterType<ZeroconfDiscovery>(uri, 1, 0, "ZeroconfDiscovery");
Engine::instance();

View File

@ -47,7 +47,7 @@ void TcpSocketInterface::connect(const QUrl &url)
{
m_url = url;
if (url.scheme() == "guhs") {
qDebug() << "connecting to" << url.host();
qDebug() << "connecting to" << url.host() << url.port();
m_socket.connectToHostEncrypted(url.host(), url.port());
} else if (url.scheme() == "guh") {
m_socket.connectToHost(url.host(), url.port());

View File

@ -12,8 +12,8 @@ Page {
if (settings.lastConnectedHost.length > 0) {
internalPageStack.push(connectingPage)
Engine.connection.connect(settings.lastConnectedHost)
} else if (upnpDiscovery.discoveryModel.count <= 1) {
upnpDiscovery.discover()
} else if (discovery.discoveryModel.count <= 1) {
discovery.discover()
} else {
internalPageStack.pop();
internalPageStack.push(searchResultsPage)
@ -29,25 +29,25 @@ Page {
certDialog.open();
}
onConnectionError: {
upnpDiscovery.discover();
discovery.discover();
}
}
Connections {
target: upnpDiscovery
target: discovery
onDiscoveringChanged: {
print("discoverying changed", upnpDiscovery.discovering)
if (upnpDiscovery.discovering) {
print("discoverying changed", discovery.discovering)
if (discovery.discovering) {
internalPageStack.pop();
} else {
switch (upnpDiscovery.discoveryModel.count) {
switch (discovery.discoveryModel.count) {
case 0:
internalPageStack.push(noResultsPage);
break;
case 1:
Engine.connection.connect(upnpDiscovery.discoveryModel.get(0, "guhRpcUrl"))
Engine.connection.connect(discovery.discoveryModel.get(0, "guhRpcUrl"))
internalPageStack.push(connectingPage);
break;
default:
@ -77,7 +77,7 @@ Page {
BusyIndicator {
anchors.horizontalCenter: parent.horizontalCenter
running: upnpDiscovery.discovering
running: discovery.discovering
}
}
}
@ -107,7 +107,7 @@ Page {
Button {
Layout.fillWidth: true
text: "Try again!"
onClicked: upnpDiscovery.discover()
onClicked: discovery.discover()
}
}
}
@ -136,7 +136,7 @@ Page {
Label {
Layout.fillWidth: true
text: qsTr("There are %1 guh boxes in your network! Which one would you like to use?").arg(upnpDiscovery.discoveryModel.count)
text: qsTr("There are %1 guh boxes in your network! Which one would you like to use?").arg(discovery.discoveryModel.count)
wrapMode: Text.WordWrap
}
}
@ -150,7 +150,7 @@ Page {
ListView {
Layout.fillWidth: true
Layout.fillHeight: true
model: upnpDiscovery.discoveryModel
model: discovery.discoveryModel
delegate: ItemDelegate {
width: parent.width
height: app.delegateHeight
@ -194,7 +194,7 @@ Page {
text: "Search again!"
Layout.fillWidth: true
onClicked: {
upnpDiscovery.discover()
discovery.discover()
}
}
}

View File

@ -71,6 +71,10 @@ ApplicationWindow {
}
UpnpDiscovery {
id: upnpDiscovery
id: discovery
}
// ZeroconfDiscovery {
// id: discovery
// }
}