diff --git a/debian/control b/debian/control index e9fe26ad..3b3a2220 100644 --- a/debian/control +++ b/debian/control @@ -7,8 +7,11 @@ Build-Depends: debhelper (>= 9.0.0), hardening-wrapper, python, qt5-default, - qtbase5-dev -Standards-Version: 3.9.3 + qtbase5-dev, + qtconnectivity5-dev, + libavahi-client-dev, + libavahi-common-dev +Standards-Version: 3.9.5 Package: guh Architecture: any @@ -83,7 +86,9 @@ Section: libs Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, - libqt5bluetooth5 + libqt5bluetooth5, + libavahi-client3, + libavahi-common3 Description: An open source IoT server - core library guh is an open source IoT (Internet of Things) server, which allows to control a lot of different devices from many different diff --git a/debian/copyright b/debian/copyright index 4a3a9769..0a13c3d9 100644 --- a/debian/copyright +++ b/debian/copyright @@ -16,3 +16,16 @@ Files: debian/* Copyright: (C) 2014 Michael Zanetti 2014 Simon Stürz License: GPL-2+ + +Files: libguh/avahi/zconf* +Copyright: (c) 2012 Johannes Hilden + 2016 Simon Stürz +License: LGPL-2.1 + + +Files: libguh/avahi/qt-watch* +Copyright: (c) avahi + 2016 Simon Stürz +License: LGPL-2.1 + + diff --git a/libguh/coap/coap.cpp b/libguh/coap/coap.cpp index 819340e4..b16ad21c 100644 --- a/libguh/coap/coap.cpp +++ b/libguh/coap/coap.cpp @@ -83,7 +83,7 @@ Coap::Coap(QObject *parent, const quint16 &port) : { m_socket = new QUdpSocket(this); - if (!m_socket->bind(QHostAddress::Any, port)) + if (!m_socket->bind(QHostAddress::Any, port, QAbstractSocket::ShareAddress)) qCWarning(dcCoap) << "Could not bind to port" << port << m_socket->errorString(); connect(m_socket, SIGNAL(readyRead()), this, SLOT(onReadyRead())); diff --git a/libguh/devicemanager.h b/libguh/devicemanager.h index 0f654f6c..2b93dd03 100644 --- a/libguh/devicemanager.h +++ b/libguh/devicemanager.h @@ -33,6 +33,8 @@ #include "types/vendor.h" #include "network/networkmanager.h" +#include "network/upnp/upnpdiscovery.h" +#include "network/upnp/upnpdevicedescriptor.h" #ifdef BLUETOOTH_LE #include "bluetooth/bluetoothscanner.h" diff --git a/libguh/libguh.pro b/libguh/libguh.pro index 9432ae37..071e7b0a 100644 --- a/libguh/libguh.pro +++ b/libguh/libguh.pro @@ -11,6 +11,10 @@ QMAKE_LFLAGS += -fPIC target.path = /usr/lib INSTALLS += target +# Avahi libs +LIBS += -lavahi-common -lavahi-client + +# check Bluetooth LE support contains(DEFINES, BLUETOOTH_LE) { HEADERS += bluetooth/bluetoothscanner.h \ bluetooth/bluetoothlowenergydevice.h \ @@ -32,15 +36,21 @@ HEADERS += devicemanager.h \ plugin/devicepairinginfo.h \ hardware/gpio.h \ hardware/gpiomonitor.h \ + hardware/pwm.h \ hardware/radio433/radio433.h \ hardware/radio433/radio433transmitter.h \ hardware/radio433/radio433brennenstuhlgateway.h \ - network/upnpdiscovery/upnpdiscovery.h \ - network/upnpdiscovery/upnpdevice.h \ - network/upnpdiscovery/upnpdevicedescriptor.h \ - network/upnpdiscovery/upnpdiscoveryrequest.h \ + network/upnp/upnpdiscovery.h \ + network/upnp/upnpdevice.h \ + network/upnp/upnpdevicedescriptor.h \ + network/upnp/upnpdiscoveryrequest.h \ network/networkmanager.h \ network/oauth2.h \ + network/avahi/qt-watch.h \ + network/avahi/avahiserviceentry.h \ + network/avahi/zconfservicebrowser.h \ + network/avahi/zconfserviceclient.h \ + network/avahi/zconfservice.h \ coap/coap.h \ coap/coappdu.h \ coap/coapoption.h \ @@ -64,7 +74,6 @@ HEADERS += devicemanager.h \ types/ruleaction.h \ types/ruleactionparam.h \ types/statedescriptor.h \ - hardware/pwm.h SOURCES += devicemanager.cpp \ @@ -77,15 +86,21 @@ SOURCES += devicemanager.cpp \ plugin/devicepairinginfo.cpp \ hardware/gpio.cpp \ hardware/gpiomonitor.cpp \ + hardware/pwm.cpp \ hardware/radio433/radio433.cpp \ hardware/radio433/radio433transmitter.cpp \ hardware/radio433/radio433brennenstuhlgateway.cpp \ - network/upnpdiscovery/upnpdiscovery.cpp \ - network/upnpdiscovery/upnpdevice.cpp \ - network/upnpdiscovery/upnpdevicedescriptor.cpp \ - network/upnpdiscovery/upnpdiscoveryrequest.cpp \ + network/upnp/upnpdiscovery.cpp \ + network/upnp/upnpdevice.cpp \ + network/upnp/upnpdevicedescriptor.cpp \ + network/upnp/upnpdiscoveryrequest.cpp \ network/networkmanager.cpp \ network/oauth2.cpp \ + network/avahi/qt-watch.cpp \ + network/avahi/avahiserviceentry.cpp \ + network/avahi/zconfservicebrowser.cpp \ + network/avahi/zconfserviceclient.cpp \ + network/avahi/zconfservice.cpp \ coap/coap.cpp \ coap/coappdu.cpp \ coap/coapoption.cpp \ @@ -109,7 +124,6 @@ SOURCES += devicemanager.cpp \ types/ruleaction.cpp \ types/ruleactionparam.cpp \ types/statedescriptor.cpp \ - hardware/pwm.cpp # install plugininfo python script for libguh-dev diff --git a/libguh/network/avahi/avahiserviceentry.cpp b/libguh/network/avahi/avahiserviceentry.cpp new file mode 100644 index 00000000..f0447abd --- /dev/null +++ b/libguh/network/avahi/avahiserviceentry.cpp @@ -0,0 +1,113 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2016 Simon Stürz * + * * + * This file is part of guh. * + * * + * Guh 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 2 of the License. * + * * + * Guh 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. If not, see . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "avahiserviceentry.h" + +AvahiServiceEntry::AvahiServiceEntry() : + m_port(0), + m_protocol(QAbstractSocket::UnknownNetworkLayerProtocol) +{ + +} + +AvahiServiceEntry::AvahiServiceEntry(QString name, QHostAddress hostAddress, QString domain, QString hostName, quint16 port, QAbstractSocket::NetworkLayerProtocol protocol, QStringList txt, AvahiLookupResultFlags flags) : + m_name(name), + m_hostAddress(hostAddress), + m_domain(domain), + m_hostName(hostName), + m_port(port), + m_protocol(protocol), + m_txt(txt), + m_flags(flags) +{ + +} + +QString AvahiServiceEntry::name() const +{ + return m_name; +} + + +QHostAddress AvahiServiceEntry::hostAddress() const +{ + return m_hostAddress; +} + +QString AvahiServiceEntry::domain() const +{ + return m_domain; +} + +QString AvahiServiceEntry::hostName() const +{ + return m_hostName; +} + +quint16 AvahiServiceEntry::port() const +{ + return m_port; +} + +QAbstractSocket::NetworkLayerProtocol AvahiServiceEntry::protocol() const +{ + return m_protocol; +} + +AvahiLookupResultFlags AvahiServiceEntry::flags() const +{ + return m_flags; +} + +QStringList AvahiServiceEntry::txt() const +{ + return m_txt; +} + +bool AvahiServiceEntry::isValid() const +{ + return !m_hostAddress.isNull() && !m_hostName.isEmpty() && m_port != 0 && m_protocol != QAbstractSocket::UnknownNetworkLayerProtocol; +} + +bool AvahiServiceEntry::isChached() const +{ + return m_flags & AVAHI_LOOKUP_RESULT_CACHED; +} + +bool AvahiServiceEntry::isWideArea() const +{ + return m_flags & AVAHI_LOOKUP_RESULT_WIDE_AREA; +} + +bool AvahiServiceEntry::isMulticast() const +{ + return m_flags & AVAHI_LOOKUP_RESULT_MULTICAST; +} + +bool AvahiServiceEntry::isLocal() const +{ + return m_flags & AVAHI_LOOKUP_RESULT_LOCAL; +} + +bool AvahiServiceEntry::isOurOwn() const +{ + return m_flags & AVAHI_LOOKUP_RESULT_OUR_OWN; +} + diff --git a/libguh/network/avahi/avahiserviceentry.h b/libguh/network/avahi/avahiserviceentry.h new file mode 100644 index 00000000..22a9f4f5 --- /dev/null +++ b/libguh/network/avahi/avahiserviceentry.h @@ -0,0 +1,64 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2016 Simon Stürz * + * * + * This file is part of guh. * + * * + * Guh 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 2 of the License. * + * * + * Guh 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. If not, see . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef AVAHISERVICEENTRY_H +#define AVAHISERVICEENTRY_H + +#include +#include +#include +#include + +class AvahiServiceEntry +{ +public: + AvahiServiceEntry(); + AvahiServiceEntry(QString name, QHostAddress hostAddress, QString domain, QString hostName, quint16 port, QAbstractSocket::NetworkLayerProtocol protocol, QStringList txt, AvahiLookupResultFlags flags); + + QString name() 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; + +private: + QString m_name; + QHostAddress m_hostAddress; + QString m_domain; + QString m_hostName; + quint16 m_port; + QAbstractSocket::NetworkLayerProtocol m_protocol; + QStringList m_txt; + AvahiLookupResultFlags m_flags; + +}; + +#endif // AVAHISERVICEENTRY_H diff --git a/libguh/network/avahi/qt-watch.cpp b/libguh/network/avahi/qt-watch.cpp new file mode 100644 index 00000000..b39fc080 --- /dev/null +++ b/libguh/network/avahi/qt-watch.cpp @@ -0,0 +1,193 @@ +/*** + This file is part of avahi. + + Modified: (C) 2016 Simon Stürz + + 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. + +***/ + +#include +#include +#include + +#include +#include + +#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_IN; + 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" + diff --git a/libguh/network/avahi/qt-watch.h b/libguh/network/avahi/qt-watch.h new file mode 100644 index 00000000..6cde472b --- /dev/null +++ b/libguh/network/avahi/qt-watch.h @@ -0,0 +1,35 @@ +#ifndef QAVAHI_H +#define QAVAHI_H + +/*** + This file is part of avahi. + + Modified: (C) 2016 Simon Stürz + + 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. + +***/ + +/** \file qt-watch.h Qt main loop adapter */ + +#include + +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 diff --git a/libguh/network/avahi/zconfservice.cpp b/libguh/network/avahi/zconfservice.cpp new file mode 100644 index 00000000..1ea4790b --- /dev/null +++ b/libguh/network/avahi/zconfservice.cpp @@ -0,0 +1,169 @@ +/* + * This file is part of qtzeroconf. (c) 2012 Johannes Hilden + * https://github.com/johanneshilden/qtzeroconf + * + * Modified: (C) 2016 Simon Stürz + * + * qtzeroconf is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * qtzeroconf is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + * Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with qtzeroconf; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include +#include +#include +#include +#include +#include "zconfserviceclient.h" +#include "zconfservice.h" + +class ZConfServicePrivate +{ +public: + ZConfServicePrivate() + : client(0), group(0), error(0) + { + } + + static void callback(AvahiEntryGroup *group, AvahiEntryGroupState state, void *userdata) + { + Q_UNUSED(group); + ZConfService *serviceGroup = static_cast(userdata); + if (serviceGroup) { + switch (state) + { + case AVAHI_ENTRY_GROUP_ESTABLISHED: + emit serviceGroup->entryGroupEstablished(); + qDebug() << ("Service '" % serviceGroup->d_ptr->name % "' successfully establised."); + break; + case AVAHI_ENTRY_GROUP_COLLISION: + emit serviceGroup->entryGroupNameCollision(); + break; + case AVAHI_ENTRY_GROUP_FAILURE: + emit serviceGroup->entryGroupFailure(); + qDebug() << ("Entry group failure: " % serviceGroup->errorString()); + break; + case AVAHI_ENTRY_GROUP_UNCOMMITED: + qDebug() << "AVAHI_ENTRY_GROUP_UNCOMMITED"; + break; + case AVAHI_ENTRY_GROUP_REGISTERING: + qDebug() << "AVAHI_ENTRY_GROUP_REGISTERING"; + } // end switch + } + } + + ZConfServiceClient *client; + AvahiEntryGroup *group; + QString name; + in_port_t port; + QString type; + int error; +}; + +/*! + \class ZConfService + + \brief This class provides Avahi Zeroconf service registration. It can be + used by server applications to announce a service on the local area network. + + Typical use involves creating an instance of ZConfService and calling + registerService() with a service name and port number. + */ + +ZConfService::ZConfService(QObject *parent) + : QObject(parent), + d_ptr(new ZConfServicePrivate) +{ + d_ptr->client = new ZConfServiceClient(this); + d_ptr->client->run(); +} + +/*! + Destroys the object and releases all resources associated with it. + */ +ZConfService::~ZConfService() +{ + if (d_ptr->group) + avahi_entry_group_free(d_ptr->group); + delete d_ptr; +} + +/*! + Returns true if the service group was added and commited without error. + */ +bool ZConfService::isValid() const +{ + return (d_ptr->group && !d_ptr->error); +} + +/*! + Returns a human readable error string with details of the last error that + occured. + */ +QString ZConfService::errorString() const +{ + if (!d_ptr->client->client) + return "No client!"; + return avahi_strerror(avahi_client_errno(d_ptr->client->client)); +} + +/*! + Registers a Zeroconf service on the LAN. If no service type is specified, + "_http._tcp" is assumed. Needless to say, the server should be available + and listen on the specified port. + */ +void ZConfService::registerService(QString name, in_port_t port, QString type) +{ + if (!d_ptr->client->client || AVAHI_CLIENT_S_RUNNING + != avahi_client_get_state(d_ptr->client->client)) { + qDebug() << "ZConfService error: Client is not running."; + return; + } + + d_ptr->name = name; + d_ptr->port = port; + d_ptr->type = type; + + if (!d_ptr->group) { + d_ptr->group = avahi_entry_group_new(d_ptr->client->client, + ZConfServicePrivate::callback, + this); + } + + if (avahi_entry_group_is_empty(d_ptr->group)) { + d_ptr->error = avahi_entry_group_add_service(d_ptr->group, + AVAHI_IF_UNSPEC, + AVAHI_PROTO_UNSPEC, + (AvahiPublishFlags) 0, + d_ptr->name.toLatin1().data(), + d_ptr->type.toLatin1().data(), + 0, + 0, + d_ptr->port, + NULL); + if (!d_ptr->error) { + d_ptr->error = avahi_entry_group_commit(d_ptr->group); + } + if (d_ptr->error) + qDebug() << ("Error creating service: " % errorString()); + } +} + +/*! + Deregisters the service associated with this object. You can reuse the same + ZConfService object at any time to register another service on the network. + */ +void ZConfService::resetService() +{ + avahi_entry_group_reset(d_ptr->group); +} diff --git a/libguh/network/avahi/zconfservice.h b/libguh/network/avahi/zconfservice.h new file mode 100644 index 00000000..bcf3a495 --- /dev/null +++ b/libguh/network/avahi/zconfservice.h @@ -0,0 +1,56 @@ +/* + * This file is part of qtzeroconf. (c) 2012 Johannes Hilden + * https://github.com/johanneshilden/qtzeroconf + * + * Modified: (C) 2016 Simon Stürz + * + * qtzeroconf is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * qtzeroconf is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + * Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with qtzeroconf; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#ifndef ZCONFSERVICE_H +#define ZCONFSERVICE_H + +#include +#include + +class ZConfServicePrivate; +class ZConfService : public QObject +{ + Q_OBJECT + +public: + explicit ZConfService(QObject *parent = 0); + ~ZConfService(); + + bool isValid() const; + QString errorString() const; + +signals: + void entryGroupEstablished(); + void entryGroupNameCollision(); + void entryGroupFailure(); + +public slots: + void registerService(QString name, in_port_t port, QString type = "_http._tcp"); + void resetService(); + +protected: + ZConfServicePrivate *const d_ptr; + +private: + Q_DECLARE_PRIVATE(ZConfService) +}; + +#endif // ZCONFSERVICE_H diff --git a/libguh/network/avahi/zconfservicebrowser.cpp b/libguh/network/avahi/zconfservicebrowser.cpp new file mode 100644 index 00000000..298fc0cb --- /dev/null +++ b/libguh/network/avahi/zconfservicebrowser.cpp @@ -0,0 +1,230 @@ +/* + * This file is part of qtzeroconf. (c) 2012 Johannes Hilden + * https://github.com/johanneshilden/qtzeroconf + * + * Modified: (C) 2016 Simon Stürz + * + * qtzeroconf is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * qtzeroconf is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + * Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with qtzeroconf; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include +#include +#include +#include + +#include "zconfservicebrowser.h" +#include "zconfserviceclient.h" + +class ZConfServiceBrowserPrivate +{ +public: + ZConfServiceBrowserPrivate(ZConfServiceClient *client) + : client(client), browser(0) + { + } + + static void callback(AvahiServiceBrowser *browser, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *name, + const char *type, + const char *domain, + AvahiLookupResultFlags flags, + void *userdata) + { + Q_UNUSED(browser); + Q_UNUSED(flags); + + ZConfServiceBrowser *serviceBrowser = static_cast(userdata); + if (serviceBrowser) { + switch (event) { + case AVAHI_BROWSER_FAILURE: + qDebug() << ("Avahi browser error: " % QString(avahi_strerror(avahi_client_errno(serviceBrowser->d_ptr->client->client)))); + break; + case AVAHI_BROWSER_NEW: + qDebug() << ("New service '" % QString(name) % "' of type " % QString(type) % " in domain " % QString(domain) % "."); + + // We ignore the returned resolver object. In the callback + // function we free it. If the server is terminated before + // the callback function is called the server will free + // the resolver for us. + if (!(avahi_service_resolver_new(serviceBrowser->d_ptr->client->client, + interface, + protocol, + name, + serviceBrowser->d_ptr->type.toLatin1().data(), + domain, + AVAHI_PROTO_UNSPEC, + (AvahiLookupFlags) 0, + ZConfServiceBrowserPrivate::resolve, + serviceBrowser))) + qDebug() << ("Failed to resolve service '" % QString(name) % "': " % avahi_strerror(avahi_client_errno(serviceBrowser->d_ptr->client->client))); + break; + case AVAHI_BROWSER_REMOVE: + serviceBrowser->d_ptr->entries.remove(name); + emit serviceBrowser->serviceEntryRemoved(name); + qDebug() << "Service '" % QString(name) % "' removed from the network."; + break; + case AVAHI_BROWSER_ALL_FOR_NOW: + case AVAHI_BROWSER_CACHE_EXHAUSTED: + qDebug() << (AVAHI_BROWSER_ALL_FOR_NOW == event + ? "AVAHI_BROWSER_ALL_FOR_NOW" + : "AVAHI_BROWSER_CACHE_EXHAUSTED"); + } // end switch + } + } + + static void resolve(AvahiServiceResolver *resolver, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiResolverEvent event, + const char *name, + const char *type, + const char *domain, + const char *host_name, + const AvahiAddress *address, + uint16_t port, + AvahiStringList *txt, + AvahiLookupResultFlags flags, + void *userdata) + { + Q_UNUSED(interface); + Q_UNUSED(type); + Q_UNUSED(txt); + + ZConfServiceBrowser *serviceBrowser = static_cast(userdata); + if (serviceBrowser) { + switch (event) { + case AVAHI_RESOLVER_FAILURE: + qDebug() << ("Failed to resolve service '" % QString(name) % "': " % avahi_strerror(avahi_client_errno(serviceBrowser->d_ptr->client->client))); + break; + case AVAHI_RESOLVER_FOUND: + { + char a[AVAHI_ADDRESS_STR_MAX]; + avahi_address_snprint(a, sizeof(a), address); + + // convert protocol + QAbstractSocket::NetworkLayerProtocol p; + if (protocol == AVAHI_PROTO_INET) { + p = QAbstractSocket::IPv4Protocol; + } else if (protocol == AVAHI_PROTO_INET6) { + p = QAbstractSocket::IPv6Protocol; + } else { + p = QAbstractSocket::UnknownNetworkLayerProtocol; + } + + QStringList txtList; + // TODO: get txt string list + // // get txt string list + // if (txt) { + // AvahiStringList *txtAtt = txt->text; + // txtAtt = (txt ? txt->next : 0); + // while (txtAtt) { + // qDebug() << txt; + // txtList.append(QString(txt->)); + // } + // } + + + AvahiServiceEntry entry = AvahiServiceEntry(name, QHostAddress(QString(a)), QString(domain), QString(host_name), (quint16)port, p, txtList, flags); + + serviceBrowser->d_ptr->entries.insert(name, entry); + emit serviceBrowser->serviceEntryAdded(name); + } + } + avahi_service_resolver_free(resolver); + } + } + + typedef QHash ZConfServiceEntryTable; + + ZConfServiceClient *const client; + AvahiServiceBrowser *browser; + ZConfServiceEntryTable entries; + QString type; +}; + +/*! + \class ZConfServiceBrowser + + \brief AvahiServiceBrowser wrapper that lets you browse for services + available on the local network. This class can be used to handle Zeroconf + service discovery in a Qt-based client application. + + Instantiate a ZConfServiceBrowser object and call browse() with the desired + service type as argument (e.g., "_http._tcp" or "_ipp._tcp"). + + ZConfServiceBrowser will emit serviceEntryAdded() when a new service is + discovered and serviceEntryRemoved() when a service is removed from the + network. + */ + +/*! + Creates a Zeroconf service browser. Call browse() to start browsing for + services. + */ +ZConfServiceBrowser::ZConfServiceBrowser(QObject *parent) + : QObject(parent), + d_ptr(new ZConfServiceBrowserPrivate(new ZConfServiceClient(this))) +{ + connect(d_ptr->client, SIGNAL(clientRunning()), this, SLOT(createServiceBrowser())); +} + +/*! + Destroys the browser object and releases all resources associated with it. + */ +ZConfServiceBrowser::~ZConfServiceBrowser() +{ + if (d_ptr->browser) + avahi_service_browser_free(d_ptr->browser); + delete d_ptr; +} + +/*! + Browses for Zeroconf services on the LAN. This is a non-blocking call. + ZConfServiceBrowser will emit serviceEntryAdded() when a new service is + discovered and serviceEntryRemoved() when a service is removed from the + network. + */ +void ZConfServiceBrowser::browse(QString serviceType) +{ + d_ptr->type = serviceType; + if(d_ptr->client) + d_ptr->client->run(); +} + +/*! + Returns a ZConfServiceEntry struct with detailed information about the + Zeroconf service associated with the name. + */ +AvahiServiceEntry ZConfServiceBrowser::serviceEntry(QString name) +{ + return d_ptr->entries.value(name); +} + +void ZConfServiceBrowser::createServiceBrowser() +{ + if (d_ptr->browser) + return; + d_ptr->browser = avahi_service_browser_new(d_ptr->client->client, + AVAHI_IF_UNSPEC, + AVAHI_PROTO_UNSPEC, + d_ptr->type.toLatin1().data(), + NULL, + (AvahiLookupFlags) 0, + ZConfServiceBrowserPrivate::callback, + this); +} diff --git a/libguh/network/avahi/zconfservicebrowser.h b/libguh/network/avahi/zconfservicebrowser.h new file mode 100644 index 00000000..d18fc1be --- /dev/null +++ b/libguh/network/avahi/zconfservicebrowser.h @@ -0,0 +1,57 @@ +/* + * This file is part of qtzeroconf. (c) 2012 Johannes Hilden + * https://github.com/johanneshilden/qtzeroconf + * + * Modified: (C) 2016 Simon Stürz + * + * qtzeroconf is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * qtzeroconf is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + * Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with qtzeroconf; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#ifndef ZCONFSERVICEBROWSER_H +#define ZCONFSERVICEBROWSER_H + +#include +#include +#include + +#include "avahiserviceentry.h" + +class ZConfServiceBrowserPrivate; +class ZConfServiceBrowser : public QObject +{ + Q_OBJECT + +public: + explicit ZConfServiceBrowser(QObject *parent = 0); + ~ZConfServiceBrowser(); + + void browse(QString serviceType = "_http._tcp"); + AvahiServiceEntry serviceEntry(QString name); + +signals: + void serviceEntryAdded(QString); + void serviceEntryRemoved(QString); + +protected slots: + void createServiceBrowser(); + +protected: + ZConfServiceBrowserPrivate *const d_ptr; + +private: + Q_DECLARE_PRIVATE(ZConfServiceBrowser) +}; + +#endif // ZCONFSERVICEBROWSER_H diff --git a/libguh/network/avahi/zconfserviceclient.cpp b/libguh/network/avahi/zconfserviceclient.cpp new file mode 100644 index 00000000..1be1bcbe --- /dev/null +++ b/libguh/network/avahi/zconfserviceclient.cpp @@ -0,0 +1,84 @@ +/* + * This file is part of qtzeroconf. (c) 2012 Johannes Hilden + * https://github.com/johanneshilden/qtzeroconf + * + * Modified: (C) 2016 Simon Stürz + * + * qtzeroconf is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * qtzeroconf is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + * Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with qtzeroconf; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include +#include +#include "zconfserviceclient.h" +#include "qt-watch.h" + +void ZConfServiceClient::run() +{ + if (client) + return; + avahi_client_new(poll, (AvahiClientFlags) 0, ZConfServiceClient::callback, this, &error); +} + +QString ZConfServiceClient::errorString() const +{ + return avahi_strerror(error); +} + +ZConfServiceClient::ZConfServiceClient(QObject *parent) + : QObject(parent), + poll(avahi_qt_poll_get()), + client(0), + error(0) +{ +} + +ZConfServiceClient::~ZConfServiceClient() +{ + if (client) + // This will automatically free all associated browser, + // resolve and entry group objects. + avahi_client_free(client); +} + +void ZConfServiceClient::callback(AvahiClient *client, AvahiClientState state, void *userdata) +{ + ZConfServiceClient *service = static_cast(userdata); + if (service) { + service->client = client; + switch (state) + { + case AVAHI_CLIENT_S_RUNNING: + qDebug() << "AVAHI_CLIENT_S_RUNNING"; + // The server has started up successfully and registered its host + // name on the network. + emit service->clientRunning(); + break; + case AVAHI_CLIENT_FAILURE: + qDebug() << "AVAHI_CLIENT_FAILURE"; + emit service->clientFailure(); + break; + case AVAHI_CLIENT_S_COLLISION: + case AVAHI_CLIENT_S_REGISTERING: + qDebug() << (AVAHI_CLIENT_S_COLLISION == state + ? "AVAHI_CLIENT_S_COLLISION" + : "AVAHI_CLIENT_S_REGISTERING"); + emit service->clientReset(); + break; + case AVAHI_CLIENT_CONNECTING: + qDebug() << "AVAHI_CLIENT_CONNECTING"; + emit service->clientConnecting(); + } // end switch + } +} diff --git a/libguh/network/avahi/zconfserviceclient.h b/libguh/network/avahi/zconfserviceclient.h new file mode 100644 index 00000000..21569569 --- /dev/null +++ b/libguh/network/avahi/zconfserviceclient.h @@ -0,0 +1,56 @@ +/* + * This file is part of qtzeroconf. (c) 2012 Johannes Hilden + * https://github.com/johanneshilden/qtzeroconf + * + * Modified: (C) 2016 Simon Stürz + * + * qtzeroconf is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * qtzeroconf is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + * Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with qtzeroconf; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#ifndef ZCONFSERVICECLIENT_H +#define ZCONFSERVICECLIENT_H + +#include +#include + +class ZConfServiceClient : public QObject +{ + Q_OBJECT + +signals: + void clientRunning(); + void clientFailure(); + void clientConnecting(); + void clientReset(); + +private: + friend class ZConfService; + friend class ZConfServiceBrowser; + friend class ZConfServiceBrowserPrivate; + + ZConfServiceClient(QObject *parent = 0); + ~ZConfServiceClient(); + + void run(); + QString errorString() const; + + static void callback(AvahiClient *client, AvahiClientState state, void *userdata); + + const AvahiPoll *const poll; + AvahiClient *client; + int error; +}; + +#endif // ZCONFSERVICECLIENT_H diff --git a/libguh/network/upnpdiscovery/upnpdevice.cpp b/libguh/network/upnp/upnpdevice.cpp similarity index 100% rename from libguh/network/upnpdiscovery/upnpdevice.cpp rename to libguh/network/upnp/upnpdevice.cpp diff --git a/libguh/network/upnpdiscovery/upnpdevice.h b/libguh/network/upnp/upnpdevice.h similarity index 100% rename from libguh/network/upnpdiscovery/upnpdevice.h rename to libguh/network/upnp/upnpdevice.h diff --git a/libguh/network/upnpdiscovery/upnpdevicedescriptor.cpp b/libguh/network/upnp/upnpdevicedescriptor.cpp similarity index 100% rename from libguh/network/upnpdiscovery/upnpdevicedescriptor.cpp rename to libguh/network/upnp/upnpdevicedescriptor.cpp diff --git a/libguh/network/upnpdiscovery/upnpdevicedescriptor.h b/libguh/network/upnp/upnpdevicedescriptor.h similarity index 100% rename from libguh/network/upnpdiscovery/upnpdevicedescriptor.h rename to libguh/network/upnp/upnpdevicedescriptor.h diff --git a/libguh/network/upnpdiscovery/upnpdiscovery.cpp b/libguh/network/upnp/upnpdiscovery.cpp similarity index 100% rename from libguh/network/upnpdiscovery/upnpdiscovery.cpp rename to libguh/network/upnp/upnpdiscovery.cpp diff --git a/libguh/network/upnpdiscovery/upnpdiscovery.h b/libguh/network/upnp/upnpdiscovery.h similarity index 100% rename from libguh/network/upnpdiscovery/upnpdiscovery.h rename to libguh/network/upnp/upnpdiscovery.h diff --git a/libguh/network/upnpdiscovery/upnpdiscoveryrequest.cpp b/libguh/network/upnp/upnpdiscoveryrequest.cpp similarity index 100% rename from libguh/network/upnpdiscovery/upnpdiscoveryrequest.cpp rename to libguh/network/upnp/upnpdiscoveryrequest.cpp diff --git a/libguh/network/upnpdiscovery/upnpdiscoveryrequest.h b/libguh/network/upnp/upnpdiscoveryrequest.h similarity index 100% rename from libguh/network/upnpdiscovery/upnpdiscoveryrequest.h rename to libguh/network/upnp/upnpdiscoveryrequest.h diff --git a/libguh/plugin/deviceplugin.cpp b/libguh/plugin/deviceplugin.cpp index cd326836..ec1b87a8 100644 --- a/libguh/plugin/deviceplugin.cpp +++ b/libguh/plugin/deviceplugin.cpp @@ -139,7 +139,7 @@ #include "devicemanager.h" #include "guhsettings.h" #include "hardware/radio433/radio433.h" -#include "network/upnpdiscovery/upnpdiscovery.h" +#include "network/upnp/upnpdiscovery.h" #include #include diff --git a/plugins/deviceplugins/lgsmarttv/devicepluginlgsmarttv.h b/plugins/deviceplugins/lgsmarttv/devicepluginlgsmarttv.h index 7044b268..603dc87f 100644 --- a/plugins/deviceplugins/lgsmarttv/devicepluginlgsmarttv.h +++ b/plugins/deviceplugins/lgsmarttv/devicepluginlgsmarttv.h @@ -23,7 +23,7 @@ #include "plugin/deviceplugin.h" #include "tvdevice.h" -#include "network/upnpdiscovery/upnpdevicedescriptor.h" +#include "network/upnp/upnpdevicedescriptor.h" class DevicePluginLgSmartTv : public DevicePlugin {