Mostly working, cleanup still to be done
This commit is contained in:
parent
22dd3fe27d
commit
f75b66c0ff
@ -7,6 +7,7 @@
|
|||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QUuid>
|
#include <QUuid>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
#include "sigv4utils.h"
|
#include "sigv4utils.h"
|
||||||
|
|
||||||
@ -872,6 +873,11 @@ bool AWSClient::postToMQTT(const QString &boxId, const QString ×tamp, std::
|
|||||||
// }
|
// }
|
||||||
// qDebug() << "Payload:" << payload;
|
// qDebug() << "Payload:" << payload;
|
||||||
QNetworkReply *reply = m_nam->post(request, payload);
|
QNetworkReply *reply = m_nam->post(request, payload);
|
||||||
|
QTimer::singleShot(5000, reply, [reply, callback](){
|
||||||
|
reply->deleteLater();
|
||||||
|
qWarning() << "Timeout posting to MQTT";
|
||||||
|
callback(false);
|
||||||
|
});
|
||||||
connect(reply, &QNetworkReply::finished, this, [reply, callback]() {
|
connect(reply, &QNetworkReply::finished, this, [reply, callback]() {
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
QByteArray data = reply->readAll();
|
QByteArray data = reply->readAll();
|
||||||
|
|||||||
@ -54,6 +54,7 @@ bool CloudTransport::connect(const QUrl &url)
|
|||||||
m_timestamp = QDateTime::currentDateTime();
|
m_timestamp = QDateTime::currentDateTime();
|
||||||
bool postResult = m_awsClient->postToMQTT(url.host(), QString::number(m_timestamp.toMSecsSinceEpoch()), [this](bool success) {
|
bool postResult = m_awsClient->postToMQTT(url.host(), QString::number(m_timestamp.toMSecsSinceEpoch()), [this](bool success) {
|
||||||
if (success) {
|
if (success) {
|
||||||
|
qDebug() << "MQTT Post done. Connecting to remote proxy";
|
||||||
m_remoteproxyConnection->connectServer(QUrl("wss://remoteproxy.nymea.io"));
|
m_remoteproxyConnection->connectServer(QUrl("wss://remoteproxy.nymea.io"));
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "Posting to MQTT failed";
|
qDebug() << "Posting to MQTT failed";
|
||||||
|
|||||||
@ -180,12 +180,13 @@ void ZeroconfDiscovery::serviceEntryRemoved(const QZeroConfService &entry)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ok, now we need to remove it
|
// Ok, now we need to remove it
|
||||||
host->connections()->removeConnection(connection);
|
// host->connections()->removeConnection(connection);
|
||||||
|
connection->setOnline(false);
|
||||||
|
|
||||||
// And if there aren't any connections left, remove the entire device
|
// And if there aren't any connections left, remove the entire device
|
||||||
if (host->connections()->rowCount() == 0) {
|
// if (host->connections()->rowCount() == 0) {
|
||||||
qDebug() << "Zeroconf: Removing connection from host:" << host->name() << url.toString();
|
// qDebug() << "Zeroconf: Removing connection from host:" << host->name() << url.toString();
|
||||||
m_nymeaHosts->removeHost(host);
|
// m_nymeaHosts->removeHost(host);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
#include "nymeatransportinterface.h"
|
#include "nymeatransportinterface.h"
|
||||||
|
|
||||||
@ -32,6 +33,9 @@ NymeaConnection::NymeaConnection(QObject *parent) : QObject(parent)
|
|||||||
void NymeaConnection::acceptCertificate(const QString &url, const QByteArray &pem)
|
void NymeaConnection::acceptCertificate(const QString &url, const QByteArray &pem)
|
||||||
{
|
{
|
||||||
storePem(url, pem);
|
storePem(url, pem);
|
||||||
|
if (m_currentHost) {
|
||||||
|
connectInternal(m_currentHost);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NymeaConnection::isTrusted(const QString &url)
|
bool NymeaConnection::isTrusted(const QString &url)
|
||||||
@ -62,6 +66,11 @@ bool NymeaConnection::connected()
|
|||||||
return m_currentHost && m_currentTransport && m_currentTransport->connectionState() == NymeaTransportInterface::ConnectionStateConnected;
|
return m_currentHost && m_currentTransport && m_currentTransport->connectionState() == NymeaTransportInterface::ConnectionStateConnected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NymeaConnection::ConnectionStatus NymeaConnection::connectionStatus() const
|
||||||
|
{
|
||||||
|
return m_connectionStatus;
|
||||||
|
}
|
||||||
|
|
||||||
NymeaHost *NymeaConnection::currentHost() const
|
NymeaHost *NymeaConnection::currentHost() const
|
||||||
{
|
{
|
||||||
return m_currentHost;
|
return m_currentHost;
|
||||||
@ -91,6 +100,9 @@ void NymeaConnection::setCurrentHost(NymeaHost *host)
|
|||||||
m_currentHost = host;
|
m_currentHost = host;
|
||||||
emit currentHostChanged();
|
emit currentHostChanged();
|
||||||
|
|
||||||
|
m_connectionStatus = ConnectionStatusConnecting;
|
||||||
|
emit connectionStatusChanged();
|
||||||
|
|
||||||
if (m_currentHost) {
|
if (m_currentHost) {
|
||||||
connectInternal(m_currentHost);
|
connectInternal(m_currentHost);
|
||||||
}
|
}
|
||||||
@ -175,6 +187,8 @@ void NymeaConnection::onSslErrors(const QList<QSslError> &errors)
|
|||||||
// info << tr("Name Qualifier:")<< error.certificate().issuerInfo(QSslCertificate::DistinguishedNameQualifier);
|
// info << tr("Name Qualifier:")<< error.certificate().issuerInfo(QSslCertificate::DistinguishedNameQualifier);
|
||||||
// info << tr("Email:")<< error.certificate().issuerInfo(QSslCertificate::EmailAddress);
|
// info << tr("Email:")<< error.certificate().issuerInfo(QSslCertificate::EmailAddress);
|
||||||
|
|
||||||
|
m_connectionStatus = ConnectionStatusSslUntrusted;
|
||||||
|
emit connectionStatusChanged();
|
||||||
emit verifyConnectionCertificate(transport->url().toString(), info, certificateFingerprint, error.certificate().toPem());
|
emit verifyConnectionCertificate(transport->url().toString(), info, certificateFingerprint, error.certificate().toPem());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -196,22 +210,58 @@ void NymeaConnection::onError(QAbstractSocket::SocketError error)
|
|||||||
|
|
||||||
NymeaTransportInterface* transport = qobject_cast<NymeaTransportInterface*>(sender());
|
NymeaTransportInterface* transport = qobject_cast<NymeaTransportInterface*>(sender());
|
||||||
|
|
||||||
|
ConnectionStatus errorStatus = ConnectionStatusUnknownError;
|
||||||
|
switch (error) {
|
||||||
|
case QAbstractSocket::ConnectionRefusedError:
|
||||||
|
errorStatus = ConnectionStatusConnectionRefused;
|
||||||
|
break;
|
||||||
|
case QAbstractSocket::HostNotFoundError:
|
||||||
|
errorStatus = ConnectionStatusHostNotFound;
|
||||||
|
break;
|
||||||
|
case QAbstractSocket::NetworkError:
|
||||||
|
errorStatus = ConnectionStatusBearerFailed;
|
||||||
|
break;
|
||||||
|
case QAbstractSocket::RemoteHostClosedError:
|
||||||
|
errorStatus = ConnectionStatusRemoteHostClosed;
|
||||||
|
break;
|
||||||
|
case QAbstractSocket::SocketTimeoutError:
|
||||||
|
errorStatus = ConnectionStatusTimeout;
|
||||||
|
break;
|
||||||
|
case QAbstractSocket::SslInternalError:
|
||||||
|
case QAbstractSocket::SslInvalidUserDataError:
|
||||||
|
errorStatus = ConnectionStatusSslError;
|
||||||
|
break;
|
||||||
|
case QAbstractSocket::SslHandshakeFailedError:
|
||||||
|
errorStatus = ConnectionStatusSslUntrusted;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
errorStatus = ConnectionStatusUnknownError;
|
||||||
|
}
|
||||||
|
|
||||||
if (transport == m_currentTransport) {
|
if (transport == m_currentTransport) {
|
||||||
qDebug() << "Current transport failed:" << error;
|
qDebug() << "Current transport failed:" << error;
|
||||||
// The current transport failed, forward the error
|
// The current transport failed, forward the error
|
||||||
emit connectionError(errorString);
|
m_connectionStatus = errorStatus;
|
||||||
|
emit connectionStatusChanged();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_currentTransport) {
|
if (!m_currentTransport) {
|
||||||
// We're trying to connect and one of the transports failed...
|
// We're trying to connect and one of the transports failed...
|
||||||
qDebug() << "A transport error happened for" << transport->url() << error;
|
qDebug() << "A transport error happened for" << transport->url() << error << "(Still trying on" << m_transportCandidates.count() << "connections)";
|
||||||
if (m_transportCandidates.contains(transport)) {
|
if (m_transportCandidates.contains(transport)) {
|
||||||
m_transportCandidates.remove(transport);
|
m_transportCandidates.remove(transport);
|
||||||
transport->deleteLater();
|
transport->deleteLater();
|
||||||
}
|
}
|
||||||
if (m_transportCandidates.isEmpty()) {
|
if (m_transportCandidates.isEmpty()) {
|
||||||
emit connectionError(errorString);
|
m_connectionStatus = errorStatus;
|
||||||
|
emit connectionStatusChanged();
|
||||||
|
|
||||||
|
if (m_connectionStatus != ConnectionStatusSslUntrusted) {
|
||||||
|
QTimer::singleShot(1000, m_currentHost, [this](){
|
||||||
|
connectInternal(m_currentHost);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -263,7 +313,10 @@ void NymeaConnection::onDisconnected()
|
|||||||
qDebug() << "NymeaConnection: disconnected.";
|
qDebug() << "NymeaConnection: disconnected.";
|
||||||
emit connectedChanged(false);
|
emit connectedChanged(false);
|
||||||
|
|
||||||
connectInternal(m_currentHost);
|
// Try to reconnect, only if we're not waiting for SSL certs to be trusted.
|
||||||
|
if (m_connectionStatus != ConnectionStatusSslUntrusted) {
|
||||||
|
connectInternal(m_currentHost);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NymeaConnection::updateActiveBearers()
|
void NymeaConnection::updateActiveBearers()
|
||||||
@ -277,18 +330,20 @@ void NymeaConnection::updateActiveBearers()
|
|||||||
}
|
}
|
||||||
// qDebug() << "Available bearers:" << availableBearerTypes;
|
// qDebug() << "Available bearers:" << availableBearerTypes;
|
||||||
if (m_availableBearerTypes != availableBearerTypes) {
|
if (m_availableBearerTypes != availableBearerTypes) {
|
||||||
qDebug() << "Available Bearer Types changed:" << availableBearerTypes;
|
|
||||||
|
|
||||||
|
qDebug() << "Available Bearer Types changed:" << availableBearerTypes;
|
||||||
m_availableBearerTypes = availableBearerTypes;
|
m_availableBearerTypes = availableBearerTypes;
|
||||||
emit availableBearerTypesChanged();
|
emit availableBearerTypesChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_currentHost) {
|
if (!m_currentHost) {
|
||||||
// No host set... Nothing to do...
|
// No host set... Nothing to do...
|
||||||
|
qDebug() << "No current host... Nothing to do...";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!m_currentTransport) {
|
if (!m_currentTransport) {
|
||||||
// There's a host but no connection. Try connecting now...
|
// There's a host but no connection. Try connecting now...
|
||||||
|
qDebug() << "There's a host but no connection. Trying to connect now...";
|
||||||
connectInternal(m_currentHost);
|
connectInternal(m_currentHost);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,6 +393,7 @@ bool NymeaConnection::storePem(const QUrl &host, const QByteArray &pem)
|
|||||||
bool NymeaConnection::loadPem(const QUrl &host, QByteArray &pem)
|
bool NymeaConnection::loadPem(const QUrl &host, QByteArray &pem)
|
||||||
{
|
{
|
||||||
QDir dir(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/sslcerts/");
|
QDir dir(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/sslcerts/");
|
||||||
|
qDebug() << "Loading certificates from:" << dir.absoluteFilePath(host.host() + ".pem");
|
||||||
QFile certFile(dir.absoluteFilePath(host.host() + ".pem"));
|
QFile certFile(dir.absoluteFilePath(host.host() + ".pem"));
|
||||||
if (!certFile.open(QFile::ReadOnly)) {
|
if (!certFile.open(QFile::ReadOnly)) {
|
||||||
return false;
|
return false;
|
||||||
@ -361,6 +417,12 @@ void NymeaConnection::connect(NymeaHost *nymeaHost)
|
|||||||
|
|
||||||
void NymeaConnection::connectInternal(NymeaHost *host)
|
void NymeaConnection::connectInternal(NymeaHost *host)
|
||||||
{
|
{
|
||||||
|
if (m_availableBearerTypes == Connection::BearerTypeNone) {
|
||||||
|
qDebug() << "No available bearer. Not connecting... (" << m_availableBearerTypes << ")";
|
||||||
|
m_connectionStatus = ConnectionStatusNoBearerAvailable;
|
||||||
|
emit connectionStatusChanged();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (m_availableBearerTypes.testFlag(Connection::BearerTypeWifi) || m_availableBearerTypes.testFlag(Connection::BearerTypeEthernet)) {
|
if (m_availableBearerTypes.testFlag(Connection::BearerTypeWifi) || m_availableBearerTypes.testFlag(Connection::BearerTypeEthernet)) {
|
||||||
Connection* lanConnection = host->connections()->bestMatch(Connection::BearerTypeWifi | Connection::BearerTypeEthernet);
|
Connection* lanConnection = host->connections()->bestMatch(Connection::BearerTypeWifi | Connection::BearerTypeEthernet);
|
||||||
if (lanConnection) {
|
if (lanConnection) {
|
||||||
|
|||||||
@ -21,8 +21,24 @@ class NymeaConnection : public QObject
|
|||||||
Q_PROPERTY(NymeaHost* currentHost READ currentHost WRITE setCurrentHost NOTIFY currentHostChanged)
|
Q_PROPERTY(NymeaHost* currentHost READ currentHost WRITE setCurrentHost NOTIFY currentHostChanged)
|
||||||
Q_PROPERTY(Connection* currentConnection READ currentConnection NOTIFY currentConnectionChanged)
|
Q_PROPERTY(Connection* currentConnection READ currentConnection NOTIFY currentConnectionChanged)
|
||||||
Q_PROPERTY(Connection::BearerTypes availableBearerTypes READ availableBearerTypes NOTIFY availableBearerTypesChanged)
|
Q_PROPERTY(Connection::BearerTypes availableBearerTypes READ availableBearerTypes NOTIFY availableBearerTypesChanged)
|
||||||
|
Q_PROPERTY(ConnectionStatus connectionStatus READ connectionStatus NOTIFY connectionStatusChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
enum ConnectionStatus {
|
||||||
|
ConnectionStatusUnconnected,
|
||||||
|
ConnectionStatusConnecting,
|
||||||
|
ConnectionStatusNoBearerAvailable,
|
||||||
|
ConnectionStatusBearerFailed,
|
||||||
|
ConnectionStatusHostNotFound,
|
||||||
|
ConnectionStatusConnectionRefused,
|
||||||
|
ConnectionStatusRemoteHostClosed,
|
||||||
|
ConnectionStatusTimeout,
|
||||||
|
ConnectionStatusSslError,
|
||||||
|
ConnectionStatusSslUntrusted,
|
||||||
|
ConnectionStatusUnknownError,
|
||||||
|
ConnectionStatusConnected
|
||||||
|
};
|
||||||
|
Q_ENUM(ConnectionStatus)
|
||||||
explicit NymeaConnection(QObject *parent = nullptr);
|
explicit NymeaConnection(QObject *parent = nullptr);
|
||||||
|
|
||||||
void registerTransport(NymeaTransportInterfaceFactory *transportFactory);
|
void registerTransport(NymeaTransportInterfaceFactory *transportFactory);
|
||||||
@ -35,12 +51,14 @@ public:
|
|||||||
Connection::BearerTypes availableBearerTypes() const;
|
Connection::BearerTypes availableBearerTypes() const;
|
||||||
|
|
||||||
bool connected();
|
bool connected();
|
||||||
|
ConnectionStatus connectionStatus() const;
|
||||||
|
|
||||||
NymeaHost* currentHost() const;
|
NymeaHost* currentHost() const;
|
||||||
void setCurrentHost(NymeaHost *host);
|
void setCurrentHost(NymeaHost *host);
|
||||||
|
|
||||||
Connection* currentConnection() const;
|
Connection* currentConnection() const;
|
||||||
|
|
||||||
|
|
||||||
void sendData(const QByteArray &data);
|
void sendData(const QByteArray &data);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
@ -48,8 +66,8 @@ signals:
|
|||||||
void verifyConnectionCertificate(const QString &url, const QStringList &issuerInfo, const QByteArray &fingerprint, const QByteArray &pem);
|
void verifyConnectionCertificate(const QString &url, const QStringList &issuerInfo, const QByteArray &fingerprint, const QByteArray &pem);
|
||||||
void currentHostChanged();
|
void currentHostChanged();
|
||||||
void connectedChanged(bool connected);
|
void connectedChanged(bool connected);
|
||||||
|
void connectionStatusChanged();
|
||||||
void currentConnectionChanged();
|
void currentConnectionChanged();
|
||||||
void connectionError(const QString &error);
|
|
||||||
void dataAvailable(const QByteArray &data);
|
void dataAvailable(const QByteArray &data);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
@ -69,6 +87,7 @@ private:
|
|||||||
Connection::BearerType qBearerTypeToNymeaBearerType(QNetworkConfiguration::BearerType type) const;
|
Connection::BearerType qBearerTypeToNymeaBearerType(QNetworkConfiguration::BearerType type) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
ConnectionStatus m_connectionStatus = ConnectionStatusUnconnected;
|
||||||
QNetworkConfigurationManager *m_networkConfigManager = nullptr;
|
QNetworkConfigurationManager *m_networkConfigManager = nullptr;
|
||||||
Connection::BearerTypes m_availableBearerTypes = Connection::BearerTypeNone;
|
Connection::BearerTypes m_availableBearerTypes = Connection::BearerTypeNone;
|
||||||
|
|
||||||
|
|||||||
@ -158,7 +158,7 @@ void NymeaHostsFilterModel::setNymeaConnection(NymeaConnection *nymeaConnection)
|
|||||||
emit nymeaConnectionChanged();
|
emit nymeaConnectionChanged();
|
||||||
|
|
||||||
connect(m_nymeaConnection, &NymeaConnection::availableBearerTypesChanged, this, [this](){
|
connect(m_nymeaConnection, &NymeaConnection::availableBearerTypesChanged, this, [this](){
|
||||||
qDebug() << "Bearer Types Changed!";
|
// qDebug() << "Bearer Types Changed!";
|
||||||
invalidateFilter();
|
invalidateFilter();
|
||||||
emit countChanged();
|
emit countChanged();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -163,5 +163,6 @@
|
|||||||
<file>ui/thingconfiguration/SetupWizard.qml</file>
|
<file>ui/thingconfiguration/SetupWizard.qml</file>
|
||||||
<file>ui/thingconfiguration/EditThingsPage.qml</file>
|
<file>ui/thingconfiguration/EditThingsPage.qml</file>
|
||||||
<file>ui/thingconfiguration/ConfigureThingPage.qml</file>
|
<file>ui/thingconfiguration/ConfigureThingPage.qml</file>
|
||||||
|
<file>ui/connection/CertificateDialog.qml</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
@ -75,7 +75,7 @@ Item {
|
|||||||
height: swipeView.height
|
height: swipeView.height
|
||||||
width: swipeView.width
|
width: swipeView.width
|
||||||
objectName: "pageStack"
|
objectName: "pageStack"
|
||||||
initialItem: ConnectPage {}
|
initialItem: Page {}
|
||||||
|
|
||||||
property var tabSettings: Settings {
|
property var tabSettings: Settings {
|
||||||
category: "tabSettings" + index
|
category: "tabSettings" + index
|
||||||
@ -98,23 +98,30 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
// pageStack.push(Qt.resolvedUrl("connection/ConnectPage.qml"), StackView.Immediate)
|
if (tabSettings.lastConnectedHost.length > 0) {
|
||||||
// setupPushNotifications();
|
print("Last connected host was", tabSettings.lastConnectedHost)
|
||||||
|
var cachedHost = discovery.nymeaHosts.find(tabSettings.lastConnectedHost);
|
||||||
|
if (cachedHost) {
|
||||||
|
engine.connection.currentHost = cachedHost
|
||||||
|
} else {
|
||||||
|
print("Warning: There is a last connected host but UUID is unknown to discovery...")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
PlatformHelper.hideSplashScreen();
|
||||||
|
pageStack.push(Qt.resolvedUrl("connection/ConnectPage.qml"), StackView.Immediate)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
print("calling init. Auth required:", engine.jsonRpcClient.authenticationRequired, "initial setup required:", engine.jsonRpcClient.initialSetupRequired, "jsonrpc connected:", engine.jsonRpcClient.connected, "Current host:", engine.connection.currentHost)
|
print("calling init. Auth required:", engine.jsonRpcClient.authenticationRequired, "initial setup required:", engine.jsonRpcClient.initialSetupRequired, "jsonrpc connected:", engine.jsonRpcClient.connected, "Current host:", engine.connection.currentHost)
|
||||||
pageStack.clear()
|
pageStack.clear()
|
||||||
if (!engine.connection.currentHost) {
|
if (!engine.connection.currentHost) {
|
||||||
|
print("pushing ConnectPage")
|
||||||
pageStack.push(Qt.resolvedUrl("connection/ConnectPage.qml"))
|
pageStack.push(Qt.resolvedUrl("connection/ConnectPage.qml"))
|
||||||
PlatformHelper.hideSplashScreen();
|
PlatformHelper.hideSplashScreen();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (engine.jsonRpcClient.connected) {
|
|
||||||
pageStack.push(Qt.resolvedUrl("MainPage.qml"))
|
|
||||||
PlatformHelper.hideSplashScreen();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (engine.jsonRpcClient.authenticationRequired || engine.jsonRpcClient.initialSetupRequired) {
|
if (engine.jsonRpcClient.authenticationRequired || engine.jsonRpcClient.initialSetupRequired) {
|
||||||
PlatformHelper.hideSplashScreen();
|
PlatformHelper.hideSplashScreen();
|
||||||
@ -126,6 +133,7 @@ Item {
|
|||||||
engine.connection.disconnect();
|
engine.connection.disconnect();
|
||||||
init();
|
init();
|
||||||
})
|
})
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
var page = pageStack.push(Qt.resolvedUrl("LoginPage.qml"));
|
var page = pageStack.push(Qt.resolvedUrl("LoginPage.qml"));
|
||||||
page.backPressed.connect(function() {
|
page.backPressed.connect(function() {
|
||||||
@ -133,9 +141,22 @@ Item {
|
|||||||
engine.connection.disconnect()
|
engine.connection.disconnect()
|
||||||
init();
|
init();
|
||||||
})
|
})
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pageStack.push(Qt.resolvedUrl("connection/ConnectingPage.qml"))
|
|
||||||
|
if (engine.jsonRpcClient.connected) {
|
||||||
|
pageStack.push(Qt.resolvedUrl("MainPage.qml"))
|
||||||
|
PlatformHelper.hideSplashScreen();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
print("pushing ConnectingPage")
|
||||||
|
PlatformHelper.hideSplashScreen();
|
||||||
|
var page = pageStack.push(Qt.resolvedUrl("connection/ConnectingPage.qml"));
|
||||||
|
page.cancel.connect(function(){
|
||||||
|
engine.connection.disconnect();
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleCloseEvent(close) {
|
function handleCloseEvent(close) {
|
||||||
@ -179,8 +200,15 @@ Item {
|
|||||||
onCurrentHostChanged: {
|
onCurrentHostChanged: {
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
onVerifyConnectionCertificate: {
|
||||||
|
print("verify cert!")
|
||||||
|
var certDialogComponent = Qt.createComponent(Qt.resolvedUrl("connection/CertificateDialog.qml"));
|
||||||
|
var popup = certDialogComponent.createObject(root, {url: url, issuerInfo: issuerInfo, fingerprint: fingerprint, pem: pem});
|
||||||
|
popup.open();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: engine.jsonRpcClient
|
target: engine.jsonRpcClient
|
||||||
onConnectedChanged: {
|
onConnectedChanged: {
|
||||||
|
|||||||
110
nymea-app/ui/connection/CertificateDialog.qml
Normal file
110
nymea-app/ui/connection/CertificateDialog.qml
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
import QtQuick 2.9
|
||||||
|
import QtQuick.Controls 2.2
|
||||||
|
import QtQuick.Controls.Material 2.2
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
import Nymea 1.0
|
||||||
|
import "../components"
|
||||||
|
|
||||||
|
Dialog {
|
||||||
|
id: certDialog
|
||||||
|
width: Math.min(parent.width * .9, 400)
|
||||||
|
x: (parent.width - width) / 2
|
||||||
|
y: (parent.height - height) / 2
|
||||||
|
standardButtons: Dialog.Yes | Dialog.No
|
||||||
|
|
||||||
|
property string url
|
||||||
|
property var fingerprint
|
||||||
|
property var issuerInfo
|
||||||
|
property var pem
|
||||||
|
|
||||||
|
readonly property bool hasOldFingerprint: engine.connection.isTrusted(url)
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: certLayout
|
||||||
|
anchors.fill: parent
|
||||||
|
// spacing: app.margins
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: app.margins
|
||||||
|
ColorIcon {
|
||||||
|
Layout.preferredHeight: app.iconSize * 2
|
||||||
|
Layout.preferredWidth: height
|
||||||
|
name: certDialog.hasOldFingerprint ? "../images/lock-broken.svg" : "../images/info.svg"
|
||||||
|
color: certDialog.hasOldFingerprint ? "red" : app.accentColor
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: titleLabel
|
||||||
|
Layout.fillWidth: true
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
text: certDialog.hasOldFingerprint ? qsTr("Warning") : qsTr("Hi there!")
|
||||||
|
color: certDialog.hasOldFingerprint ? "red" : app.accentColor
|
||||||
|
font.pixelSize: app.largeFont
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
text: certDialog.hasOldFingerprint ? qsTr("The certificate of this %1 box has changed!").arg(app.systemName) : qsTr("It seems this is the first time you connect to this %1 box.").arg(app.systemName)
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
text: certDialog.hasOldFingerprint ? qsTr("Did you change the box's configuration? Verify if this information is correct.") : qsTr("This is the box's certificate. Once you trust it, an encrypted connection will be established.")
|
||||||
|
}
|
||||||
|
|
||||||
|
ThinDivider {}
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
implicitHeight: certGridLayout.implicitHeight
|
||||||
|
Flickable {
|
||||||
|
anchors.fill: parent
|
||||||
|
contentHeight: certGridLayout.implicitHeight
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
ScrollBar.vertical: ScrollBar {
|
||||||
|
policy: contentHeight > height ? ScrollBar.AlwaysOn : ScrollBar.AsNeeded
|
||||||
|
}
|
||||||
|
|
||||||
|
GridLayout {
|
||||||
|
id: certGridLayout
|
||||||
|
columns: 2
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: certDialog.issuerInfo
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
text: modelData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.columnSpan: 2
|
||||||
|
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||||
|
text: qsTr("Fingerprint: ") + certDialog.fingerprint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ThinDivider {}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
text: certDialog.hasOldFingerprint ? qsTr("Do you want to connect nevertheless?") : qsTr("Do you want to trust this device?")
|
||||||
|
font.bold: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onAccepted: {
|
||||||
|
engine.connection.acceptCertificate(certDialog.url, certDialog.pem)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -12,28 +12,8 @@ Page {
|
|||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
print("Ready to connect")
|
print("Ready to connect")
|
||||||
if (tabSettings.lastConnectedHost.length > 0) {
|
|
||||||
print("Last connected host was", tabSettings.lastConnectedHost)
|
|
||||||
var cachedHost = discovery.nymeaHosts.find(tabSettings.lastConnectedHost);
|
|
||||||
if (cachedHost) {
|
|
||||||
engine.connection.currentHost = cachedHost
|
|
||||||
} else {
|
|
||||||
print("Warning: There is a last connected host but UUID is unknown to discovery...")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
PlatformHelper.hideSplashScreen();
|
|
||||||
}
|
|
||||||
|
|
||||||
// if (settings.lastConnectedHost.length > 0 && Engine.connection.connect(tabSettings.lastConnectedHost)) {
|
pageStack.push(discoveryPage, StackView.Immediate)
|
||||||
// var page = pageStack.push(Qt.resolvedUrl("ConnectingPage.qml"))
|
|
||||||
// page.cancel.connect(function() {
|
|
||||||
// Engine.connection.disconnect();
|
|
||||||
// pageStack.pop(root, StackView.Immediate);
|
|
||||||
// pageStack.push(discoveryPage)
|
|
||||||
// })
|
|
||||||
// } else {
|
|
||||||
pageStack.push(discoveryPage, StackView.Immediate)
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -65,55 +45,6 @@ Page {
|
|||||||
nymeaConnection: engine.connection
|
nymeaConnection: engine.connection
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: engine.connection
|
|
||||||
onVerifyConnectionCertificate: {
|
|
||||||
print("verify cert!")
|
|
||||||
var popup = certDialogComponent.createObject(root, {url: url, issuerInfo: issuerInfo, fingerprint: fingerprint, pem: pem});
|
|
||||||
popup.open();
|
|
||||||
}
|
|
||||||
onConnectionError: {
|
|
||||||
var errorMessage;
|
|
||||||
switch (error) {
|
|
||||||
case "ConnectionRefusedError":
|
|
||||||
errorMessage = qsTr("The host has rejected our connection. This probably means that %1 stopped running. Did you unplug your %1 box?").arg(app.systemName);
|
|
||||||
break;
|
|
||||||
case "SslInvalidUserDataError":
|
|
||||||
case "SslHandshakeFailedError":
|
|
||||||
// silently ignore. They'll be handled by the SSL logic
|
|
||||||
return;
|
|
||||||
case "HostNotFoundError":
|
|
||||||
errorMessage = qsTr("%1:core could not be found on this address. Please make sure you entered the address correctly and that the box is powered on.").arg(app.systemName);
|
|
||||||
break;
|
|
||||||
case "NetworkError":
|
|
||||||
errorMessage = qsTr("It seems you're not connected to the network.");
|
|
||||||
break;
|
|
||||||
case "RemoteHostClosedError":
|
|
||||||
errorMessage = qsTr("%1:core has closed the connection. This probably means it has been turned off or restarted.").arg(app.systemName);
|
|
||||||
break;
|
|
||||||
case "SocketTimeoutError":
|
|
||||||
errorMessage = qsTr("%1:core did not respond. Please make sure your network connection works properly").arg(app.systemName);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
errorMessage = qsTr("An unknown error happened. We're very sorry for that. (Error code: %1)").arg(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
pageStack.pop(root, StackView.Immediate)
|
|
||||||
pageStack.push(discoveryPage)
|
|
||||||
|
|
||||||
print("opening ErrorDialog with message:", errorMessage, error)
|
|
||||||
var comp = Qt.createComponent(Qt.resolvedUrl("../components/ErrorDialog.qml"))
|
|
||||||
var popup = comp.createObject(app, {text: errorMessage})
|
|
||||||
popup.open()
|
|
||||||
}
|
|
||||||
onConnectedChanged: {
|
|
||||||
if (!connected) {
|
|
||||||
pageStack.pop(root, StackView.Immediate)
|
|
||||||
pageStack.push(discoveryPage)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: discoveryPage
|
id: discoveryPage
|
||||||
|
|
||||||
@ -357,116 +288,6 @@ Page {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
|
||||||
id: certDialogComponent
|
|
||||||
|
|
||||||
Dialog {
|
|
||||||
id: certDialog
|
|
||||||
width: Math.min(parent.width * .9, 400)
|
|
||||||
x: (parent.width - width) / 2
|
|
||||||
y: (parent.height - height) / 2
|
|
||||||
standardButtons: Dialog.Yes | Dialog.No
|
|
||||||
|
|
||||||
property string url
|
|
||||||
property var fingerprint
|
|
||||||
property var issuerInfo
|
|
||||||
property var pem
|
|
||||||
|
|
||||||
readonly property bool hasOldFingerprint: engine.connection.isTrusted(url)
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: certLayout
|
|
||||||
anchors.fill: parent
|
|
||||||
// spacing: app.margins
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
spacing: app.margins
|
|
||||||
ColorIcon {
|
|
||||||
Layout.preferredHeight: app.iconSize * 2
|
|
||||||
Layout.preferredWidth: height
|
|
||||||
name: certDialog.hasOldFingerprint ? "../images/lock-broken.svg" : "../images/info.svg"
|
|
||||||
color: certDialog.hasOldFingerprint ? "red" : app.accentColor
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
id: titleLabel
|
|
||||||
Layout.fillWidth: true
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
text: certDialog.hasOldFingerprint ? qsTr("Warning") : qsTr("Hi there!")
|
|
||||||
color: certDialog.hasOldFingerprint ? "red" : app.accentColor
|
|
||||||
font.pixelSize: app.largeFont
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
text: certDialog.hasOldFingerprint ? qsTr("The certificate of this %1 box has changed!").arg(app.systemName) : qsTr("It seems this is the first time you connect to this %1 box.").arg(app.systemName)
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
text: certDialog.hasOldFingerprint ? qsTr("Did you change the box's configuration? Verify if this information is correct.") : qsTr("This is the box's certificate. Once you trust it, an encrypted connection will be established.")
|
|
||||||
}
|
|
||||||
|
|
||||||
ThinDivider {}
|
|
||||||
Item {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
implicitHeight: certGridLayout.implicitHeight
|
|
||||||
Flickable {
|
|
||||||
anchors.fill: parent
|
|
||||||
contentHeight: certGridLayout.implicitHeight
|
|
||||||
clip: true
|
|
||||||
|
|
||||||
ScrollBar.vertical: ScrollBar {
|
|
||||||
policy: contentHeight > height ? ScrollBar.AlwaysOn : ScrollBar.AsNeeded
|
|
||||||
}
|
|
||||||
|
|
||||||
GridLayout {
|
|
||||||
id: certGridLayout
|
|
||||||
columns: 2
|
|
||||||
width: parent.width
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: certDialog.issuerInfo
|
|
||||||
|
|
||||||
Label {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
text: modelData
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Label {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.columnSpan: 2
|
|
||||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
|
||||||
text: qsTr("Fingerprint: ") + certDialog.fingerprint
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ThinDivider {}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
text: certDialog.hasOldFingerprint ? qsTr("Do you want to connect nevertheless?") : qsTr("Do you want to trust this device?")
|
|
||||||
font.bold: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onAccepted: {
|
|
||||||
engine.connection.acceptCertificate(certDialog.url, certDialog.pem)
|
|
||||||
root.connectToHost(certDialog.url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: infoDialog
|
id: infoDialog
|
||||||
Dialog {
|
Dialog {
|
||||||
|
|||||||
@ -33,6 +33,51 @@ Page {
|
|||||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
}
|
}
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: {
|
||||||
|
var errorMessage;
|
||||||
|
switch (engine.connection.connectionStatus) {
|
||||||
|
case NymeaConnection.ConnectionStatusUnconnected:
|
||||||
|
case NymeaConnection.ConnectionStatusConnecting:
|
||||||
|
case NymeaConnection.ConnectionStatusConnected:
|
||||||
|
errorMessage = "";
|
||||||
|
break;
|
||||||
|
case NymeaConnection.ConnectionStatusBearerFailed:
|
||||||
|
errorMessage = qsTr("The network connection failed.")
|
||||||
|
break;
|
||||||
|
case NymeaConnection.ConnectionStatusNoBearerAvailable:
|
||||||
|
errorMessage = qsTr("It seems you're not connected to the network.");
|
||||||
|
break;
|
||||||
|
case NymeaConnection.ConnectionStatusHostNotFound:
|
||||||
|
errorMessage = qsTr("%1:core could not be found on this address. Please make sure you entered the address correctly and that the box is powered on.").arg(app.systemName);
|
||||||
|
break;
|
||||||
|
case NymeaConnection.ConnectionStatusConnectionRefused:
|
||||||
|
errorMessage = qsTr("The host has rejected our connection. This probably means that %1 stopped running. Did you unplug your %1 box?").arg(app.systemName);
|
||||||
|
break;
|
||||||
|
case NymeaConnection.ConnectionStatusRemoteHostClosed:
|
||||||
|
errorMessage = qsTr("%1:core has closed the connection. This probably means it has been turned off or restarted.").arg(app.systemName);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NymeaConnection.ConnectionStatusTimeout:
|
||||||
|
errorMessage = qsTr("%1:core did not respond. Please make sure your network connection works properly").arg(app.systemName);
|
||||||
|
break;
|
||||||
|
case NymeaConnection.ConnectionStatusSslError:
|
||||||
|
errorMessage = qsTr("An unrecovareable SSL Error happened. Please make sure certificates are installed correctly.");
|
||||||
|
break;
|
||||||
|
case NymeaConnection.ConnectionStatusSslUntrusted:
|
||||||
|
errorMessage = qsTr("The SSL Certificate is not trusted.");
|
||||||
|
break;
|
||||||
|
case NymeaConnection.ConnectionStatusUnknownError:
|
||||||
|
default:
|
||||||
|
errorMessage = qsTr("An unknown error happened. We're very sorry for that. (Error code: %1)").arg(engine.connection.connectionStatus);
|
||||||
|
}
|
||||||
|
return errorMessage;
|
||||||
|
}
|
||||||
|
font.pixelSize: app.smallFont
|
||||||
|
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
|
|||||||
Reference in New Issue
Block a user