hyperion.ng/libsrc/ssdp/SSDPServer.cpp

223 lines
6.8 KiB
C++
Raw Normal View History

2018-12-30 22:07:53 +01:00
#include <ssdp/SSDPServer.h>
// utils
2018-12-30 22:07:53 +01:00
#include <utils/SysInfo.h>
LED Device Features, Fixes and Refactoring (Resubmit PR855) (#875) * Refactor LedDevices - Initial version * Small renamings * Add WLED as own device * Lpd8806 Remove open() method * remove dependency on Qt 5.10 * Lpd8806 Remove open() method * Update WS281x * Update WS2812SPI * Add writeBlack for WLED powerOff * WLED remove extra bracket * Allow different Nanoleaf panel numbering sequence (Feature req.#827) * build(deps): bump websocket-extensions from 0.1.3 to 0.1.4 in /docs (#826) * Bumps [websocket-extensions](https://github.com/faye/websocket-extensions-node) from 0.1.3 to 0.1.4. - [Release notes](https://github.com/faye/websocket-extensions-node/releases) - [Changelog](https://github.com/faye/websocket-extensions-node/blob/master/CHANGELOG.md) - [Commits](https://github.com/faye/websocket-extensions-node/compare/0.1.3...0.1.4) * Fix typos * Nanoleaf clean-up * Yeelight support, generalize wizard elements * Update Yeelight to handle quota in music mode * Yeelight extend rage for extraTimeDarkness for testing * Clean-up - Add commentary, Remove development debug statements * Fix brightnessSwitchOffOnMinimum typo and default value * Yeelight support restoreOriginalState, additional Fixes * WLED - Remove UDP-Port, as it is not configurable * Fix merging issue * Remove QHostAddress::operator=(const QString&)' is deprecated * Windows compile errors and (Qt 5.15 deprecation) warnings * Fix order includes * LedDeviceFile Support Qt5.7 and greater * Windows compatibility and other Fixes * Fix Qt Version compatability * Rs232 - Resolve portname from unix /dev/ style, fix DMX sub-type support * Disable WLED Wizard Button (until Wizard is available) * Yeelight updates * Add wrong log-type as per #505 * Fixes and Clean-up after clang-tidy report * Fix udpe131 not enabled for generated CID * Change timer into dynamic for Qt Thread-Affinity * Hue clean-up and diyHue workaround * Updates after review feedback by m-seker * Add "chrono" includes
2020-07-12 20:27:56 +02:00
#include <utils/QStringUtils.h>
// Hyperion
2018-12-30 22:07:53 +01:00
#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"
"HYPERION-JSS-PORT: %7\r\n"
"HYPERION-NAME: %8\r\n"
2018-12-30 22:07:53 +01:00
"\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"
"HYPERION-JSS-PORT: %8\r\n"
"HYPERION-NAME: %9\r\n"
2018-12-30 22:07:53 +01:00
"\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);
connect(_udpSocket, &QUdpSocket::readyRead, this, &SSDPServer::readPendingDatagrams);
}
bool SSDPServer::start()
2018-12-30 22:07:53 +01:00
{
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)
{
_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
LED Device Features, Fixes and Refactoring (Resubmit PR855) (#875) * Refactor LedDevices - Initial version * Small renamings * Add WLED as own device * Lpd8806 Remove open() method * remove dependency on Qt 5.10 * Lpd8806 Remove open() method * Update WS281x * Update WS2812SPI * Add writeBlack for WLED powerOff * WLED remove extra bracket * Allow different Nanoleaf panel numbering sequence (Feature req.#827) * build(deps): bump websocket-extensions from 0.1.3 to 0.1.4 in /docs (#826) * Bumps [websocket-extensions](https://github.com/faye/websocket-extensions-node) from 0.1.3 to 0.1.4. - [Release notes](https://github.com/faye/websocket-extensions-node/releases) - [Changelog](https://github.com/faye/websocket-extensions-node/blob/master/CHANGELOG.md) - [Commits](https://github.com/faye/websocket-extensions-node/compare/0.1.3...0.1.4) * Fix typos * Nanoleaf clean-up * Yeelight support, generalize wizard elements * Update Yeelight to handle quota in music mode * Yeelight extend rage for extraTimeDarkness for testing * Clean-up - Add commentary, Remove development debug statements * Fix brightnessSwitchOffOnMinimum typo and default value * Yeelight support restoreOriginalState, additional Fixes * WLED - Remove UDP-Port, as it is not configurable * Fix merging issue * Remove QHostAddress::operator=(const QString&)' is deprecated * Windows compile errors and (Qt 5.15 deprecation) warnings * Fix order includes * LedDeviceFile Support Qt5.7 and greater * Windows compatibility and other Fixes * Fix Qt Version compatability * Rs232 - Resolve portname from unix /dev/ style, fix DMX sub-type support * Disable WLED Wizard Button (until Wizard is available) * Yeelight updates * Add wrong log-type as per #505 * Fixes and Clean-up after clang-tidy report * Fix udpe131 not enabled for generated CID * Change timer into dynamic for Qt Thread-Affinity * Hue clean-up and diyHue workaround * Updates after review feedback by m-seker * Add "chrono" includes
2020-07-12 20:27:56 +02:00
QStringList entries = QStringUtils::split(data,"\n", QStringUtils::SplitBehavior::SkipEmptyParts);
2018-12-30 22:07:53 +01:00
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);
2018-12-30 22:07:53 +01:00
}
}
}
2020-08-08 13:09:15 +02:00
void SSDPServer::sendMSearchResponse(const QString& st, const QString& senderIp, quint16 senderPort)
2018-12-30 22:07:53 +01:00
{
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
, _jssPort
, _name );
2018-12-30 22:07:53 +01:00
_udpSocket->writeDatagram(message.toUtf8(), QHostAddress(senderIp), senderPort);
2018-12-30 22:07:53 +01:00
}
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);
2018-12-30 22:07:53 +01:00
}
}
void SSDPServer::sendAlive(const QString& st)
{
const QString tempUSN = (st == "upnp:rootdevice ") ? _uuid+"::"+st : _uuid;
2018-12-30 22:07:53 +01:00
QString message = UPNP_ALIVE_MESSAGE.arg(SSDP_MAX_AGE
, _descAddress
, st
, _serverHeader
, tempUSN
, _fbsPort
, _jssPort
, _name );
2018-12-30 22:07:53 +01:00
// we repeat 3 times
quint8 rep = 0;
while(rep++ < 3) {
_udpSocket->writeDatagram(message.toUtf8(), QHostAddress(SSDP_ADDR), SSDP_PORT);
2018-12-30 22:07:53 +01:00
}
}
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);
2018-12-30 22:07:53 +01:00
}