diff --git a/server/cloud/cloudauthenticationhandler.cpp b/server/cloud/cloudauthenticationhandler.cpp new file mode 100644 index 00000000..fc99d6f9 --- /dev/null +++ b/server/cloud/cloudauthenticationhandler.cpp @@ -0,0 +1,53 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * 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 "cloudauthenticationhandler.h" +#include "loggingcategories.h" +#include "guhcore.h" + +#include + + +namespace guhserver { + +CloudAuthenticationHandler::CloudAuthenticationHandler(QObject *parent) : + CloudJsonHandler(parent) +{ + +} + +QString CloudAuthenticationHandler::nameSpace() const +{ + return "Authentication"; +} + +void CloudAuthenticationHandler::processAuthenticate(const QVariantMap ¶ms) +{ + if (params.contains("authenticationError")) { + if (params.value("authenticationError").toString() == "AuthenticationErrorSuccess") { + if (params.contains("connectionId")) + GuhCore::instance()->cloudManager()->onConnectionAuthentificationFinished(true, params.value("connectionId").toUuid()); + } else { + GuhCore::instance()->cloudManager()->onConnectionAuthentificationFinished(false, QUuid()); + } + } +} + +} diff --git a/server/cloud/cloudauthenticationhandler.h b/server/cloud/cloudauthenticationhandler.h new file mode 100644 index 00000000..100c691e --- /dev/null +++ b/server/cloud/cloudauthenticationhandler.h @@ -0,0 +1,45 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * 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 CLOUDAUTHENTICATIONHANDLER_H +#define CLOUDAUTHENTICATIONHANDLER_H + +#include + +#include "cloudjsonhandler.h" + +namespace guhserver { + +class CloudAuthenticationHandler : public CloudJsonHandler +{ + Q_OBJECT +public: + explicit CloudAuthenticationHandler(QObject *parent = 0); + + QString nameSpace() const; + + // API methods + Q_INVOKABLE void processAuthenticate(const QVariantMap ¶ms); + +}; + +} + +#endif // CLOUDAUTHENTICATIONHANDLER_H diff --git a/server/cloud/cloudauthenticator.cpp b/server/cloud/cloudauthenticator.cpp index 93b4634c..ec8cf911 100644 --- a/server/cloud/cloudauthenticator.cpp +++ b/server/cloud/cloudauthenticator.cpp @@ -19,6 +19,7 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "cloudauthenticator.h" +#include "guhsettings.h" #include #include @@ -31,6 +32,9 @@ CloudAuthenticator::CloudAuthenticator(QString clientId, QString clientSecret, Q m_clientSecret(clientSecret), m_authenticated(false) { + m_refreshToken = loadRefreshToken(); + m_username = loadUserName(); + m_networkManager = new QNetworkAccessManager(this); connect(m_networkManager, &QNetworkAccessManager::finished, this, &CloudAuthenticator::replyFinished); @@ -67,6 +71,10 @@ QString CloudAuthenticator::username() const void CloudAuthenticator::setUsername(const QString &username) { + GuhSettings settings(GuhSettings::SettingsRoleDevices); + settings.beginGroup("Cloud"); + settings.setValue("userName", username); + settings.endGroup(); m_username = username; } @@ -100,16 +108,6 @@ void CloudAuthenticator::setClientSecret(const QString clientSecret) m_clientSecret = clientSecret; } -QString CloudAuthenticator::scope() const -{ - return m_scope; -} - -void CloudAuthenticator::setScope(const QString &scope) -{ - m_scope = scope; -} - QString CloudAuthenticator::token() const { return m_token; @@ -120,30 +118,47 @@ bool CloudAuthenticator::authenticated() const return m_authenticated; } -void CloudAuthenticator::startAuthentication() +bool CloudAuthenticator::startAuthentication() { - qCDebug(dcCloud()) << "Start authentication" << m_username; + qCDebug(dcCloud()) << "Authenticator: Start authentication" << m_username; - QUrlQuery query; - query.addQueryItem("grant_type", "password"); - query.addQueryItem("username", m_username); - query.addQueryItem("password", m_password); - setQuery(query); + // Check if we have username and password + if(!m_username.isEmpty() && !m_password.isEmpty()) { + QUrlQuery query; + query.addQueryItem("grant_type", "password"); + query.addQueryItem("username", m_username); + query.addQueryItem("password", m_password); + setQuery(query); - QNetworkRequest request(m_url); - QByteArray data = QString(m_clientId + ":" + m_clientSecret).toUtf8().toBase64(); - QString header = "Basic " + data; - request.setRawHeader("Authorization", header.toLocal8Bit()); - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); + QNetworkRequest request(m_url); + QByteArray data = QString(m_clientId + ":" + m_clientSecret).toUtf8().toBase64(); + QString header = "Basic " + data; + request.setRawHeader("Authorization", header.toLocal8Bit()); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); - m_tokenRequests.append(m_networkManager->post(request, m_query.toString().toUtf8())); + m_tokenRequests.append(m_networkManager->post(request, m_query.toString().toUtf8())); + return true; + } else if (!m_refreshToken.isEmpty()) { + // Use the refreshtoken if there is any + refreshTimeout(); + return true; + } + + qCWarning(dcCloud()) << "Authenticator: Cannot start authentication. There is no refresh token, username or password around."; + stopAuthentication(); + return false; +} + +void CloudAuthenticator::stopAuthentication() +{ + m_timer->stop(); } void CloudAuthenticator::setAuthenticated(const bool &authenticated) { if (!authenticated) { m_timer->stop(); - qCWarning(dcCloud()) << "Authentication failed" << m_username; + qCWarning(dcCloud()) << "Authenticator: Authentication failed" << m_username; } m_authenticated = authenticated; emit authenticationChanged(); @@ -155,6 +170,35 @@ void CloudAuthenticator::setToken(const QString &token) emit tokenChanged(); } +void CloudAuthenticator::setRefreshToken(const QString &refreshToken) +{ + GuhSettings settings(GuhSettings::SettingsRoleDevices); + settings.beginGroup("Cloud"); + settings.setValue("refreshToken", refreshToken); + settings.endGroup(); + m_refreshToken = refreshToken; +} + +QString CloudAuthenticator::loadRefreshToken() const +{ + QString refreshToken; + GuhSettings settings(GuhSettings::SettingsRoleDevices); + settings.beginGroup("Cloud"); + refreshToken = settings.value("refreshToken", QString()).toString(); + settings.endGroup(); + return refreshToken; +} + +QString CloudAuthenticator::loadUserName() const +{ + QString userName; + GuhSettings settings(GuhSettings::SettingsRoleDevices); + settings.beginGroup("Cloud"); + userName = settings.value("userName", QString()).toString(); + settings.endGroup(); + return userName; +} + void CloudAuthenticator::replyFinished(QNetworkReply *reply) { int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); @@ -167,7 +211,7 @@ void CloudAuthenticator::replyFinished(QNetworkReply *reply) // check HTTP status code if (status != 200) { - qCWarning(dcCloud()) << "Request token reply HTTP error:" << status << reply->errorString(); + qCWarning(dcCloud()) << "Authenticator: Request token reply HTTP error:" << status << reply->errorString(); qCWarning(dcCloud()) << data; setAuthenticated(false); reply->deleteLater(); @@ -178,14 +222,14 @@ void CloudAuthenticator::replyFinished(QNetworkReply *reply) QJsonParseError error; QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); if (error.error != QJsonParseError::NoError) { - qCWarning(dcCloud()) << "Request token reply JSON error:" << error.errorString(); + qCWarning(dcCloud()) << "Authenticator: Request token reply JSON error:" << error.errorString(); setAuthenticated(false); reply->deleteLater(); return; } if (!jsonDoc.toVariant().toMap().contains("access_token")) { - qCWarning(dcCloud()) << "Could not get access token" << jsonDoc.toJson(); + qCWarning(dcCloud()) << "Authenticator: Could not get access token" << jsonDoc.toJson(); setAuthenticated(false); reply->deleteLater(); return; @@ -196,8 +240,8 @@ void CloudAuthenticator::replyFinished(QNetworkReply *reply) if (jsonDoc.toVariant().toMap().contains("expires_in") && jsonDoc.toVariant().toMap().contains("refresh_token")) { int expireTime = jsonDoc.toVariant().toMap().value("expires_in").toInt(); - m_refreshToken = jsonDoc.toVariant().toMap().value("refresh_token").toString(); - qCDebug(dcCloud()) << "Token will be refreshed in" << expireTime << "[s]"; + setRefreshToken(jsonDoc.toVariant().toMap().value("refresh_token").toString()); + qCDebug(dcCloud()) << "Authenticator: Token will be refreshed in" << expireTime << "[s]"; m_timer->start((expireTime - 20) * 1000); } @@ -208,8 +252,7 @@ void CloudAuthenticator::replyFinished(QNetworkReply *reply) // check HTTP status code if (status != 200) { - qCWarning(dcCloud()) << "Refresh token reply HTTP error:" << status << reply->errorString(); - qCWarning(dcCloud()) << data; + qCWarning(dcCloud()) << "Authenticator: Refresh token reply HTTP error:" << status << reply->errorString(); setAuthenticated(false); reply->deleteLater(); return; @@ -219,26 +262,26 @@ void CloudAuthenticator::replyFinished(QNetworkReply *reply) QJsonParseError error; QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); if (error.error != QJsonParseError::NoError) { - qCWarning(dcCloud()) << "Refresh token reply JSON error:" << error.errorString(); + qCWarning(dcCloud()) << "Authenticator: Refresh token reply JSON error:" << error.errorString(); setAuthenticated(false); reply->deleteLater(); return; } if (!jsonDoc.toVariant().toMap().contains("access_token")) { - qCWarning(dcCloud()) << "Could not get access token after refresh" << jsonDoc.toJson(); + qCWarning(dcCloud()) << "Authenticator: Could not get access token after refresh" << jsonDoc.toJson(); setAuthenticated(false); reply->deleteLater(); return; } setToken(jsonDoc.toVariant().toMap().value("access_token").toString()); - qCDebug(dcCloud()) << "Token refreshed successfully"; + qCDebug(dcCloud()) << "Authenticator: Token refreshed successfully"; if (jsonDoc.toVariant().toMap().contains("expires_in") && jsonDoc.toVariant().toMap().contains("refresh_token")) { int expireTime = jsonDoc.toVariant().toMap().value("expires_in").toInt(); - m_refreshToken = jsonDoc.toVariant().toMap().value("refresh_token").toString(); - qCDebug(dcCloud()) << "Token will be refreshed in" << expireTime << "[s]"; + setRefreshToken(jsonDoc.toVariant().toMap().value("refresh_token").toString()); + qCDebug(dcCloud()) << "Authenticator: Token will be refreshed in" << expireTime << "[s]"; m_timer->start((expireTime - 20) * 1000); } @@ -251,13 +294,11 @@ void CloudAuthenticator::replyFinished(QNetworkReply *reply) void CloudAuthenticator::refreshTimeout() { - qCDebug(dcCloud()) << "Refresh authentication token for" << m_username; + qCDebug(dcCloud()) << "Authenticator: Refresh authentication token for" << m_username; QUrlQuery query; query.addQueryItem("grant_type", "refresh_token"); query.addQueryItem("refresh_token", m_refreshToken); - query.addQueryItem("client_id", m_clientId); - query.addQueryItem("client_secret", m_clientSecret); QNetworkRequest request(m_url); QByteArray data = QString(m_clientId + ":" + m_clientSecret).toUtf8().toBase64(); diff --git a/server/cloud/cloudauthenticator.h b/server/cloud/cloudauthenticator.h index 279a9920..d0e326a6 100644 --- a/server/cloud/cloudauthenticator.h +++ b/server/cloud/cloudauthenticator.h @@ -57,14 +57,12 @@ public: QString clientSecret() const; void setClientSecret(const QString clientSecret); - QString scope() const; - void setScope(const QString &scope); - + // Read only QString token() const; - bool authenticated() const; - void startAuthentication(); + bool startAuthentication(); + void stopAuthentication(); private: QNetworkAccessManager *m_networkManager; @@ -78,7 +76,6 @@ private: QString m_password; QString m_clientId; QString m_clientSecret; - QString m_scope; QString m_token; QString m_refreshToken; @@ -88,6 +85,10 @@ private: void setAuthenticated(const bool &authenticated); void setToken(const QString &token); + void setRefreshToken(const QString &refreshToken); + QString loadRefreshToken() const; + QString loadUserName() const; + private slots: void replyFinished(QNetworkReply *reply); void refreshTimeout(); diff --git a/server/cloud/cloudclient.cpp b/server/cloud/cloudclient.cpp deleted file mode 100644 index ab5f10ea..00000000 --- a/server/cloud/cloudclient.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include "cloudclient.h" - -CloudClient::CloudClient(QObject *parent) : - QObject(parent) -{ - -} - diff --git a/server/cloud/cloudclient.h b/server/cloud/cloudclient.h deleted file mode 100644 index efcf5e98..00000000 --- a/server/cloud/cloudclient.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef CLOUDCLIENT_H -#define CLOUDCLIENT_H - -#include - -class CloudClient : public QObject -{ - Q_OBJECT -public: - explicit CloudClient(QObject *parent = 0); - -signals: - -public slots: -}; - -#endif // CLOUDCLIENT_H diff --git a/server/cloud/cloudconnection.cpp b/server/cloud/cloudconnection.cpp index e716b461..58580edd 100644 --- a/server/cloud/cloudconnection.cpp +++ b/server/cloud/cloudconnection.cpp @@ -29,22 +29,15 @@ namespace guhserver { CloudConnection::CloudConnection(QObject *parent) : QObject(parent), m_error(CloudConnectionErrorNoError), - m_enabled(false), m_connected(false), - m_active(false), m_authenticated(false) { m_proxyUrl = QUrl("ws://127.0.0.1:1212"); m_keystoneUrl = QUrl("http://localhost:8000/oauth2/token"); - GuhSettings settings(GuhSettings::SettingsRoleDevices); - settings.beginGroup("guhd"); - m_guhUuid = settings.value("uuid", QVariant()).toUuid(); - if (m_guhUuid.isNull()) { - m_guhUuid = QUuid::createUuid().toString(); - settings.setValue("uuid", m_guhUuid); - } - settings.endGroup(); + m_reconnectionTimer = new QTimer(this); + m_reconnectionTimer->setSingleShot(false); + connect(m_reconnectionTimer, &QTimer::timeout, this, &CloudConnection::reconnectionTimeout); m_connection = new QWebSocket("guhd", QWebSocketProtocol::Version13, this); connect(m_connection, SIGNAL(connected()), this, SLOT(onConnected())); @@ -56,56 +49,46 @@ CloudConnection::CloudConnection(QObject *parent) : m_authenticator->setUrl(m_keystoneUrl); connect(m_authenticator, &CloudAuthenticator::authenticationChanged, this, &CloudConnection::onAuthenticationChanged); - - connectToCloud("simon.stuerz@guh.guru", "wshslwshsl"); } -void CloudConnection::connectToCloud(const QString &username, const QString &password) +void CloudConnection::connectToCloud() { - m_authenticator->setUsername(username); - m_authenticator->setPassword(password); + if (m_connection->state() == QAbstractSocket::ConnectedState) { + disconnectFromCloud(); + } + m_authenticator->startAuthentication(); } +void CloudConnection::disconnectFromCloud() +{ + m_authenticator->stopAuthentication(); + m_connection->close(QWebSocketProtocol::CloseCodeNormal, "Disconnecting"); +} + +CloudAuthenticator *CloudConnection::authenticator() const +{ + return m_authenticator; +} + CloudConnection::CloudConnectionError CloudConnection::error() const { return m_error; } -void CloudConnection::enable() -{ - m_enabled = true; -} - -void CloudConnection::disable() -{ - m_enabled = false; -} - -bool CloudConnection::enabled() const -{ - return m_enabled; -} - bool CloudConnection::connected() const { return m_connected; } -bool CloudConnection::active() const -{ - return m_active; -} - bool CloudConnection::authenticated() const { return m_authenticated; } -void CloudConnection::setEnabled(const bool &enabled) +void CloudConnection::sendData(const QByteArray &data) { - m_enabled = enabled; - emit enabledChanged(); + m_connection->sendTextMessage(data); } void CloudConnection::setConnected(const bool &connected) @@ -114,12 +97,6 @@ void CloudConnection::setConnected(const bool &connected) emit connectedChanged(); } -void CloudConnection::setActive(const bool &active) -{ - m_active = active; - emit activeChanged(); -} - void CloudConnection::setAuthenticated(const bool &authenticated) { m_authenticated = authenticated; @@ -128,7 +105,6 @@ void CloudConnection::setAuthenticated(const bool &authenticated) void CloudConnection::onAuthenticationChanged() { - qCDebug(dcCloud()) << "Authentication changed" << m_authenticator->authenticated(); setAuthenticated(m_authenticator->authenticated()); if (m_authenticated) { @@ -143,42 +119,49 @@ void CloudConnection::onConnected() { qCDebug(dcCloud()) << "Connected to cloud proxy server" << m_proxyUrl.toString(); setConnected(true); - - QVariantMap introspectMap; - introspectMap.insert("id", 0); - introspectMap.insert("method", "Interface.Introspect"); - m_connection->sendTextMessage(QJsonDocument::fromVariant(introspectMap).toJson()); - - QVariantMap authenticationMap; - authenticationMap.insert("id", 1); - authenticationMap.insert("method", "Authentication.Authenticate"); - - QVariantMap params; - // TODO: use server name - params.insert("name", "guhIO"); - params.insert("id", m_guhUuid); - params.insert("token", m_authenticator->token()); - params.insert("type", "ConnectionTypeServer"); - - authenticationMap.insert("params", params); - - m_connection->sendTextMessage(QJsonDocument::fromVariant(authenticationMap).toJson()); + m_reconnectionTimer->stop(); } void CloudConnection::onDisconnected() { - qCDebug(dcCloud()) << "Disconnected from cloud:" << m_connection->closeReason(); + if (!m_reconnectionTimer->isActive()) + qCDebug(dcCloud()) << "Disconnected from cloud:" << m_connection->closeReason(); + setConnected(false); + m_reconnectionTimer->start(10000); } void CloudConnection::onError(const QAbstractSocket::SocketError &error) { - qCWarning(dcCloud()) << "Websocket error:" << error << m_connection->errorString(); + if (!m_reconnectionTimer->isActive()) + qCWarning(dcCloud()) << "Websocket error:" << error << m_connection->errorString(); + m_reconnectionTimer->start(10000); } void CloudConnection::onTextMessageReceived(const QString &message) { - qCDebug(dcCloud()) << "Cloud message -> " << qUtf8Printable(message.toUtf8()); + //qCDebug(dcCloud()) << "Cloud message -> " << qUtf8Printable(message.toUtf8()); + + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(message.toUtf8(), &error); + if (error.error != QJsonParseError::NoError) { + qCWarning(dcCloud()) << "Could not parse json data from guh" << qUtf8Printable(message.toUtf8()) << error.errorString(); + return; + } + + emit dataReceived(jsonDoc.toVariant().toMap()); } +void CloudConnection::reconnectionTimeout() +{ + if (m_authenticated) { + m_connection->open(m_proxyUrl); + } else { + m_reconnectionTimer->stop(); + m_error = CloudConnectionErrorAuthenticationFailed; + } + +} + + } diff --git a/server/cloud/cloudconnection.h b/server/cloud/cloudconnection.h index 0eab28e6..a641393d 100644 --- a/server/cloud/cloudconnection.h +++ b/server/cloud/cloudconnection.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "cloudauthenticator.h" @@ -44,37 +45,39 @@ public: explicit CloudConnection(QObject *parent = 0); - void connectToCloud(const QString &username, const QString &password); + void connectToCloud(); + void disconnectFromCloud(); + + CloudAuthenticator *authenticator() const; CloudConnectionError error() const; - void enable(); void disable(); - bool enabled() const; bool connected() const; - bool active() const; bool authenticated() const; + void sendData(const QByteArray &data); + void sendRequest(const QVariantMap &request); + private: QWebSocket *m_connection; CloudAuthenticator *m_authenticator; CloudConnectionError m_error; - QUuid m_guhUuid; + + QTimer *m_reconnectionTimer; + QUrl m_proxyUrl; QUrl m_keystoneUrl; - bool m_enabled; bool m_connected; - bool m_active; bool m_authenticated; - void setEnabled(const bool &enabled); void setConnected(const bool &connected); - void setActive(const bool &active); void setAuthenticated(const bool &authenticated); signals: + void dataReceived(const QVariantMap &data); void enabledChanged(); void connectedChanged(); void activeChanged(); @@ -87,7 +90,10 @@ private slots: void onError(const QAbstractSocket::SocketError &error); void onTextMessageReceived(const QString &message); + void reconnectionTimeout(); + }; } + #endif // CLOUDCONNECTION_H diff --git a/server/cloud/cloudconnectionhandler.cpp b/server/cloud/cloudconnectionhandler.cpp new file mode 100644 index 00000000..473c4f44 --- /dev/null +++ b/server/cloud/cloudconnectionhandler.cpp @@ -0,0 +1,94 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * 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 "cloudconnectionhandler.h" +#include "loggingcategories.h" +#include "guhcore.h" + +namespace guhserver { + +CloudConnectionHandler::CloudConnectionHandler(QObject *parent) : + CloudJsonHandler(parent) +{ + +} + +QString CloudConnectionHandler::nameSpace() const +{ + return "Connection"; +} + +void CloudConnectionHandler::processGetConnections(const QVariantMap ¶ms) +{ + Q_UNUSED(params) +} + +void CloudConnectionHandler::processGetTunnels(const QVariantMap ¶ms) +{ + foreach (const QVariant &tunnelVariant, params.value("tunnel").toList()) { + QVariantMap tunnelMap = tunnelVariant.toMap(); + + QUuid tunnelId = tunnelMap.value("id").toUuid(); + QUuid serverId = tunnelMap.value("serverConnection").toMap().value("id").toUuid(); + QUuid clientId = tunnelMap.value("clientConnection").toMap().value("id").toUuid(); + + GuhCore::instance()->cloudManager()->onTunnelAdded(tunnelId, serverId, clientId); + } +} + +void CloudConnectionHandler::processSendData(const QVariantMap ¶ms) +{ + Q_UNUSED(params) +} + +void CloudConnectionHandler::processConnectionAdded(const QVariantMap ¶ms) +{ + Q_UNUSED(params) +} + +void CloudConnectionHandler::processConnectionRemoved(const QVariantMap ¶ms) +{ + Q_UNUSED(params) +} + +void CloudConnectionHandler::processTunnelAdded(const QVariantMap ¶ms) +{ + QVariantMap tunnelMap = params.value("tunnel").toMap(); + + QUuid tunnelId = tunnelMap.value("id").toUuid(); + QUuid serverId = tunnelMap.value("serverConnection").toMap().value("id").toUuid(); + QUuid clientId = tunnelMap.value("clientConnection").toMap().value("id").toUuid(); + + GuhCore::instance()->cloudManager()->onTunnelAdded(tunnelId, serverId, clientId); +} + +void CloudConnectionHandler::processTunnelRemoved(const QVariantMap ¶ms) +{ + QUuid tunnelId = params.value("tunnelId").toUuid(); + GuhCore::instance()->cloudManager()->onTunnelRemoved(tunnelId); +} + +void CloudConnectionHandler::processDataReceived(const QVariantMap ¶ms) +{ + QUuid tunnelId = params.value("tunnelId").toUuid(); + GuhCore::instance()->cloudManager()->onCloudDataReceived(tunnelId, params.value("data").toMap()); +} + +} diff --git a/server/cloud/cloudconnectionhandler.h b/server/cloud/cloudconnectionhandler.h new file mode 100644 index 00000000..4d49ba97 --- /dev/null +++ b/server/cloud/cloudconnectionhandler.h @@ -0,0 +1,54 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * 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 CLOUDCONNECTIONHANDLER_H +#define CLOUDCONNECTIONHANDLER_H + +#include + +#include "cloudjsonhandler.h" + +namespace guhserver { + +class CloudConnectionHandler : public CloudJsonHandler +{ + Q_OBJECT +public: + explicit CloudConnectionHandler(QObject *parent = 0); + + QString nameSpace() const; + + // API methods + Q_INVOKABLE void processGetConnections(const QVariantMap ¶ms); + Q_INVOKABLE void processGetTunnels(const QVariantMap ¶ms); + Q_INVOKABLE void processSendData(const QVariantMap ¶ms); + + // API notifications + Q_INVOKABLE void processConnectionAdded(const QVariantMap ¶ms); + Q_INVOKABLE void processConnectionRemoved(const QVariantMap ¶ms); + Q_INVOKABLE void processTunnelAdded(const QVariantMap ¶ms); + Q_INVOKABLE void processTunnelRemoved(const QVariantMap ¶ms); + Q_INVOKABLE void processDataReceived(const QVariantMap ¶ms); + +}; + +} + +#endif // CLOUDCONNECTIONHANDLER_H diff --git a/server/cloud/cloudinterface.cpp b/server/cloud/cloudinterface.cpp index 1aef539f..4e0b5879 100644 --- a/server/cloud/cloudinterface.cpp +++ b/server/cloud/cloudinterface.cpp @@ -1,8 +1,126 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * 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 "cloudinterface.h" +#include "loggingcategories.h" +#include "guhsettings.h" +#include "guhcore.h" + +namespace guhserver { CloudInterface::CloudInterface(QObject *parent) : QObject(parent) { + GuhSettings settings(GuhSettings::SettingsRoleDevices); + settings.beginGroup("guhd"); + m_guhUuid = settings.value("uuid", QVariant()).toUuid(); + if (m_guhUuid.isNull()) { + m_guhUuid = QUuid::createUuid().toString(); + settings.setValue("uuid", m_guhUuid); + } + settings.endGroup(); + m_authenticationHandler = new CloudAuthenticationHandler(this); + m_connectionHandler = new CloudConnectionHandler(this); + + m_handlers.insert(m_authenticationHandler->nameSpace(), m_authenticationHandler); + m_handlers.insert(m_connectionHandler->nameSpace(), m_connectionHandler); } +void CloudInterface::authenticateConnection(const QString &token) +{ + qCDebug(dcCloud()) << "Start cloud connection authentication" << token; + + QVariantMap params; + // TODO: use server/station name + params.insert("name", "guhIO"); + params.insert("id", m_guhUuid); + params.insert("token", token); + params.insert("type", "ConnectionTypeServer"); + + CloudJsonReply *reply = createReply("Authentication", "Authenticate", params); + GuhCore::instance()->cloudManager()->sendCloudData(reply->requestMap()); + m_replies.insert(reply->commandId(), reply); +} + +void CloudInterface::getTunnels() +{ + CloudJsonReply *reply = createReply("Connection", "GetTunnels"); + GuhCore::instance()->cloudManager()->sendCloudData(reply->requestMap()); + m_replies.insert(reply->commandId(), reply); +} + +void CloudInterface::sendApiData(const QUuid &tunnelId, const QVariantMap &data) +{ + //qCDebug(dcCloud()) << "Send API data" << tunnelId.toString() << data; + + QVariantMap params; + params.insert("tunnelId", tunnelId.toString()); + params.insert("data", data); + + CloudJsonReply *reply = createReply("Connection", "SendData", params); + GuhCore::instance()->cloudManager()->sendCloudData(reply->requestMap()); + m_replies.insert(reply->commandId(), reply); +} + +CloudJsonReply *CloudInterface::createReply(QString nameSpace, QString method, QVariantMap params) +{ + m_id++; + return new CloudJsonReply(m_id, nameSpace, method, params, this); +} + +void CloudInterface::dataReceived(const QVariantMap &data) +{ + int commandId = data.value("id").toInt(); + QPointer reply = m_replies.take(commandId); + + QVariantMap params = data.value("params").toMap(); + + // check if this is a reply to a request + if (!reply.isNull() && !data.contains("notification")) { + qCDebug(dcCloud()) << "JsonRpc: got response for" << QString("%1.%2").arg(reply->nameSpace(), reply->method()); + CloudJsonHandler *handler = m_handlers.value(reply->nameSpace()); + + if (!QMetaObject::invokeMethod(handler, QString("process" + reply->method()).toLatin1().data(), Q_ARG(QVariantMap, params))) + qCWarning(dcCloud()) << "JsonRpc: method not implemented:" << reply->method(); + + reply->deleteLater(); + return; + } + + // check if this is a notification + if (data.contains("notification")) { + QStringList notification = data.value("notification").toString().split("."); + QString nameSpace = notification.first(); + QString method = notification.last(); + CloudJsonHandler *handler = m_handlers.value(nameSpace); + + if (!handler) { + qCWarning(dcCloud()) << "JsonRpc: handler not implemented:" << nameSpace; + return; + } + + if (!QMetaObject::invokeMethod(handler, QString("process" + method).toLatin1().data(), Q_ARG(QVariantMap, params))) + qCWarning(dcCloud()) << "JsonRpc: Method not implemented"; + + } +} + +} diff --git a/server/cloud/cloudinterface.h b/server/cloud/cloudinterface.h index 18c1439e..bf54817d 100644 --- a/server/cloud/cloudinterface.h +++ b/server/cloud/cloudinterface.h @@ -1,7 +1,36 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * 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 CLOUDINTERFACE_H #define CLOUDINTERFACE_H +#include +#include #include +#include + +#include "cloudjsonreply.h" +#include "cloudconnectionhandler.h" +#include "cloudauthenticationhandler.h" + +namespace guhserver { class CloudInterface : public QObject { @@ -9,9 +38,30 @@ class CloudInterface : public QObject public: explicit CloudInterface(QObject *parent = 0); + Q_INVOKABLE void authenticateConnection(const QString &token); + Q_INVOKABLE void getTunnels(); + Q_INVOKABLE void sendApiData(const QUuid &tunnelId, const QVariantMap &data); + +private: + int m_id; + QUuid m_guhUuid; + + QHash m_handlers; + QHash m_replies; + + CloudJsonReply *createReply(QString nameSpace, QString method, QVariantMap params = QVariantMap()); + + CloudAuthenticationHandler *m_authenticationHandler; + CloudConnectionHandler *m_connectionHandler; + signals: + void responseReceived(const int &commandId, const QVariantMap &response); public slots: + void dataReceived(const QVariantMap &data); + }; +} + #endif // CLOUDINTERFACE_H diff --git a/server/cloud/cloudjsonhandler.cpp b/server/cloud/cloudjsonhandler.cpp new file mode 100644 index 00000000..287cb7e3 --- /dev/null +++ b/server/cloud/cloudjsonhandler.cpp @@ -0,0 +1,31 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * 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 "cloudjsonhandler.h" + +namespace guhserver { + +CloudJsonHandler::CloudJsonHandler(QObject *parent) : + QObject(parent) +{ + +} + +} diff --git a/server/cloud/cloudjsonhandler.h b/server/cloud/cloudjsonhandler.h new file mode 100644 index 00000000..0df93520 --- /dev/null +++ b/server/cloud/cloudjsonhandler.h @@ -0,0 +1,39 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * 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 CLOUDJSONHANDLER_H +#define CLOUDJSONHANDLER_H + +#include + +namespace guhserver { + +class CloudJsonHandler : public QObject +{ + Q_OBJECT +public: + explicit CloudJsonHandler(QObject *parent = 0); + virtual QString nameSpace() const = 0; + +}; + +} + +#endif // CLOUDJSONHANDLER_H diff --git a/server/cloud/cloudjsonreply.cpp b/server/cloud/cloudjsonreply.cpp new file mode 100644 index 00000000..0c98f0d0 --- /dev/null +++ b/server/cloud/cloudjsonreply.cpp @@ -0,0 +1,66 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * 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 "cloudjsonreply.h" + +namespace guhserver { + +CloudJsonReply::CloudJsonReply(int commandId, QString nameSpace, QString method, QVariantMap params, QObject *parent) : + QObject(parent), + m_commandId(commandId), + m_nameSpace(nameSpace), + m_method(method), + m_params(params) +{ + +} + +int CloudJsonReply::commandId() const +{ + return m_commandId; +} + +QString CloudJsonReply::nameSpace() const +{ + return m_nameSpace; +} + +QString CloudJsonReply::method() const +{ + return m_method; +} + +QVariantMap CloudJsonReply::params() const +{ + return m_params; +} + +QVariantMap CloudJsonReply::requestMap() +{ + QVariantMap request; + request.insert("id", m_commandId); + request.insert("method", m_nameSpace + "." + m_method); + if (!m_params.isEmpty()) + request.insert("params", m_params); + + return request; +} + +} diff --git a/server/cloud/cloudjsonreply.h b/server/cloud/cloudjsonreply.h new file mode 100644 index 00000000..2115c8b3 --- /dev/null +++ b/server/cloud/cloudjsonreply.h @@ -0,0 +1,51 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * 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 CLOUDJSONREPLY_H +#define CLOUDJSONREPLY_H + +#include +#include + +namespace guhserver { + +class CloudJsonReply : public QObject +{ + Q_OBJECT + +public: + explicit CloudJsonReply(int commandId, QString nameSpace, QString method, QVariantMap params = QVariantMap(), QObject *parent = 0); + int commandId() const; + QString nameSpace() const; + QString method() const; + QVariantMap params() const; + QVariantMap requestMap(); + +private: + int m_commandId; + QString m_nameSpace; + QString m_method; + QVariantMap m_params; + +}; + +} + +#endif // CLOUDJSONREPLY_H diff --git a/server/cloud/cloudmanager.cpp b/server/cloud/cloudmanager.cpp new file mode 100644 index 00000000..11db2004 --- /dev/null +++ b/server/cloud/cloudmanager.cpp @@ -0,0 +1,197 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * 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 "cloudmanager.h" +#include "jsonhandler.h" +#include "guhcore.h" + +#include +#include + +namespace guhserver { + +CloudManager::CloudManager(QObject *parent) : + TransportInterface(parent), + m_enabled(true), + m_authenticated(false) +{ + m_cloudConnection = new CloudConnection(this); + connect(m_cloudConnection, &CloudConnection::authenticatedChanged, this, &CloudManager::onAuthenticatedChanged); + connect(m_cloudConnection, &CloudConnection::connectedChanged, this, &CloudManager::onConnectedChanged); + + m_interface = new CloudInterface(this); + connect(m_cloudConnection, &CloudConnection::dataReceived, m_interface, &CloudInterface::dataReceived); +} + +CloudManager::~CloudManager() +{ + qCDebug(dcApplication) << "Shutting down \"Cloud Manager\""; + stopServer(); +} + +void CloudManager::connectToCloud(const QString &username, const QString &password) +{ + m_cloudConnection->authenticator()->setUsername(username); + m_cloudConnection->authenticator()->setPassword(password); + + m_cloudConnection->connectToCloud(); +} + +void CloudManager::sendData(const QUuid &clientId, const QVariantMap &data) +{ + // Used from the JsonRpcServer + m_interface->sendApiData(m_tunnelClients.value(clientId), data); +} + +void CloudManager::sendData(const QList &clients, const QVariantMap &data) +{ + // Used from the JsonRpcServer + foreach (const QUuid &clientId, clients) { + sendData(clientId, data); + } +} + +bool CloudManager::enabled() const +{ + return m_enabled; +} + +bool CloudManager::connected() const +{ + return m_cloudConnection->connected(); +} + +bool CloudManager::active() const +{ + return m_active; +} + +bool CloudManager::authenticated() const +{ + return m_authenticated; +} + +bool CloudManager::startServer() +{ + m_cloudConnection->connectToCloud(); + return true; +} + +bool CloudManager::stopServer() +{ + m_cloudConnection->disconnectFromCloud(); + return true; +} + +void CloudManager::sendCloudData(const QVariantMap &data) +{ + m_cloudConnection->sendData(QJsonDocument::fromVariant(data).toJson()); +} + +void CloudManager::onConnectionAuthentificationFinished(const bool &authenticated, const QUuid &connectionId) +{ + qCDebug(dcCloud()) << "Cloud connection authenticated" << authenticated; + if (authenticated) { + m_authenticated = true; + m_connectionId = connectionId; + qCDebug(dcCloud()) << "Connection ID:" << connectionId.toString(); + emit authenticatedChanged(); + + // Get tunnel connections + m_interface->getTunnels(); + + } else { + m_authenticated = false; + authenticatedChanged(); + } +} + +void CloudManager::onTunnelAdded(const QUuid &tunnelId, const QUuid &serverId, const QUuid &clientId) +{ + if (serverId == m_connectionId) { + qCDebug(dcCloud()) << "New tunnel connection from" << clientId.toString(); + m_tunnelClients.insert(clientId, tunnelId); + emit clientConnected(clientId); + } +} + +void CloudManager::onTunnelRemoved(const QUuid &tunnelId) +{ + if (m_tunnelClients.values().contains(tunnelId)) { + QUuid clientId = m_tunnelClients.key(tunnelId); + m_tunnelClients.remove(clientId); + emit clientDisconnected(clientId); + } +} + +void CloudManager::onCloudDataReceived(const QUuid &tunnelId, const QVariantMap &data) +{ + if (m_tunnelClients.values().contains(tunnelId)) { + QUuid clientId = m_tunnelClients.key(tunnelId); + + bool success; + int commandId = data.value("id").toInt(&success); + if (!success) { + qCWarning(dcCloud()) << "TunnelData: Error parsing command. Missing \"id\":" << data; + sendErrorResponse(clientId, commandId, "Error parsing command. Missing 'id'"); + return; + } + + QStringList commandList = data.value("method").toString().split('.'); + if (commandList.count() != 2) { + qCWarning(dcCloud()) << "TunnelData: Error parsing method.\nGot:" << data.value("method").toString() << "\nExpected: \"Namespace.method\""; + return; + } + + QString targetNamespace = commandList.first(); + QString method = commandList.last(); + + JsonHandler *handler = GuhCore::instance()->jsonRPCServer()->handlers().value(targetNamespace); + if (!handler) { + sendErrorResponse(clientId, commandId, "No such namespace"); + return; + } + if (!handler->hasMethod(method)) { + sendErrorResponse(clientId, commandId, "No such method"); + return; + } + + emit dataAvailable(clientId, targetNamespace, method, data); + } +} + +void CloudManager::onConnectedChanged() +{ + // Start authentication if connected + if (m_cloudConnection->connected()) { + m_interface->authenticateConnection(m_cloudConnection->authenticator()->token()); + emit connectedChanged(); + } +} + +void CloudManager::onAuthenticatedChanged() +{ + if (!m_cloudConnection->authenticator()->authenticated()) { + m_authenticated = false; + emit authenticatedChanged(); + } +} + +} diff --git a/server/cloud/cloudmanager.h b/server/cloud/cloudmanager.h new file mode 100644 index 00000000..562608f8 --- /dev/null +++ b/server/cloud/cloudmanager.h @@ -0,0 +1,102 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * 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 CLOUDMANAGER_H +#define CLOUDMANAGER_H + +#include + + +#include "transportinterface.h" +#include "cloudinterface.h" +#include "cloudauthenticator.h" +#include "cloudconnection.h" + +namespace guhserver { + +class CloudManager : public TransportInterface +{ + Q_OBJECT +public: + friend class CloudInterface; + friend class CloudConnectionHandler; + friend class CloudAuthenticationHandler; + + explicit CloudManager(QObject *parent = 0); + ~CloudManager(); + + void connectToCloud(const QString &username, const QString &password) ; + + void sendData(const QUuid &clientId, const QVariantMap &data) override; + void sendData(const QList &clients, const QVariantMap &data) override; + + bool enabled() const; + bool connected() const; + bool connectionAuthenticated() const; + bool active() const; + bool authenticated() const; + +public slots: + bool startServer() override; + bool stopServer() override; + +private: + CloudConnection *m_cloudConnection; + CloudInterface *m_interface; + + QList m_clients; + + QHash m_replies; + + QHash m_tunnelClients; + + QUuid m_connectionId; + bool m_enabled; + bool m_active; + bool m_authenticated; + +protected: + void sendCloudData(const QVariantMap &data); + + void onConnectionAuthentificationFinished(const bool &status, const QUuid &connectionId); + void onTunnelAdded(const QUuid &tunnelId, const QUuid &serverId, const QUuid &clientId); + void onTunnelRemoved(const QUuid &tunnelId); + void onCloudDataReceived(const QUuid &tunnelId, const QVariantMap &data); + +signals: + void enabledChanged(); + void connectedChanged(); + void activeChanged(); + void authenticatedChanged(); + + // Transport interface signals + void clientConnected(const QUuid &clientId); + void clientDisconnected(const QUuid &clientId); + void dataAvailable(const QUuid &clientId, const QString &targetNamespace, const QString &method, const QVariantMap &message); + +private slots: + void onConnectedChanged(); + void onAuthenticatedChanged(); + +}; + +} + +#endif // CLOUDMANAGER_H diff --git a/server/guhcore.cpp b/server/guhcore.cpp index 2404f7d6..8c121dec 100644 --- a/server/guhcore.cpp +++ b/server/guhcore.cpp @@ -362,9 +362,9 @@ TimeManager *GuhCore::timeManager() const return m_timeManager; } -CloudConnection *GuhCore::cloudConnection() const +CloudManager *GuhCore::cloudManager() const { - return m_cloudConnection; + return m_cloudManager; } /*! Constructs GuhCore with the given \a parent. This is private. @@ -378,8 +378,8 @@ GuhCore::GuhCore(QObject *parent) : qCDebug(dcApplication) << "Creating Log Engine"; m_logger = new LogEngine(this); - qCDebug(dcApplication) << "Creating Cloud Connection"; - m_cloudConnection = new CloudConnection(this); + qCDebug(dcApplication) << "Creating Cloud Manager"; + m_cloudManager = new CloudManager(this); qCDebug(dcApplication) << "Creating Device Manager"; m_deviceManager = new DeviceManager(this); @@ -390,6 +390,9 @@ GuhCore::GuhCore(QObject *parent) : qCDebug(dcApplication) << "Creating Server Manager"; m_serverManager = new ServerManager(this); + // Register cloud connection transport interface + m_serverManager->jsonServer()->registerTransportInterface(m_cloudManager); + 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 acfa394a..daf6f73c 100644 --- a/server/guhcore.h +++ b/server/guhcore.h @@ -32,7 +32,7 @@ #include "ruleengine.h" #include "servermanager.h" -#include "cloud/cloudconnection.h" +#include "cloud/cloudmanager.h" #include "time/timemanager.h" #include @@ -71,7 +71,7 @@ public: DeviceManager *deviceManager() const; RuleEngine *ruleEngine() const; TimeManager *timeManager() const; - CloudConnection *cloudConnection() const; + CloudManager *cloudManager() const; signals: void eventTriggered(const Event &event); @@ -100,7 +100,7 @@ private: RuleEngine *m_ruleEngine; LogEngine *m_logger; TimeManager *m_timeManager; - CloudConnection *m_cloudConnection; + CloudManager *m_cloudManager; QHash m_pendingActions; diff --git a/server/jsonrpc/cloudhandler.cpp b/server/jsonrpc/cloudhandler.cpp index 3c37c4df..c41f5aa2 100644 --- a/server/jsonrpc/cloudhandler.cpp +++ b/server/jsonrpc/cloudhandler.cpp @@ -53,10 +53,10 @@ CloudHandler::CloudHandler(QObject *parent) : params.insert("authenticated", JsonTypes::basicTypeToString(JsonTypes::Bool)); setParams("ConnectionStatusChanged", params); - connect(GuhCore::instance()->cloudConnection(), SIGNAL(enabledChanged()), this, SLOT(onConnectionStatusChanged())); - connect(GuhCore::instance()->cloudConnection(), SIGNAL(connectedChanged()), this, SLOT(onConnectionStatusChanged())); - connect(GuhCore::instance()->cloudConnection(), SIGNAL(activeChanged()), this, SLOT(onConnectionStatusChanged())); - connect(GuhCore::instance()->cloudConnection(), SIGNAL(authenticatedChanged()), this, SLOT(onConnectionStatusChanged())); + connect(GuhCore::instance()->cloudManager(), SIGNAL(enabledChanged()), this, SLOT(onConnectionStatusChanged())); + connect(GuhCore::instance()->cloudManager(), SIGNAL(connectedChanged()), this, SLOT(onConnectionStatusChanged())); + connect(GuhCore::instance()->cloudManager(), SIGNAL(activeChanged()), this, SLOT(onConnectionStatusChanged())); + connect(GuhCore::instance()->cloudManager(), SIGNAL(authenticatedChanged()), this, SLOT(onConnectionStatusChanged())); } QString CloudHandler::name() const @@ -72,7 +72,7 @@ JsonReply *CloudHandler::Authenticate(const QVariantMap ¶ms) const QString password = params.value("password").toString(); qCDebug(dcJsonRpc()) << "Authenticate cloud connection for user" << username; - GuhCore::instance()->cloudConnection()->connectToCloud(username, password); + GuhCore::instance()->cloudManager()->connectToCloud(username, password); QVariantMap returns; returns.insert("cloudConnectionError", JsonTypes::cloudConnectionErrorToString(CloudConnection::CloudConnectionErrorNoError)); @@ -84,20 +84,20 @@ JsonReply *CloudHandler::GetConnectionStatus(const QVariantMap ¶ms) const Q_UNUSED(params) QVariantMap returns; - returns.insert("enabled", GuhCore::instance()->cloudConnection()->enabled()); - returns.insert("connected", GuhCore::instance()->cloudConnection()->connected()); - returns.insert("active", GuhCore::instance()->cloudConnection()->active()); - returns.insert("authenticated", GuhCore::instance()->cloudConnection()->authenticated()); + returns.insert("enabled", GuhCore::instance()->cloudManager()->enabled()); + returns.insert("connected", GuhCore::instance()->cloudManager()->connected()); + returns.insert("active", GuhCore::instance()->cloudManager()->active()); + returns.insert("authenticated", GuhCore::instance()->cloudManager()->authenticated()); return createReply(returns); } void CloudHandler::onConnectionStatusChanged() { QVariantMap params; - params.insert("enabled", GuhCore::instance()->cloudConnection()->enabled()); - params.insert("connected", GuhCore::instance()->cloudConnection()->connected()); - params.insert("active", GuhCore::instance()->cloudConnection()->active()); - params.insert("authenticated", GuhCore::instance()->cloudConnection()->authenticated()); + params.insert("enabled", GuhCore::instance()->cloudManager()->enabled()); + params.insert("connected", GuhCore::instance()->cloudManager()->connected()); + params.insert("active", GuhCore::instance()->cloudManager()->active()); + params.insert("authenticated", GuhCore::instance()->cloudManager()->authenticated()); emit ConnectionStatusChanged(params); } diff --git a/server/jsonrpc/jsonrpcserver.cpp b/server/jsonrpc/jsonrpcserver.cpp index 8ce7e7dc..a388674a 100644 --- a/server/jsonrpc/jsonrpcserver.cpp +++ b/server/jsonrpc/jsonrpcserver.cpp @@ -105,20 +105,8 @@ JsonRPCServer::JsonRPCServer(const QSslConfiguration &sslConfiguration, QObject returns.insert("enabled", JsonTypes::basicTypeToString(JsonTypes::Bool)); setReturns("SetNotificationStatus", returns); - // Now set up the logic - connect(m_tcpServer, SIGNAL(clientConnected(const QUuid &)), this, SLOT(clientConnected(const QUuid &))); - connect(m_tcpServer, SIGNAL(clientDisconnected(const QUuid &)), this, SLOT(clientDisconnected(const QUuid &))); - connect(m_tcpServer, SIGNAL(dataAvailable(QUuid, QString, QString, QVariantMap)), this, SLOT(processData(QUuid, QString, QString, QVariantMap))); - m_tcpServer->startServer(); - - m_interfaces.append(m_tcpServer); - - connect(m_websocketServer, SIGNAL(clientConnected(const QUuid &)), this, SLOT(clientConnected(const QUuid &))); - connect(m_websocketServer, SIGNAL(clientDisconnected(const QUuid &)), this, SLOT(clientDisconnected(const QUuid &))); - connect(m_websocketServer, SIGNAL(dataAvailable(QUuid, QString, QString, QVariantMap)), this, SLOT(processData(QUuid, QString, QString, QVariantMap))); - - m_websocketServer->startServer(); - m_interfaces.append(m_websocketServer); + registerTransportInterface(m_tcpServer); + registerTransportInterface(m_websocketServer); QMetaObject::invokeMethod(this, "setup", Qt::QueuedConnection); } @@ -175,6 +163,16 @@ QHash JsonRPCServer::handlers() const return m_handlers; } +void JsonRPCServer::registerTransportInterface(TransportInterface *interface) +{ + // Now set up the logic + connect(interface, SIGNAL(clientConnected(const QUuid &)), this, SLOT(clientConnected(const QUuid &))); + connect(interface, SIGNAL(clientDisconnected(const QUuid &)), this, SLOT(clientDisconnected(const QUuid &))); + connect(interface, SIGNAL(dataAvailable(QUuid, QString, QString, QVariantMap)), this, SLOT(processData(QUuid, QString, QString, QVariantMap))); + interface->startServer(); + m_interfaces.append(interface); +} + void JsonRPCServer::setup() { registerHandler(this); diff --git a/server/jsonrpc/jsonrpcserver.h b/server/jsonrpc/jsonrpcserver.h index 71461126..0a02966c 100644 --- a/server/jsonrpc/jsonrpcserver.h +++ b/server/jsonrpc/jsonrpcserver.h @@ -63,6 +63,8 @@ public: QHash handlers() const; + void registerTransportInterface(TransportInterface *interface); + private slots: void setup(); @@ -83,6 +85,7 @@ private: #endif WebSocketServer *m_websocketServer; + QList m_interfaces; QHash m_handlers; QHash m_asyncReplies; diff --git a/server/main.cpp b/server/main.cpp index cdbcbd60..5bf9cce9 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -83,6 +83,7 @@ static void consoleLogHandler(QtMsgType type, const QMessageLogContext& context, fprintf(stdout, " F | %s: %s\n", context.category, message.toUtf8().data()); break; } + fflush(stdout); QFile logFile(GuhSettings::consoleLogPath()); if (!logFile.open(QIODevice::WriteOnly | QIODevice::Append)) { diff --git a/server/server.pri b/server/server.pri index 6afb0fae..68ea3034 100644 --- a/server/server.pri +++ b/server/server.pri @@ -41,8 +41,12 @@ HEADERS += $$top_srcdir/server/guhcore.h \ $$top_srcdir/server/time/timemanager.h \ $$top_srcdir/server/cloud/cloudconnection.h \ $$top_srcdir/server/cloud/cloudauthenticator.h \ - $$PWD/cloud/cloudinterface.h \ - $$PWD/cloud/cloudclient.h + $$top_srcdir/server/cloud/cloudinterface.h \ + $$top_srcdir/server/cloud/cloudjsonreply.h \ + $$top_srcdir/server/cloud/cloudauthenticationhandler.h \ + $$top_srcdir/server/cloud/cloudconnectionhandler.h \ + $$top_srcdir/server/cloud/cloudjsonhandler.h \ + $$top_srcdir/server/cloud/cloudmanager.h \ SOURCES += $$top_srcdir/server/guhcore.cpp \ @@ -84,5 +88,9 @@ SOURCES += $$top_srcdir/server/guhcore.cpp \ $$top_srcdir/server/time/timemanager.cpp \ $$top_srcdir/server/cloud/cloudconnection.cpp \ $$top_srcdir/server/cloud/cloudauthenticator.cpp \ - $$PWD/cloud/cloudinterface.cpp \ - $$PWD/cloud/cloudclient.cpp + $$top_srcdir/server/cloud/cloudinterface.cpp \ + $$top_srcdir/server/cloud/cloudjsonreply.cpp \ + $$top_srcdir/server/cloud/cloudauthenticationhandler.cpp \ + $$top_srcdir/server/cloud/cloudconnectionhandler.cpp \ + $$top_srcdir/server/cloud/cloudjsonhandler.cpp \ + $$top_srcdir/server/cloud/cloudmanager.cpp \