remove protobuf (part 2)

This commit is contained in:
Paulchen-Panther
2018-12-30 22:07:53 +01:00
parent 559311e18c
commit 38950edf35
54 changed files with 1441 additions and 377 deletions

View File

@@ -0,0 +1,14 @@
# Define the current source locations
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/ssdp)
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/ssdp)
FILE ( GLOB SSDP_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" )
add_library(ssdp
${SSDP_SOURCES}
)
target_link_libraries(ssdp
Qt5::Network
webserver
)

View File

@@ -0,0 +1,41 @@
#pragma once
///
/// The xml to fill with data
/// %1 base url http://192.168.0.177:80/
/// %2 friendly name Hyperion 2.0.0 (192.168.0.177)
/// %3 modelNumber 2.0.0
/// %4 serialNumber / UDN (H ID) Fjsa723dD0....
///
/// def URN urn:schemas-upnp-org:device:Basic:1
static const QString SSDP_DESCRIPTION = "<?xml version=\"1.0\"?>"
"<root xmlns=\"urn:schemas-upnp-org:device-1-0\">"
"<specVersion>"
"<major>1</major>"
"<minor>0</minor>"
"</specVersion>"
"<URLBase>%1</URLBase>"
"<device>"
"<deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType>"
"<friendlyName>%2</friendlyName>"
"<manufacturer>Hyperion Open Source Ambient Lighting</manufacturer>"
"<manufacturerURL>https://www.hyperion-project.org</manufacturerURL>"
"<modelDescription>Hyperion Open Source Ambient Light</modelDescription>"
"<modelName>Hyperion</modelName>"
"<modelNumber>%3</modelNumber>"
"<modelURL>https://www.hyperion-project.org</modelURL>"
"<serialNumber>%4</serialNumber>"
"<UDN>uuid:%4</UDN>"
"<presentationURL>index.html</presentationURL>"
"<iconList>"
"<icon>"
"<mimetype>image/png</mimetype>"
"<height>100</height>"
"<width>100</width>"
"<depth>32</depth>"
"<url>img/hyperion/ssdp_icon.png</url>"
"</icon>"
"</iconList>"
"</device>"
"</root>";

View File

@@ -0,0 +1,169 @@
#include <ssdp/SSDPDiscover.h>
// qt inc
#include <QUdpSocket>
#include <QUrl>
static const QHostAddress SSDP_ADDR("239.255.255.250");
static const quint16 SSDP_PORT(1900);
// as per upnp spec 1.1, section 1.2.2.
// TODO: Make IP and port below another #define and replace message below
static const QString UPNP_DISCOVER_MESSAGE = "M-SEARCH * HTTP/1.1\r\n"
"HOST: 239.255.255.250:1900\r\n"
"MAN: \"ssdp:discover\"\r\n"
"MX: 1\r\n"
"ST: %1\r\n"
"\r\n";
SSDPDiscover::SSDPDiscover(QObject* parent)
: QObject(parent)
, _log(Logger::getInstance("SSDPDISCOVER"))
, _udpSocket(new QUdpSocket(this))
{
}
void SSDPDiscover::searchForService(const QString& st)
{
_searchTarget = st;
_usnList.clear();
// setup socket
connect(_udpSocket, &QUdpSocket::readyRead, this, &SSDPDiscover::readPendingDatagrams, Qt::UniqueConnection);
sendSearch(st);
}
const QString SSDPDiscover::getFirstService(const searchType& type, const QString& st, const int& timeout_ms)
{
Info(_log, "Search for Hyperion server...");
_searchTarget = st;
// search
sendSearch(st);
_udpSocket->waitForReadyRead(timeout_ms);
while (_udpSocket->hasPendingDatagrams())
{
QByteArray datagram;
datagram.resize(_udpSocket->pendingDatagramSize());
QHostAddress sender;
quint16 senderPort;
_udpSocket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);
QString data(datagram);
QMap<QString,QString> headers;
QString address;
// parse request
QStringList entries = data.split("\n", QString::SkipEmptyParts);
for(auto entry : entries)
{
// http header parse skip
if(entry.contains("HTTP/1.1"))
continue;
// split into key:vale, be aware that value field may contain also a ":"
entry = entry.simplified();
int pos = entry.indexOf(":");
if(pos == -1)
continue;
headers[entry.left(pos).trimmed().toLower()] = entry.mid(pos+1).trimmed();
}
// verify ssdp spec
if(!headers.contains("st"))
continue;
// usn duplicates
if (_usnList.contains(headers.value("usn")))
continue;
if (headers.value("st") == _searchTarget)
{
_usnList << headers.value("usn");
QUrl url(headers.value("location"));
//Info(_log, "Received msearch response from '%s:%d'. Search target: %s",QSTRING_CSTR(sender.toString()), senderPort, QSTRING_CSTR(headers.value("st")));
if(type == STY_WEBSERVER)
{
Info(_log, "Found Hyperion server at: %s:%d", QSTRING_CSTR(url.host()), url.port());
return url.host()+":"+QString::number(url.port());
}
else if(type == STY_FLATBUFSERVER)
{
const QString fbsport = headers.value("hyperion-fbs-port");
if(fbsport.isEmpty())
{
continue;
}
else
{
Info(_log, "Found Hyperion server at: %s:%d", QSTRING_CSTR(url.host()), fbsport);
return url.host()+":"+fbsport;
}
}
}
}
Info(_log,"Search timeout, no Hyperion server found");
return QString();
}
void SSDPDiscover::readPendingDatagrams()
{
while (_udpSocket->hasPendingDatagrams()) {
QByteArray datagram;
datagram.resize(_udpSocket->pendingDatagramSize());
QHostAddress sender;
quint16 senderPort;
_udpSocket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);
QString data(datagram);
QMap<QString,QString> headers;
// parse request
QStringList entries = data.split("\n", QString::SkipEmptyParts);
for(auto entry : entries)
{
// http header parse skip
if(entry.contains("HTTP/1.1"))
continue;
// split into key:vale, be aware that value field may contain also a ":"
entry = entry.simplified();
int pos = entry.indexOf(":");
if(pos == -1)
continue;
headers[entry.left(pos).trimmed().toLower()] = entry.mid(pos+1).trimmed();
}
// verify ssdp spec
if(!headers.contains("st"))
continue;
// usn duplicates
if (_usnList.contains(headers.value("usn")))
continue;
if (headers.value("st") == _searchTarget)
{
_usnList << headers.value("usn");
//Info(_log, "Received msearch response from '%s:%d'. Search target: %s",QSTRING_CSTR(sender.toString()), senderPort, QSTRING_CSTR(headers.value("st")));
QUrl url(headers.value("location"));
emit newService(url.host()+":"+QString::number(url.port()));
}
}
}
void SSDPDiscover::sendSearch(const QString& st)
{
const QString msg = UPNP_DISCOVER_MESSAGE.arg(st);
_udpSocket->writeDatagram(msg.toUtf8(),
QHostAddress(SSDP_ADDR),
SSDP_PORT);
}

144
libsrc/ssdp/SSDPHandler.cpp Normal file
View File

@@ -0,0 +1,144 @@
#include <ssdp/SSDPHandler.h>
#include <webserver/WebServer.h>
#include "SSDPDescription.h"
#include <hyperion/Hyperion.h>
#include <HyperionConfig.h>
#include <QNetworkInterface>
#include <QNetworkConfigurationManager>
SSDPHandler::SSDPHandler(WebServer* webserver, const quint16& flatBufPort, QObject * parent)
: SSDPServer(parent)
, _webserver(webserver)
, _localAddress()
, _NCA(nullptr)
{
_flatbufPort = flatBufPort;
setFlatBufPort(_flatbufPort);
}
void SSDPHandler::initServer()
{
// prep server
SSDPServer::initServer();
_NCA = new QNetworkConfigurationManager(this);
// listen for mSearchRequestes
connect(this, &SSDPServer::msearchRequestReceived, this, &SSDPHandler::handleMSearchRequest);
connect(_NCA, &QNetworkConfigurationManager::configurationChanged, this, &SSDPHandler::handleNetworkConfigurationChanged);
// get localAddress from interface
if(!getLocalAddress().isEmpty())
{
_localAddress = getLocalAddress();
}
// startup if localAddress is found
if(!_localAddress.isEmpty() && _webserver->isInited())
{
handleWebServerStateChange(true);
}
}
void SSDPHandler::handleSettingsUpdate(const settings::type& type, const QJsonDocument& config)
{
if(type == settings::FLATBUFSERVER)
{
const QJsonObject& obj = config.object();
if(obj["port"].toInt() != _flatbufPort)
{
_flatbufPort = obj["port"].toInt();
setFlatBufPort(_flatbufPort);
}
}
}
void SSDPHandler::handleWebServerStateChange(const bool newState)
{
if(newState)
{
// refresh info
_webserver->setSSDPDescription(buildDesc());
setDescriptionAddress(getDescAddress());
if(start())
{
sendAlive("upnp:rootdevice");
sendAlive("urn:schemas-upnp-org:device:basic:1");
sendAlive("urn:hyperion-project.org:device:basic:1");
}
}
else
{
_webserver->setSSDPDescription("");
stop();
}
}
void SSDPHandler::handleNetworkConfigurationChanged(const QNetworkConfiguration &config)
{
// get localAddress from interface
if(!getLocalAddress().isEmpty())
{
QString localAddress = getLocalAddress();
if(_localAddress != localAddress)
{
// revoke old ip
sendByeBye("upnp:rootdevice");
sendByeBye("urn:schemas-upnp-org:device:basic:1");
sendByeBye("urn:hyperion-project.org:device:basic:1");
// update desc & notify new ip
_localAddress = localAddress;
_webserver->setSSDPDescription(buildDesc());
setDescriptionAddress(getDescAddress());
sendAlive("upnp:rootdevice");
sendAlive("urn:schemas-upnp-org:device:basic:1");
sendAlive("urn:hyperion-project.org:device:basic:1");
}
}
}
const QString SSDPHandler::getLocalAddress()
{
// get the first valid IPv4 address. This is probably not that one we actually want to announce
for( const auto & address : QNetworkInterface::allAddresses())
{
// is valid when, no loopback, IPv4
if (!address.isLoopback() && address.protocol() == QAbstractSocket::IPv4Protocol )
{
return address.toString();
}
}
return QString();
}
void SSDPHandler::handleMSearchRequest(const QString& target, const QString& mx, const QString address, const quint16 & port)
{
// TODO Response delay according to MX field (sec) random between 0 and MX
// when searched for all devices / root devices / basic device
if(target == "ssdp:all" || target == "upnp:rootdevice" || target == "urn:schemas-upnp-org:device:basic:1" || target == "urn:hyperion-project.org:device:basic:1")
sendMSearchResponse(target, address, port);
}
const QString SSDPHandler::getDescAddress()
{
return getBaseAddress()+"description.xml";
}
const QString SSDPHandler::getBaseAddress()
{
return "http://"+_localAddress+":"+QString::number(_webserver->getPort())+"/";
}
const QString SSDPHandler::buildDesc()
{
/// %1 base url http://192.168.0.177:80/
/// %2 friendly name Hyperion 2.0.0 (192.168.0.177)
/// %3 modelNumber 2.0.0
/// %4 serialNumber / UDN (H ID) Fjsa723dD0....
return SSDP_DESCRIPTION.arg(getBaseAddress(), QString("Hyperion (%2)").arg(_localAddress), QString(HYPERION_VERSION), Hyperion::getInstance()->getId());
}

227
libsrc/ssdp/SSDPServer.cpp Normal file
View File

@@ -0,0 +1,227 @@
#include <ssdp/SSDPServer.h>
// util
#include <utils/SysInfo.h>
#include <hyperion/Hyperion.h>
#include <HyperionConfig.h>
#include <QUdpSocket>
#include <QDateTime>
static const QHostAddress SSDP_ADDR("239.255.255.250");
static const quint16 SSDP_PORT(1900);
static const QString SSDP_MAX_AGE("1800");
// as per upnp spec 1.1, section 1.2.2.
// - BOOTID.UPNP.ORG
// - CONFIGID.UPNP.ORG
// - SEARCHPORT.UPNP.ORG (optional)
// TODO: Make IP and port below another #define and replace message below
static const QString UPNP_ALIVE_MESSAGE = "NOTIFY * HTTP/1.1\r\n"
"HOST: 239.255.255.250:1900\r\n"
"CACHE-CONTROL: max-age=%1\r\n"
"LOCATION: %2\r\n"
"NT: %3\r\n"
"NTS: ssdp:alive\r\n"
"SERVER: %4\r\n"
"USN: uuid:%5\r\n"
"HYPERION-FBS-PORT: %6\r\n"
"\r\n";
// Implement ssdp:update as per spec 1.1, section 1.2.4
// and use the below define to build the message, where
// SEARCHPORT.UPNP.ORG are optional.
// TODO: Make IP and port below another #define and replace message below
static const QString UPNP_UPDATE_MESSAGE = "NOTIFY * HTTP/1.1\r\n"
"HOST: 239.255.255.250:1900\r\n"
"LOCATION: %1\r\n"
"NT: %2\r\n"
"NTS: ssdp:update\r\n"
"USN: uuid:%3\r\n"
/* "CONFIGID.UPNP.ORG: %4\r\n"
UPNP spec = 1.1 "NEXTBOOTID.UPNP.ORG: %5\r\n"
"SEARCHPORT.UPNP.ORG: %6\r\n"
*/ "\r\n";
// TODO: Add this two fields commented below in the BYEBYE MESSAGE
// as per upnp spec 1.1, section 1.2.2 and 1.2.3.
// - BOOTID.UPNP.ORG
// - CONFIGID.UPNP.ORG
// TODO: Make IP and port below another #define and replace message below
static const QString UPNP_BYEBYE_MESSAGE = "NOTIFY * HTTP/1.1\r\n"
"HOST: 239.255.255.250:1900\r\n"
"NT: %1\r\n"
"NTS: ssdp:byebye\r\n"
"USN: uuid:%2\r\n"
"\r\n";
// TODO: Add this three fields commented below in the MSEARCH_RESPONSE
// as per upnp spec 1.1, section 1.3.3.
// - BOOTID.UPNP.ORG
// - CONFIGID.UPNP.ORG
// - SEARCHPORT.UPNP.ORG (optional)
static const QString UPNP_MSEARCH_RESPONSE = "HTTP/1.1 200 OK\r\n"
"CACHE-CONTROL: max-age = %1\r\n"
"DATE: %2\r\n"
"EXT: \r\n"
"LOCATION: %3\r\n"
"SERVER: %4\r\n"
"ST: %5\r\n"
"USN: uuid:%6\r\n"
"HYPERION-FBS-PORT: %7\r\n"
"\r\n";
SSDPServer::SSDPServer(QObject * parent)
: QObject(parent)
, _log(Logger::getInstance("SSDP"))
, _udpSocket(nullptr)
, _running(false)
{
}
SSDPServer::~SSDPServer()
{
stop();
}
void SSDPServer::initServer()
{
_udpSocket = new QUdpSocket(this);
// get system info
SysInfo::HyperionSysInfo data = SysInfo::get();
// create SERVER String
_serverHeader = data.prettyName+"/"+data.productVersion+" UPnP/1.0 Hyperion/"+QString(HYPERION_VERSION);
// usn uuid
_uuid = Hyperion::getInstance()->getId();
connect(_udpSocket, &QUdpSocket::readyRead, this, &SSDPServer::readPendingDatagrams);
}
const bool SSDPServer::start()
{
if(!_running && _udpSocket->bind(QHostAddress::AnyIPv4, SSDP_PORT, QAbstractSocket::ShareAddress))
{
_udpSocket->joinMulticastGroup(SSDP_ADDR);
_running = true;
return true;
}
return false;
}
void SSDPServer::stop()
{
if(_running)
{
// send BYEBYE Msg
sendByeBye("upnp:rootdevice");
sendByeBye("urn:schemas-upnp-org:device:basic:1");
sendByeBye("urn:hyperion-project.org:device:basic:1");
_udpSocket->close();
_running = false;
}
}
void SSDPServer::readPendingDatagrams()
{
while (_udpSocket->hasPendingDatagrams()) {
QByteArray datagram;
datagram.resize(_udpSocket->pendingDatagramSize());
QHostAddress sender;
quint16 senderPort;
_udpSocket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);
QString data(datagram);
QMap<QString,QString> headers;
// parse request
QStringList entries = data.split("\n", QString::SkipEmptyParts);
for(auto entry : entries)
{
// http header parse skip
if(entry.contains("HTTP/1.1"))
continue;
// split into key:vale, be aware that value field may contain also a ":"
entry = entry.simplified();
int pos = entry.indexOf(":");
if(pos == -1)
continue;
headers[entry.left(pos).trimmed().toLower()] = entry.mid(pos+1).trimmed();
}
// verify ssdp spec
if(!headers.contains("man"))
continue;
if (headers.value("man") == "\"ssdp:discover\"")
{
//Debug(_log, "Received msearch from '%s:%d'. Search target: %s",QSTRING_CSTR(sender.toString()), senderPort, QSTRING_CSTR(headers.value("st")));
emit msearchRequestReceived(headers.value("st"), headers.value("mx"), sender.toString(), senderPort);
}
}
}
void SSDPServer::sendMSearchResponse(const QString& st, const QString& senderIp, const quint16& senderPort)
{
QString message = UPNP_MSEARCH_RESPONSE.arg(SSDP_MAX_AGE
, QDateTime::currentDateTimeUtc().toString("ddd, dd MMM yyyy HH:mm:ss GMT")
, _descAddress
, _serverHeader
, st
, _uuid
, _fbsPort );
_udpSocket->writeDatagram(message.toUtf8(),
QHostAddress(senderIp),
senderPort);
}
void SSDPServer::sendByeBye(const QString& st)
{
QString message = UPNP_BYEBYE_MESSAGE.arg(st, _uuid+"::"+st );
// we repeat 3 times
quint8 rep = 0;
while(rep < 3) {
_udpSocket->writeDatagram(message.toUtf8(),
QHostAddress(SSDP_ADDR),
SSDP_PORT);
rep++;
}
}
void SSDPServer::sendAlive(const QString& st)
{
QString message = UPNP_ALIVE_MESSAGE.arg(SSDP_MAX_AGE
, _descAddress
, st
, _serverHeader
, _uuid+"::"+st
, _fbsPort);
// we repeat 3 times
quint8 rep = 0;
while(rep < 3) {
_udpSocket->writeDatagram(message.toUtf8(),
QHostAddress(SSDP_ADDR),
SSDP_PORT);
rep++;
}
}
void SSDPServer::sendUpdate(const QString& st)
{
QString message = UPNP_UPDATE_MESSAGE.arg(_descAddress
, st
, _uuid+"::"+st );
_udpSocket->writeDatagram(message.toUtf8(),
QHostAddress(SSDP_ADDR),
SSDP_PORT);
}