webui - "cgi" handler and multiple fixes (#700)

* initial commit of webconfig

* update example config with webconfig and fix format of file
update debian postinst script for install example config

* fix compiling
add new web server command "serverinfo" to use in webapp to retrieve json port

* change web default port to 8099

* add cgi engine to webserver

* fix include
This commit is contained in:
redPanther 2016-06-14 20:14:06 +02:00 committed by brindosch
parent eb64e7e528
commit 4a841710dd
12 changed files with 193 additions and 67 deletions

View File

@ -67,7 +67,7 @@ public:
/// ///
/// @param[in] jsonConfig The Json configuration /// @param[in] jsonConfig The Json configuration
/// ///
Hyperion(const Json::Value& jsonConfig); Hyperion(const Json::Value& jsonConfig, const std::string configFile);
/// ///
/// Destructor; cleans up resourcess /// Destructor; cleans up resourcess
@ -111,6 +111,11 @@ public:
/// @return The list of active effects /// @return The list of active effects
const std::list<ActiveEffectDefinition> &getActiveEffects(); const std::list<ActiveEffectDefinition> &getActiveEffects();
///
const Json::Value& getJsonConfig() { return _jsonConfig; };
std::string getConfigFileName() { return _configFile; };
public slots: public slots:
/// ///
/// Writes a single color to all the leds for the given time and priority /// Writes a single color to all the leds for the given time and priority
@ -293,6 +298,12 @@ private:
// proto and json Message forwarder // proto and json Message forwarder
MessageForwarder * _messageForwarder; MessageForwarder * _messageForwarder;
// json configuration
const Json::Value& _jsonConfig;
// the name of config file
std::string _configFile;
/// The timer for handling priority channel timeouts /// The timer for handling priority channel timeouts
QTimer _timer; QTimer _timer;
}; };

View File

@ -5,6 +5,7 @@
#include <QString> #include <QString>
#include <string> #include <string>
#include <utils/jsonschema/JsonFactory.h> #include <utils/jsonschema/JsonFactory.h>
#include <hyperion/Hyperion.h>
class StaticFileServing; class StaticFileServing;
@ -12,8 +13,7 @@ class WebConfig : public QObject {
Q_OBJECT Q_OBJECT
public: public:
WebConfig (std::string baseUrl, quint16 port, quint16 jsonPort, QObject * parent = NULL); WebConfig (Hyperion *hyperion, QObject * parent = NULL);
WebConfig (const Json::Value &config, QObject * parent = NULL);
virtual ~WebConfig (void); virtual ~WebConfig (void);
@ -21,10 +21,9 @@ public:
void stop(); void stop();
private: private:
QObject* _parent; Hyperion* _hyperion;
QString _baseUrl; QString _baseUrl;
quint16 _port; quint16 _port;
quint16 _jsonPort;
StaticFileServing* _server; StaticFileServing* _server;
const std::string WEBCONFIG_DEFAULT_PATH = "/usr/share/hyperion/webconfig"; const std::string WEBCONFIG_DEFAULT_PATH = "/usr/share/hyperion/webconfig";

View File

@ -114,6 +114,7 @@ void Effect::run()
{ {
std::cerr << "EFFECTENGINE ERROR: Unable to open script file " << _script << std::endl; std::cerr << "EFFECTENGINE ERROR: Unable to open script file " << _script << std::endl;
} }
fclose(file);
// Clean up the thread state // Clean up the thread state
Py_EndInterpreter(_interpreterThreadState); Py_EndInterpreter(_interpreterThreadState);

View File

@ -615,7 +615,7 @@ MessageForwarder * Hyperion::getForwarder()
return _messageForwarder; return _messageForwarder;
} }
Hyperion::Hyperion(const Json::Value &jsonConfig) : Hyperion::Hyperion(const Json::Value &jsonConfig, const std::string configFile) :
_ledString(createLedString(jsonConfig["leds"], createColorOrder(jsonConfig["device"]))), _ledString(createLedString(jsonConfig["leds"], createColorOrder(jsonConfig["device"]))),
_muxer(_ledString.leds().size()), _muxer(_ledString.leds().size()),
_raw2ledTransform(createLedColorsTransform(_ledString.leds().size(), jsonConfig["color"])), _raw2ledTransform(createLedColorsTransform(_ledString.leds().size(), jsonConfig["color"])),
@ -625,6 +625,8 @@ Hyperion::Hyperion(const Json::Value &jsonConfig) :
_device(LedDeviceFactory::construct(jsonConfig["device"])), _device(LedDeviceFactory::construct(jsonConfig["device"])),
_effectEngine(nullptr), _effectEngine(nullptr),
_messageForwarder(createMessageForwarder(jsonConfig["forwarder"])), _messageForwarder(createMessageForwarder(jsonConfig["forwarder"])),
_jsonConfig(jsonConfig),
_configFile(configFile),
_timer() _timer()
{ {
if (!_raw2ledAdjustment->verifyAdjustments()) if (!_raw2ledAdjustment->verifyAdjustments())

View File

@ -46,7 +46,7 @@ Logger::Logger ( std::string name, LogLevel minLevel ):
_syslogEnabled(true), _syslogEnabled(true),
_loggerId(loggerId++) _loggerId(loggerId++)
{ {
#ifdef __linux__ #ifdef __GLIBC__
_appname = std::string(program_invocation_short_name); _appname = std::string(program_invocation_short_name);
#else #else
_appname = std::string(getprogname()); _appname = std::string(getprogname());

View File

@ -10,6 +10,7 @@ set(WebConfig_QT_HEADERS
${CURRENT_SOURCE_DIR}/QtHttpReply.h ${CURRENT_SOURCE_DIR}/QtHttpReply.h
${CURRENT_SOURCE_DIR}/QtHttpRequest.h ${CURRENT_SOURCE_DIR}/QtHttpRequest.h
${CURRENT_SOURCE_DIR}/QtHttpServer.h ${CURRENT_SOURCE_DIR}/QtHttpServer.h
${CURRENT_SOURCE_DIR}/CgiHandler.h
${CURRENT_SOURCE_DIR}/StaticFileServing.h ${CURRENT_SOURCE_DIR}/StaticFileServing.h
${CURRENT_HEADER_DIR}/WebConfig.h ${CURRENT_HEADER_DIR}/WebConfig.h
) )
@ -23,6 +24,7 @@ set(WebConfig_SOURCES
${CURRENT_SOURCE_DIR}/QtHttpReply.cpp ${CURRENT_SOURCE_DIR}/QtHttpReply.cpp
${CURRENT_SOURCE_DIR}/QtHttpRequest.cpp ${CURRENT_SOURCE_DIR}/QtHttpRequest.cpp
${CURRENT_SOURCE_DIR}/QtHttpServer.cpp ${CURRENT_SOURCE_DIR}/QtHttpServer.cpp
${CURRENT_SOURCE_DIR}/CgiHandler.cpp
${CURRENT_SOURCE_DIR}/StaticFileServing.cpp ${CURRENT_SOURCE_DIR}/StaticFileServing.cpp
${CURRENT_SOURCE_DIR}/WebConfig.cpp ${CURRENT_SOURCE_DIR}/WebConfig.cpp
) )

View File

@ -0,0 +1,69 @@
#include <QStringBuilder>
#include <QUrlQuery>
#include <QFile>
#include <QByteArray>
#include "CgiHandler.h"
CgiHandler::CgiHandler (Hyperion * hyperion, QObject * parent)
: QObject(parent)
, _hyperion(hyperion)
, _hyperionConfig(_hyperion->getJsonConfig())
{
}
CgiHandler::~CgiHandler()
{
}
void CgiHandler::exec(const QStringList & args, QtHttpReply * reply)
{
try
{
cmd_cfg_jsonserver(args,reply);
cmd_cfg_hyperion(args,reply);
throw 1;
}
catch(int e)
{
if (e != 0)
throw 1;
}
}
void CgiHandler::cmd_cfg_jsonserver(const QStringList & args, QtHttpReply * reply)
{
if ( args.at(0) == "cfg_jsonserver" )
{
quint16 jsonPort = 19444;
if (_hyperionConfig.isMember("jsonServer"))
{
const Json::Value & jsonConfig = _hyperionConfig["jsonServer"];
jsonPort = jsonConfig.get("port", jsonPort).asUInt();
}
// send result as reply
reply->addHeader ("Content-Type", "text/plain" );
reply->appendRawData (QByteArrayLiteral(":") % QString::number(jsonPort).toUtf8() );
throw 0;
}
}
void CgiHandler::cmd_cfg_hyperion(const QStringList & args, QtHttpReply * reply)
{
if ( args.at(0) == "cfg_hyperion" )
{
QFile file ( _hyperion->getConfigFileName().c_str() );
if (file.exists ())
{
if (file.open (QFile::ReadOnly)) {
QByteArray data = file.readAll ();
reply->addHeader ("Content-Type", "text/plain");
reply->appendRawData (data);
file.close ();
}
}
throw 0;
}
}

View File

@ -0,0 +1,34 @@
#ifndef CGIHANDLER_H
#define CGIHANDLER_H
#include <QObject>
#include <QString>
#include <QStringList>
#include <utils/jsonschema/JsonFactory.h>
#include <hyperion/Hyperion.h>
#include "QtHttpReply.h"
class CgiHandler : public QObject {
Q_OBJECT
public:
CgiHandler (Hyperion * hyperion, QObject * parent = NULL);
virtual ~CgiHandler (void);
void exec(const QStringList & args, QtHttpReply * reply);
// cgi commands
void cmd_cfg_jsonserver(const QStringList & args, QtHttpReply * reply);
void cmd_cfg_hyperion (const QStringList & args, QtHttpReply * reply);
private:
Hyperion* _hyperion;
QtHttpReply * _reply;
const Json::Value &_hyperionConfig;
};
#endif // CGIHANDLER_H

View File

@ -8,12 +8,13 @@
#include <QPair> #include <QPair>
#include <QFile> #include <QFile>
StaticFileServing::StaticFileServing (QString baseUrl, quint16 port, quint16 jsonPort, QObject * parent) StaticFileServing::StaticFileServing (Hyperion *hyperion, QString baseUrl, quint16 port, QObject * parent)
: QObject (parent) : QObject (parent)
, m_baseUrl (baseUrl) , _hyperion(hyperion)
, _jsonPort (jsonPort) , _baseUrl (baseUrl)
, _cgi(hyperion, this)
{ {
m_mimeDb = new QMimeDatabase; _mimeDb = new QMimeDatabase;
_server = new QtHttpServer (this); _server = new QtHttpServer (this);
_server->setServerName (QStringLiteral ("Qt Static HTTP File Server")); _server->setServerName (QStringLiteral ("Qt Static HTTP File Server"));
@ -57,23 +58,31 @@ void StaticFileServing::onRequestNeedsReply (QtHttpRequest * request, QtHttpRepl
if (command == QStringLiteral ("GET")) if (command == QStringLiteral ("GET"))
{ {
QString path = request->getUrl ().path (); QString path = request->getUrl ().path ();
QStringList uri_parts = path.split('/', QString::SkipEmptyParts);
// special uri handling for server commands // special uri handling for server commands
if ( path == "/serverinfo" ) if ( ! uri_parts.empty() && uri_parts.at(0) == "cgi" )
{ {
reply->addHeader ("Content-Type", "text/plain" ); uri_parts.removeAt(0);
reply->appendRawData (QByteArrayLiteral(":") % QString::number(_jsonPort).toUtf8() ); try
{
_cgi.exec(uri_parts, reply);
}
catch(...)
{
printErrorToReply (reply, "cgi script failed (" % path % ")");
}
return; return;
} }
// get static files // get static files
if ( path == "/" || path.isEmpty() || ! QFile::exists(m_baseUrl % "/" % path) ) if ( path == "/" || path.isEmpty() || ! QFile::exists(_baseUrl % "/" % path) )
path = "index.html"; path = "index.html";
QFile file (m_baseUrl % "/" % path); QFile file (_baseUrl % "/" % path);
if (file.exists ()) if (file.exists ())
{ {
QMimeType mime = m_mimeDb->mimeTypeForFile (file.fileName ()); QMimeType mime = _mimeDb->mimeTypeForFile (file.fileName ());
if (file.open (QFile::ReadOnly)) { if (file.open (QFile::ReadOnly)) {
QByteArray data = file.readAll (); QByteArray data = file.readAll ();
reply->addHeader ("Content-Type", mime.name ().toLocal8Bit ()); reply->addHeader ("Content-Type", mime.name ().toLocal8Bit ());
@ -82,7 +91,7 @@ void StaticFileServing::onRequestNeedsReply (QtHttpRequest * request, QtHttpRepl
} }
else else
{ {
printErrorToReply (reply, "Requested file " % m_baseUrl % "/" % path % " couldn't be open for reading !"); printErrorToReply (reply, "Requested file " % path % " couldn't be open for reading !");
} }
} }
else else

View File

@ -8,12 +8,15 @@
#include "QtHttpRequest.h" #include "QtHttpRequest.h"
#include "QtHttpReply.h" #include "QtHttpReply.h"
#include "QtHttpHeader.h" #include "QtHttpHeader.h"
#include "CgiHandler.h"
#include <hyperion/Hyperion.h>
class StaticFileServing : public QObject { class StaticFileServing : public QObject {
Q_OBJECT Q_OBJECT
public: public:
explicit StaticFileServing (QString baseUrl, quint16 port, quint16 jsonPort, QObject * parent = NULL); explicit StaticFileServing (Hyperion *hyperion, QString baseUrl, quint16 port, QObject * parent = NULL);
virtual ~StaticFileServing (void); virtual ~StaticFileServing (void);
public slots: public slots:
@ -23,10 +26,11 @@ public slots:
void onRequestNeedsReply (QtHttpRequest * request, QtHttpReply * reply); void onRequestNeedsReply (QtHttpRequest * request, QtHttpReply * reply);
private: private:
QString m_baseUrl; Hyperion * _hyperion;
QtHttpServer * _server; QString _baseUrl;
QMimeDatabase * m_mimeDb; QtHttpServer * _server;
quint16 _jsonPort; QMimeDatabase * _mimeDb;
CgiHandler _cgi;
}; };
#endif // STATICFILESERVING_H #endif // STATICFILESERVING_H

View File

@ -1,23 +1,16 @@
#include "webconfig/webconfig.h" #include "webconfig/WebConfig.h"
#include "StaticFileServing.h" #include "StaticFileServing.h"
WebConfig::WebConfig(std::string baseUrl, quint16 port, quint16 jsonPort, QObject * parent) : WebConfig::WebConfig(Hyperion *hyperion, QObject * parent)
_parent(parent), : QObject(parent)
_baseUrl(QString::fromStdString(baseUrl)), , _hyperion(hyperion)
_port(port), , _port(WEBCONFIG_DEFAULT_PORT)
_jsonPort(jsonPort), , _server(nullptr)
_server(nullptr)
{
}
WebConfig::WebConfig(const Json::Value &config, QObject * parent) :
_parent(parent),
_port(WEBCONFIG_DEFAULT_PORT),
_server(nullptr)
{ {
const Json::Value &config = hyperion->getJsonConfig();
_baseUrl = QString::fromStdString(WEBCONFIG_DEFAULT_PATH); _baseUrl = QString::fromStdString(WEBCONFIG_DEFAULT_PATH);
_jsonPort = 19444;
bool webconfigEnable = true; bool webconfigEnable = true;
if (config.isMember("webConfig")) if (config.isMember("webConfig"))
@ -28,13 +21,6 @@ WebConfig::WebConfig(const Json::Value &config, QObject * parent) :
_baseUrl = QString::fromStdString( webconfigConfig.get("document_root", WEBCONFIG_DEFAULT_PATH).asString() ); _baseUrl = QString::fromStdString( webconfigConfig.get("document_root", WEBCONFIG_DEFAULT_PATH).asString() );
} }
if (config.isMember("jsonServer"))
{
const Json::Value & jsonConfig = config["jsonServer"];
_jsonPort = jsonConfig.get("port", 19444).asUInt();
}
if ( webconfigEnable ) if ( webconfigEnable )
start(); start();
} }
@ -49,7 +35,7 @@ WebConfig::~WebConfig()
void WebConfig::start() void WebConfig::start()
{ {
if ( _server == nullptr ) if ( _server == nullptr )
_server = new StaticFileServing (_baseUrl, _port, _jsonPort, this); _server = new StaticFileServing (_hyperion, _baseUrl, _port, this);
} }
void WebConfig::stop() void WebConfig::stop()

View File

@ -203,7 +203,7 @@ void startNetworkServices(const Json::Value &config, Hyperion &hyperion, JsonSer
{ {
const Json::Value & jsonServerConfig = config["jsonServer"]; const Json::Value & jsonServerConfig = config["jsonServer"];
//jsonEnable = jsonServerConfig.get("enable", true).asBool(); //jsonEnable = jsonServerConfig.get("enable", true).asBool();
jsonPort = jsonServerConfig.get("port", 19444).asUInt(); jsonPort = jsonServerConfig.get("port", jsonPort).asUInt();
} }
jsonServer = new JsonServer(&hyperion, jsonPort ); jsonServer = new JsonServer(&hyperion, jsonPort );
@ -215,7 +215,7 @@ void startNetworkServices(const Json::Value &config, Hyperion &hyperion, JsonSer
{ {
const Json::Value & protoServerConfig = config["protoServer"]; const Json::Value & protoServerConfig = config["protoServer"];
//protoEnable = protoServerConfig.get("enable", true).asBool(); //protoEnable = protoServerConfig.get("enable", true).asBool();
protoPort = protoServerConfig.get("port", 19445).asUInt(); protoPort = protoServerConfig.get("port", protoPort).asUInt();
} }
protoServer = new ProtoServer(&hyperion, protoPort ); protoServer = new ProtoServer(&hyperion, protoPort );
@ -231,24 +231,33 @@ void startNetworkServices(const Json::Value &config, Hyperion &hyperion, JsonSer
const std::string deviceName = deviceConfig.get("name", "").asString(); const std::string deviceName = deviceConfig.get("name", "").asString();
const std::string hostname = QHostInfo::localHostName().toStdString(); const std::string hostname = QHostInfo::localHostName().toStdString();
const std::string mDNSDescr = jsonServerConfig.get("mDNSDescr", hostname).asString();
const std::string mDNSService = jsonServerConfig.get("mDNSService", "_hyperiond_json._tcp").asString(); std::string mDNSDescr_json = hostname;
BonjourServiceRegister *bonjourRegister_json; std::string mDNSService_json = "_hyperiond_json._tcp";
bonjourRegister_json = new BonjourServiceRegister(); if (config.isMember("jsonServer"))
bonjourRegister_json->registerService(BonjourRecord((deviceName + " @ " + mDNSDescr).c_str(), mDNSService.c_str(), {
QString()), jsonServerConfig["port"].asUInt()); const Json::Value & jsonServerConfig = config["jsonServer"];
mDNSDescr_json = jsonServerConfig.get("mDNSDescr", mDNSDescr_json).asString();
mDNSService_json = jsonServerConfig.get("mDNSService", mDNSService_json).asString();
}
BonjourServiceRegister *bonjourRegister_json = new BonjourServiceRegister();
bonjourRegister_json->registerService(BonjourRecord((deviceName + " @ " + mDNSDescr_json).c_str(), mDNSService_json.c_str(),
QString()), jsonServer->getPort() );
std::cout << "INFO: Json mDNS responder started" << std::endl; std::cout << "INFO: Json mDNS responder started" << std::endl;
const Json::Value & deviceConfig = config["device"]; std::string mDNSDescr_proto = hostname;
const std::string deviceName = deviceConfig.get("name", "").asString(); std::string mDNSService_proto = "_hyperiond_proto._tcp";
if (config.isMember("protoServer"))
{
const Json::Value & protoServerConfig = config["protoServer"];
mDNSDescr_proto = protoServerConfig.get("mDNSDescr", mDNSDescr_proto).asString();
mDNSService_proto = protoServerConfig.get("mDNSService", mDNSService_proto).asString();
}
const std::string hostname = QHostInfo::localHostName().toStdString(); BonjourServiceRegister *bonjourRegister_proto = new BonjourServiceRegister();
const std::string mDNSDescr = protoServerConfig.get("mDNSDescr", hostname).asString(); bonjourRegister_proto->registerService(BonjourRecord((deviceName + " @ " + mDNSDescr_proto).c_str(), mDNSService_proto.c_str(),
const std::string mDNSService = protoServerConfig.get("mDNSService", "_hyperiond_proto._tcp").asString(); QString()), protoServer->getPort() );
BonjourServiceRegister *bonjourRegister_proto;
bonjourRegister_proto = new BonjourServiceRegister();
bonjourRegister_proto->registerService(BonjourRecord((deviceName + " @ " + mDNSDescr).c_str(), mDNSService.c_str(),
QString()), protoServerConfig["port"].asUInt());
std::cout << "INFO: Proto mDNS responder started" << std::endl; std::cout << "INFO: Proto mDNS responder started" << std::endl;
#endif #endif
@ -487,7 +496,7 @@ int main(int argc, char** argv)
std::cout << "INFO: Selected configuration file: " << configFile.c_str() << std::endl; std::cout << "INFO: Selected configuration file: " << configFile.c_str() << std::endl;
const Json::Value config = loadConfig(configFile); const Json::Value config = loadConfig(configFile);
Hyperion hyperion(config); Hyperion hyperion(config, configFile);
std::cout << "INFO: Hyperion started and initialised" << std::endl; std::cout << "INFO: Hyperion started and initialised" << std::endl;
@ -503,7 +512,7 @@ int main(int argc, char** argv)
startNetworkServices(config, hyperion, jsonServer, protoServer, boblightServer, xbmcVideoChecker); startNetworkServices(config, hyperion, jsonServer, protoServer, boblightServer, xbmcVideoChecker);
#ifdef ENABLE_QT5 #ifdef ENABLE_QT5
WebConfig webConfig(config, &app); WebConfig webConfig(&hyperion, &app);
#endif #endif
// ---- grabber ----- // ---- grabber -----