diff --git a/plugins/deviceplugins/eq-3/deviceplugineq-3.json b/plugins/deviceplugins/eq-3/deviceplugineq-3.json index 38d3c82e..138d6555 100644 --- a/plugins/deviceplugins/eq-3/deviceplugineq-3.json +++ b/plugins/deviceplugins/eq-3/deviceplugineq-3.json @@ -468,6 +468,7 @@ "idName": "deviceModeString", "name": "device mode string", "type": "QString", + "defaultValue": "-", "index": 12 }, { diff --git a/server/cloud/cloudauthenticator.cpp b/server/cloud/cloudauthenticator.cpp index 777201f5..4fe2250d 100644 --- a/server/cloud/cloudauthenticator.cpp +++ b/server/cloud/cloudauthenticator.cpp @@ -156,10 +156,9 @@ void CloudAuthenticator::stopAuthentication() void CloudAuthenticator::setAuthenticated(const bool &authenticated) { - if (!authenticated) { + if (!authenticated) m_timer->stop(); - qCWarning(dcCloud()) << "Authenticator: Authentication failed" << m_username; - } + m_authenticated = authenticated; emit authenticationChanged(); } diff --git a/server/cloud/cloudconnection.cpp b/server/cloud/cloudconnection.cpp index b872385a..027b8ad7 100644 --- a/server/cloud/cloudconnection.cpp +++ b/server/cloud/cloudconnection.cpp @@ -111,8 +111,9 @@ void CloudConnection::onAuthenticationChanged() qCDebug(dcCloud()) << "Connecting to" << m_proxyUrl.toString(); m_connection->open(m_proxyUrl); } else { - m_error = CloudConnectionErrorAuthenticationFailed; + m_error = m_authenticator->error(); } + emit authenticatedChanged(); } void CloudConnection::onConnected() diff --git a/server/guhcore.cpp b/server/guhcore.cpp index 8c121dec..d9b75eee 100644 --- a/server/guhcore.cpp +++ b/server/guhcore.cpp @@ -104,7 +104,6 @@ #include "devicemanager.h" #include "plugin/device.h" - namespace guhserver { GuhCore* GuhCore::s_instance = 0; @@ -362,16 +361,47 @@ TimeManager *GuhCore::timeManager() const return m_timeManager; } +WebServer *GuhCore::webServer() const +{ + return m_webServer; +} + +/*! Returns a pointer to the \l{WebSocketServer} instance owned by GuhCore.*/ +WebSocketServer *GuhCore::webSocketServer() const +{ + return m_webSocketServer; +} + CloudManager *GuhCore::cloudManager() const { return m_cloudManager; } +ServerManager *GuhCore::serverManager() const +{ + return m_serverManager; +} + +#ifdef TESTING_ENABLED +MockTcpServer *GuhCore::tcpServer() const +{ + return m_tcpServer; +} +#else +TcpServer *GuhCore::tcpServer() const +{ + return m_tcpServer; +} +#endif + /*! Constructs GuhCore with the given \a parent. This is private. Use \l{GuhCore::instance()} to access the single instance.*/ GuhCore::GuhCore(QObject *parent) : QObject(parent) { + qCDebug(dcApplication()) << "Loading guh configurations" << GuhSettings(GuhSettings::SettingsRoleGlobal).fileName(); + m_configuration = new GuhConfiguration(this); + qCDebug(dcApplication()) << "Creating Time Manager"; m_timeManager = new TimeManager(QTimeZone::systemTimeZoneId(), this); @@ -379,7 +409,7 @@ GuhCore::GuhCore(QObject *parent) : m_logger = new LogEngine(this); qCDebug(dcApplication) << "Creating Cloud Manager"; - m_cloudManager = new CloudManager(this); + m_cloudManager = new CloudManager(m_configuration->cloudEnabled(), m_configuration->cloudAuthenticationServer(), m_configuration->cloudProxyServer(), this); qCDebug(dcApplication) << "Creating Device Manager"; m_deviceManager = new DeviceManager(this); @@ -390,9 +420,27 @@ GuhCore::GuhCore(QObject *parent) : qCDebug(dcApplication) << "Creating Server Manager"; m_serverManager = new ServerManager(this); - // Register cloud connection transport interface +#ifdef TESTING_ENABLED + m_tcpServer = new MockTcpServer(this); +#else + m_tcpServer = new TcpServer(m_configuration->tcpServerAddress(), m_configuration->tcpServerPort(), this); +#endif + + m_webSocketServer = new WebSocketServer(m_configuration->webSocketAddress(), m_configuration->webSocketPort(), m_configuration->sslEnabled(), this); + + // Register transport interface in the JSON RPC server + m_serverManager->jsonServer()->registerTransportInterface(m_tcpServer); + m_serverManager->jsonServer()->registerTransportInterface(m_webSocketServer); m_serverManager->jsonServer()->registerTransportInterface(m_cloudManager); + m_webServer = new WebServer(m_configuration->webServerAddress(), m_configuration->webServerPort(), m_configuration->webServerPublicFolder(), this); + m_serverManager->restServer()->registerWebserver(m_webServer); + + // Connect the configuration changes + connect(m_configuration, &GuhConfiguration::cloudEnabledChanged, m_cloudManager, &CloudManager::onCloudEnabledChanged); + connect(m_configuration, &GuhConfiguration::cloudProxyServerChanged, m_cloudManager, &CloudManager::onProxyServerUrlChanged); + connect(m_configuration, &GuhConfiguration::cloudAuthenticationServerChanged, m_cloudManager, &CloudManager::onAuthenticationServerUrlChanged); + connect(m_deviceManager, &DeviceManager::eventTriggered, this, &GuhCore::gotEvent); connect(m_deviceManager, &DeviceManager::deviceStateChanged, this, &GuhCore::deviceStateChanged); connect(m_deviceManager, &DeviceManager::deviceAdded, this, &GuhCore::deviceAdded); diff --git a/server/guhcore.h b/server/guhcore.h index daf6f73c..53591c4e 100644 --- a/server/guhcore.h +++ b/server/guhcore.h @@ -31,10 +31,17 @@ #include "devicemanager.h" #include "ruleengine.h" #include "servermanager.h" +#include "websocketserver.h" #include "cloud/cloudmanager.h" #include "time/timemanager.h" +#ifndef TESTING_ENABLED +#include "tcpserver.h" +#else +#include "mocktcpserver.h" +#endif + #include class Device; @@ -71,7 +78,17 @@ public: DeviceManager *deviceManager() const; RuleEngine *ruleEngine() const; TimeManager *timeManager() const; + WebServer *webServer() const; + WebSocketServer *webSocketServer() const; CloudManager *cloudManager() const; + ServerManager *serverManager() const; + + +#ifdef TESTING_ENABLED + MockTcpServer *tcpServer() const; +#else + TcpServer *tcpServer() const; +#endif signals: void eventTriggered(const Event &event); @@ -100,6 +117,13 @@ private: RuleEngine *m_ruleEngine; LogEngine *m_logger; TimeManager *m_timeManager; +#ifdef TESTING_ENABLED + MockTcpServer *m_tcpServer; +#else + TcpServer *m_tcpServer; +#endif + WebSocketServer *m_webSocketServer; + WebServer *m_webServer; CloudManager *m_cloudManager; QHash m_pendingActions; diff --git a/server/jsonrpc/configurationhandler.cpp b/server/jsonrpc/configurationhandler.cpp new file mode 100644 index 00000000..dd93c2be --- /dev/null +++ b/server/jsonrpc/configurationhandler.cpp @@ -0,0 +1,269 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * 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 "configurationhandler.h" +#include "guhcore.h" + +namespace guhserver { + +ConfigurationHandler::ConfigurationHandler(QObject *parent): + JsonHandler(parent) +{ + // Methods + QVariantMap params; QVariantMap returns; + setDescription("GetTimeZones", "Get the list of available timezones."); + setParams("GetTimeZones", params); + returns.insert("timeZones", QVariantList() << JsonTypes::basicTypeToString(JsonTypes::String)); + setReturns("GetTimeZones", returns); + + params.clear(); returns.clear(); + setDescription("GetConfigurations", "Get all configuration parameters of the server."); + setParams("GetConfigurations", params); + QVariantMap basicConfiguration; + basicConfiguration.insert("serverName", JsonTypes::basicTypeToString(JsonTypes::String)); + basicConfiguration.insert("serverUuid", JsonTypes::basicTypeToString(JsonTypes::Uuid)); + basicConfiguration.insert("serverTime", JsonTypes::basicTypeToString(JsonTypes::Uint)); + basicConfiguration.insert("timeZone", JsonTypes::basicTypeToString(JsonTypes::String)); + returns.insert("basicConfiguration", basicConfiguration); + QVariantMap tcpServerConfiguration; + tcpServerConfiguration.insert("host", JsonTypes::basicTypeToString(JsonTypes::String)); + tcpServerConfiguration.insert("port", JsonTypes::basicTypeToString(JsonTypes::Uint)); + returns.insert("tcpServerConfiguration", tcpServerConfiguration); + QVariantMap webServerConfiguration; + webServerConfiguration.insert("host", JsonTypes::basicTypeToString(JsonTypes::String)); + webServerConfiguration.insert("port", JsonTypes::basicTypeToString(JsonTypes::Uint)); + returns.insert("webServerConfiguration", webServerConfiguration); + QVariantMap webSocketServerConfiguration; + webSocketServerConfiguration.insert("host", JsonTypes::basicTypeToString(JsonTypes::String)); + webSocketServerConfiguration.insert("port", JsonTypes::basicTypeToString(JsonTypes::Uint)); + returns.insert("webSocketServerConfiguration", webSocketServerConfiguration); + QVariantMap sslConfiguration; + sslConfiguration.insert("enabled", JsonTypes::basicTypeToString(JsonTypes::Bool)); + returns.insert("sslConfiguration", sslConfiguration); + setReturns("GetConfigurations", returns); + + params.clear(); returns.clear(); + setDescription("SetServerName", "Set the name of the server. Default is guhIO."); + params.insert("serverName", JsonTypes::basicTypeToString(JsonTypes::String)); + setParams("SetServerName", params); + returns.insert("configurationError", JsonTypes::configurationErrorRef()); + setReturns("SetServerName", returns); + + params.clear(); returns.clear(); + setDescription("SetTimeZone", "Set the time zone of the server. See also: \"GetTimeZones\""); + params.insert("timeZone", JsonTypes::basicTypeToString(JsonTypes::String)); + setParams("SetTimeZone", params); + returns.insert("configurationError", JsonTypes::configurationErrorRef()); + setReturns("SetTimeZone", returns); + + params.clear(); returns.clear(); + setDescription("SetTcpServerConfiguration", "Configure the TCP interface of the server. Note: if you are using the TCP server for this call you will loose the connection."); + params.insert("host", JsonTypes::basicTypeToString(JsonTypes::String)); + params.insert("port", JsonTypes::basicTypeToString(JsonTypes::Uint)); + setParams("SetTcpServerConfiguration", params); + returns.insert("configurationError", JsonTypes::configurationErrorRef()); + setReturns("SetTcpServerConfiguration", returns); + + params.clear(); returns.clear(); + setDescription("SetWebSocketServerConfiguration", "Configure the web socket interface of the server. Note: if you are using the web socket server for this call you will loose the connection."); + params.insert("host", JsonTypes::basicTypeToString(JsonTypes::String)); + params.insert("port", JsonTypes::basicTypeToString(JsonTypes::Uint)); + setParams("SetWebSocketServerConfiguration", params); + returns.insert("configurationError", JsonTypes::configurationErrorRef()); + setReturns("SetWebSocketServerConfiguration", returns); + + params.clear(); returns.clear(); + setDescription("SetWebServerConfiguration", "Configure the web server of the server."); + params.insert("host", JsonTypes::basicTypeToString(JsonTypes::String)); + params.insert("port", JsonTypes::basicTypeToString(JsonTypes::Uint)); + setParams("SetWebServerConfiguration", params); + returns.insert("configurationError", JsonTypes::configurationErrorRef()); + setReturns("SetWebServerConfiguration", returns); + + // Notifications + params.clear(); returns.clear(); + setDescription("BasicConfigurationChanged", "Emitted whenever the basic configuration of this server changes."); + params.insert("serverName", JsonTypes::basicTypeToString(JsonTypes::String)); + params.insert("serverUuid", JsonTypes::basicTypeToString(JsonTypes::Uuid)); + params.insert("serverTime", JsonTypes::basicTypeToString(JsonTypes::Uint)); + params.insert("timeZone", JsonTypes::basicTypeToString(JsonTypes::String)); + setParams("BasicConfigurationChanged", params); + + params.clear(); returns.clear(); + setDescription("TcpServerConfigurationChanged", "Emitted whenever the TCP server configuration changes."); + params.insert("host", JsonTypes::basicTypeToString(JsonTypes::String)); + params.insert("port", JsonTypes::basicTypeToString(JsonTypes::Uint)); + setParams("TcpServerConfigurationChanged", params); + + connect(GuhCore::instance()->configuration(), &GuhConfiguration::timeZoneChanged, this, &ConfigurationHandler::onBasicConfigurationChanged); + connect(GuhCore::instance()->configuration(), &GuhConfiguration::serverNameChanged, this, &ConfigurationHandler::onBasicConfigurationChanged); + connect(GuhCore::instance()->configuration(), &GuhConfiguration::tcpServerConfigurationChanged, this, &ConfigurationHandler::onTcpServerConfigurationChanged); + connect(GuhCore::instance()->configuration(), &GuhConfiguration::webServerConfigurationChanged, this, &ConfigurationHandler::onWebServerConfigurationChanged); + connect(GuhCore::instance()->configuration(), &GuhConfiguration::webSocketServerConfigurationChanged, this, &ConfigurationHandler::onWebSocketServerConfigurationChanged); +} + +QString ConfigurationHandler::name() const +{ + return "Configuration"; +} + +JsonReply *ConfigurationHandler::GetConfigurations(const QVariantMap ¶ms) const +{ + Q_UNUSED(params) + QVariantMap returns; + returns.insert("basicConfiguration", JsonTypes::packBasicConfiguration()); + returns.insert("tcpServerConfiguration", JsonTypes::packTcpServerConfiguration()); + returns.insert("webServerConfiguration", JsonTypes::packWebServerConfiguration()); + returns.insert("webSocketServerConfiguration", JsonTypes::packWebSocketServerConfiguration()); + QVariantMap sslConfiguration; + sslConfiguration.insert("enabled", GuhCore::instance()->configuration()->sslEnabled()); + returns.insert("sslConfiguration", sslConfiguration); + return createReply(returns); +} + +JsonReply *ConfigurationHandler::GetTimeZones(const QVariantMap ¶ms) const +{ + Q_UNUSED(params) + QVariantList timeZones; + foreach (const QByteArray &timeZoneId, GuhCore::instance()->timeManager()->availableTimeZones()) { + timeZones.append(QString::fromUtf8(timeZoneId)); + } + + QVariantMap returns; + returns.insert("timeZones", timeZones); + return createReply(returns); +} + +JsonReply *ConfigurationHandler::SetServerName(const QVariantMap ¶ms) const +{ + QString serverName = params.value("serverName").toString(); + GuhCore::instance()->configuration()->setServerName(serverName); + return createReply(statusToReply(GuhConfiguration::ConfigurationErrorNoError)); +} + +JsonReply *ConfigurationHandler::SetTimeZone(const QVariantMap ¶ms) const +{ + qCDebug(dcJsonRpc()) << "Setting time zone to" << params.value("timeZone").toString(); + + QByteArray timeZone = params.value("timeZone").toString().toUtf8(); + if (!GuhCore::instance()->timeManager()->setTimeZone(timeZone)) + return createReply(statusToReply(GuhConfiguration::ConfigurationErrorInvalidTimeZone)); + + GuhCore::instance()->configuration()->setTimeZone(timeZone); + return createReply(statusToReply(GuhConfiguration::ConfigurationErrorNoError)); +} + +JsonReply *ConfigurationHandler::SetTcpServerConfiguration(const QVariantMap ¶ms) const +{ + QHostAddress host = QHostAddress(params.value("host").toString()); + if (host.isNull()) + return createReply(statusToReply(GuhConfiguration::ConfigurationErrorInvalidHostAddress)); + + uint port = params.value("port").toUInt(); + if (port <= 0 || port > 65535) { + qCWarning(dcJsonRpc()) << "Port out of range"; + return createReply(statusToReply(GuhConfiguration::ConfigurationErrorInvalidPort)); + } + + qCDebug(dcJsonRpc()) << QString("Configure TCP server %1:%2").arg(host.toString()).arg(port); + + if (!GuhCore::instance()->tcpServer()->reconfigureServer(host, port)) + return createReply(statusToReply(GuhConfiguration::ConfigurationErrorInvalidPort)); + + GuhCore::instance()->configuration()->setTcpServerConfiguration(port, host); + + return createReply(statusToReply(GuhConfiguration::ConfigurationErrorNoError)); +} + +JsonReply *ConfigurationHandler::SetWebServerConfiguration(const QVariantMap ¶ms) const +{ + QHostAddress host = QHostAddress(params.value("host").toString()); + if (host.isNull()) + return createReply(statusToReply(GuhConfiguration::ConfigurationErrorInvalidHostAddress)); + + uint port = params.value("port").toUInt(); + if (port <= 0 || port > 65535) { + qCWarning(dcJsonRpc()) << "Port out of range"; + return createReply(statusToReply(GuhConfiguration::ConfigurationErrorInvalidPort)); + } + + qCDebug(dcJsonRpc()) << QString("Configure web server %1:%2").arg(host.toString()).arg(port); + + if (!GuhCore::instance()->webServer()->reconfigureServer(host, port)) + return createReply(statusToReply(GuhConfiguration::ConfigurationErrorInvalidPort)); + + GuhCore::instance()->configuration()->setWebServerConfiguration(port, host); + return createReply(statusToReply(GuhConfiguration::ConfigurationErrorNoError)); +} + +JsonReply *ConfigurationHandler::SetWebSocketServerConfiguration(const QVariantMap ¶ms) const +{ + QHostAddress host = QHostAddress(params.value("host").toString()); + if (host.isNull()) + return createReply(statusToReply(GuhConfiguration::ConfigurationErrorInvalidHostAddress)); + + uint port = params.value("port").toUInt(); + if (port <= 0 || port > 65535) { + qCWarning(dcJsonRpc()) << "Port out of range"; + return createReply(statusToReply(GuhConfiguration::ConfigurationErrorInvalidPort)); + } + + qCDebug(dcJsonRpc()) << QString("Configure web socket server %1:%2").arg(host.toString()).arg(port); + + if (!GuhCore::instance()->webSocketServer()->reconfigureServer(host, port)) + return createReply(statusToReply(GuhConfiguration::ConfigurationErrorInvalidPort)); + + GuhCore::instance()->configuration()->setWebServerConfiguration(port, host); + return createReply(statusToReply(GuhConfiguration::ConfigurationErrorNoError)); +} + +void ConfigurationHandler::onBasicConfigurationChanged() +{ + QVariantMap params; + qCDebug(dcJsonRpc()) << "Notification: Basic configuration changed"; + params.insert("basicConfiguration", JsonTypes::packBasicConfiguration()); + emit BasicConfigurationChanged(params); +} + +void ConfigurationHandler::onTcpServerConfigurationChanged() +{ + QVariantMap params; + qCDebug(dcJsonRpc()) << "Notification: TCP server configuration changed"; + params.insert("tcpServerConfiguration", JsonTypes::packTcpServerConfiguration()); + emit TcpServerConfigurationChanged(params); +} + +void ConfigurationHandler::onWebServerConfigurationChanged() +{ + QVariantMap params; + qCDebug(dcJsonRpc()) << "Notification: web server configuration changed"; + params.insert("webServerConfiguration", JsonTypes::packWebServerConfiguration()); + emit WebServerConfigurationChanged(params); +} + +void ConfigurationHandler::onWebSocketServerConfigurationChanged() +{ + QVariantMap params; + qCDebug(dcJsonRpc()) << "Notification: web socket server configuration changed"; + params.insert("webSocketServerConfiguration", JsonTypes::packWebSocketServerConfiguration()); + emit WebSocketServerConfigurationChanged(params); +} + +} diff --git a/server/jsonrpc/configurationhandler.h b/server/jsonrpc/configurationhandler.h new file mode 100644 index 00000000..c6754a40 --- /dev/null +++ b/server/jsonrpc/configurationhandler.h @@ -0,0 +1,62 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * 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 CONFIGURATIONHANDLER_H +#define CONFIGURATIONHANDLER_H + +#include + +#include "jsonhandler.h" + +namespace guhserver { + +class ConfigurationHandler : public JsonHandler +{ + Q_OBJECT + +public: + ConfigurationHandler(QObject *parent = 0); + QString name() const; + + Q_INVOKABLE JsonReply* GetConfigurations(const QVariantMap ¶ms) const; + Q_INVOKABLE JsonReply* GetTimeZones(const QVariantMap ¶ms) const; + Q_INVOKABLE JsonReply* SetServerName(const QVariantMap ¶ms) const; + Q_INVOKABLE JsonReply* SetTimeZone(const QVariantMap ¶ms) const; + Q_INVOKABLE JsonReply* SetTcpServerConfiguration(const QVariantMap ¶ms) const; + Q_INVOKABLE JsonReply *SetWebServerConfiguration(const QVariantMap ¶ms) const; + Q_INVOKABLE JsonReply* SetWebSocketServerConfiguration(const QVariantMap ¶ms) const; + +signals: + void BasicConfigurationChanged(const QVariantMap ¶ms); + void TcpServerConfigurationChanged(const QVariantMap ¶ms); + void WebServerConfigurationChanged(const QVariantMap ¶ms); + void WebSocketServerConfigurationChanged(const QVariantMap ¶ms); + +private slots: + void onBasicConfigurationChanged(); + void onTcpServerConfigurationChanged(); + void onWebServerConfigurationChanged(); + void onWebSocketServerConfigurationChanged(); + +}; + +} + +#endif // CONFIGURATIONHANDLER_H diff --git a/server/jsonrpc/jsonhandler.cpp b/server/jsonrpc/jsonhandler.cpp index bd0036bf..396cdcf4 100644 --- a/server/jsonrpc/jsonhandler.cpp +++ b/server/jsonrpc/jsonhandler.cpp @@ -204,6 +204,22 @@ QVariantMap JsonHandler::statusToReply(Logging::LoggingError status) const return returns; } +/*! Returns the formated error map for the given \a status. */ +QVariantMap JsonHandler::statusToReply(Cloud::CloudError status) const +{ + QVariantMap returns; + returns.insert("cloudError", JsonTypes::cloudErrorToString(status)); + return returns; +} + +/*! Returns the formated error map for the given \a status. */ +QVariantMap JsonHandler::statusToReply(GuhConfiguration::ConfigurationError status) const +{ + QVariantMap returns; + returns.insert("configurationError", JsonTypes::configurationErrorToString(status)); + return returns; +} + /*! \class guhserver::JsonReply diff --git a/server/jsonrpc/jsonhandler.h b/server/jsonrpc/jsonhandler.h index 88391289..c90fef79 100644 --- a/server/jsonrpc/jsonhandler.h +++ b/server/jsonrpc/jsonhandler.h @@ -111,6 +111,8 @@ protected: QVariantMap statusToReply(DeviceManager::DeviceError status) const; QVariantMap statusToReply(RuleEngine::RuleError status) const; QVariantMap statusToReply(Logging::LoggingError status) const; + QVariantMap statusToReply(Cloud::CloudError status) const; + QVariantMap statusToReply(GuhConfiguration::ConfigurationError status) const; private: QHash m_descriptions; diff --git a/server/jsonrpc/jsonrpcserver.cpp b/server/jsonrpc/jsonrpcserver.cpp index a388674a..4b888d9b 100644 --- a/server/jsonrpc/jsonrpcserver.cpp +++ b/server/jsonrpc/jsonrpcserver.cpp @@ -56,12 +56,7 @@ #include "statehandler.h" #include "websocketserver.h" #include "cloudhandler.h" - -#ifndef TESTING_ENABLED -#include "tcpserver.h" -#else -#include "mocktcpserver.h" -#endif +#include "configurationhandler.h" #include #include @@ -72,14 +67,9 @@ namespace guhserver { /*! Constructs a \l{JsonRPCServer} with the given \a sslConfiguration and \a parent. */ JsonRPCServer::JsonRPCServer(const QSslConfiguration &sslConfiguration, QObject *parent): JsonHandler(parent), - #ifdef TESTING_ENABLED - m_tcpServer(new MockTcpServer(this)), - #else - m_tcpServer(new TcpServer(this)), - #endif - m_websocketServer(new WebSocketServer(sslConfiguration, this)), m_notificationId(0) { + Q_UNUSED(sslConfiguration) // First, define our own JSONRPC methods QVariantMap returns; QVariantMap params; @@ -105,9 +95,6 @@ JsonRPCServer::JsonRPCServer(const QSslConfiguration &sslConfiguration, QObject returns.insert("enabled", JsonTypes::basicTypeToString(JsonTypes::Bool)); setReturns("SetNotificationStatus", returns); - registerTransportInterface(m_tcpServer); - registerTransportInterface(m_websocketServer); - QMetaObject::invokeMethod(this, "setup", Qt::QueuedConnection); } @@ -183,6 +170,7 @@ void JsonRPCServer::setup() registerHandler(new LoggingHandler(this)); registerHandler(new StateHandler(this)); registerHandler(new CloudHandler(this)); + registerHandler(new ConfigurationHandler(this)); } void JsonRPCServer::processData(const QUuid &clientId, const QString &targetNamespace, const QString &method, const QVariantMap &message) diff --git a/server/jsonrpc/jsonrpcserver.h b/server/jsonrpc/jsonrpcserver.h index 0a02966c..09b10eab 100644 --- a/server/jsonrpc/jsonrpcserver.h +++ b/server/jsonrpc/jsonrpcserver.h @@ -36,19 +36,8 @@ class Device; class QSslConfiguration; - -#ifdef TESTING_ENABLED -class MockTcpServer; -#endif - namespace guhserver { -class WebSocketServer; - -#ifndef TESTING_ENABLED -class TcpServer; -#endif - class JsonRPCServer: public JsonHandler { Q_OBJECT @@ -78,14 +67,6 @@ private slots: void asyncReplyFinished(); private: -#ifdef TESTING_ENABLED - MockTcpServer *m_tcpServer; -#else - TcpServer *m_tcpServer; -#endif - - WebSocketServer *m_websocketServer; - QList m_interfaces; QHash m_handlers; QHash m_asyncReplies; diff --git a/server/jsonrpc/jsontypes.cpp b/server/jsonrpc/jsontypes.cpp index a6de453f..355efd61 100644 --- a/server/jsonrpc/jsontypes.cpp +++ b/server/jsonrpc/jsontypes.cpp @@ -82,7 +82,8 @@ QVariantList JsonTypes::s_loggingSource; QVariantList JsonTypes::s_loggingLevel; QVariantList JsonTypes::s_loggingEventType; QVariantList JsonTypes::s_repeatingMode; -QVariantList JsonTypes::s_cloudConnectionError; +QVariantList JsonTypes::s_cloudError; +QVariantList JsonTypes::s_configurationError; QVariantMap JsonTypes::s_paramType; QVariantMap JsonTypes::s_param; @@ -131,7 +132,8 @@ void JsonTypes::init() s_loggingLevel = enumToStrings(Logging::staticMetaObject, "LoggingLevel"); s_loggingEventType = enumToStrings(Logging::staticMetaObject, "LoggingEventType"); s_repeatingMode = enumToStrings(RepeatingOption::staticMetaObject, "RepeatingMode"); - s_cloudConnectionError = enumToStrings(CloudConnection::staticMetaObject, "CloudConnectionError"); + s_cloudError = enumToStrings(Cloud::staticMetaObject, "CloudError"); + s_configurationError = enumToStrings(GuhConfiguration::staticMetaObject, "ConfigurationError"); // ParamType s_paramType.insert("name", basicTypeToString(String)); @@ -360,7 +362,8 @@ QVariantMap JsonTypes::allTypes() allTypes.insert("LoggingSource", loggingSource()); allTypes.insert("LoggingEventType", loggingEventType()); allTypes.insert("RepeatingMode", repeatingMode()); - allTypes.insert("CloudConnectionError", cloudConnectionError()); + allTypes.insert("CloudError", cloudError()); + allTypes.insert("ConfigurationError", configurationError()); allTypes.insert("StateType", stateTypeDescription()); allTypes.insert("StateDescriptor", stateDescriptorDescription()); @@ -989,6 +992,40 @@ QVariantList JsonTypes::packDeviceDescriptors(const QList devi return deviceDescriptorList; } +QVariantMap JsonTypes::packBasicConfiguration() +{ + QVariantMap basicConfiguration; + basicConfiguration.insert("serverName", GuhCore::instance()->configuration()->serverName()); + basicConfiguration.insert("serverUuid", GuhCore::instance()->configuration()->serverUuid()); + basicConfiguration.insert("serverTime", GuhCore::instance()->timeManager()->currentDateTime().toTime_t()); + basicConfiguration.insert("timeZone", QString::fromUtf8(GuhCore::instance()->timeManager()->timeZone())); + return basicConfiguration; +} + +QVariantMap JsonTypes::packTcpServerConfiguration() +{ + QVariantMap tcpServerConfiguration; + tcpServerConfiguration.insert("host", GuhCore::instance()->configuration()->tcpServerAddress().toString()); + tcpServerConfiguration.insert("port", GuhCore::instance()->configuration()->tcpServerPort()); + return tcpServerConfiguration; +} + +QVariantMap JsonTypes::packWebServerConfiguration() +{ + QVariantMap webServerConfiguration; + webServerConfiguration.insert("host", GuhCore::instance()->configuration()->webServerAddress().toString()); + webServerConfiguration.insert("port", GuhCore::instance()->configuration()->webServerPort()); + return webServerConfiguration; +} + +QVariantMap JsonTypes::packWebSocketServerConfiguration() +{ + QVariantMap webSocketServerConfiguration; + webSocketServerConfiguration.insert("host", GuhCore::instance()->configuration()->webSocketAddress().toString()); + webSocketServerConfiguration.insert("port", GuhCore::instance()->configuration()->webSocketPort()); + return webSocketServerConfiguration; +} + /*! Returns a variant list containing all rule descriptions. */ QVariantList JsonTypes::packRuleDescriptions() { @@ -1756,6 +1793,12 @@ QPair JsonTypes::validateVariant(const QVariant &templateVariant, qCWarning(dcJsonRpc) << QString("Value %1 not allowed in %2").arg(variant.toString()).arg(cloudConnectionErrorRef()); return result; } + } else if (refName == configurationErrorRef()) { + QPair result = validateEnum(s_configurationError, variant); + if (!result.first) { + qCWarning(dcJsonRpc) << QString("Value %1 not allowed in %2").arg(variant.toString()).arg(configurationErrorRef()); + return result; + } } else { Q_ASSERT_X(false, "JsonTypes", QString("Unhandled ref: %1").arg(refName).toLatin1().data()); return report(false, QString("Unhandled ref %1. Server implementation incomplete.").arg(refName)); diff --git a/server/jsonrpc/jsontypes.h b/server/jsonrpc/jsontypes.h index ff1ce97c..4bb8d75f 100644 --- a/server/jsonrpc/jsontypes.h +++ b/server/jsonrpc/jsontypes.h @@ -27,6 +27,7 @@ #include "rule.h" #include "devicemanager.h" #include "ruleengine.h" +#include "guhconfiguration.h" #include "types/event.h" #include "types/action.h" @@ -124,7 +125,8 @@ public: DECLARE_TYPE(loggingLevel, "LoggingLevel", Logging, LoggingLevel) DECLARE_TYPE(loggingEventType, "LoggingEventType", Logging, LoggingEventType) DECLARE_TYPE(repeatingMode, "RepeatingMode", RepeatingOption, RepeatingMode) - DECLARE_TYPE(cloudConnectionError, "CloudConnectionError", CloudConnection, CloudConnectionError) + DECLARE_TYPE(cloudError, "CloudError", Cloud, CloudError) + DECLARE_TYPE(configurationError, "ConfigurationError", GuhConfiguration, ConfigurationError) DECLARE_OBJECT(paramType, "ParamType") DECLARE_OBJECT(param, "Param") @@ -190,6 +192,11 @@ public: static QVariantList packDeviceStates(Device *device); static QVariantList packDeviceDescriptors(const QList deviceDescriptors); + static QVariantMap packBasicConfiguration(); + static QVariantMap packTcpServerConfiguration(); + static QVariantMap packWebServerConfiguration(); + static QVariantMap packWebSocketServerConfiguration(); + static QVariantList packRuleDescriptions(); static QVariantList packRuleDescriptions(const QList &rules); diff --git a/server/rest/restserver.cpp b/server/rest/restserver.cpp index 416f6aed..ffe7eac3 100644 --- a/server/rest/restserver.cpp +++ b/server/rest/restserver.cpp @@ -47,16 +47,22 @@ namespace guhserver { /*! Constructs a \l{RestServer} with the given \a sslConfiguration and \a parent. */ RestServer::RestServer(const QSslConfiguration &sslConfiguration, QObject *parent) : - QObject(parent) + QObject(parent), + m_webserver(0) { - m_webserver = new WebServer(sslConfiguration, this); + Q_UNUSED(sslConfiguration) + + QMetaObject::invokeMethod(this, "setup", Qt::QueuedConnection); +} + +void RestServer::registerWebserver(WebServer *webServer) +{ + m_webserver = webServer; connect(m_webserver, &WebServer::clientConnected, this, &RestServer::clientConnected); connect(m_webserver, &WebServer::clientDisconnected, this, &RestServer::clientDisconnected); connect(m_webserver, &WebServer::httpRequestReady, this, &RestServer::processHttpRequest); m_webserver->startServer(); - - QMetaObject::invokeMethod(this, "setup", Qt::QueuedConnection); } void RestServer::setup() diff --git a/server/rest/restserver.h b/server/rest/restserver.h index 458b822b..e030dbb9 100644 --- a/server/rest/restserver.h +++ b/server/rest/restserver.h @@ -45,6 +45,8 @@ class RestServer : public QObject public: explicit RestServer(const QSslConfiguration &sslConfiguration = QSslConfiguration(), QObject *parent = 0); + void registerWebserver(WebServer *webServer); + private: WebServer *m_webserver; QList m_clientList; diff --git a/server/server.pri b/server/server.pri index 68ea3034..ee08805f 100644 --- a/server/server.pri +++ b/server/server.pri @@ -15,7 +15,7 @@ HEADERS += $$top_srcdir/server/guhcore.h \ $$top_srcdir/server/jsonrpc/statehandler.h \ $$top_srcdir/server/jsonrpc/logginghandler.h \ $$top_srcdir/server/jsonrpc/cloudhandler.h \ - $$top_srcdir/server/stateevaluator.h \ + $$top_srcdir/server/jsonrpc/configurationhandler.h \ $$top_srcdir/server/logging/logging.h \ $$top_srcdir/server/logging/logengine.h \ $$top_srcdir/server/logging/logfilter.h \ @@ -63,7 +63,7 @@ SOURCES += $$top_srcdir/server/guhcore.cpp \ $$top_srcdir/server/jsonrpc/statehandler.cpp \ $$top_srcdir/server/jsonrpc/logginghandler.cpp \ $$top_srcdir/server/jsonrpc/cloudhandler.cpp \ - $$top_srcdir/server/stateevaluator.cpp \ + $$top_srcdir/server/jsonrpc/configurationhandler.cpp \ $$top_srcdir/server/logging/logengine.cpp \ $$top_srcdir/server/logging/logfilter.cpp \ $$top_srcdir/server/logging/logentry.cpp \ diff --git a/server/tcpserver.cpp b/server/tcpserver.cpp index d688f96d..69df4a58 100644 --- a/server/tcpserver.cpp +++ b/server/tcpserver.cpp @@ -48,40 +48,13 @@ namespace guhserver { * * \sa ServerManager */ -TcpServer::TcpServer(QObject *parent) : - TransportInterface(parent) +TcpServer::TcpServer(const QHostAddress &host, const uint &port, QObject *parent) : + TransportInterface(parent), + m_server(NULL), + m_host(host), + m_port(port) { - // Timer for scanning network interfaces ever 5 seconds - // Note: QNetworkConfigurationManager does not work on embedded platforms - m_timer = new QTimer(this); - m_timer->setSingleShot(false); - m_timer->setInterval(5000); - connect (m_timer, &QTimer::timeout, this, &TcpServer::onTimeout); - // load JSON-RPC server settings - GuhSettings settings(GuhSettings::SettingsRoleGlobal); - qCDebug(dcTcpServer) << "Loading Tcp server settings from" << settings.fileName(); - settings.beginGroup("JSONRPC"); - - // load port - // 2222 Official free according to https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers - m_port = settings.value("port", 2222).toUInt(); - - // load interfaces - QStringList interfaceList = settings.value("interfaces", QStringList("all")).toStringList(); - if (interfaceList.contains("all")) { - m_networkInterfaces = QNetworkInterface::allInterfaces(); - } else { - foreach (const QNetworkInterface &interface, QNetworkInterface::allInterfaces()) { - if (interfaceList.contains(interface.name())) { - m_networkInterfaces.append(interface); - } - } - } - - // load IP versions (IPv4, IPv6 or any) - m_ipVersions = settings.value("ip", QStringList("any")).toStringList(); - settings.endGroup(); } /*! Destructor of this \l{TcpServer}. */ @@ -109,27 +82,6 @@ void TcpServer::sendData(const QUuid &clientId, const QVariantMap &data) } } -void TcpServer::reloadNetworkInterfaces() -{ - GuhSettings settings(GuhSettings::SettingsRoleGlobal); - settings.beginGroup("JSONRPC"); - - // reload network interfaces - m_networkInterfaces.clear(); - - QStringList interfaceList = settings.value("interfaces", QStringList("all")).toStringList(); - if (interfaceList.contains("all")) { - m_networkInterfaces = QNetworkInterface::allInterfaces(); - } else { - foreach (const QNetworkInterface &interface, QNetworkInterface::allInterfaces()) { - if (interfaceList.contains(interface.name())) { - m_networkInterfaces.append(interface); - } - } - } - settings.endGroup(); -} - void TcpServer::onClientConnected() { // got a new client connected @@ -151,11 +103,11 @@ void TcpServer::onClientConnected() void TcpServer::readPackage() { QTcpSocket *client = qobject_cast(sender()); - qCDebug(dcTcpServer) << "data comming from" << client->peerAddress().toString(); + qCDebug(dcTcpServer) << "Data comming from" << client->peerAddress().toString(); QByteArray message; while (client->canReadLine()) { QByteArray dataLine = client->readLine(); - qCDebug(dcTcpServer) << "line in:" << dataLine; + qCDebug(dcTcpServer) << "Line in:" << dataLine; message.append(dataLine); if (dataLine.endsWith('\n')) { validateMessage(m_clientList.key(client), message); @@ -166,7 +118,10 @@ void TcpServer::readPackage() void TcpServer::onClientDisconnected() { - QTcpSocket *client = qobject_cast(sender()); + QPointer client = qobject_cast(sender()); + if (client.isNull()) + return; + qCDebug(dcConnection) << "Tcp server: client disconnected:" << client->peerAddress().toString(); QUuid clientId = m_clientList.key(client); m_clientList.take(clientId)->deleteLater(); @@ -175,85 +130,35 @@ void TcpServer::onClientDisconnected() void TcpServer::onError(QAbstractSocket::SocketError error) { QTcpServer *server = qobject_cast(sender()); - QUuid uuid = m_serverList.key(server); - qCWarning(dcTcpServer) << "Tcp server" << server->serverAddress().toString() << "error:" << error << server->errorString(); - qCWarning(dcTcpServer) << "shutting down Tcp server" << server->serverAddress().toString(); - - server->close(); - m_serverList.take(uuid)->deleteLater(); + qCWarning(dcTcpServer) << server->serverAddress().toString() << "error:" << error << server->errorString(); + stopServer(); } -void TcpServer::onTimeout() + +bool TcpServer::reconfigureServer(const QHostAddress &address, const uint &port) { - bool ipV4 = m_ipVersions.contains("IPv4"); - bool ipV6 = m_ipVersions.contains("IPv6"); - if (m_ipVersions.contains("any")) { - ipV4 = ipV6 = true; + if (m_host == address && m_port == (qint16)port && m_server->isListening()) + return true; + + stopServer(); + + QTcpServer *server = new QTcpServer(this); + if(!server->listen(address, port)) { + qCWarning(dcConnection) << "Tcp server error: can not listen on" << address.toString() << port; + delete server; + // Restart the server with the old configuration + qCDebug(dcTcpServer()) << "Restart server with old configuration."; + startServer(); + return false; } + // Remove the test server.. + server->close(); + delete server; - reloadNetworkInterfaces(); - - // get all available host addresses - QList allAddresses; - foreach (const QNetworkInterface &interface, m_networkInterfaces) { - QList addresseEntries = interface.addressEntries(); - - foreach (QNetworkAddressEntry entry, addresseEntries) { - if (ipV4 && entry.ip().protocol() == QAbstractSocket::IPv4Protocol) { - allAddresses.append(entry.ip()); - } - if (ipV6 && entry.ip().protocol() == QAbstractSocket::IPv6Protocol) { - allAddresses.append(entry.ip()); - } - } - } - - // check if a new server should be created - QList serversToCreate; - QList serversToDelete; - - foreach (const QHostAddress &address, allAddresses) { - // check if there is a server for this host address - bool found = false; - foreach (QTcpServer *s, m_serverList) { - if (s->serverAddress() == address) { - found = true; - break; - } - } - if (!found) - serversToCreate.append(address); - } - - // delete every server which has no longer an interface - foreach (QTcpServer *s, m_serverList) { - if (!allAddresses.contains(s->serverAddress())) { - qCDebug(dcConnection) << "Closing Tcp server on" << s->serverAddress().toString() << s->serverPort(); - QUuid uuid = m_serverList.key(s); - s->close(); - m_serverList.take(uuid)->deleteLater(); - } - - } - - foreach(const QHostAddress &address, serversToCreate){ - QTcpServer *server = new QTcpServer(this); - if(server->listen(address, m_port)) { - qCDebug(dcConnection) << "Started TCP server on" << address.toString() << m_port; - connect(server, &QTcpServer::newConnection, this, &TcpServer::onClientConnected); - m_serverList.insert(QUuid::createUuid(), server); - } else { - qCWarning(dcConnection) << "Tcp server error: can not listen on" << address.toString() << m_port; - delete server; - } - } - - if (!serversToCreate.isEmpty() || !serversToDelete.isEmpty()) { - qCDebug(dcConnection) << "Current server list:"; - foreach (QTcpServer *s, m_serverList) { - qCDebug(dcConnection) << " - Tcp server on" << s->serverAddress().toString() << s->serverPort(); - } - } + // Start server with new configuration + m_host = address; + m_port = port; + return startServer(); } /*! Returns true if this \l{TcpServer} started successfully. @@ -262,44 +167,16 @@ void TcpServer::onTimeout() */ bool TcpServer::startServer() { - bool ipV4 = m_ipVersions.contains("IPv4"); - bool ipV6 = m_ipVersions.contains("IPv6"); - if (m_ipVersions.contains("any")) { - ipV4 = ipV6 = true; - } - - foreach (const QNetworkInterface &interface, m_networkInterfaces) { - QList addresseEntries = interface.addressEntries(); - QList addresses; - - foreach (QNetworkAddressEntry entry, addresseEntries) { - if (ipV4 && entry.ip().protocol() == QAbstractSocket::IPv4Protocol) { - addresses.append(entry.ip()); - } - if (ipV6 && entry.ip().protocol() == QAbstractSocket::IPv6Protocol) { - addresses.append(entry.ip()); - } - } - - // create a tcp server for each address found for the corresponding settings - foreach(const QHostAddress &address, addresses){ - QTcpServer *server = new QTcpServer(this); - if(server->listen(address, m_port)) { - qCDebug(dcConnection) << "Started Tcp server on" << server->serverAddress().toString() << server->serverPort(); - connect(server, SIGNAL(newConnection()), SLOT(onClientConnected())); - m_serverList.insert(QUuid::createUuid(), server); - } else { - qCWarning(dcConnection) << "Tcp server error: can not listen on" << interface.name() << address.toString() << m_port; - delete server; - } - } - } - - m_timer->start(); - - if (m_serverList.empty()) + m_server = new QTcpServer(this); + if(!m_server->listen(m_host, m_port)) { + qCWarning(dcConnection) << "Tcp server error: can not listen on" << m_host.toString() << m_port; + delete m_server; + m_server = NULL; return false; + } + qCDebug(dcConnection) << "Started Tcp server on" << m_server->serverAddress().toString() << m_server->serverPort(); + connect(m_server, SIGNAL(newConnection()), SLOT(onClientConnected())); return true; } @@ -309,16 +186,12 @@ bool TcpServer::startServer() */ bool TcpServer::stopServer() { - // Listen on all Networkinterfaces - foreach (QTcpServer *s, m_serverList) { - QUuid uuid = m_serverList.key(s); - s->close(); - m_serverList.take(uuid)->deleteLater(); - } - - if (!m_serverList.empty()) - return false; + if (!m_server) + return true; + m_server->close(); + m_server->deleteLater(); + m_server = NULL; return true; } diff --git a/server/tcpserver.h b/server/tcpserver.h index cc736c85..ac107c0b 100644 --- a/server/tcpserver.h +++ b/server/tcpserver.h @@ -37,7 +37,7 @@ class TcpServer : public TransportInterface { Q_OBJECT public: - explicit TcpServer(QObject *parent = 0); + explicit TcpServer(const QHostAddress &host, const uint &port, QObject *parent = 0); ~TcpServer(); void sendData(const QUuid &clientId, const QVariantMap &data) override; @@ -46,24 +46,20 @@ public: private: QTimer *m_timer; - QHash m_serverList; - QHash m_clientList; - - uint m_port; - QList m_networkInterfaces; - QStringList m_ipVersions; - - void reloadNetworkInterfaces(); + QTcpServer * m_server; + QHash m_clientList; + QHostAddress m_host; + qint16 m_port; private slots: void onClientConnected(); void onClientDisconnected(); void readPackage(); void onError(QAbstractSocket::SocketError error); - void onTimeout(); public slots: + bool reconfigureServer(const QHostAddress &address, const uint &port); bool startServer() override; bool stopServer() override; }; diff --git a/server/time/timemanager.cpp b/server/time/timemanager.cpp index 3a4fc47b..a2be9d84 100644 --- a/server/time/timemanager.cpp +++ b/server/time/timemanager.cpp @@ -45,10 +45,8 @@ TimeManager::TimeManager(const QByteArray &timeZone, QObject *parent) : { m_dateTime = QDateTime::currentDateTimeUtc(); m_dateTime.setTimeSpec(Qt::UTC); - qCDebug(dcTimeManager()) << "UTC" << m_dateTime.toString("dd.MM.yyyy hh:mm:ss"); setTimeZone(timeZone); - qCDebug(dcTimeManager) << m_dateTime.toTimeZone(m_timeZone).toString("dd.MM.yyyy hh:mm:ss"); m_guhTimer = new QTimer(this); m_guhTimer->setInterval(1000); @@ -65,17 +63,24 @@ QByteArray TimeManager::timeZone() const return m_timeZone.id(); } -/*! Sets the \a timeZone of this \l{TimeManager}. Allowed values according to the \l{http://www.iana.org/time-zones}{IANA database}. */ -void TimeManager::setTimeZone(const QByteArray &timeZone) +/*! Sets the \a timeZone of this \l{TimeManager}. Allowed values according to the \l{http://www.iana.org/time-zones}{IANA database}. + * Returns false if the given timezone is not valid. */ +bool TimeManager::setTimeZone(const QByteArray &timeZone) { if (!QTimeZone(timeZone).isValid()) { qCWarning(dcTimeManager()) << "Invalid time zone" << timeZone; qCWarning(dcTimeManager()) << "Using system time zone" << QTimeZone::systemTimeZoneId(); m_timeZone = QTimeZone(QTimeZone::systemTimeZoneId()); - } else { - qCDebug(dcTimeManager()) << "Set time zone" << timeZone; - m_timeZone = QTimeZone(timeZone); + emit dateTimeChanged(currentDateTime()); + return false; } + + qCDebug(dcTimeManager()) << "Set time zone" << timeZone; + m_timeZone = QTimeZone(timeZone); + qCDebug(dcTimeManager()) << "UTC" << m_dateTime.toString("dd.MM.yyyy hh:mm:ss"); + qCDebug(dcTimeManager) << "Zone time" << currentDateTime().toString("dd.MM.yyyy hh:mm:ss"); + emit dateTimeChanged(currentDateTime()); + return true; } /*! Returns the current dateTime of this \l{TimeManager}. */ @@ -96,6 +101,11 @@ QDate TimeManager::currentDate() const return QDateTime::currentDateTimeUtc().toTimeZone(m_timeZone).date(); } +QList TimeManager::availableTimeZones() const +{ + return QTimeZone::availableTimeZoneIds(); +} + #ifdef TESTING_ENABLED void TimeManager::stopTimer() { @@ -105,7 +115,7 @@ void TimeManager::stopTimer() void TimeManager::setTime(const QDateTime &dateTime) { - // This method will only be called for testing + // This method will only be called for testing to set the guhIO intern time emit tick(); emit dateTimeChanged(dateTime.toTimeZone(m_timeZone)); } diff --git a/server/time/timemanager.h b/server/time/timemanager.h index 71332777..d8406d9f 100644 --- a/server/time/timemanager.h +++ b/server/time/timemanager.h @@ -35,12 +35,14 @@ public: explicit TimeManager(const QByteArray &timeZone, QObject *parent = 0); QByteArray timeZone() const; - void setTimeZone(const QByteArray &timeZone = QTimeZone::systemTimeZoneId()); + bool setTimeZone(const QByteArray &timeZone = QTimeZone::systemTimeZoneId()); QDateTime currentDateTime() const; QTime currentTime() const; QDate currentDate() const; + QList availableTimeZones() const; + #ifdef TESTING_ENABLED void stopTimer(); void setTime(const QDateTime &dateTime); diff --git a/server/webserver.cpp b/server/webserver.cpp index 6ddfe260..714e5646 100644 --- a/server/webserver.cpp +++ b/server/webserver.cpp @@ -95,38 +95,15 @@ namespace guhserver { * * \sa ServerManager */ -WebServer::WebServer(const QSslConfiguration &sslConfiguration, QObject *parent) : +WebServer::WebServer(const QHostAddress &host, const uint &port, const QString &publicFolder, QObject *parent) : QTcpServer(parent), m_avahiService(NULL), - m_sslConfiguration(sslConfiguration), + m_host(host), + m_port(port), + m_webinterfaceDir(publicFolder), m_useSsl(false), m_enabled(false) { - // load webserver settings - GuhSettings settings(GuhSettings::SettingsRoleGlobal); - qCDebug(dcWebSocketServer) << "Loading web socket server settings from" << settings.fileName(); - - settings.beginGroup("WebServer"); - // 3333 Official free according to https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers - m_port = settings.value("port", 3333).toInt(); - m_useSsl = settings.value("https", false).toBool(); - m_webinterfaceDir = QDir(settings.value("publicFolder", "/usr/share/guh-webinterface/public/").toString()); - settings.endGroup(); - - // check public directory - qCDebug(dcWebServer) << "Publish webinterface folder" << m_webinterfaceDir.path(); - if (!m_webinterfaceDir.exists()) - qCWarning(dcWebServer) << "Web interface public folder" << m_webinterfaceDir.path() << "does not exist."; - - if (QCoreApplication::instance()->organizationName() == "guh-test") { - m_webinterfaceDir = QDir(QCoreApplication::applicationDirPath()); - qCWarning(dcWebServer) << "Using public folder" << m_webinterfaceDir.path(); - } - - // check SSL - if (m_useSsl && m_sslConfiguration.isNull()) - m_useSsl = false; - // Create avahi service #ifndef TESTING_ENABLED m_avahiService = new QtAvahiService(this); @@ -540,10 +517,32 @@ void WebServer::onAvahiServiceStateChanged(const QtAvahiService::QtAvahiServiceS } } +bool WebServer::reconfigureServer(const QHostAddress &address, const uint &port) +{ + if (m_host == address && m_port == (qint16)port && isListening()) + return true; + + stopServer(); + + if (!listen(address, port)) { + qCWarning(dcConnection()) << "Webserver could not listen on" << serverAddress().toString() << m_port; + qCDebug(dcWebServer()) << "Restart server with old configuration."; + startServer(); + return false; + } + + close(); + m_host = address; + m_port = port; + startServer(); + + return true; +} + /*! Returns true if this \l{WebServer} started successfully. */ bool WebServer::startServer() { - if (!listen(QHostAddress::AnyIPv4, m_port)) { + if (!listen(m_host, m_port)) { qCWarning(dcConnection) << "Webserver could not listen on" << serverAddress().toString() << m_port; m_enabled = false; return false; @@ -564,6 +563,9 @@ bool WebServer::startServer() /*! Returns true if this \l{WebServer} stopped successfully. */ bool WebServer::stopServer() { + foreach (QSslSocket *client, m_clientList.values()) + client->close(); + close(); m_enabled = false; qCDebug(dcConnection) << "Webserver closed."; @@ -573,20 +575,8 @@ bool WebServer::stopServer() QByteArray WebServer::createServerXmlDocument(QHostAddress address) { - GuhSettings settings(GuhSettings::SettingsRoleDevices); - settings.beginGroup("guhd"); - QByteArray uuid = settings.value("uuid", QVariant()).toByteArray(); - if (uuid.isEmpty()) { - uuid = QUuid::createUuid().toByteArray().replace("{", "").replace("}",""); - settings.setValue("uuid", uuid); - } - settings.endGroup(); - - GuhSettings globalSettings(GuhSettings::SettingsRoleGlobal); - globalSettings.beginGroup("WebSocketServer"); - int websocketPort = globalSettings.value("port", 4444).toInt(); - globalSettings.endGroup(); - + QByteArray uuid = GuhCore::instance()->configuration()->serverUuid().toByteArray(); + uint websocketPort = GuhCore::instance()->configuration()->webSocketPort(); QByteArray data; QXmlStreamWriter writer(&data); diff --git a/server/webserver.h b/server/webserver.h index b12c9290..6d70d559 100644 --- a/server/webserver.h +++ b/server/webserver.h @@ -72,7 +72,7 @@ class WebServer : public QTcpServer { Q_OBJECT public: - explicit WebServer(const QSslConfiguration &sslConfiguration = QSslConfiguration(), QObject *parent = 0); + explicit WebServer(const QHostAddress &host, const uint &port, const QString &publicFolder, QObject *parent = 0); ~WebServer(); void sendHttpReply(HttpReply *reply); @@ -87,12 +87,14 @@ private: QtAvahiService *m_avahiService; + QHostAddress m_host; + qint16 m_port; + QDir m_webinterfaceDir; + QSslConfiguration m_sslConfiguration; bool m_useSsl; bool m_enabled; - qint16 m_port; - QDir m_webinterfaceDir; bool verifyFile(QSslSocket *socket, const QString &fileName); QString fileName(const QString &query); @@ -117,8 +119,8 @@ private slots: void onAvahiServiceStateChanged(const QtAvahiService::QtAvahiServiceState &state); - public slots: + bool reconfigureServer(const QHostAddress &address, const uint &port); bool startServer(); bool stopServer(); diff --git a/server/websocketserver.cpp b/server/websocketserver.cpp index 4dce6b93..13a355b1 100644 --- a/server/websocketserver.cpp +++ b/server/websocketserver.cpp @@ -60,26 +60,15 @@ namespace guhserver { * * \sa ServerManager */ -WebSocketServer::WebSocketServer(const QSslConfiguration &sslConfiguration, QObject *parent) : +WebSocketServer::WebSocketServer(const QHostAddress &address, const uint &port, const bool &sslEnabled, QObject *parent) : TransportInterface(parent), m_server(0), - m_sslConfiguration(sslConfiguration), - m_useSsl(false), + m_host(address), + m_port(port), + m_useSsl(sslEnabled), m_enabled(false) { - // load webserver settings - GuhSettings settings(GuhSettings::SettingsRoleGlobal); - qCDebug(dcWebServer) << "Loading webserver settings from" << settings.fileName(); - settings.beginGroup("WebSocketServer"); - // 4444 Official free according to https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers - m_port = settings.value("port", 4444).toInt(); - m_useSsl = settings.value("https", false).toBool(); - settings.endGroup(); - - // check SSL - if (m_useSsl && m_sslConfiguration.isNull()) - m_useSsl = false; } /*! Destructor of this \l{WebSocketServer}. */ @@ -179,17 +168,44 @@ void WebSocketServer::onPing(quint64 elapsedTime, const QByteArray &payload) qCDebug(dcWebSocketServer) << "ping response" << client->peerAddress() << elapsedTime << payload; } +bool WebSocketServer::reconfigureServer(const QHostAddress &address, const uint &port) +{ + if (m_host == address && m_port == (qint16)port && m_server->isListening()) + return true; + + stopServer(); + QWebSocketServer *server; + if (m_useSsl) { + server = new QWebSocketServer("guh", QWebSocketServer::SecureMode, this); + server->setSslConfiguration(m_sslConfiguration); + } else { + server = new QWebSocketServer("guh", QWebSocketServer::NonSecureMode, this); + } + + if(!server->listen(address, port)) { + qCWarning(dcConnection) << "Websocket server error: can not listen on" << address.toString() << port; + delete server; + // Restart the server with the old configuration + qCDebug(dcWebSocketServer()) << "Restart server with old configuration."; + startServer(); + return false; + } + // Remove the test server.. + server->close(); + delete server; + + // Start server with new configuration + m_host = address; + m_port = port; + return startServer(); +} + /*! Returns true if this \l{WebSocketServer} started successfully. * * \sa TransportInterface::startServer() */ bool WebSocketServer::startServer() { - if (m_server) { - qCWarning(dcConnection) << "There is allready a websocket server instance. This should never happen!!! Please report this bug!"; - return false; - } - if (m_useSsl) { m_server = new QWebSocketServer("guh", QWebSocketServer::SecureMode, this); m_server->setSslConfiguration(m_sslConfiguration); @@ -199,7 +215,7 @@ bool WebSocketServer::startServer() connect (m_server, &QWebSocketServer::newConnection, this, &WebSocketServer::onClientConnected); connect (m_server, &QWebSocketServer::acceptError, this, &WebSocketServer::onServerError); - if (!m_server->listen(QHostAddress::Any, m_port)) { + if (!m_server->listen(m_host, m_port)) { qCWarning(dcConnection) << "Websocket server" << m_server->serverName() << QString("could not listen on %1:%2").arg(m_server->serverAddress().toString()).arg(m_port); return false; } @@ -218,6 +234,10 @@ bool WebSocketServer::startServer() */ bool WebSocketServer::stopServer() { + foreach (QWebSocket *client, m_clientList.values()) { + client->close(QWebSocketProtocol::CloseCodeNormal, "Stop server"); + } + m_server->close(); delete m_server; m_server = 0; diff --git a/server/websocketserver.h b/server/websocketserver.h index 824f3e63..ead728f9 100644 --- a/server/websocketserver.h +++ b/server/websocketserver.h @@ -41,7 +41,7 @@ class WebSocketServer : public TransportInterface { Q_OBJECT public: - explicit WebSocketServer(const QSslConfiguration &sslConfiguration = QSslConfiguration(), QObject *parent = 0); + explicit WebSocketServer(const QHostAddress &address, const uint &port, const bool &sslEnabled, QObject *parent = 0); ~WebSocketServer(); void sendData(const QUuid &clientId, const QVariantMap &data) override; @@ -51,11 +51,13 @@ private: QWebSocketServer *m_server; QHash m_clientList; + QHostAddress m_host; + qint16 m_port; + QSslConfiguration m_sslConfiguration; bool m_useSsl; bool m_enabled; - qint16 m_port; private slots: void onClientConnected(); @@ -67,6 +69,7 @@ private slots: void onPing(quint64 elapsedTime, const QByteArray & payload); public slots: + bool reconfigureServer(const QHostAddress &address, const uint &port); bool startServer() override; bool stopServer() override; }; diff --git a/tests/auto/mocktcpserver.cpp b/tests/auto/mocktcpserver.cpp index ef38c7a2..dfc605e1 100644 --- a/tests/auto/mocktcpserver.cpp +++ b/tests/auto/mocktcpserver.cpp @@ -124,6 +124,13 @@ void MockTcpServer::sendErrorResponse(const QUuid &clientId, int commandId, cons sendData(clientId, errorResponse); } +bool MockTcpServer::reconfigureServer(const QHostAddress &address, const uint &port) +{ + Q_UNUSED(address) + Q_UNUSED(port) + return true; +} + bool MockTcpServer::startServer() { return true; diff --git a/tests/auto/mocktcpserver.h b/tests/auto/mocktcpserver.h index c1fd4418..5c202469 100644 --- a/tests/auto/mocktcpserver.h +++ b/tests/auto/mocktcpserver.h @@ -53,6 +53,7 @@ public: void sendErrorResponse(const QUuid &clientId, int commandId, const QString &error); public slots: + bool reconfigureServer(const QHostAddress &address, const uint &port); bool startServer() override; bool stopServer() override;