added ability to configure each server interface individually

This commit is contained in:
Michael Zanetti 2017-09-03 23:19:04 +02:00
parent 63ffc163ba
commit 44dd07950b
25 changed files with 508 additions and 225 deletions

View File

@ -5,7 +5,7 @@ GUH_VERSION_STRING=$$system('dpkg-parsechangelog | sed -n -e "s/^Version: //p"')
GUH_PLUGINS_PATH=/usr/lib/$$system('dpkg-architecture -q DEB_HOST_MULTIARCH')/guh/plugins/
# define protocol versions
JSON_PROTOCOL_VERSION=49
JSON_PROTOCOL_VERSION=50
REST_API_VERSION=1
DEFINES += GUH_VERSION_STRING=\\\"$${GUH_VERSION_STRING}\\\" \

View File

@ -45,7 +45,7 @@ namespace guhserver {
static const QBluetoothUuid serviceUuid(QUuid("997936b5-d2cd-4c57-b41b-c6048320cd2b"));
/*! Constructs a \l{BluetoothServer} with the given \a parent. */
BluetoothServer::BluetoothServer(QObject *parent) :
TransportInterface(parent),
TransportInterface(ServerConfiguration(), parent),
m_server(0)
{

View File

@ -48,7 +48,6 @@ GuhConfiguration::GuhConfiguration(QObject *parent) :
if (settings.childGroups().contains("TcpServer")) {
settings.beginGroup("TcpServer");
foreach (const QString &key, settings.childGroups()) {
qDebug() << "have key" << key;
ServerConfiguration config = readServerConfig("TcpServer", key);
m_tcpServerConfigs[config.id] = config;
}
@ -70,16 +69,7 @@ GuhConfiguration::GuhConfiguration(QObject *parent) :
if (settings.childGroups().contains("WebServer")) {
settings.beginGroup("WebServer");
foreach (const QString &key, settings.childGroups()) {
ServerConfiguration tmp = readServerConfig("WebServer", key);
WebServerConfiguration config;
config.id = tmp.id;
config.address = tmp.address;
config.port = tmp.port;
config.sslEnabled = tmp.sslEnabled;
config.authenticationEnabled = tmp.authenticationEnabled;
settings.beginGroup(key);
config.publicFolder = settings.value("publicFolder").toString();
settings.endGroup();
WebServerConfiguration config = readWebServerConfig(key);
m_webServerConfigs[config.id] = config;
}
settings.endGroup();
@ -92,23 +82,21 @@ GuhConfiguration::GuhConfiguration(QObject *parent) :
// TODO enable encryption/authentication by default once the important clients are supporting it
config.sslEnabled = false;
config.authenticationEnabled = false;
config.publicFolder = settings.value("publicFolder", "/usr/share/guh-webinterface/public/").toString();
config.publicFolder = "/usr/share/guh-webinterface/public/";
#ifdef SNAPPY
// Override default public folder path for snappy
config.publicFolder = settings.value("publicFolder", QString(qgetenv("SNAP")) + "/guh-webinterface/").toString();
#endif
m_webServerConfigs[config.id] = config;
storeServerConfig("WebServer", config);
storeWebServerConfig(config);
}
// WebSocket Server
if (settings.childGroups().contains("WebSocketServer")) {
settings.beginGroup("WebSocketServer");
foreach (const QString &key, settings.childGroups()) {
qWarning() << "have key" << key;
ServerConfiguration config = readServerConfig("WebSocketServer", key);
m_webSocketServerConfigs[config.id] = config;
qWarning() << "cound:" << m_webSocketServerConfigs.keys();
}
settings.endGroup();
} else {
@ -234,6 +222,13 @@ void GuhConfiguration::setTcpServerConfiguration(const ServerConfiguration &conf
emit tcpServerConfigurationChanged(config.id);
}
void GuhConfiguration::removeTcpServerConfiguration(const QString &id)
{
m_tcpServerConfigs.take(id);
deleteServerConfig("TcpServer", id);
emit tcpServerConfigurationRemoved(id);
}
QHash<QString, WebServerConfiguration> GuhConfiguration::webServerConfigurations() const
{
return m_webServerConfigs;
@ -256,6 +251,13 @@ void GuhConfiguration::setWebServerConfiguration(const WebServerConfiguration &c
emit webServerConfigurationChanged(config.id);
}
void GuhConfiguration::removeWebServerConfiguration(const QString &id)
{
m_webServerConfigs.take(id);
deleteServerConfig("WebServer", id);
emit webServerConfigurationRemoved(id);
}
QHash<QString, ServerConfiguration> GuhConfiguration::webSocketServerConfigurations() const
{
return m_webSocketServerConfigs;
@ -268,6 +270,13 @@ void GuhConfiguration::setWebSocketServerConfiguration(const ServerConfiguration
emit webSocketServerConfigurationChanged(config.id);
}
void GuhConfiguration::removeWebSocketServerConfiguration(const QString &id)
{
m_webSocketServerConfigs.take(id);
deleteServerConfig("WebSocketServer", id);
emit webSocketServerConfigurationRemoved(id);
}
bool GuhConfiguration::bluetoothServerEnabled() const
{
return m_bluetoothServerEnabled;
@ -328,6 +337,7 @@ void GuhConfiguration::storeServerConfig(const QString &group, const ServerConfi
{
GuhSettings settings(GuhSettings::SettingsRoleGlobal);
settings.beginGroup(group);
settings.remove("disabled");
settings.beginGroup(config.id);
settings.setValue("address", config.address.toString());
settings.setValue("port", config.port);
@ -353,4 +363,43 @@ ServerConfiguration GuhConfiguration::readServerConfig(const QString &group, con
return config;
}
void GuhConfiguration::deleteServerConfig(const QString &group, const QString &id)
{
GuhSettings settings(GuhSettings::SettingsRoleGlobal);
settings.beginGroup(group);
settings.remove(id);
if (settings.childGroups().isEmpty()) {
settings.setValue("disabled", true);
}
settings.endGroup();
}
void GuhConfiguration::storeWebServerConfig(const WebServerConfiguration &config)
{
storeServerConfig("WebServer", config);
GuhSettings settings(GuhSettings::SettingsRoleGlobal);
settings.beginGroup("WebServer");
settings.beginGroup(config.id);
settings.setValue("publicFolder", config.publicFolder);
settings.endGroup();
settings.endGroup();
}
WebServerConfiguration GuhConfiguration::readWebServerConfig(const QString &id)
{
WebServerConfiguration config;
GuhSettings settings(GuhSettings::SettingsRoleGlobal);
settings.beginGroup("WebServer");
settings.beginGroup(id);
config.id = id;
config.address = QHostAddress(settings.value("address").toString());
config.port = settings.value("port").toUInt();
config.sslEnabled = settings.value("sslEnabled", true).toBool();
config.authenticationEnabled = settings.value("authenticationEnabled", true).toBool();
config.publicFolder = settings.value("publicFolder").toString();
settings.endGroup();
settings.endGroup();
return config;
}
}

View File

@ -36,6 +36,14 @@ public:
uint port = 0;
bool sslEnabled = true;
bool authenticationEnabled = true;
bool operator==(const ServerConfiguration &other) {
return id == other.id
&& address == other.address
&& port == other.port
&& sslEnabled == other.sslEnabled
&& authenticationEnabled == other.authenticationEnabled;
}
};
class WebServerConfiguration: public ServerConfiguration
{
@ -53,6 +61,7 @@ public:
ConfigurationErrorNoError,
ConfigurationErrorInvalidTimeZone,
ConfigurationErrorInvalidStationName,
ConfigurationErrorInvalidId,
ConfigurationErrorInvalidPort,
ConfigurationErrorInvalidHostAddress,
ConfigurationErrorBluetoothHardwareNotAvailable,
@ -76,15 +85,17 @@ public:
// TCP server
QHash<QString, ServerConfiguration> tcpServerConfigurations() const;
void setTcpServerConfiguration(const ServerConfiguration &config);
void removeTcpServerConfiguration(const QUuid &id);
void removeTcpServerConfiguration(const QString &id);
// Web server
QHash<QString, WebServerConfiguration> webServerConfigurations() const;
void setWebServerConfiguration(const WebServerConfiguration &config);
void removeWebServerConfiguration(const QString &id);
// Websocket
QHash<QString, ServerConfiguration> webSocketServerConfigurations() const;
void setWebSocketServerConfiguration(const ServerConfiguration &config);
void removeWebSocketServerConfiguration(const QString &id);
// Bluetooth
bool bluetoothServerEnabled() const;
@ -114,6 +125,9 @@ private:
void storeServerConfig(const QString &group, const ServerConfiguration &config);
ServerConfiguration readServerConfig(const QString &group, const QString &id);
void deleteServerConfig(const QString &group, const QString &id);
void storeWebServerConfig(const WebServerConfiguration &config);
WebServerConfiguration readWebServerConfig(const QString &id);
signals:
void serverNameChanged();
@ -121,8 +135,11 @@ signals:
void localeChanged();
void tcpServerConfigurationChanged(const QString &configId);
void tcpServerConfigurationRemoved(const QString &configId);
void webServerConfigurationChanged(const QString &configId);
void webServerConfigurationRemoved(const QString &configId);
void webSocketServerConfigurationChanged(const QString &configId);
void webSocketServerConfigurationRemoved(const QString &configId);
void bluetoothServerEnabledChanged();

View File

@ -402,6 +402,10 @@ UserManager *GuhCore::userManager() const
GuhCore::GuhCore(QObject *parent) :
QObject(parent)
{
staticMetaObject.invokeMethod(this, "init", Qt::QueuedConnection);
}
void GuhCore::init() {
qCDebug(dcApplication()) << "Loading guh configurations" << GuhSettings(GuhSettings::SettingsRoleGlobal).fileName();
m_configuration = new GuhConfiguration(this);
@ -447,6 +451,7 @@ GuhCore::GuhCore(QObject *parent) :
connect(m_timeManager, &TimeManager::tick, m_deviceManager, &DeviceManager::timeTick);
m_logger->logSystemEvent(m_timeManager->currentDateTime(), true);
emit initialized();
}
/*! Connected to the DeviceManager's emitEvent signal. Events received in

View File

@ -81,6 +81,8 @@ public:
static QStringList getAvailableLanguages();
signals:
void initialized();
void pluginConfigChanged(const PluginId &id, const ParamList &config);
void eventTriggered(const Event &event);
void deviceStateChanged(Device *device, const QUuid &stateTypeId, const QVariant &value);
@ -115,6 +117,7 @@ private:
QHash<ActionId, Action> m_pendingActions;
private slots:
void init();
void gotEvent(const Event &event);
void onDateTimeChanged(const QDateTime &dateTime);
void onLocaleChanged();

View File

@ -90,21 +90,15 @@ ConfigurationHandler::ConfigurationHandler(QObject *parent):
basicConfiguration.insert("timeZone", JsonTypes::basicTypeToString(JsonTypes::String));
basicConfiguration.insert("language", 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);
QVariantList tcpServerConfigurations;
tcpServerConfigurations.append(JsonTypes::serverConfigurationRef());
returns.insert("tcpServerConfigurations", tcpServerConfigurations);
QVariantList webServerConfigurations;
webServerConfigurations.append(JsonTypes::webServerConfigurationRef());
returns.insert("webServerConfigurations", webServerConfigurations);
QVariantList webSocketServerConfigurations;
webSocketServerConfigurations.append(JsonTypes::serverConfigurationRef());
returns.insert("webSocketServerConfigurations", webSocketServerConfigurations);
setReturns("GetConfigurations", returns);
params.clear(); returns.clear();
@ -129,29 +123,47 @@ ConfigurationHandler::ConfigurationHandler(QObject *parent):
setReturns("SetLanguage", 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));
setDescription("SetTcpServerConfiguration", "Configure a TCP interface of the server. If the ID is an existing one, the existing config will be modified, otherwise a new one will be added. Note: if you are changing the configuration for the interface you are currently connected to, the connection will be dropped.");
params.insert("configuration", JsonTypes::serverConfigurationRef());
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));
setDescription("DeleteTcpServerConfiguration", "Delete a TCP interface of the server. Note: if you are deleting the configuration for the interface you are currently connected to, the connection will be dropped.");
params.insert("id", JsonTypes::basicTypeToString(QVariant::String));
setParams("DeleteTcpServerConfiguration", params);
returns.insert("configurationError", JsonTypes::configurationErrorRef());
setReturns("DeleteTcpServerConfiguration", returns);
params.clear(); returns.clear();
setDescription("SetWebSocketServerConfiguration", "Configure a WebSocket Server interface of the server. If the ID is an existing one, the existing config will be modified, otherwise a new one will be added. Note: if you are changing the configuration for the interface you are currently connected to, the connection will be dropped.");
params.insert("configuration", JsonTypes::serverConfigurationRef());
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));
setDescription("DeleteWebSocketServerConfiguration", "Delete a WebSocket Server interface of the server. Note: if you are deleting the configuration for the interface you are currently connected to, the connection will be dropped.");
params.insert("id", JsonTypes::basicTypeToString(QVariant::String));
setParams("DeleteWebSocketServerConfiguration", params);
returns.insert("configurationError", JsonTypes::configurationErrorRef());
setReturns("DeleteWebSocketServerConfiguration", returns);
params.clear(); returns.clear();
setDescription("SetWebServerConfiguration", "Configure a WebServer interface of the server. If the ID is an existing one, the existing config will be modified, otherwise a new one will be added.");
params.insert("configuration", JsonTypes::webServerConfigurationRef());
setParams("SetWebServerConfiguration", params);
returns.insert("configurationError", JsonTypes::configurationErrorRef());
setReturns("SetWebServerConfiguration", returns);
params.clear(); returns.clear();
setDescription("DeleteWebServerConfiguration", "Delete a WebServer interface of the server.");
params.insert("id", JsonTypes::basicTypeToString(QVariant::String));
setParams("DeleteWebServerConfiguration", params);
returns.insert("configurationError", JsonTypes::configurationErrorRef());
setReturns("DeleteWebServerConfiguration", returns);
// Notifications
params.clear(); returns.clear();
setDescription("BasicConfigurationChanged", "Emitted whenever the basic configuration of this server changes.");
@ -215,13 +227,13 @@ JsonReply *ConfigurationHandler::GetConfigurations(const QVariantMap &params) co
webServerConfigs.append(JsonTypes::packWebServerConfiguration(config));
}
returns.insert("webServerConfigurations", tcpServerConfigs);
returns.insert("webServerConfigurations", webServerConfigs);
QVariantList webSocketServerConfigs;
foreach (const ServerConfiguration &config, GuhCore::instance()->configuration()->webSocketServerConfigurations()) {
tcpServerConfigs.append(JsonTypes::packServerConfiguration(config));
webSocketServerConfigs.append(JsonTypes::packServerConfiguration(config));
}
returns.insert("webServerConfigurations", tcpServerConfigs);
returns.insert("webSocketServerConfigurations", webSocketServerConfigs);
return createReply(returns);
}
@ -282,7 +294,10 @@ JsonReply *ConfigurationHandler::SetLanguage(const QVariantMap &params) const
JsonReply *ConfigurationHandler::SetTcpServerConfiguration(const QVariantMap &params) const
{
ServerConfiguration config = JsonTypes::unpackServerConfiguration(params);
ServerConfiguration config = JsonTypes::unpackServerConfiguration(params.value("configuration").toMap());
if (config.id.isEmpty()) {
return createReply(statusToReply(GuhConfiguration::ConfigurationErrorInvalidId));
}
if (config.address.isNull())
return createReply(statusToReply(GuhConfiguration::ConfigurationErrorInvalidHostAddress));
@ -300,10 +315,24 @@ JsonReply *ConfigurationHandler::SetTcpServerConfiguration(const QVariantMap &pa
return createReply(statusToReply(GuhConfiguration::ConfigurationErrorNoError));
}
JsonReply *ConfigurationHandler::DeleteTcpServerConfiguration(const QVariantMap &params) const
{
QString id = params.value("id").toString();
if (id.isEmpty() || !GuhCore::instance()->configuration()->tcpServerConfigurations().contains(id)) {
return createReply(statusToReply(GuhConfiguration::ConfigurationErrorInvalidId));
}
GuhCore::instance()->configuration()->removeTcpServerConfiguration(id);
return createReply(statusToReply(GuhConfiguration::ConfigurationErrorNoError));
}
JsonReply *ConfigurationHandler::SetWebServerConfiguration(const QVariantMap &params) const
{
WebServerConfiguration config = JsonTypes::unpackWebServerConfiguration(params);
qWarning() << params;
WebServerConfiguration config = JsonTypes::unpackWebServerConfiguration(params.value("configuration").toMap());
if (config.id.isEmpty()) {
return createReply(statusToReply(GuhConfiguration::ConfigurationErrorInvalidId));
}
if (config.address.isNull())
return createReply(statusToReply(GuhConfiguration::ConfigurationErrorInvalidHostAddress));
@ -320,9 +349,22 @@ JsonReply *ConfigurationHandler::SetWebServerConfiguration(const QVariantMap &pa
return createReply(statusToReply(GuhConfiguration::ConfigurationErrorNoError));
}
JsonReply *ConfigurationHandler::DeleteWebServerConfiguration(const QVariantMap &params) const
{
QString id = params.value("id").toString();
if (id.isEmpty() || !GuhCore::instance()->configuration()->webServerConfigurations().contains(id)) {
return createReply(statusToReply(GuhConfiguration::ConfigurationErrorInvalidId));
}
GuhCore::instance()->configuration()->removeWebServerConfiguration(id);
return createReply(statusToReply(GuhConfiguration::ConfigurationErrorNoError));
}
JsonReply *ConfigurationHandler::SetWebSocketServerConfiguration(const QVariantMap &params) const
{
ServerConfiguration config = JsonTypes::unpackServerConfiguration(params);
ServerConfiguration config = JsonTypes::unpackServerConfiguration(params.value("configuration").toMap());
if (config.id.isEmpty()) {
return createReply(statusToReply(GuhConfiguration::ConfigurationErrorInvalidId));
}
if (config.address.isNull())
return createReply(statusToReply(GuhConfiguration::ConfigurationErrorInvalidHostAddress));
@ -334,11 +376,20 @@ JsonReply *ConfigurationHandler::SetWebSocketServerConfiguration(const QVariantM
qCDebug(dcJsonRpc()) << QString("Configure web socket server %1:%2").arg(config.address.toString()).arg(config.port);
GuhCore::instance()->configuration()->setWebSocketServerConfiguration(config);
// GuhCore::instance()->webSocketServer()->reconfigureServer(config);
return createReply(statusToReply(GuhConfiguration::ConfigurationErrorNoError));
}
JsonReply *ConfigurationHandler::DeleteWebSocketServerConfiguration(const QVariantMap &params) const
{
QString id = params.value("id").toString();
if (id.isEmpty() || !GuhCore::instance()->configuration()->webSocketServerConfigurations().contains(id)) {
return createReply(statusToReply(GuhConfiguration::ConfigurationErrorInvalidId));
}
GuhCore::instance()->configuration()->removeWebSocketServerConfiguration(id);
return createReply(statusToReply(GuhConfiguration::ConfigurationErrorNoError));
}
void ConfigurationHandler::onBasicConfigurationChanged()
{
QVariantMap params;

View File

@ -42,8 +42,11 @@ public:
Q_INVOKABLE JsonReply *SetTimeZone(const QVariantMap &params) const;
Q_INVOKABLE JsonReply *SetLanguage(const QVariantMap &params) const;
Q_INVOKABLE JsonReply *SetTcpServerConfiguration(const QVariantMap &params) const;
Q_INVOKABLE JsonReply *DeleteTcpServerConfiguration(const QVariantMap &params) const;
Q_INVOKABLE JsonReply *SetWebServerConfiguration(const QVariantMap &params) const;
Q_INVOKABLE JsonReply *DeleteWebServerConfiguration(const QVariantMap &params) const;
Q_INVOKABLE JsonReply *SetWebSocketServerConfiguration(const QVariantMap &params) const;
Q_INVOKABLE JsonReply *DeleteWebSocketServerConfiguration(const QVariantMap &params) const;
signals:
void BasicConfigurationChanged(const QVariantMap &params);

View File

@ -237,18 +237,23 @@ QHash<QString, JsonHandler *> JsonRPCServer::handlers() const
return m_handlers;
}
/*! Register a new \l{TransportInterface} to the JSON server. The \a enabled flag indivates if the given \a interface sould be enebeld on startup. */
void JsonRPCServer::registerTransportInterface(TransportInterface *interface, bool enabled, bool authenticationRequired)
/*! Register a new \l{TransportInterface} to the JSON server. If the given interface is already registered, just the authenticationRequired flag will be updated. */
void JsonRPCServer::registerTransportInterface(TransportInterface *interface, bool authenticationRequired)
{
connect(interface, &TransportInterface::clientConnected, this, &JsonRPCServer::clientConnected);
connect(interface, &TransportInterface::clientDisconnected, this, &JsonRPCServer::clientDisconnected);
connect(interface, &TransportInterface::dataAvailable, this, &JsonRPCServer::processData);
m_interfaces.insert(interface, authenticationRequired);
if (enabled) {
QMetaObject::invokeMethod(interface, "startServer", Qt::QueuedConnection);
if (!m_interfaces.contains(interface)) {
connect(interface, &TransportInterface::clientConnected, this, &JsonRPCServer::clientConnected);
connect(interface, &TransportInterface::clientDisconnected, this, &JsonRPCServer::clientDisconnected);
connect(interface, &TransportInterface::dataAvailable, this, &JsonRPCServer::processData);
}
m_interfaces[interface] = authenticationRequired;
}
void JsonRPCServer::unregisterTransportInterface(TransportInterface *interface)
{
disconnect(interface, &TransportInterface::clientConnected, this, &JsonRPCServer::clientConnected);
disconnect(interface, &TransportInterface::clientDisconnected, this, &JsonRPCServer::clientDisconnected);
disconnect(interface, &TransportInterface::dataAvailable, this, &JsonRPCServer::processData);
m_interfaces.take(interface);
}
/*! Send a JSON success response to the client with the given \a clientId,
@ -454,7 +459,8 @@ void JsonRPCServer::clientConnected(const QUuid &clientId)
handshake.insert("uuid", GuhCore::instance()->configuration()->serverUuid().toString());
handshake.insert("language", GuhCore::instance()->configuration()->locale().name());
handshake.insert("protocol version", JSON_PROTOCOL_VERSION);
handshake.insert("initialSetupRequired", GuhCore::instance()->userManager()->users().isEmpty());
handshake.insert("initialSetupRequired", (interface->configuration().authenticationEnabled ? GuhCore::instance()->userManager()->users().isEmpty() : false));
handshake.insert("authenticationRequired", interface->configuration().authenticationEnabled);
interface->sendData(clientId, QJsonDocument::fromVariant(handshake).toJson());
}

View File

@ -58,7 +58,8 @@ public:
QHash<QString, JsonHandler *> handlers() const;
void registerTransportInterface(TransportInterface *interface, bool enabled, bool authenticationRequired);
void registerTransportInterface(TransportInterface *interface, bool authenticationRequired);
void unregisterTransportInterface(TransportInterface *interface);
private:
void sendResponse(TransportInterface *interface, const QUuid &clientId, int commandId, const QVariantMap &params = QVariantMap());

View File

@ -119,6 +119,8 @@ QVariantMap JsonTypes::s_wirelessAccessPoint;
QVariantMap JsonTypes::s_wiredNetworkDevice;
QVariantMap JsonTypes::s_wirelessNetworkDevice;
QVariantMap JsonTypes::s_tokenInfo;
QVariantMap JsonTypes::s_serverConfiguration;
QVariantMap JsonTypes::s_webServerConfiguration;
void JsonTypes::init()
{
@ -359,6 +361,16 @@ void JsonTypes::init()
s_tokenInfo.insert("deviceName", basicTypeToString(QVariant::String));
s_tokenInfo.insert("creationTime", basicTypeToString(QVariant::UInt));
// ServerConfiguration
s_serverConfiguration.insert("id", basicTypeToString(QVariant::String));
s_serverConfiguration.insert("address", basicTypeToString(QVariant::String));
s_serverConfiguration.insert("port", basicTypeToString(QVariant::UInt));
s_serverConfiguration.insert("sslEnabled", basicTypeToString(QVariant::Bool));
s_serverConfiguration.insert("authenticationEnabled", basicTypeToString(QVariant::Bool));
s_webServerConfiguration = s_serverConfiguration;
s_webServerConfiguration.insert("publicFolder", basicTypeToString(QVariant::String));
s_initialized = true;
}
@ -437,6 +449,7 @@ QVariantMap JsonTypes::allTypes()
allTypes.insert("WiredNetworkDevice", wiredNetworkDeviceDescription());
allTypes.insert("WirelessNetworkDevice", wirelessNetworkDeviceDescription());
allTypes.insert("TokenInfo", tokenInfoDescription());
allTypes.insert("ServerConfiguration", serverConfigurationDescription());
return allTypes;
}
@ -1091,7 +1104,8 @@ QVariantMap JsonTypes::packBasicConfiguration()
QVariantMap JsonTypes::packServerConfiguration(const ServerConfiguration &config)
{
QVariantMap serverConfiguration;
serverConfiguration.insert("host", config.address.toString());
serverConfiguration.insert("id", config.id);
serverConfiguration.insert("address", config.address.toString());
serverConfiguration.insert("port", config.port);
serverConfiguration.insert("sslEnabled", config.sslEnabled);
serverConfiguration.insert("authenticationEnabled", config.authenticationEnabled);
@ -1834,6 +1848,18 @@ QPair<bool, QString> JsonTypes::validateVariant(const QVariant &templateVariant,
qCWarning(dcJsonRpc) << "TokenInfo not matching";
return result;
}
} else if (refName == serverConfigurationRef()) {
QPair<bool, QString> result = validateMap(serverConfigurationDescription(), variant.toMap());
if (!result.first) {
qCWarning(dcJsonRpc) << "ServerConfiguration not matching";
return result;
}
} else if (refName == webServerConfigurationRef()) {
QPair<bool, QString> result = validateMap(webServerConfigurationDescription(), variant.toMap());
if (!result.first) {
qCWarning(dcJsonRpc) << "WebServerConfiguration not matching";
return result;
}
} else if (refName == basicTypeRef()) {
QPair<bool, QString> result = validateBasicType(variant);
if (!result.first) {

View File

@ -166,6 +166,8 @@ public:
DECLARE_OBJECT(wiredNetworkDevice, "WiredNetworkDevice")
DECLARE_OBJECT(wirelessNetworkDevice, "WirelessNetworkDevice")
DECLARE_OBJECT(tokenInfo, "TokenInfo")
DECLARE_OBJECT(serverConfiguration, "ServerConfiguration")
DECLARE_OBJECT(webServerConfiguration, "WebServerConfiguration")
// pack types
static QVariantMap packEventType(const EventType &eventType);

View File

@ -94,27 +94,43 @@ ServerManager::ServerManager(GuhConfiguration* configuration, QObject *parent) :
// Transports
#ifdef TESTING_ENABLED
MockTcpServer *tcpServer = new MockTcpServer(this);
m_jsonServer->registerTransportInterface(tcpServer, true, true);
m_jsonServer->registerTransportInterface(tcpServer, true);
tcpServer->startServer();
#else
foreach (const ServerConfiguration &config, configuration->tcpServerConfigurations()) {
TcpServer *tcpServer = new TcpServer(config.address, config.port, config.sslEnabled, m_sslConfiguration, this);
m_jsonServer->registerTransportInterface(tcpServer, true, config.authenticationEnabled);
TcpServer *tcpServer = new TcpServer(config, m_sslConfiguration, this);
m_jsonServer->registerTransportInterface(tcpServer, config.authenticationEnabled);
m_tcpServers.insert(config.id, tcpServer);
tcpServer->startServer();
}
#endif
foreach (const ServerConfiguration &config, configuration->webSocketServerConfigurations()) {
qWarning() << "Have websockeserver config" << config.id;
WebSocketServer *webSocketServer = new WebSocketServer(config.address, config.port, config.sslEnabled, m_sslConfiguration, this);
m_jsonServer->registerTransportInterface(webSocketServer, true, config.authenticationEnabled);
WebSocketServer *webSocketServer = new WebSocketServer(config, m_sslConfiguration, this);
m_jsonServer->registerTransportInterface(webSocketServer, config.authenticationEnabled);
m_webSocketServers.insert(config.id, webSocketServer);
webSocketServer->startServer();
}
m_bluetoothServer = new BluetoothServer(this);
m_jsonServer->registerTransportInterface(m_bluetoothServer, configuration->bluetoothServerEnabled(), true);
m_jsonServer->registerTransportInterface(m_bluetoothServer, true);
if (configuration->bluetoothServerEnabled()) {
m_bluetoothServer->startServer();
}
foreach (const WebServerConfiguration &config, configuration->webServerConfigurations()) {
WebServer *webServer = new WebServer(config.address, config.port, config.publicFolder, config.sslEnabled, m_sslConfiguration, this);
WebServer *webServer = new WebServer(config, m_sslConfiguration, this);
m_restServer->registerWebserver(webServer);
m_webServers.insert(config.id, webServer);
}
connect(configuration, &GuhConfiguration::tcpServerConfigurationChanged, this, &ServerManager::tcpServerConfigurationChanged);
connect(configuration, &GuhConfiguration::tcpServerConfigurationRemoved, this, &ServerManager::tcpServerConfigurationRemoved);
connect(configuration, &GuhConfiguration::webSocketServerConfigurationChanged, this, &ServerManager::webSocketServerConfigurationChanged);
connect(configuration, &GuhConfiguration::webSocketServerConfigurationRemoved, this, &ServerManager::webSocketServerConfigurationRemoved);
connect(configuration, &GuhConfiguration::webServerConfigurationChanged, this, &ServerManager::webServerConfigurationChanged);
connect(configuration, &GuhConfiguration::webServerConfigurationRemoved, this, &ServerManager::webServerConfigurationRemoved);
}
/*! Returns the pointer to the created \l{JsonRPCServer} in this \l{ServerManager}. */
@ -134,6 +150,99 @@ BluetoothServer *ServerManager::bluetoothServer() const
return m_bluetoothServer;
}
void ServerManager::tcpServerConfigurationChanged(const QString &id)
{
#ifndef TESTING_ENABLED
ServerConfiguration config = GuhCore::instance()->configuration()->tcpServerConfigurations().value(id);
TcpServer *server = m_tcpServers.value(id);
if (server) {
qDebug(dcConnection) << "Restarting TCP server for" << config.address << config.port << "SSL" << (config.sslEnabled ? "enabled" : "disabled") << "Authentication" << (config.authenticationEnabled ? "enabled" : "disabled");
server->stopServer();
server->setConfiguration(config);
} else {
qDebug(dcConnection) << "Received a TCP Server config change event but don't have a TCP Server instance for it. Creating new Server instance.";
server = new TcpServer(config, m_sslConfiguration, this);
m_tcpServers.insert(config.id, server);
}
m_jsonServer->registerTransportInterface(server, config.authenticationEnabled);
server->startServer();
#else
qWarning() << "Configure called for" << id << "but disabled in testing";
#endif
}
void ServerManager::tcpServerConfigurationRemoved(const QString &id)
{
#ifndef TESTING_ENABLED
if (!m_tcpServers.contains(id)) {
qWarning(dcConnection) << "Received a TCP Server config removed event but don't have a TCP Server instance for it.";
return;
}
TcpServer *server = m_tcpServers.take(id);
m_jsonServer->unregisterTransportInterface(server);
server->stopServer();
server->deleteLater();
#else
qWarning() << "Delete configuration called for" << id << "but disabled in testing";
#endif
}
void ServerManager::webSocketServerConfigurationChanged(const QString &id)
{
WebSocketServer *server = m_webSocketServers.value(id);
ServerConfiguration config = GuhCore::instance()->configuration()->webSocketServerConfigurations().value(id);
if (server) {
qDebug(dcConnection) << "Restarting WebSocket server for" << config.address << config.port << "SSL" << (config.sslEnabled ? "enabled" : "disabled") << "Authentication" << (config.authenticationEnabled ? "enabled" : "disabled");
server->stopServer();
server->setConfiguration(config);
} else {
qDebug(dcConnection) << "Received a WebSocket Server config change event but don't have a WebSocket Server instance for it. Creating new instance.";
server = new WebSocketServer(config, m_sslConfiguration, this);
m_webSocketServers.insert(server->configuration().id, server);
}
m_jsonServer->registerTransportInterface(server, config.authenticationEnabled);
server->startServer();
}
void ServerManager::webSocketServerConfigurationRemoved(const QString &id)
{
if (!m_webSocketServers.contains(id)) {
qWarning(dcConnection) << "Received a WebSocket Server config removed event but don't have a WebSocket Server instance for it.";
return;
}
WebSocketServer *server = m_webSocketServers.take(id);
m_jsonServer->unregisterTransportInterface(server);
server->stopServer();
server->deleteLater();
}
void ServerManager::webServerConfigurationChanged(const QString &id)
{
WebServerConfiguration config = GuhCore::instance()->configuration()->webServerConfigurations().value(id);
WebServer *server = m_webServers.value(id);
if (server) {
qDebug(dcConnection) << "Restarting Web server for" << config.address << config.port << "SSL" << (config.sslEnabled ? "enabled" : "disabled") << "Authentication" << (config.authenticationEnabled ? "enabled" : "disabled");
server->stopServer();
server->reconfigureServer(config);
} else {
qDebug(dcConnection) << "Received a Web Server config change event but don't have a Web Server instance for it.";
server = new WebServer(config, m_sslConfiguration, this);
m_webServers.insert(config.id, server);
}
server->startServer();
}
void ServerManager::webServerConfigurationRemoved(const QString &id)
{
if (!m_webServers.contains(id)) {
qWarning(dcConnection) << "Received a Web Server config removed event but don't have a Web Server instance for it.";
return;
}
WebServer *server = m_webServers.take(id);
server->stopServer();
server->deleteLater();
}
bool ServerManager::loadCertificate(const QString &certificateKeyFileName, const QString &certificateFileName)
{
QFile certificateKeyFile(certificateKeyFileName);

View File

@ -62,12 +62,25 @@ public:
TcpServer *tcpServer() const;
#endif
private slots:
void tcpServerConfigurationChanged(const QString &id);
void tcpServerConfigurationRemoved(const QString &id);
void webSocketServerConfigurationChanged(const QString &id);
void webSocketServerConfigurationRemoved(const QString &id);
void webServerConfigurationChanged(const QString &id);
void webServerConfigurationRemoved(const QString &id);
private:
// Interfaces
JsonRPCServer *m_jsonServer;
RestServer *m_restServer;
BluetoothServer *m_bluetoothServer;
#ifndef TESTING_ENABLED
QHash<QString, TcpServer*> m_tcpServers;
#endif
QHash<QString, WebSocketServer*> m_webSocketServers;
QHash<QString, WebServer*> m_webServers;
// Encrytption and stuff
QSslConfiguration m_sslConfiguration;

View File

@ -45,14 +45,12 @@ namespace guhserver {
*
* \sa ServerManager
*/
TcpServer::TcpServer(const QHostAddress &host, const uint &port, bool sslEnabled, const QSslConfiguration &sslConfiguration, QObject *parent) :
TransportInterface(parent),
TcpServer::TcpServer(const ServerConfiguration &configuration, const QSslConfiguration &sslConfiguration, QObject *parent) :
TransportInterface(configuration, parent),
m_server(NULL),
m_host(host),
m_port(port),
m_sslEnabled(sslEnabled),
m_sslConfig(sslConfiguration)
{
qWarning() << "****" << configuration.address << configuration.port;
#ifndef TESTING_ENABLED
m_avahiService = new QtAvahiService(this);
connect(m_avahiService, &QtAvahiService::serviceStateChanged, this, &TcpServer::onAvahiServiceStateChanged);
@ -128,30 +126,18 @@ void TcpServer::onAvahiServiceStateChanged(const QtAvahiService::QtAvahiServiceS
/*! Returns true if this \l{TcpServer} could be reconfigured with the given \a address and \a port. */
bool TcpServer::reconfigureServer(const QHostAddress &address, const uint &port)
void TcpServer::reconfigureServer(const ServerConfiguration &config)
{
if (m_host == address && m_port == (qint16)port && m_server->isListening())
return true;
if (configuration().address == config.address &&
configuration().port == config.port &&
configuration().sslEnabled == config.sslEnabled &&
configuration().authenticationEnabled == config.authenticationEnabled &&
m_server->isListening())
return;
stopServer();
SslServer *server = new SslServer(m_sslEnabled, m_sslConfig);
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;
// Start server with new configuration
m_host = address;
m_port = port;
return startServer();
setConfiguration(config);
startServer();
}
/*! Returns true if this \l{TcpServer} started successfully.
@ -160,9 +146,9 @@ bool TcpServer::reconfigureServer(const QHostAddress &address, const uint &port)
*/
bool TcpServer::startServer()
{
m_server = new SslServer(m_sslEnabled, m_sslConfig);
if(!m_server->listen(m_host, m_port)) {
qCWarning(dcConnection) << "Tcp server error: can not listen on" << m_host.toString() << m_port;
m_server = new SslServer(configuration().sslEnabled, m_sslConfig);
if(!m_server->listen(configuration().address, configuration().port)) {
qCWarning(dcConnection) << "Tcp server error: can not listen on" << configuration().address.toString() << configuration().port;
delete m_server;
m_server = NULL;
return false;
@ -177,7 +163,7 @@ bool TcpServer::startServer()
txt.insert("manufacturer", "guh GmbH");
txt.insert("uuid", GuhCore::instance()->configuration()->serverUuid().toString());
txt.insert("name", GuhCore::instance()->configuration()->serverName());
m_avahiService->registerService("guhIO", m_port, "_jsonrpc._tcp", txt);
m_avahiService->registerService("guhIO", configuration().port, "_jsonrpc._tcp", txt);
#endif
qCDebug(dcConnection) << "Started Tcp server on" << m_server->serverAddress().toString() << m_server->serverPort();

View File

@ -71,7 +71,7 @@ class TcpServer : public TransportInterface
{
Q_OBJECT
public:
explicit TcpServer(const QHostAddress &host, const uint &port, bool sslEnabled, const QSslConfiguration &sslConfiguration, QObject *parent = 0);
explicit TcpServer(const ServerConfiguration &configuration, const QSslConfiguration &sslConfiguration, QObject *parent = 0);
~TcpServer();
void sendData(const QUuid &clientId, const QByteArray &data) override;
@ -85,10 +85,6 @@ private:
SslServer * m_server;
QHash<QUuid, QTcpSocket *> m_clientList;
QHostAddress m_host;
qint16 m_port;
bool m_sslEnabled = false;
QSslConfiguration m_sslConfig;
private slots:
@ -102,7 +98,7 @@ private slots:
public slots:
bool reconfigureServer(const QHostAddress &address, const uint &port);
void reconfigureServer(const ServerConfiguration &configuration);
bool startServer() override;
bool stopServer() override;
};

View File

@ -78,12 +78,24 @@
namespace guhserver {
/*! Constructs a \l{TransportInterface} with the given \a parent. */
TransportInterface::TransportInterface(QObject *parent) :
QObject(parent)
TransportInterface::TransportInterface(const ServerConfiguration &config, QObject *parent) :
QObject(parent),
m_config(config)
{
}
/*! Pure virtual destructor for \l{TransportInterface}. */
void TransportInterface::setConfiguration(const ServerConfiguration &config)
{
m_config = config;
}
/*! Returns the \{ServerConfiguration}. */
ServerConfiguration TransportInterface::configuration() const
{
return m_config;
}
/*! Virtual destructor for \l{TransportInterface}. */
TransportInterface::~TransportInterface()
{
}

View File

@ -26,18 +26,23 @@
#include <QList>
#include <QUuid>
#include "guhconfiguration.h"
namespace guhserver {
class TransportInterface : public QObject
{
Q_OBJECT
public:
explicit TransportInterface(QObject *parent = 0);
explicit TransportInterface(const ServerConfiguration &config, QObject *parent = 0);
virtual ~TransportInterface() = 0;
virtual void sendData(const QUuid &clientId, const QByteArray &data) = 0;
virtual void sendData(const QList<QUuid> &clients, const QByteArray &data) = 0;
void setConfiguration(const ServerConfiguration &config);
ServerConfiguration configuration() const;
signals:
void clientConnected(const QUuid &clientId);
void clientDisconnected(const QUuid &clientId);
@ -46,6 +51,9 @@ signals:
public slots:
virtual bool startServer() = 0;
virtual bool stopServer() = 0;
private:
ServerConfiguration m_config;
};
}

View File

@ -95,20 +95,18 @@ namespace guhserver {
*
* \sa ServerManager
*/
WebServer::WebServer(const QHostAddress &host, const uint &port, const QString &publicFolder, bool sslEnabled, const QSslConfiguration &sslConfiguration, QObject *parent) :
WebServer::WebServer(const WebServerConfiguration &configuration, const QSslConfiguration &sslConfiguration, QObject *parent) :
QTcpServer(parent),
m_avahiService(NULL),
m_host(host),
m_port(port),
m_webinterfaceDir(publicFolder),
m_configuration(configuration),
m_sslConfiguration(sslConfiguration),
m_useSsl(sslEnabled),
m_enabled(false)
{
if (QCoreApplication::instance()->organizationName() == "guh-test") {
m_webinterfaceDir = QDir(QCoreApplication::applicationDirPath());
qCWarning(dcWebServer) << "Using public folder" << m_webinterfaceDir.path();
m_configuration.publicFolder = QCoreApplication::applicationDirPath();
qCWarning(dcWebServer) << "Using public folder" << m_configuration.publicFolder;
}
qCDebug(dcWebServer) << "Using public folder" << m_configuration.publicFolder;
#ifndef TESTING_ENABLED
m_avahiService = new QtAvahiService(this);
@ -158,7 +156,7 @@ bool WebServer::verifyFile(QSslSocket *socket, const QString &fileName)
}
// make shore the file is in the public directory
if (!file.canonicalFilePath().startsWith(m_webinterfaceDir.path())) {
if (!file.canonicalFilePath().startsWith(m_configuration.publicFolder)) {
qCWarning(dcWebServer) << "requested file" << file.fileName() << "is outside the public folder.";
HttpReply *reply = RestResource::createErrorReply(HttpReply::Forbidden);
reply->setClientId(m_clientList.key(socket));
@ -189,7 +187,7 @@ QString WebServer::fileName(const QString &query)
fileName = query;
}
return m_webinterfaceDir.path() + fileName;
return m_configuration.publicFolder + "/" + fileName;
}
HttpReply *WebServer::processIconRequest(const QString &fileName)
@ -255,7 +253,7 @@ void WebServer::incomingConnection(qintptr socketDescriptor)
qCDebug(dcConnection) << QString("Webserver client %1:%2 connected").arg(socket->peerAddress().toString()).arg(socket->peerPort());
if (m_useSsl) {
if (m_configuration.sslEnabled) {
// configure client connection
socket->setSslConfiguration(m_sslConfiguration);
connect(socket, SIGNAL(encrypted()), this, SLOT(onEncrypted()));
@ -365,7 +363,7 @@ void WebServer::readClient()
qCDebug(dcWebServer) << "server XML request call";
HttpReply *reply = RestResource::createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "text/xml");
reply->setPayload(createServerXmlDocument(m_host));
reply->setPayload(createServerXmlDocument(m_configuration.address));
reply->setClientId(clientId);
sendHttpReply(reply);
reply->deleteLater();
@ -376,8 +374,8 @@ void WebServer::readClient()
// request for a file...
if (request.method() == HttpRequest::Get) {
// check if the webinterface dir does exist, otherwise a filerequest is not relevant
if (!m_webinterfaceDir.exists()) {
qCWarning(dcWebServer) << "webinterface folder" << m_webinterfaceDir.path() << "does not exist.";
if (!QDir(m_configuration.publicFolder).exists()) {
qCWarning(dcWebServer) << "webinterface folder" << m_configuration.publicFolder << "does not exist.";
HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
reply->setClientId(clientId);
sendHttpReply(reply);
@ -489,41 +487,34 @@ void WebServer::onAvahiServiceStateChanged(const QtAvahiService::QtAvahiServiceS
}
/*! Returns true if this \l{WebServer} could be reconfigured with the given \a address and \a port. */
bool WebServer::reconfigureServer(const QHostAddress &address, const uint &port)
void WebServer::reconfigureServer(const WebServerConfiguration &config)
{
if (m_host == address && m_port == (qint16)port && isListening())
return true;
if (m_configuration.address == config.address &&
m_configuration.port == config.port &&
m_configuration.sslEnabled == config.sslEnabled &&
m_configuration.authenticationEnabled == config.authenticationEnabled &&
m_configuration.publicFolder == config.publicFolder &&
isListening())
return;
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;
m_configuration = config;
startServer();
return true;
}
/*! Returns true if this \l{WebServer} started successfully. */
bool WebServer::startServer()
{
if (!listen(m_host, m_port)) {
qCWarning(dcConnection) << "Webserver could not listen on" << serverAddress().toString() << m_port;
if (!listen(m_configuration.address, m_configuration.port)) {
qCWarning(dcConnection) << "Webserver could not listen on" << m_configuration.address.toString() << m_configuration.port;
m_enabled = false;
return false;
}
if (m_useSsl) {
qCDebug(dcConnection) << "Started webserver on" << QString("https://%1:%2").arg(m_host.toString()).arg(m_port);
if (m_configuration.sslEnabled) {
qCDebug(dcConnection) << "Started webserver on" << QString("https://%1:%2").arg(m_configuration.address.toString()).arg(m_configuration.port);
} else {
qCDebug(dcConnection) << "Started webserver on" << QString("http://%1:%2").arg(m_host.toString()).arg(m_port);
qCDebug(dcConnection) << "Started webserver on" << QString("http://%1:%2").arg(m_configuration.address.toString()).arg(m_configuration.port);
}
#ifndef TESTING_ENABLED
@ -534,7 +525,7 @@ bool WebServer::startServer()
txt.insert("manufacturer", "guh GmbH");
txt.insert("uuid", GuhCore::instance()->configuration()->serverUuid().toString());
txt.insert("name", GuhCore::instance()->configuration()->serverName());
m_avahiService->registerService("guhIO", m_port, "_http._tcp", txt);
m_avahiService->registerService("guhIO", m_configuration.port, "_http._tcp", txt);
#endif
m_enabled = true;
@ -580,13 +571,13 @@ QByteArray WebServer::createServerXmlDocument(QHostAddress address)
writer.writeTextElement("minor", "1");
writer.writeEndElement(); // specVersion
if (m_useSsl) {
writer.writeTextElement("URLBase", "https://" + address.toString() + ":" + QString::number(m_port));
if (m_configuration.sslEnabled) {
writer.writeTextElement("URLBase", "https://" + address.toString() + ":" + QString::number(m_configuration.port));
} else {
writer.writeTextElement("URLBase", "http://" + address.toString() + ":" + QString::number(m_port));
writer.writeTextElement("URLBase", "http://" + address.toString() + ":" + QString::number(m_configuration.port));
}
if (m_useSsl) {
if (m_configuration.sslEnabled) {
writer.writeTextElement("websocketURL", "wss://" + address.toString() + ":" + QString::number(websocketPort));
} else {
writer.writeTextElement("websocketURL", "ws://" + address.toString() + ":" + QString::number(websocketPort));

View File

@ -36,6 +36,8 @@
#include "network/avahi/qtavahiservice.h"
#include "guhconfiguration.h"
// Note: Hypertext Transfer Protocol (HTTP/1.1) from the Internet Engineering Task Force (IETF):
// https://tools.ietf.org/html/rfc7231
@ -72,7 +74,7 @@ class WebServer : public QTcpServer
{
Q_OBJECT
public:
explicit WebServer(const QHostAddress &host, const uint &port, const QString &publicFolder, bool sslEnabled, const QSslConfiguration &sslConfiguration, QObject *parent = 0);
explicit WebServer(const WebServerConfiguration &configuration, const QSslConfiguration &sslConfiguration, QObject *parent = 0);
~WebServer();
void sendHttpReply(HttpReply *reply);
@ -84,12 +86,9 @@ private:
QtAvahiService *m_avahiService;
QHostAddress m_host;
qint16 m_port;
QDir m_webinterfaceDir;
WebServerConfiguration m_configuration;
QSslConfiguration m_sslConfiguration;
bool m_useSsl;
bool m_enabled;
@ -116,7 +115,7 @@ private slots:
void onAvahiServiceStateChanged(const QtAvahiService::QtAvahiServiceState &state);
public slots:
bool reconfigureServer(const QHostAddress &address, const uint &port);
void reconfigureServer(const WebServerConfiguration &config);
bool startServer();
bool stopServer();

View File

@ -60,13 +60,10 @@ namespace guhserver {
*
* \sa ServerManager
*/
WebSocketServer::WebSocketServer(const QHostAddress &address, const uint &port, const bool &sslEnabled, const QSslConfiguration &sslConfiguration, QObject *parent) :
TransportInterface(parent),
WebSocketServer::WebSocketServer(const ServerConfiguration &configuration, const QSslConfiguration &sslConfiguration, QObject *parent) :
TransportInterface(configuration, parent),
m_server(0),
m_host(address),
m_port(port),
m_sslConfiguration(sslConfiguration),
m_useSsl(sslEnabled),
m_enabled(false)
{
#ifndef TESTING_ENABLED
@ -180,41 +177,21 @@ void WebSocketServer::onAvahiServiceStateChanged(const QtAvahiService::QtAvahiSe
}
/*! Returns true if this \l{WebSocketServer} could be reconfigured with the given \a address and \a port. */
bool WebSocketServer::reconfigureServer(const QHostAddress &address, const uint &port)
void WebSocketServer::reconfigureServer(const ServerConfiguration &config)
{
if (m_host == address && m_port == (qint16)port && m_server->isListening()) {
if (configuration() == config && m_server->isListening()) {
qCDebug(dcWebSocketServer()) << "Configuration unchanged. Not restarting the server.";
return true;
return;
}
stopServer();
qCDebug(dcWebSocketServer()) << "Stopped server for reconfiguration.";
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;
setConfiguration(config);
// Start server with new configuration
m_host = address;
m_port = port;
qCDebug(dcWebSocketServer()) << "Restart server with new configuration.";
return startServer();
startServer();
}
/*! Returns true if this \l{WebSocketServer} started successfully.
@ -223,7 +200,7 @@ bool WebSocketServer::reconfigureServer(const QHostAddress &address, const uint
*/
bool WebSocketServer::startServer()
{
if (m_useSsl) {
if (configuration().sslEnabled) {
m_server = new QWebSocketServer("guh", QWebSocketServer::SecureMode, this);
m_server->setSslConfiguration(m_sslConfiguration);
} else {
@ -232,15 +209,15 @@ bool WebSocketServer::startServer()
connect (m_server, &QWebSocketServer::newConnection, this, &WebSocketServer::onClientConnected);
connect (m_server, &QWebSocketServer::acceptError, this, &WebSocketServer::onServerError);
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);
if (!m_server->listen(configuration().address, configuration().port)) {
qCWarning(dcConnection) << "Websocket server" << m_server->serverName() << QString("could not listen on %1:%2").arg(m_server->serverAddress().toString()).arg(configuration().port);
return false;
}
if (m_server->secureMode() == QWebSocketServer::NonSecureMode) {
qCDebug(dcConnection) << "Started websocket server" << m_server->serverName() << QString("on ws://%1:%2").arg(m_server->serverAddress().toString()).arg(m_port);
qCDebug(dcConnection) << "Started websocket server" << m_server->serverName() << QString("on ws://%1:%2").arg(m_server->serverAddress().toString()).arg(configuration().port);
} else {
qCDebug(dcConnection) << "Started websocket server" << m_server->serverName() << QString("on wss://%1:%2").arg(m_server->serverAddress().toString()).arg(m_port);
qCDebug(dcConnection) << "Started websocket server" << m_server->serverName() << QString("on wss://%1:%2").arg(m_server->serverAddress().toString()).arg(configuration().port);
}
#ifndef TESTING_ENABLED
@ -251,7 +228,7 @@ bool WebSocketServer::startServer()
txt.insert("manufacturer", "guh GmbH");
txt.insert("uuid", GuhCore::instance()->configuration()->serverUuid().toString());
txt.insert("name", GuhCore::instance()->configuration()->serverName());
m_avahiService->registerService("guhIO", m_port, "_ws._tcp", txt);
m_avahiService->registerService("guhIO", configuration().port, "_ws._tcp", txt);
#endif
return true;
@ -272,9 +249,11 @@ bool WebSocketServer::stopServer()
client->close(QWebSocketProtocol::CloseCodeNormal, "Stop server");
}
m_server->close();
delete m_server;
m_server = 0;
if (m_server) {
m_server->close();
delete m_server;
m_server = nullptr;
}
return true;
}

View File

@ -42,7 +42,7 @@ class WebSocketServer : public TransportInterface
{
Q_OBJECT
public:
explicit WebSocketServer(const QHostAddress &address, const uint &port, const bool &sslEnabled, const QSslConfiguration &sslConfiguration, QObject *parent = 0);
explicit WebSocketServer(const ServerConfiguration &configuration, const QSslConfiguration &sslConfiguration, QObject *parent = 0);
~WebSocketServer();
void sendData(const QUuid &clientId, const QByteArray &data) override;
@ -54,11 +54,7 @@ private:
QtAvahiService *m_avahiService;
QHostAddress m_host;
qint16 m_port;
QSslConfiguration m_sslConfiguration;
bool m_useSsl;
bool m_enabled;
@ -74,7 +70,7 @@ private slots:
void onAvahiServiceStateChanged(const QtAvahiService::QtAvahiServiceState &state);
public slots:
bool reconfigureServer(const QHostAddress &address, const uint &port);
void reconfigureServer(const ServerConfiguration &config);
bool startServer() override;
bool stopServer() override;
};

View File

@ -1,4 +1,4 @@
49
50
{
"methods": {
"Actions.ExecuteAction": {
@ -31,6 +31,33 @@
}
}
},
"Configuration.DeleteTcpServerConfiguration": {
"description": "Delete a TCP interface of the server. Note: if you are deleting the configuration for the interface you are currently connected to, the connection will be dropped.",
"params": {
"id": "String"
},
"returns": {
"configurationError": "$ref:ConfigurationError"
}
},
"Configuration.DeleteWebServerConfiguration": {
"description": "Delete a WebServer interface of the server.",
"params": {
"id": "String"
},
"returns": {
"configurationError": "$ref:ConfigurationError"
}
},
"Configuration.DeleteWebSocketServerConfiguration": {
"description": "Delete a WebSocket Server interface of the server. Note: if you are deleting the configuration for the interface you are currently connected to, the connection will be dropped.",
"params": {
"id": "String"
},
"returns": {
"configurationError": "$ref:ConfigurationError"
}
},
"Configuration.GetAvailableLanguages": {
"description": "Returns a list of locale codes available for the server. i.e. en_US, de_AT",
"params": {
@ -53,21 +80,15 @@
"serverUuid": "Uuid",
"timeZone": "String"
},
"sslConfiguration": {
"enabled": "Bool"
},
"tcpServerConfiguration": {
"host": "String",
"port": "Uint"
},
"webServerConfiguration": {
"host": "String",
"port": "Uint"
},
"webSocketServerConfiguration": {
"host": "String",
"port": "Uint"
}
"tcpServerConfigurations": [
"$ref:ServerConfiguration"
],
"webServerConfigurations": [
"$ref:WebServerConfiguration"
],
"webSocketServerConfigurations": [
"$ref:ServerConfiguration"
]
}
},
"Configuration.GetTimeZones": {
@ -99,10 +120,9 @@
}
},
"Configuration.SetTcpServerConfiguration": {
"description": "Configure the TCP interface of the server. Note: if you are using the TCP server for this call you will loose the connection.",
"description": "Configure a TCP interface of the server. If the ID is an existing one, the existing config will be modified, otherwise a new one will be added. Note: if you are changing the configuration for the interface you are currently connected to, the connection will be dropped.",
"params": {
"host": "String",
"port": "Uint"
"configuration": "$ref:ServerConfiguration"
},
"returns": {
"configurationError": "$ref:ConfigurationError"
@ -118,20 +138,18 @@
}
},
"Configuration.SetWebServerConfiguration": {
"description": "Configure the web server of the server.",
"description": "Configure a WebServer interface of the server. If the ID is an existing one, the existing config will be modified, otherwise a new one will be added.",
"params": {
"host": "String",
"port": "Uint"
"configuration": "$ref:WebServerConfiguration"
},
"returns": {
"configurationError": "$ref:ConfigurationError"
}
},
"Configuration.SetWebSocketServerConfiguration": {
"description": "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.",
"description": "Configure a WebSocket Server interface of the server. If the ID is an existing one, the existing config will be modified, otherwise a new one will be added. Note: if you are changing the configuration for the interface you are currently connected to, the connection will be dropped.",
"params": {
"host": "String",
"port": "Uint"
"configuration": "$ref:ServerConfiguration"
},
"returns": {
"configurationError": "$ref:ConfigurationError"
@ -905,6 +923,7 @@
"ConfigurationErrorNoError",
"ConfigurationErrorInvalidTimeZone",
"ConfigurationErrorInvalidStationName",
"ConfigurationErrorInvalidId",
"ConfigurationErrorInvalidPort",
"ConfigurationErrorInvalidHostAddress",
"ConfigurationErrorBluetoothHardwareNotAvailable",
@ -1254,6 +1273,13 @@
"RuleErrorContainsEventBasesAction",
"RuleErrorNoExitActions"
],
"ServerConfiguration": {
"address": "String",
"authenticationEnabled": "Bool",
"id": "String",
"port": "Uint",
"sslEnabled": "Bool"
},
"SetupMethod": [
"SetupMethodJustAdd",
"SetupMethodDisplayPin",

View File

@ -118,6 +118,9 @@ GuhTestBase::GuhTestBase(QObject *parent) :
m_mockDevice2Port = 7331 + (qrand() % 1000);
QCoreApplication::instance()->setOrganizationName("guh-test");
QSignalSpy spy(GuhCore::instance(), SIGNAL(initialized()));
spy.wait();
GuhCore::instance()->userManager()->removeUser("dummy@guh.io");
GuhCore::instance()->userManager()->createUser("dummy@guh.io", "DummyPW1!");
m_apiToken = GuhCore::instance()->userManager()->authenticate("dummy@guh.io", "DummyPW1!", "testcase");
@ -476,6 +479,8 @@ void GuhTestBase::restartServer()
{
// Destroy and recreate the core instance...
GuhCore::instance()->destroy();
QSignalSpy coreSpy(GuhCore::instance(), SIGNAL(initialized()));
coreSpy.wait();
QSignalSpy spy(GuhCore::instance()->deviceManager(), SIGNAL(loaded()));
spy.wait();
m_mockTcpServer = MockTcpServer::servers().first();

View File

@ -32,7 +32,7 @@ using namespace guhserver;
QList<MockTcpServer*> MockTcpServer::s_allServers;
MockTcpServer::MockTcpServer(QObject *parent):
TransportInterface(parent)
TransportInterface(ServerConfiguration(), parent)
{
s_allServers.append(this);
}