diff --git a/libnymea-core/cloud/awsconnector.cpp b/libnymea-core/cloud/awsconnector.cpp deleted file mode 100644 index c85d6291..00000000 --- a/libnymea-core/cloud/awsconnector.cpp +++ /dev/null @@ -1,482 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* Copyright 2013 - 2020, nymea GmbH -* Contact: contact@nymea.io -* -* This file is part of nymea. -* This project including source code and documentation is protected by -* copyright law, and remains the property of nymea GmbH. All rights, including -* reproduction, publication, editing and translation, are reserved. The use of -* this project is subject to the terms of a license agreement to be concluded -* with nymea GmbH in accordance with the terms of use of nymea GmbH, available -* under https://nymea.io/license -* -* GNU General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the -* terms of the GNU General Public License as published by the Free Software -* Foundation, GNU version 3. This project 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 -* this project. If not, see . -* -* For any further details and any questions please contact us under -* contact@nymea.io or see our FAQ/Licensing Information on -* https://nymea.io/license/faq -* -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#include "awsconnector.h" -#include "loggingcategories.h" -#include "nymeasettings.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -AWSConnector::AWSConnector(QObject *parent) : QObject(parent) -{ - m_clientName = readSyncedNameCache(); - - m_reconnectTimer.setSingleShot(true); - connect(&m_reconnectTimer, &QTimer::timeout, this, &AWSConnector::doConnect); - - // In rare circumstances (I've observed this when the wifi driver panics) it happens that a connection - // attempt never completes but also doesn't emit any error (bug in Qt?). To catch that, manually call the - // disconnected slot to trigger reconnecting. - m_connectTimer.setSingleShot(true); - connect(&m_connectTimer, &QTimer::timeout, this, [this](){ - if (m_client && !m_client->isConnected()) { - qCWarning(dcAWS()) << "Connection guard timer timed out! Resetting connection."; - onDisconnected(); - } - }); -} - -AWSConnector::~AWSConnector() -{ - if (m_client) { - m_client->disconnectFromHost(); - qCDebug(dcAWS()) << "Disconnected from AWS."; - emit disconnected(); - } -} - -void AWSConnector::connect2AWS(const QString &endpoint, const QString &clientId, const QString &clientName, const QString &caFile, const QString &clientCertFile, const QString &clientPrivKeyFile) -{ - if (m_client) { - qCDebug(dcAWS()) << "Cleaning up old connection"; - m_shouldReconnect = false; - m_client->disconnectFromHost(); - // Don't rely on the graceful disconnect to actually happen... Force cleanup of the old connection - onDisconnected(); - } - - m_shouldReconnect = true; - m_currentEndpoint = endpoint; - m_caFile = caFile; - m_clientCertFile = clientCertFile; - m_clientPrivKeyFile = clientPrivKeyFile; - m_clientId = clientId; - m_clientName = clientName; - - doConnect(); -} - -void AWSConnector::doConnect() -{ - if (m_setupInProgress) { - qCWarning(dcAWS()) << "Connection attempt already in progress..."; - return; - } - if (isConnected()) { - qCWarning(dcAWS()) << "Already connected. Not connecting again..."; - return; - } - m_setupInProgress = true; - m_subscriptionCache.clear(); - - QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration(); - QFile certFile(m_clientCertFile); - certFile.open(QFile::ReadOnly); - QSslCertificate certificate(certFile.readAll()); - - QFile keyFile(m_clientPrivKeyFile); - keyFile.open(QFile::ReadOnly); - QSslKey key(keyFile.readAll(), QSsl::Rsa); - - sslConfig.setLocalCertificate(certificate); - sslConfig.setPrivateKey(key); - sslConfig.setPeerVerifyMode(QSslSocket::VerifyNone); - - QFile caCertFile(m_caFile); - caCertFile.open(QFile::ReadOnly); - QSslCertificate caCertificate(caCertFile.readAll()); - - sslConfig.setCaCertificates({caCertificate}); - - m_client = new MqttClient(m_clientId, this); - m_client->setKeepAlive(30); - m_client->setAutoReconnect(false); - qCDebug(dcAWS()).nospace().noquote() << "Connecting MQTT to " << m_currentEndpoint << " as " << m_clientId << " with certificate " << getCertificateFingerprint(certificate); - m_client->connectToHost(m_currentEndpoint, 8883, true, true, sslConfig); - - connect(m_client, &MqttClient::connected, this, &AWSConnector::onConnected); - connect(m_client, &MqttClient::disconnected, this, &AWSConnector::onDisconnected); - connect(m_client, &MqttClient::error, this, [this](const QAbstractSocket::SocketError error){ - m_connectTimer.stop(); - qCWarning(dcAWS()) << "An error happened in the MQTT transport" << error; - // In order to also call onDisconnected (and start the reconnect timer) even when we have never been connected - // we'll call it here. However, that might cause onDisconnected to be called twice. Let's prevent that. - disconnect(m_client, &MqttClient::disconnected, this, &AWSConnector::onDisconnected); - onDisconnected(); - }); - - connect(m_client, &MqttClient::subscribed, this, &AWSConnector::onSubscribed); - connect(m_client, &MqttClient::publishReceived, this, &AWSConnector::onPublishReceived); - connect(m_client, &MqttClient::published, this, &AWSConnector::onPublished); - - // If none of the above slots are called within a minute, reset ourselves... - m_connectTimer.start(60000); -} - -void AWSConnector::onConnected() -{ - m_connectTimer.stop(); - - if (!readRegisteredFlag()) { - qCDebug(dcAWS()) << "AWS connected. Device not registered yet. Registering..."; - registerDevice(); - return; - } - qCDebug(dcAWS()) << "AWS connected. Device already registered in cloud."; - - // OK, we're registerd already, go straight to subscription setup - setupSubscriptions(); -} - -void AWSConnector::registerDevice() -{ - // We create a temporary UUID for which will be used by the server to post the reply to our create/device call. - // Before the first create/device call the cloud doesn't know about us. In order to receive the reply for the - // call we need to subscribe to a topic every device can subscribe to. If we'd use our deviceId, a potential - // black hat could snoop in all the devices we register on the system. So in case someone actually does that - // let's give him meaningless IDs instead of real device ids. - m_createDeviceId = QUuid::createUuid().toString().remove(QRegExp("[{}]*")); - - // first subscribe to this tmp id topic - subscribe({QString("create/device/%1").arg(m_createDeviceId)}); -} - -void AWSConnector::onDeviceRegistered(bool needsReconnect) -{ - storeRegisteredFlag(true); - - if (needsReconnect) { - qCDebug(dcAWS()) << "Disconnecting from AWS and reconnecting to use new policies"; - m_client->disconnectFromHost(); - return; - } - - setupSubscriptions(); -} - -void AWSConnector::setupSubscriptions() -{ - // Subscribe to our namespace - subscribe({QString("%1/#").arg(m_clientId)}); - - // fetch previous pairings - fetchPairings(); -} - -void AWSConnector::fetchPairings() -{ - QVariantMap params; - params.insert("timestamp", QDateTime::currentMSecsSinceEpoch()); - params.insert("id", ++m_transactionId); - params.insert("command", "getUsers"); - publish(QString("%1/device/users").arg(m_clientId), params); -} - -void AWSConnector::onPairingsRetrieved(const QVariantMap &pairings) -{ - if (m_setupInProgress) { - m_setupInProgress = false; - emit connected(); - } - - qCDebug(dcAWS) << pairings.value("users").toList().count() << "devices paired in cloud."; - - if (readSyncedNameCache() != m_clientName) { - setName(); - } -} - -void AWSConnector::disconnectAWS() -{ - m_shouldReconnect = false; - if (isConnected()) { - m_client->disconnectFromHost(); - qCDebug(dcAWS()) << "Disconnecting from AWS."; - } -} - -bool AWSConnector::isConnected() const -{ - return m_client && m_client->isConnected() && !m_setupInProgress; -} - -void AWSConnector::setDeviceName(const QString &deviceName) -{ - if (m_clientName != deviceName) { - m_clientName = deviceName; - storeSyncedNameCache(QString()); - if (isConnected()) { - setName(); - } - } -} - -void AWSConnector::pairDevice(const QString &idToken, const QString &userId) -{ - QVariantMap map; - map.insert("idToken", idToken); - map.insert("userId", userId); - map.insert("id", ++m_transactionId); - map.insert("timestamp", QDateTime::currentMSecsSinceEpoch()); - publish(QString("%1/pair").arg(m_clientId), map); - m_pairingRequests.insert(m_transactionId, userId); -} - -quint16 AWSConnector::publish(const QString &topic, const QVariantMap &message) -{ - if (!m_setupInProgress && !isConnected()) { - qCWarning(dcAWS()) << "Can't publish to AWS: Not connected."; - return -1; - } - QJsonDocument jsonDoc = QJsonDocument::fromVariant(message); - - qCDebug(dcAWSTraffic()) << "Publishing:" << topic << jsonDoc.toJson(QJsonDocument::Compact); - quint16 packetId = m_client->publish(topic, jsonDoc.toJson(QJsonDocument::Compact), Mqtt::QoS1, false); - return packetId; -} - -void AWSConnector::onDisconnected() -{ - m_connectTimer.stop(); - - qCDebug(dcAWS) << "AWS disconnected."; - m_client->deleteLater(); - m_client = nullptr; - emit disconnected(); - - bool needReRegistering = false; - if (m_setupInProgress) { - qCWarning(dcAWS()) << "Setup process interrupted by disconnect."; - m_setupInProgress = false; - needReRegistering = true; - } else { - if (m_lastConnectionDrop.addSecs(60) > QDateTime::currentDateTime()) { - m_reconnectCounter++; - } else { - m_reconnectCounter = 0; - } - m_lastConnectionDrop = QDateTime::currentDateTime(); - if (m_reconnectCounter > 5) { - qCWarning(dcAWS()) << "Connection dropped 5 times in a row within a minute."; - needReRegistering = true; - } - } - - if (needReRegistering) { - qCDebug(dcAWS) << "Trying to reregister the device in the cloud"; - storeRegisteredFlag(false); - storeSyncedNameCache(QString()); - } - - if (m_shouldReconnect) { - qCDebug(dcAWS()) << "Reconnecting to AWS in 5 seconds..."; - m_reconnectTimer.start(5000); - } -} - -void AWSConnector::setName() -{ - QVariantMap params; - params.insert("id", ++m_transactionId); - params.insert("timestamp", QDateTime::currentMSecsSinceEpoch() / 1000); - params.insert("command", "postName"); - params.insert("name", m_clientName); - publish(QString("%1/device/name").arg(m_clientId), params); -} - -void AWSConnector::subscribe(const QStringList &topics) -{ - // Note: Do not check for isConnected here because subscribing is part of the connection - // flow and it needs to work before we are actually connected. - if (!m_client) { - qCWarning(dcAWS()) << "Not connected. Cannot subscribe."; - return; - } - - foreach (const QString &topic, topics) { - if (m_subscriptionCache.contains(topic)) { - qCDebug(dcAWS()) << "Already subscribed to topic:" << topic << ". Not resubscribing"; - continue; - } - qCDebug(dcAWSTraffic()) << "Topic to subscribe is" << topic; - MqttSubscription subscription(topic.toUtf8(), Mqtt::QoS1); - m_client->subscribe(subscription); - m_subscriptionCache.append(topic); - } -} - -void AWSConnector::onPublished(quint16 msgid, const QString &topic) -{ - qCDebug(dcAWS()) << "Published message:" << msgid << topic; -} - -void AWSConnector::onSubscribed(const QString &topic, Mqtt::SubscribeReturnCode returnCode) -{ - qCDebug(dcAWSTraffic()) << "Subscribed to topic:" << topic << returnCode; - - if (topic.startsWith("create/device/")) { - qCDebug(dcAWS()) << "Subscribed to create/device/"; - // We might get this callback even if we didn't explicitly ask for it as the - // library automatically resubscribes to all the topics upon reconnect. - if (!readRegisteredFlag()) { - QVariantMap params; - params.insert("id", m_createDeviceId); - params.insert("UUID", m_clientId); - publish("create/device", params); - } - return; - } -} - -void AWSConnector::onPublishReceived(const QString &topic, const QByteArray &payload) -{ - QJsonParseError error; - QJsonDocument jsonDoc = QJsonDocument::fromJson(payload, &error); - if (error.error != QJsonParseError::NoError) { - qCDebug(dcAWS()) << "Failed to parse JSON from AWS subscription on topic" << topic << ":" << error.errorString() << "\n" << payload; - return; - } - - qCDebug(dcAWSTraffic()) << "Subscription received on topic" << topic; - qCDebug(dcAWSTraffic()) << qUtf8Printable(jsonDoc.toJson(QJsonDocument::Indented)); - - if (topic.startsWith("create/device/")) { - int statusCode = jsonDoc.toVariant().toMap().value("result").toMap().value("code").toInt(); - switch (statusCode) { - case 201: - qCDebug(dcAWS()) << "Device successfully registered to the cloud server:" << statusCode << jsonDoc.toVariant().toMap().value("result").toMap().value("message").toString(); - onDeviceRegistered(true); - return; - case 200: - qCDebug(dcAWS()) << "Device already known to the cloud server:" << statusCode << jsonDoc.toVariant().toMap().value("result").toMap().value("message").toString(); - // Ok, we have confirmation that everything went fine and we can proceed, let's remember that to minimize traffic. - onDeviceRegistered(false); - break; - default: - qCWarning(dcAWS()) << "Error registering device in the cloud. AWS connetion will not work:" << statusCode << jsonDoc.toVariant().toMap().value("result").toMap().value("message").toString(); - return; - } - } else if (topic == QString("%1/pair/response").arg(m_clientId)) { - int statusCode = jsonDoc.toVariant().toMap().value("status").toInt(); - quint16 id = jsonDoc.toVariant().toMap().value("id").toUInt(); - QString message = jsonDoc.toVariant().toMap().value("result").toMap().value("message").toString(); - QString userId = m_pairingRequests.take(id); - if (statusCode != 200) { - qCWarning(dcAWS()) << "Pairing failed:" << statusCode << message; - emit devicePaired(userId, statusCode, message); - } else if (!userId.isEmpty()) { - qCDebug(dcAWS()) << "Pairing response for id:" << userId << statusCode; - emit devicePaired(userId, statusCode, message); - fetchPairings(); - } else { - qCWarning(dcAWS()) << "Received a pairing response for a transaction we didn't start"; - } - } else if (topic == QString("%1/device/users/response").arg(m_clientId)) { - onPairingsRetrieved(jsonDoc.toVariant().toMap()); - } else if (topic == QString("%1/device/name/response").arg(m_clientId)) { - qCDebug(dcAWS) << "Set device name in cloud with status:" << jsonDoc.toVariant().toMap().value("status").toInt(); - if (jsonDoc.toVariant().toMap().value("status").toInt() == 200) { - storeSyncedNameCache(m_clientName); - } - } else if (topic.startsWith(QString("%1/").arg(m_clientId)) && topic.contains("proxy")) { - QVariantMap payload = jsonDoc.toVariant().toMap(); - QString token = payload.value("token").toString(); - QString nonce; - // Backwards compatibility. Old protocol spec had "timestamp", keeping it for a while for backwards compatiblity - if (payload.contains("nonce")) { - nonce = payload.value("nonce").toString(); - } else { - nonce = payload.value("timestamp").toString(); - } - QString serverUrl = payload.value("serverUrl").toString(); - static QHash dupes; - QString packetId = topic + token + nonce; - if (dupes.contains(packetId)) { - qCDebug(dcAWS()) << "Dropping duplicate packet"; - return; - } - dupes.insert(packetId, QDateTime::currentDateTime()); - foreach (const QString &dupe, dupes.keys()) { - if (dupes.value(dupe).addSecs(60) < QDateTime::currentDateTime()) { - dupes.remove(dupe); - } - } - qCDebug(dcAWS) << "Proxy remote connection request received"; - proxyConnectionRequestReceived(token, nonce, serverUrl); - } else { - qCWarning(dcAWS()) << "Unhandled subscription received!" << topic << payload; - } -} - -void AWSConnector::storeRegisteredFlag(bool registered) -{ - QSettings settings(NymeaSettings::storagePath() + "/cloudstatus.conf", QSettings::IniFormat); - settings.setValue("registered", registered); -} - -bool AWSConnector::readRegisteredFlag() const -{ - QSettings settings(NymeaSettings::storagePath() + "/cloudstatus.conf", QSettings::IniFormat); - return settings.value("registered", false).toBool(); -} - -void AWSConnector::storeSyncedNameCache(const QString &syncedName) -{ - QSettings settings(NymeaSettings::storagePath() + "/cloudstatus.conf", QSettings::IniFormat); - settings.setValue("syncedName", syncedName); -} - -QString AWSConnector::readSyncedNameCache() -{ - QSettings settings(NymeaSettings::storagePath() + "/cloudstatus.conf", QSettings::IniFormat); - return settings.value("syncedName", QString()).toString(); -} - -QString AWSConnector::getCertificateFingerprint(const QSslCertificate &certificate) const -{ - QByteArray output; - QByteArray digest = certificate.digest(QCryptographicHash::Sha256); - for (int i = 0; i < digest.length(); i++) { - if (output.length() > 0) { - output.append(":"); - } - output.append(digest.mid(i,1).toHex().toUpper()); - } - return output; -} diff --git a/libnymea-core/cloud/awsconnector.h b/libnymea-core/cloud/awsconnector.h deleted file mode 100644 index d7bbf77d..00000000 --- a/libnymea-core/cloud/awsconnector.h +++ /dev/null @@ -1,117 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* Copyright 2013 - 2020, nymea GmbH -* Contact: contact@nymea.io -* -* This file is part of nymea. -* This project including source code and documentation is protected by -* copyright law, and remains the property of nymea GmbH. All rights, including -* reproduction, publication, editing and translation, are reserved. The use of -* this project is subject to the terms of a license agreement to be concluded -* with nymea GmbH in accordance with the terms of use of nymea GmbH, available -* under https://nymea.io/license -* -* GNU General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the -* terms of the GNU General Public License as published by the Free Software -* Foundation, GNU version 3. This project 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 -* this project. If not, see . -* -* For any further details and any questions please contact us under -* contact@nymea.io or see our FAQ/Licensing Information on -* https://nymea.io/license/faq -* -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#ifndef AWSCONNECTOR_H -#define AWSCONNECTOR_H - -#include -#include -#include -#include - -#include - -class AWSConnector : public QObject -{ - Q_OBJECT -public: - explicit AWSConnector(QObject *parent = nullptr); - ~AWSConnector(); - - void connect2AWS(const QString &endpoint, const QString &clientId, const QString &clientName, const QString &caFile, const QString &clientCertFile, const QString &clientPrivKeyFile); - void disconnectAWS(); - bool isConnected() const; - - void setDeviceName(const QString &deviceName); - void pairDevice(const QString &idToken, const QString &userId); - -signals: - void connected(); - void disconnected(); - void devicePaired(const QString &cognritoUserId, int errorCode, const QString &message); - - void proxyConnectionRequestReceived(const QString &token, const QString &nonce, const QString &serverUrl); - -private slots: - void doConnect(); - void onConnected(); - void onDisconnected(); - void onPublished(quint16 msgid, const QString &topic); - void onSubscribed(const QString &topic, Mqtt::SubscribeReturnCode returnCode); - void onPublishReceived(const QString &topic, const QByteArray &payload); - - void registerDevice(); - void onDeviceRegistered(bool needsReconnect); - void setupSubscriptions(); - void fetchPairings(); - void onPairingsRetrieved(const QVariantMap &pairings); - void setName(); - - -private: - quint16 publish(const QString &topic, const QVariantMap &message); - void subscribe(const QStringList &topics); - - void storeRegisteredFlag(bool registered); - bool readRegisteredFlag() const; - - void storeSyncedNameCache(const QString &syncedName); - QString readSyncedNameCache(); - - QString getCertificateFingerprint(const QSslCertificate &certificate) const; - -private: - MqttClient *m_client = nullptr; - QString m_currentEndpoint; - QString m_caFile; - QString m_clientCertFile; - QString m_clientPrivKeyFile; - - QString m_clientId; - QString m_clientName; - QFuture m_connectingFuture; - bool m_isCleanSession = true; - bool m_shouldReconnect = false; - QTimer m_reconnectTimer; - QTimer m_connectTimer; - - quint8 m_transactionId = 0; - QString m_createDeviceId; - int m_createDeviceSubscriptionId = 0; - QHash m_pairingRequests; - bool m_setupInProgress = false; - int m_reconnectCounter = 0; - QDateTime m_lastConnectionDrop; - QStringList m_subscriptionCache; - QPair m_cachedTURNCredentials; - -}; - -#endif // AWSCONNECTOR_H diff --git a/libnymea-core/cloud/cloudmanager.cpp b/libnymea-core/cloud/cloudmanager.cpp deleted file mode 100644 index 85c290d0..00000000 --- a/libnymea-core/cloud/cloudmanager.cpp +++ /dev/null @@ -1,282 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* Copyright 2013 - 2020, nymea GmbH -* Contact: contact@nymea.io -* -* This file is part of nymea. -* This project including source code and documentation is protected by -* copyright law, and remains the property of nymea GmbH. All rights, including -* reproduction, publication, editing and translation, are reserved. The use of -* this project is subject to the terms of a license agreement to be concluded -* with nymea GmbH in accordance with the terms of use of nymea GmbH, available -* under https://nymea.io/license -* -* GNU General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the -* terms of the GNU General Public License as published by the Free Software -* Foundation, GNU version 3. This project 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 -* this project. If not, see . -* -* For any further details and any questions please contact us under -* contact@nymea.io or see our FAQ/Licensing Information on -* https://nymea.io/license/faq -* -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#include "cloudmanager.h" -#include "awsconnector.h" -#include "loggingcategories.h" -#include "cloudnotifications.h" -#include "nymeaconfiguration.h" -#include "cloudtransport.h" -#include "nymeaconfiguration.h" -#include "nymeasettings.h" - -#include - -#include -#include - -using namespace remoteproxyclient; - -namespace nymeaserver { - -CloudManager::CloudManager(NymeaConfiguration *configuration, NetworkManager *networkManager, QObject *parent): - QObject(parent), - m_configuration(configuration), - m_networkManager(networkManager) -{ - m_awsConnector = new AWSConnector(this); - connect(m_awsConnector, &AWSConnector::devicePaired, this, &CloudManager::onPairingFinished); - connect(m_awsConnector, &AWSConnector::connected, this, &CloudManager::awsConnected); - connect(m_awsConnector, &AWSConnector::disconnected, this, &CloudManager::awsDisconnected); - - connect(m_networkManager, &NetworkManager::stateChanged, this, &CloudManager::onlineStateChanged); - - ServerConfiguration config; - config.id = "remote"; - config.authenticationEnabled = false; - config.sslEnabled = true; - m_transport = new CloudTransport(config, this); - connect(m_awsConnector, &AWSConnector::proxyConnectionRequestReceived, m_transport, &CloudTransport::connectToCloud); - - m_deviceId = m_configuration->serverUuid(); - m_deviceName = m_configuration->serverName(); - m_serverUrl = m_configuration->cloudServerUrl(); - m_caCertificate = m_configuration->cloudCertificateCA(); - m_clientCertificate = m_configuration->cloudCertificate(); - m_clientCertificateKey = m_configuration->cloudCertificateKey(); - - setEnabled(m_configuration->cloudEnabled()); - connect(m_configuration, &NymeaConfiguration::cloudEnabledChanged, this, &CloudManager::setEnabled); - connect(m_configuration, &NymeaConfiguration::serverNameChanged, this, &CloudManager::setDeviceName); - -} - -CloudManager::~CloudManager() -{ -} - -void CloudManager::setDeviceName(const QString &name) -{ - m_deviceName = name; - m_awsConnector->setDeviceName(name); -} - -bool CloudManager::enabled() const -{ - return m_enabled; -} - -void CloudManager::setEnabled(bool enabled) -{ - if (enabled) { - m_enabled = true; - emit connectionStateChanged(); - - bool missingConfig = false; - if (m_deviceId.isNull()) { - qCWarning(dcCloud()) << "Don't have a unique device ID."; - missingConfig = true; - } - if (m_deviceName.isEmpty()) { - qCWarning(dcCloud()) << "Don't have a device name set"; - missingConfig = true; - } - if (m_serverUrl.isEmpty()) { - qCWarning(dcCloud()) << "Cloud server URL not set."; - missingConfig = true; - } - if (m_clientCertificate.isEmpty()) { - qCWarning(dcCloud()) << "Cloud certificate not set."; - missingConfig = true; - } - if (!QFile::exists(m_clientCertificate)) { - qCWarning(dcCloud()) << "Cloud certificate file not existing."; - missingConfig = true; - } - if (m_clientCertificateKey.isEmpty()) { - qCWarning(dcCloud()) << "Cloud certificate key not set."; - missingConfig = true; - } - if (!QFile::exists(m_clientCertificateKey)) { - qCWarning(dcCloud()) << "Cloud certificate key file not existing."; - missingConfig = true; - } - if (m_caCertificate.isEmpty()) { - qCWarning(dcCloud()) << "Cloud certificate CA not set."; - missingConfig = true; - } - if (!QFile::exists(m_caCertificate)) { - qCWarning(dcCloud()) << "Cloud CA certificate file not existing."; - missingConfig = true; - } - if (missingConfig) { - qCWarning(dcCloud()) << "Cloud configuration incomplete. Not enabling cloud connection."; - return; - } - - qCDebug(dcCloud()) << "Cloud connection is now enabled. Trying to connect..."; - // FIXME: Ideally we'd check if the network is connected, however, on some platforms we have no reliable - // way to know that yet. Let's always try to connect for now. - connect2aws(); - } else { - m_enabled = false; - m_awsConnector->disconnectAWS(); - emit connectionStateChanged(); - qCDebug(dcCloud()) << "Cloud connection is now disabled."; - } -} - -bool CloudManager::installClientCertificates(const QByteArray &rootCA, const QByteArray &certificatePEM, const QByteArray &publicKey, const QByteArray &privateKey, const QString &endpoint) -{ - QString baseDir = NymeaSettings::storagePath() + "/certs/cloud/"; - QDir dir; - // We never delete old certs, cycle until we find an unused path - int i = 0; - do { - dir = QDir(baseDir + QString::number(i++) + '/'); - } while (dir.exists()); - - if (!dir.mkpath(dir.absolutePath())) { - qCWarning(dcCloud) << "Cannot install cloud certificates. Unable to create path."; - return false; - } - QFile ca(dir.absoluteFilePath("aws-certification-authority.crt")); - if (!ca.open(QFile::WriteOnly) || ca.write(rootCA) != rootCA.length()) { - qCWarning(dcCloud()) << "Cannot install cloud certificates. Unable to write CA file" << dir.absoluteFilePath(ca.fileName()); - ca.close(); - return false; - } - ca.close(); - QFile pem(dir.absoluteFilePath("guh-cloud.pem")); - if (!pem.open(QFile::WriteOnly) || pem.write(certificatePEM) != certificatePEM.length()) { - qCWarning(dcCloud()) << "Cannot install cloud certificates. Unable to write certificate file" << dir.absoluteFilePath(pem.fileName()); - pem.close(); - return false; - } - pem.close(); - QFile pub(dir.absoluteFilePath("guh-cloud.pub")); - if (!pub.open(QFile::WriteOnly) || pub.write(publicKey) != publicKey.length()) { - qCWarning(dcCloud()) << "Cannot install cloud certificates. Unable to write public key file" << dir.absoluteFilePath(pub.fileName()); - pub.close(); - return false; - } - pub.close(); - QFile key(dir.absoluteFilePath("guh-cloud.key")); - if (!key.open(QFile::WriteOnly) || key.write(privateKey) != privateKey.length()) { - qCWarning(dcCloud()) << "Cannot install cloud certificates. Unable to write private key file" << dir.absoluteFilePath(key.fileName()); - key.close(); - return false; - } - key.close(); - qCDebug(dcCloud) << "Installed cloud certificates to" << dir.absolutePath(); - m_caCertificate = dir.absoluteFilePath("aws-certification-authority.crt"); - m_clientCertificate = dir.absoluteFilePath("guh-cloud.pem"); - m_clientCertificateKey = dir.absoluteFilePath("guh-cloud.key"); - m_serverUrl = endpoint; - - if (m_enabled) { - m_awsConnector->disconnectAWS(); - connect2aws(); - } - m_configuration->setCloudCertificateCA(m_caCertificate); - m_configuration->setCloudCertificate(m_clientCertificate); - m_configuration->setCloudCertificateKey(m_clientCertificateKey); - m_configuration->setCloudServerUrl(m_serverUrl); - - emit connectionStateChanged(); - return true; -} - -CloudManager::CloudConnectionState CloudManager::connectionState() const -{ - if (m_awsConnector->isConnected()) { - return CloudConnectionStateConnected; - } - if (!m_enabled) { - return CloudConnectionStateDisabled; - } - if (m_deviceId.isNull() || m_deviceName.isEmpty() || m_serverUrl.isEmpty() || m_clientCertificate.isEmpty() || m_clientCertificateKey.isEmpty() || m_caCertificate.isEmpty()) { - return CloudConnectionStateUnconfigured; - } - return CloudConnectionStateConnecting; -} - -void CloudManager::pairDevice(const QString &idToken, const QString &userId) -{ - m_awsConnector->pairDevice(idToken, userId); -} - -CloudNotifications *CloudManager::createNotificationsPlugin() const -{ - CloudNotifications* notifications = new CloudNotifications(m_awsConnector); - return notifications; -} - -CloudTransport *CloudManager::createTransportInterface() const -{ - return m_transport; -} - -void CloudManager::connect2aws() -{ - m_awsConnector->connect2AWS(m_serverUrl, - m_deviceId.toString().remove(QRegExp("[{}]*")), - m_deviceName, - m_caCertificate, - m_clientCertificate, - m_clientCertificateKey - ); -} - -void CloudManager::onlineStateChanged() -{ - if (m_networkManager->state() == NetworkManager::NetworkManagerStateConnectedGlobal) { - if (m_enabled && !m_awsConnector->isConnected()) { - connect2aws(); - } - } -} - -void CloudManager::onPairingFinished(const QString &cognitoUserId, int errorCode, const QString &message) -{ - emit pairingReply(cognitoUserId, errorCode, message); -} - -void CloudManager::awsConnected() -{ - emit connectionStateChanged(); -} - -void CloudManager::awsDisconnected() -{ - emit connectionStateChanged(); -} - -} diff --git a/libnymea-core/cloud/cloudmanager.h b/libnymea-core/cloud/cloudmanager.h deleted file mode 100644 index 7dc2a025..00000000 --- a/libnymea-core/cloud/cloudmanager.h +++ /dev/null @@ -1,110 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* Copyright 2013 - 2020, nymea GmbH -* Contact: contact@nymea.io -* -* This file is part of nymea. -* This project including source code and documentation is protected by -* copyright law, and remains the property of nymea GmbH. All rights, including -* reproduction, publication, editing and translation, are reserved. The use of -* this project is subject to the terms of a license agreement to be concluded -* with nymea GmbH in accordance with the terms of use of nymea GmbH, available -* under https://nymea.io/license -* -* GNU General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the -* terms of the GNU General Public License as published by the Free Software -* Foundation, GNU version 3. This project 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 -* this project. If not, see . -* -* For any further details and any questions please contact us under -* contact@nymea.io or see our FAQ/Licensing Information on -* https://nymea.io/license/faq -* -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#ifndef CLOUDMANAGER_H -#define CLOUDMANAGER_H - -#include -#include -#include -#include - -class NetworkManager; - -class AWSConnector; -class CloudNotifications; -namespace remoteproxyclient { -class RemoteProxyConnection; -} - -namespace nymeaserver { - -class NymeaConfiguration; -class CloudTransport; -class CloudManager : public QObject -{ - Q_OBJECT -public: - enum CloudConnectionState { - CloudConnectionStateDisabled, - CloudConnectionStateUnconfigured, - CloudConnectionStateConnecting, - CloudConnectionStateConnected - }; - Q_ENUM(CloudConnectionState) - - explicit CloudManager(NymeaConfiguration *configuration, NetworkManager *networkManager, QObject *parent = nullptr); - ~CloudManager(); - - bool enabled() const; - void setEnabled(bool enabled); - - bool installClientCertificates(const QByteArray &rootCA, const QByteArray &certificatePEM, const QByteArray &publicKey, const QByteArray &privateKey, const QString &endpoint); - - CloudConnectionState connectionState() const; - - void pairDevice(const QString &idToken, const QString &userId); - - CloudNotifications* createNotificationsPlugin() const; - CloudTransport* createTransportInterface() const; - -signals: - void connectionStateChanged(); - - void pairingReply(QString cognitoUserId, int status, const QString &message); - -private: - void connect2aws(); - -private slots: - void onlineStateChanged(); - void onPairingFinished(const QString &cognitoUserId, int errorCode, const QString &message); - void awsConnected(); - void awsDisconnected(); - void setDeviceName(const QString &name); - -private: - QTimer m_reconnectTimer; - bool m_enabled = false; - AWSConnector *m_awsConnector = nullptr; - NymeaConfiguration *m_configuration = nullptr; - NetworkManager *m_networkManager = nullptr; - CloudTransport *m_transport = nullptr; - - QString m_serverUrl; - QUuid m_deviceId; - QString m_deviceName; - QString m_caCertificate; - QString m_clientCertificate; - QString m_clientCertificateKey; -}; - -} -#endif // CLOUDMANAGER_H diff --git a/libnymea-core/cloud/cloudnotifications.cpp b/libnymea-core/cloud/cloudnotifications.cpp deleted file mode 100644 index 97f7b8e6..00000000 --- a/libnymea-core/cloud/cloudnotifications.cpp +++ /dev/null @@ -1,155 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* Copyright 2013 - 2020, nymea GmbH -* Contact: contact@nymea.io -* -* This file is part of nymea. -* This project including source code and documentation is protected by -* copyright law, and remains the property of nymea GmbH. All rights, including -* reproduction, publication, editing and translation, are reserved. The use of -* this project is subject to the terms of a license agreement to be concluded -* with nymea GmbH in accordance with the terms of use of nymea GmbH, available -* under https://nymea.io/license -* -* GNU General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the -* terms of the GNU General Public License as published by the Free Software -* Foundation, GNU version 3. This project 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 -* this project. If not, see . -* -* For any further details and any questions please contact us under -* contact@nymea.io or see our FAQ/Licensing Information on -* https://nymea.io/license/faq -* -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#include "cloudnotifications.h" -#include "loggingcategories.h" -#include "integrations/thingsetupinfo.h" -#include "integrations/thingactioninfo.h" - -#include -#include - -ThingClassId cloudNotificationsThingClassId = ThingClassId("81c1bbcc-543a-48fd-bd18-ab6a76f9c38d"); -ParamTypeId cloudNotificationsThingClassUserParamId = ParamTypeId("5bdeaf08-91a9-42bc-a9f9-ef6b02ecaa3c"); -ParamTypeId cloudNotificationsThingClassEndpointParamId = ParamTypeId("e7c41785-dd3b-4f46-b5b4-1f8a7d060ddd"); - -ActionTypeId notifyActionTypeId = ActionTypeId("211d1f25-28e7-4eba-8938-b29de0e41571"); -ParamTypeId notifyActionParamTitleId = ParamTypeId("096503fc-b343-4d7f-8387-96162faf0f8e"); -ParamTypeId notifyActionParamBodyId = ParamTypeId("4bd0fa87-c663-4621-8040-99b6d535387c"); - -StateTypeId connectedStateTypeId = StateTypeId("518e27b6-c3bf-49d7-be24-05ae978c00f7"); - -CloudNotifications::CloudNotifications(QObject *parent): - IntegrationPlugin(parent) -{ - - // Metadata is just kept for now to not cause any dead things in the system. To be removed in 0.31 or so - QVariantMap pluginMetaData; - pluginMetaData.insert("id", "ccc6dbc8-e352-48a1-8e87-3c89a4669fc2"); - pluginMetaData.insert("name", "CloudNotifications"); - pluginMetaData.insert("displayName", tr("Cloud Notifications")); - - QVariantList interfaces; - interfaces.append("notifications"); - interfaces.append("connectable"); - - QVariantList createMethods; - createMethods.append("auto"); - - QVariantMap userIdParam; - userIdParam.insert("id", cloudNotificationsThingClassUserParamId); - userIdParam.insert("name", "userId"); - userIdParam.insert("displayName", tr("User ID")); - userIdParam.insert("type", "QString"); - - QVariantMap endpointIdParam; - endpointIdParam.insert("id", cloudNotificationsThingClassEndpointParamId); - endpointIdParam.insert("name", "endpoint"); - endpointIdParam.insert("displayName", tr("Device")); - endpointIdParam.insert("type", "QString"); - - QVariantList cloudNotificationThingClassParamTypes; - cloudNotificationThingClassParamTypes.append(userIdParam); - cloudNotificationThingClassParamTypes.append(endpointIdParam); - - QVariantMap notifyActionParamTitle; - notifyActionParamTitle.insert("id", notifyActionParamTitleId); - notifyActionParamTitle.insert("name", "title"); - notifyActionParamTitle.insert("displayName", tr("Title")); - notifyActionParamTitle.insert("type", "QString"); - - QVariantMap notifyActionParamBody; - notifyActionParamBody.insert("id", notifyActionParamBodyId); - notifyActionParamBody.insert("name", "body"); - notifyActionParamBody.insert("displayName", tr("Message text")); - notifyActionParamBody.insert("type", "QString"); - - QVariantList notifyActionParamTypes; - notifyActionParamTypes.append(notifyActionParamTitle); - notifyActionParamTypes.append(notifyActionParamBody); - - QVariantMap notifyAction; - notifyAction.insert("id", notifyActionTypeId); - notifyAction.insert("name", "notify"); - notifyAction.insert("displayName", tr("Send notification")); - notifyAction.insert("paramTypes", notifyActionParamTypes); - - QVariantList actionTypes; - actionTypes.append(notifyAction); - - QVariantMap connectedState; - connectedState.insert("id", connectedStateTypeId); - connectedState.insert("name", "connected"); - connectedState.insert("displayName", tr("connected")); - connectedState.insert("type", "bool"); - connectedState.insert("defaultValue", false); - - QVariantList stateTypes; - stateTypes.append(connectedState); - - - QVariantMap cloudNotificationsThingClass; - cloudNotificationsThingClass.insert("id", cloudNotificationsThingClassId); - cloudNotificationsThingClass.insert("name", "cloudNotifications"); - cloudNotificationsThingClass.insert("displayName", tr("Cloud Notifications")); - cloudNotificationsThingClass.insert("createMethods", createMethods); - cloudNotificationsThingClass.insert("paramTypes", cloudNotificationThingClassParamTypes); - cloudNotificationsThingClass.insert("interfaces", interfaces); - cloudNotificationsThingClass.insert("actionTypes", actionTypes); - cloudNotificationsThingClass.insert("stateTypes", stateTypes); - - QVariantList thingClasses; - thingClasses.append(cloudNotificationsThingClass); - - QVariantMap guhVendor; - guhVendor.insert("id", "2062d64d-3232-433c-88bc-0d33c0ba2ba6"); // nymea's id - guhVendor.insert("name", "nymea"); - guhVendor.insert("displayName", "nymea"); - guhVendor.insert("thingClasses", thingClasses); - - QVariantList vendors; - vendors.append(guhVendor); - pluginMetaData.insert("vendors", vendors); - - setMetaData(PluginMetadata(QJsonObject::fromVariantMap(pluginMetaData), true)); - -} - -void CloudNotifications::setupThing(ThingSetupInfo *info) -{ - // Just finishing the setup for any old existing things to not throw any errors - info->finish(Thing::ThingErrorNoError); -} - -void CloudNotifications::postSetupThing(Thing *thing) -{ - // And make it go away... - emit autoThingDisappeared(thing->id()); -} diff --git a/libnymea-core/cloud/cloudnotifications.h b/libnymea-core/cloud/cloudnotifications.h deleted file mode 100644 index 19e45b67..00000000 --- a/libnymea-core/cloud/cloudnotifications.h +++ /dev/null @@ -1,51 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* Copyright 2013 - 2020, nymea GmbH -* Contact: contact@nymea.io -* -* This file is part of nymea. -* This project including source code and documentation is protected by -* copyright law, and remains the property of nymea GmbH. All rights, including -* reproduction, publication, editing and translation, are reserved. The use of -* this project is subject to the terms of a license agreement to be concluded -* with nymea GmbH in accordance with the terms of use of nymea GmbH, available -* under https://nymea.io/license -* -* GNU General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the -* terms of the GNU General Public License as published by the Free Software -* Foundation, GNU version 3. This project 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 -* this project. If not, see . -* -* For any further details and any questions please contact us under -* contact@nymea.io or see our FAQ/Licensing Information on -* https://nymea.io/license/faq -* -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#ifndef CLOUDNOTIFICATIONS_H -#define CLOUDNOTIFICATIONS_H - -#include "integrations/integrationplugin.h" - -class CloudNotifications : public IntegrationPlugin -{ - Q_OBJECT - - Q_INTERFACES(IntegrationPlugin) - -public: - CloudNotifications(QObject* parent = nullptr); - - PluginMetadata metaData() const; - - void setupThing(ThingSetupInfo *info) override; - void postSetupThing(Thing *thing) override; -}; - -#endif // CLOUDNOTIFICATIONS_H diff --git a/libnymea-core/cloud/cloudtransport.cpp b/libnymea-core/cloud/cloudtransport.cpp deleted file mode 100644 index b7557ec1..00000000 --- a/libnymea-core/cloud/cloudtransport.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* Copyright 2013 - 2020, nymea GmbH -* Contact: contact@nymea.io -* -* This file is part of nymea. -* This project including source code and documentation is protected by -* copyright law, and remains the property of nymea GmbH. All rights, including -* reproduction, publication, editing and translation, are reserved. The use of -* this project is subject to the terms of a license agreement to be concluded -* with nymea GmbH in accordance with the terms of use of nymea GmbH, available -* under https://nymea.io/license -* -* GNU General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the -* terms of the GNU General Public License as published by the Free Software -* Foundation, GNU version 3. This project 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 -* this project. If not, see . -* -* For any further details and any questions please contact us under -* contact@nymea.io or see our FAQ/Licensing Information on -* https://nymea.io/license/faq -* -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#include "cloudtransport.h" -#include "loggingcategories.h" - -#include "nymeacore.h" - -using namespace remoteproxyclient; - -namespace nymeaserver { - -CloudTransport::CloudTransport(const ServerConfiguration &config, QObject *parent): - TransportInterface(config, parent) -{ - m_defaultProxyUrl = "ssl://remoteproxy.nymea.io:4433"; -} - -void CloudTransport::sendData(const QUuid &clientId, const QByteArray &data) -{ - qCDebug(dcCloudTraffic()) << "Sending data" << clientId << data; - foreach (const ConnectionContext &ctx, m_connections) { - if (ctx.clientId == clientId) { - ctx.proxyConnection->sendData(data + '\n'); - return; - } - } - qCWarning(dcCloud()) << "Error sending data. No such clientId"; -} - -void CloudTransport::sendData(const QList &clientIds, const QByteArray &data) -{ - foreach (const QUuid &clientId, clientIds) { - sendData(clientId, data); - } -} - -void CloudTransport::terminateClientConnection(const QUuid &clientId) -{ - foreach (const ConnectionContext &ctx, m_connections) { - if (ctx.clientId == clientId) { - ctx.proxyConnection->disconnectServer(); - return; - } - } -} - -bool CloudTransport::startServer() -{ - qCDebug(dcCloud()) << "Started cloud transport"; - return true; -} - -bool CloudTransport::stopServer() -{ - qCDebug(dcCloud()) << "Stopped cloud transport"; - return true; -} - -void CloudTransport::connectToCloud(const QString &token, const QString &nonce, const QString &serverUrl) -{ - QString proxyUrl = serverUrl.isEmpty() ? m_defaultProxyUrl : serverUrl; - qCDebug(dcCloud()) << "Connecting to remote proxy server" << proxyUrl; - - RemoteProxyConnection::ConnectionType connectionType = RemoteProxyConnection::ConnectionTypeTcpSocket; - QUrl url(proxyUrl); - if (url.scheme() == "wss" || url.scheme() == "ws") { - connectionType = RemoteProxyConnection::ConnectionTypeWebSocket; - } - - ConnectionContext context; - context.clientId = QUuid::createUuid(); - context.token = token; - context.nonce = nonce; - QString identifier = QString("nymea:core (%1)").arg(NymeaCore::instance()->configuration()->serverName()); - context.proxyConnection = new RemoteProxyConnection(NymeaCore::instance()->configuration()->serverUuid().toString(), identifier, connectionType, this); - m_connections.insert(context.proxyConnection, context); - - connect(context.proxyConnection, &RemoteProxyConnection::ready, this, &CloudTransport::transportReady); - connect(context.proxyConnection, &RemoteProxyConnection::stateChanged, this, &CloudTransport::remoteConnectionStateChanged); - connect(context.proxyConnection, &RemoteProxyConnection::dataReady, this, &CloudTransport::transportDataReady); - connect(context.proxyConnection, &RemoteProxyConnection::remoteConnectionEstablished, this, &CloudTransport::transportConnected); - connect(context.proxyConnection, &RemoteProxyConnection::disconnected, this, &CloudTransport::transportDisconnected); - - context.proxyConnection->connectServer(url); -} - -void CloudTransport::remoteConnectionStateChanged(RemoteProxyConnection::State state) -{ - qCDebug(dcCloudTraffic()) << "Remote connection state changed" << state; -} - -void CloudTransport::transportConnected() -{ - RemoteProxyConnection *proxyConnection = qobject_cast(sender()); - ConnectionContext context = m_connections.value(proxyConnection); - - qCDebug(dcCloud()) << "The remote client connected successfully" << proxyConnection->tunnelPartnerName() << proxyConnection->tunnelPartnerUuid(); - emit clientConnected(context.clientId); -} - -void CloudTransport::transportDisconnected() -{ - RemoteProxyConnection *proxyConnection = qobject_cast(sender()); - ConnectionContext context = m_connections.take(proxyConnection); - proxyConnection->deleteLater(); - - qCDebug(dcCloud()) << "The remote connection disconnected." << context.clientId; - emit clientDisconnected(context.clientId); -} - -void CloudTransport::transportReady() -{ - RemoteProxyConnection *proxyConnection = static_cast(sender()); - qCDebug(dcCloud()) << "Connected successfully to remote proxy server" << proxyConnection->proxyServerName() << - proxyConnection->proxyServerVersion() << "API version:" << proxyConnection->proxyServerApiVersion(); - - ConnectionContext context = m_connections.value(proxyConnection); - context.proxyConnection->authenticate(context.token, context.nonce); -} - -void CloudTransport::transportDataReady(const QByteArray &data) -{ - RemoteProxyConnection *proxyConnection = qobject_cast(sender()); - ConnectionContext context = m_connections.value(proxyConnection); - qCDebug(dcCloudTraffic()) << "Data received:" << context.clientId.toString() << data; - emit dataAvailable(context.clientId, data); -} - -} diff --git a/libnymea-core/cloud/cloudtransport.h b/libnymea-core/cloud/cloudtransport.h deleted file mode 100644 index a33fb4c1..00000000 --- a/libnymea-core/cloud/cloudtransport.h +++ /dev/null @@ -1,82 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* Copyright 2013 - 2020, nymea GmbH -* Contact: contact@nymea.io -* -* This file is part of nymea. -* This project including source code and documentation is protected by -* copyright law, and remains the property of nymea GmbH. All rights, including -* reproduction, publication, editing and translation, are reserved. The use of -* this project is subject to the terms of a license agreement to be concluded -* with nymea GmbH in accordance with the terms of use of nymea GmbH, available -* under https://nymea.io/license -* -* GNU General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the -* terms of the GNU General Public License as published by the Free Software -* Foundation, GNU version 3. This project 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 -* this project. If not, see . -* -* For any further details and any questions please contact us under -* contact@nymea.io or see our FAQ/Licensing Information on -* https://nymea.io/license/faq -* -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#ifndef CLOUDTRANSPORT_H -#define CLOUDTRANSPORT_H - -#include -#include "../transportinterface.h" -#include - -namespace nymeaserver { - -class CloudTransport : public TransportInterface -{ - Q_OBJECT -public: - explicit CloudTransport(const ServerConfiguration &config, QObject *parent = nullptr); - - void sendData(const QUuid &clientId, const QByteArray &data) override; - void sendData(const QList &clientIds, const QByteArray &data) override; - - void terminateClientConnection(const QUuid &clientId) override; - - bool startServer() override; - bool stopServer() override; - -signals: - -public slots: - void connectToCloud(const QString &token, const QString &nonce, const QString &serverUrl); - -private slots: - void remoteConnectionStateChanged(remoteproxyclient::RemoteProxyConnection::State state); - void transportConnected(); - void transportReady(); - void transportDataReady(const QByteArray &data); - void transportDisconnected(); - -private: - QString m_defaultProxyUrl; - - class ConnectionContext { - public: - QUuid clientId; - QString token; - QString nonce; - remoteproxyclient::RemoteProxyConnection* proxyConnection; - }; - QHash m_connections; - -}; - -} - -#endif // CLOUDTRANSPORT_H diff --git a/libnymea-core/jsonrpc/jsonrpcserverimplementation.cpp b/libnymea-core/jsonrpc/jsonrpcserverimplementation.cpp index e051c781..2517fa77 100644 --- a/libnymea-core/jsonrpc/jsonrpcserverimplementation.cpp +++ b/libnymea-core/jsonrpc/jsonrpcserverimplementation.cpp @@ -58,7 +58,6 @@ #include "loggingcategories.h" #include "platform/platform.h" #include "version.h" -#include "cloud/cloudmanager.h" #include "integrationshandler.h" #include "ruleshandler.h" @@ -91,7 +90,6 @@ JsonRPCServerImplementation::JsonRPCServerImplementation(const QSslConfiguration // Enums registerEnum(); registerEnum(); - registerEnum(); registerFlag(); // Objects @@ -215,30 +213,6 @@ JsonRPCServerImplementation::JsonRPCServerImplementation(const QSslConfiguration returns.insert("transactionId", enumValueName(Int)); registerMethod("RequestPushButtonAuth", description, params, returns, Types::PermissionScopeNone); - params.clear(); returns.clear(); - description = "Sets up the cloud connection by deploying a certificate and its configuration."; - params.insert("rootCA", enumValueName(String)); - params.insert("certificatePEM", enumValueName(String)); - params.insert("publicKey", enumValueName(String)); - params.insert("privateKey", enumValueName(String)); - params.insert("endpoint", enumValueName(String)); - returns.insert("success", enumValueName(Bool)); - registerMethod("SetupCloudConnection", description, params, returns); - - params.clear(); returns.clear(); - description = "Setup the remote connection by providing AWS token information. This requires the cloud to be connected."; - params.insert("idToken", enumValueName(String)); - params.insert("userId", enumValueName(String)); - returns.insert("status", enumValueName(Int)); - returns.insert("message", enumValueName(String)); - registerMethod("SetupRemoteAccess", description, params, returns); - - params.clear(); returns.clear(); - description = "Check whether the cloud is currently connected. \"connected\" will be true whenever connectionState equals CloudConnectionStateConnected and is deprecated. Please use the connectionState value instead."; - returns.insert("d:connected", enumValueName(Bool)); - returns.insert("connectionState", enumRef()); - registerMethod("IsCloudConnected", description, params, returns); - params.clear(); returns.clear(); description = "This is basically a Ping/Pong mechanism a client app may use to check server connectivity. Currently, the server does not actually do anything with this information and will return the call providing the given sessionId back to the caller. It is up to the client whether to use this or not and not required by the server to keep the connection alive."; params.insert("sessionId", enumValueName(String)); @@ -247,12 +221,6 @@ JsonRPCServerImplementation::JsonRPCServerImplementation(const QSslConfiguration registerMethod("KeepAlive", description, params, returns, Types::PermissionScopeNone); // Notifications - params.clear(); returns.clear(); - description = "Emitted whenever the cloud connection status changes."; - params.insert("connected", enumValueName(Bool)); - params.insert("connectionState", enumRef()); - registerNotification("CloudConnectedChanged", description, params); - params.clear(); description = "Emitted when a push button authentication reaches final state. NOTE: This notification is special. It will only be emitted to connections that did actively request a push button authentication, but also it will be emitted regardless of the notification settings. "; params.insert("success", enumValueName(Bool)); @@ -462,48 +430,6 @@ JsonReply *JsonRPCServerImplementation::RequestPushButtonAuth(const QVariantMap return createReply(data); } -JsonReply *JsonRPCServerImplementation::SetupCloudConnection(const QVariantMap ¶ms) -{ - if (NymeaCore::instance()->cloudManager()->connectionState() != CloudManager::CloudConnectionStateUnconfigured) { - qCDebug(dcCloud) << "Cloud already configured. Not changing configuration as it won't work anyways. If you want to reconfigure this instance to a different cloud, change the system UUID and wipe the cloud settings from the config."; - QVariantMap data; - data.insert("success", false); - return createReply(data); - } - QByteArray rootCA = params.value("rootCA").toByteArray(); - QByteArray certificatePEM = params.value("certificatePEM").toByteArray(); - QByteArray publicKey = params.value("publicKey").toByteArray(); - QByteArray privateKey = params.value("privateKey").toByteArray(); - QString endpoint = params.value("endpoint").toString(); - bool status = NymeaCore::instance()->cloudManager()->installClientCertificates(rootCA, certificatePEM, publicKey, privateKey, endpoint); - QVariantMap ret; - ret.insert("success", status); - return createReply(ret); -} - -JsonReply *JsonRPCServerImplementation::SetupRemoteAccess(const QVariantMap ¶ms) -{ - QString idToken = params.value("idToken").toString(); - QString userId = params.value("userId").toString(); - NymeaCore::instance()->cloudManager()->pairDevice(idToken, userId); - JsonReply *reply = createAsyncReply("SetupRemoteAccess"); - m_pairingRequests.insert(userId, reply); - connect(reply, &JsonReply::finished, [this, userId](){ - m_pairingRequests.remove(userId); - }); - return reply; -} - -JsonReply *JsonRPCServerImplementation::IsCloudConnected(const QVariantMap ¶ms) -{ - Q_UNUSED(params) - bool connected = NymeaCore::instance()->cloudManager()->connectionState() == CloudManager::CloudConnectionStateConnected; - QVariantMap data; - data.insert("connected", connected); - data.insert("connectionState", enumValueName(NymeaCore::instance()->cloudManager()->connectionState())); - return createReply(data); -} - /*! A client may use this as a ping/pong mechanism to check server connectivity. */ JsonReply *JsonRPCServerImplementation::KeepAlive(const QVariantMap ¶ms) { @@ -610,9 +536,6 @@ void JsonRPCServerImplementation::setup() registerHandler(new ZigbeeHandler(NymeaCore::instance()->zigbeeManager(), this)); registerHandler(new ZWaveHandler(NymeaCore::instance()->zwaveManager(), this)); registerHandler(new ModbusRtuHandler(NymeaCore::instance()->modbusRtuManager(), this)); - - connect(NymeaCore::instance()->cloudManager(), &CloudManager::pairingReply, this, &JsonRPCServerImplementation::pairingFinished); - connect(NymeaCore::instance()->cloudManager(), &CloudManager::connectionStateChanged, this, &JsonRPCServerImplementation::onCloudConnectionStateChanged); } void JsonRPCServerImplementation::processData(const QUuid &clientId, const QByteArray &data) @@ -897,27 +820,6 @@ void JsonRPCServerImplementation::asyncReplyFinished() reply->deleteLater(); } -void JsonRPCServerImplementation::pairingFinished(QString cognitoUserId, int status, const QString &message) -{ - JsonReply *reply = m_pairingRequests.take(cognitoUserId); - if (!reply) { - return; - } - QVariantMap returns; - returns.insert("status", status); - returns.insert("message", message); - reply->setData(returns); - reply->finished(); -} - -void JsonRPCServerImplementation::onCloudConnectionStateChanged() -{ - QVariantMap params; - params.insert("connected", NymeaCore::instance()->cloudManager()->connectionState() == CloudManager::CloudConnectionStateConnected); - params.insert("connectionState", enumValueName(NymeaCore::instance()->cloudManager()->connectionState())); - emit CloudConnectedChanged(params); -} - void JsonRPCServerImplementation::onPushButtonAuthFinished(int transactionId, bool success, const QByteArray &token) { QUuid clientId = m_pushButtonTransactions.take(transactionId); diff --git a/libnymea-core/jsonrpc/jsonrpcserverimplementation.h b/libnymea-core/jsonrpc/jsonrpcserverimplementation.h index f024c4bc..6d434bb9 100644 --- a/libnymea-core/jsonrpc/jsonrpcserverimplementation.h +++ b/libnymea-core/jsonrpc/jsonrpcserverimplementation.h @@ -65,9 +65,6 @@ public: Q_INVOKABLE JsonReply *CreateUser(const QVariantMap ¶ms); Q_INVOKABLE JsonReply *Authenticate(const QVariantMap ¶ms, const JsonContext &context); Q_INVOKABLE JsonReply *RequestPushButtonAuth(const QVariantMap ¶ms, const JsonContext &context); - Q_INVOKABLE JsonReply *SetupCloudConnection(const QVariantMap ¶ms); - Q_INVOKABLE JsonReply *SetupRemoteAccess(const QVariantMap ¶ms); - Q_INVOKABLE JsonReply *IsCloudConnected(const QVariantMap ¶ms); Q_INVOKABLE JsonReply *KeepAlive(const QVariantMap ¶ms); signals: @@ -104,8 +101,6 @@ private slots: void asyncReplyFinished(); - void pairingFinished(QString cognitoUserId, int status, const QString &message); - void onCloudConnectionStateChanged(); void onPushButtonAuthFinished(int transactionId, bool success, const QByteArray &token); private: diff --git a/libnymea-core/libnymea-core.pro b/libnymea-core/libnymea-core.pro index ab92a509..4d3a2e8a 100644 --- a/libnymea-core/libnymea-core.pro +++ b/libnymea-core/libnymea-core.pro @@ -131,9 +131,6 @@ HEADERS += nymeacore.h \ usermanager/tokeninfo.h \ usermanager/pushbuttondbusservice.h \ certificategenerator.h \ - cloud/awsconnector.h \ - cloud/cloudmanager.h \ - cloud/cloudnotifications.h \ hardwaremanagerimplementation.h \ hardware/plugintimermanagerimplementation.h \ hardware/radio433/radio433brennenstuhl.h \ @@ -161,7 +158,6 @@ HEADERS += nymeacore.h \ debugserverhandler.h \ tagging/tagsstorage.h \ tagging/tag.h \ - cloud/cloudtransport.h \ debugreportgenerator.h \ platform/platform.h \ zigbee/zigbeeadapter.h \ @@ -238,9 +234,6 @@ SOURCES += nymeacore.cpp \ usermanager/tokeninfo.cpp \ usermanager/pushbuttondbusservice.cpp \ certificategenerator.cpp \ - cloud/awsconnector.cpp \ - cloud/cloudmanager.cpp \ - cloud/cloudnotifications.cpp \ hardwaremanagerimplementation.cpp \ hardware/plugintimermanagerimplementation.cpp \ hardware/radio433/radio433brennenstuhl.cpp \ @@ -268,7 +261,6 @@ SOURCES += nymeacore.cpp \ debugserverhandler.cpp \ tagging/tagsstorage.cpp \ tagging/tag.cpp \ - cloud/cloudtransport.cpp \ debugreportgenerator.cpp \ platform/platform.cpp \ zigbee/zigbeeadapter.cpp \ diff --git a/libnymea-core/nymeacore.cpp b/libnymea-core/nymeacore.cpp index 8ca707f9..c1ff4619 100644 --- a/libnymea-core/nymeacore.cpp +++ b/libnymea-core/nymeacore.cpp @@ -47,9 +47,6 @@ #include "integrations/thingactioninfo.h" #include "integrations/browseractioninfo.h" #include "integrations/browseritemactioninfo.h" -#include "cloud/cloudmanager.h" -#include "cloud/cloudnotifications.h" -#include "cloud/cloudtransport.h" #include "zigbee/zigbeemanager.h" @@ -148,22 +145,10 @@ void NymeaCore::init(const QStringList &additionalInterfaces) { qCDebug(dcCore) << "Creating Debug Server Handler"; m_debugServerHandler = new DebugServerHandler(this); - qCDebug(dcCore) << "Creating Cloud Manager"; - m_cloudManager = new CloudManager(m_configuration, m_networkManager, this); - qCDebug(dcCore()) << "Loading experiences"; m_experienceManager = new ExperienceManager(m_thingManager, m_serverManager->jsonServer(), this); - // To be removed with 0.31 or later. - // Most of the cloud push notifications code has been removed with 0.30, this is just kept for a release or two - // to auto-remove all the cloud based push notification things users might have in their systems - CloudNotifications *cloudNotifications = m_cloudManager->createNotificationsPlugin(); - m_thingManager->registerStaticPlugin(cloudNotifications); - - CloudTransport *cloudTransport = m_cloudManager->createTransportInterface(); - m_serverManager->jsonServer()->registerTransportInterface(cloudTransport); - connect(m_configuration, &NymeaConfiguration::serverNameChanged, m_serverManager, &ServerManager::setServerName); connect(m_thingManager, &ThingManagerImplementation::pluginConfigChanged, this, &NymeaCore::pluginConfigChanged); @@ -210,8 +195,7 @@ NymeaCore::~NymeaCore() // Destroy resources used by things qCDebug(dcCore) << "Shutting down \"Server Manager\""; delete m_serverManager; - qCDebug(dcCore) << "Shutting down \"CloudManager\""; - delete m_cloudManager; + qCDebug(dcCore()) << "Shutting down \"Hardware Manager\""; delete m_hardwareManager; diff --git a/nymea.pro b/nymea.pro index 99042487..c7e29087 100644 --- a/nymea.pro +++ b/nymea.pro @@ -4,8 +4,8 @@ include(nymea.pri) NYMEA_VERSION_STRING=$$system('dpkg-parsechangelog | sed -n -e "s/^Version: //p"') # define protocol versions -JSON_PROTOCOL_VERSION_MAJOR=6 -JSON_PROTOCOL_VERSION_MINOR=2 +JSON_PROTOCOL_VERSION_MAJOR=7 +JSON_PROTOCOL_VERSION_MINOR=0 JSON_PROTOCOL_VERSION="$${JSON_PROTOCOL_VERSION_MAJOR}.$${JSON_PROTOCOL_VERSION_MINOR}" LIBNYMEA_API_VERSION_MAJOR=7 LIBNYMEA_API_VERSION_MINOR=3 diff --git a/tests/auto/api.json b/tests/auto/api.json index a7a57d85..8f0eabb8 100644 --- a/tests/auto/api.json +++ b/tests/auto/api.json @@ -1,4 +1,4 @@ -6.2 +7.0 { "enums": { "BasicType": [ @@ -26,12 +26,6 @@ "BrowserIconPackage", "BrowserIconFavorites" ], - "CloudConnectionState": [ - "CloudConnectionStateDisabled", - "CloudConnectionStateUnconfigured", - "CloudConnectionStateConnecting", - "CloudConnectionStateConnected" - ], "ConfigurationError": [ "ConfigurationErrorNoError", "ConfigurationErrorInvalidTimeZone", @@ -1271,16 +1265,6 @@ "types": "Object" } }, - "JSONRPC.IsCloudConnected": { - "description": "Check whether the cloud is currently connected. \"connected\" will be true whenever connectionState equals CloudConnectionStateConnected and is deprecated. Please use the connectionState value instead.", - "params": { - }, - "permissionScope": "PermissionScopeAdmin", - "returns": { - "connectionState": "$ref:CloudConnectionState", - "d:connected": "Bool" - } - }, "JSONRPC.KeepAlive": { "description": "This is basically a Ping/Pong mechanism a client app may use to check server connectivity. Currently, the server does not actually do anything with this information and will return the call providing the given sessionId back to the caller. It is up to the client whether to use this or not and not required by the server to keep the connection alive.", "params": { @@ -1315,32 +1299,6 @@ "namespaces": "StringList" } }, - "JSONRPC.SetupCloudConnection": { - "description": "Sets up the cloud connection by deploying a certificate and its configuration.", - "params": { - "certificatePEM": "String", - "endpoint": "String", - "privateKey": "String", - "publicKey": "String", - "rootCA": "String" - }, - "permissionScope": "PermissionScopeAdmin", - "returns": { - "success": "Bool" - } - }, - "JSONRPC.SetupRemoteAccess": { - "description": "Setup the remote connection by providing AWS token information. This requires the cloud to be connected.", - "params": { - "idToken": "String", - "userId": "String" - }, - "permissionScope": "PermissionScopeAdmin", - "returns": { - "message": "String", - "status": "Int" - } - }, "JSONRPC.Version": { "description": "Version of this nymea/JSONRPC interface.", "params": { @@ -2499,13 +2457,6 @@ "value": "Variant" } }, - "JSONRPC.CloudConnectedChanged": { - "description": "Emitted whenever the cloud connection status changes.", - "params": { - "connected": "Bool", - "connectionState": "$ref:CloudConnectionState" - } - }, "JSONRPC.PushButtonAuthFinished": { "description": "Emitted when a push button authentication reaches final state. NOTE: This notification is special. It will only be emitted to connections that did actively request a push button authentication, but also it will be emitted regardless of the notification settings. ", "params": { diff --git a/tests/auto/integrations/testintegrations.cpp b/tests/auto/integrations/testintegrations.cpp index 572599d2..be4b4d3a 100644 --- a/tests/auto/integrations/testintegrations.cpp +++ b/tests/auto/integrations/testintegrations.cpp @@ -297,8 +297,8 @@ void TestIntegrations::getThingClasses_data() QTest::addColumn>("thingClassIds"); QTest::addColumn("resultCount"); - QTest::newRow("vendor nymea") << nymeaVendorId << QList() << 17; - QTest::newRow("no filter") << VendorId() << QList() << 17; + QTest::newRow("vendor nymea") << nymeaVendorId << QList() << 16; + QTest::newRow("no filter") << VendorId() << QList() << 16; QTest::newRow("invalid vendor") << VendorId("93e7d361-8025-4354-b17e-b68406c800bc") << QList() << 0; QTest::newRow("mockThingClassId") << VendorId() << (QList() << mockThingClassId) << 1; QTest::newRow("invalid thingClassId") << VendorId() << (QList() << ThingClassId("6c78ec28-09b6-476d-ac27-1d6966a45c57")) << 0;