From 00921391a247a2edfe128688a8225697fb584052 Mon Sep 17 00:00:00 2001 From: Scharel Clemens Date: Sat, 21 Mar 2020 20:09:07 +0100 Subject: [PATCH] More state informations --- src/notesapi.cpp | 179 ++++++++++++++++++++++++++--------------------- src/notesapi.h | 47 +++++++++---- 2 files changed, 134 insertions(+), 92 deletions(-) diff --git a/src/notesapi.cpp b/src/notesapi.cpp index 88cd0c4..fd29e79 100644 --- a/src/notesapi.cpp +++ b/src/notesapi.cpp @@ -8,13 +8,15 @@ NotesApi::NotesApi(const QString statusEndpoint, const QString loginEndpoint, co : QObject(parent), m_statusEndpoint(statusEndpoint), m_loginEndpoint(loginEndpoint), m_notesEndpoint(notesEndpoint) { // TODO verify connections (also in destructor) - m_loginPollTimer.setInterval(5000); + m_loginPollTimer.setInterval(POLL_INTERVALL); connect(&m_loginPollTimer, SIGNAL(timeout()), this, SLOT(pollLoginUrl())); m_statusReply = NULL; m_loginReply = NULL; m_pollReply = NULL; - m_statusStatus = RequestStatus::StatusNone; - m_loginStatus = RequestStatus::StatusNone; + setNcStatusStatus(NextcloudStatus::NextcloudUnknown); + setLoginStatus(LoginStatus::LoginUnknown); + m_ncStatusStatus = NextcloudStatus::NextcloudUnknown; + m_loginStatus = LoginStatus::LoginUnknown; mp_model = new NotesModel(this); mp_modelProxy = new NotesProxyModel(this); mp_modelProxy->setSourceModel(mp_model); @@ -37,12 +39,13 @@ NotesApi::NotesApi(const QString statusEndpoint, const QString loginEndpoint, co } NotesApi::~NotesApi() { - disconnect(mp_model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector)), this, SLOT(saveToFile(QModelIndex,QModelIndex,QVector))); + disconnect(&m_loginPollTimer, SIGNAL(timeout()), this, SLOT(pollLoginUrl())); disconnect(this, SIGNAL(urlChanged(QUrl)), this, SLOT(verifyUrl(QUrl))); disconnect(&m_manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), this, SLOT(requireAuthentication(QNetworkReply*,QAuthenticator*))); disconnect(&m_manager, SIGNAL(networkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility)), this, SLOT(onNetworkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility))); disconnect(&m_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*))); disconnect(&m_manager, SIGNAL(sslErrors(QNetworkReply*,QList)), this, SLOT(sslError(QNetworkReply*,QList))); + disconnect(mp_model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector)), this, SLOT(saveToFile(QModelIndex,QModelIndex,QVector))); m_jsonFile.close(); if (mp_modelProxy) delete mp_modelProxy; @@ -157,35 +160,38 @@ void NotesApi::setDataFile(QString dataFile) { } } -bool NotesApi::getStatus() { - if (m_statusStatus != RequestStatus::StatusBusy) { - m_statusStatus = RequestStatus::StatusBusy; - emit statusStatusChanged(m_statusStatus); - } +bool NotesApi::getNcStatus() { QUrl url = apiEndpointUrl(m_statusEndpoint); + qDebug() << "POST" << url.toDisplayString(); if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { - qDebug() << "POST" << url.toDisplayString(); + setNcStatusStatus(NextcloudStatus::NextcloudBusy); m_request.setUrl(url); m_statusReply = m_manager.post(m_request, QByteArray()); return true; } + else { + qDebug() << "URL not valid!"; + setNcStatusStatus(NextcloudStatus::NextcloudUnknown); + } return false; } bool NotesApi::initiateFlowV2Login() { - if (m_loginStatus == RequestStatus::StatusInitiated || m_loginStatus == RequestStatus::StatusBusy) { + if (m_loginStatus == LoginStatus::LoginFlowV2Initiating || m_loginStatus == LoginStatus::LoginFlowV2Polling) { abortFlowV2Login(); } - if (m_loginStatus != RequestStatus::StatusInitiated) { - m_loginStatus = RequestStatus::StatusInitiated; - emit loginStatusChanged(m_loginStatus); - QUrl url = apiEndpointUrl(m_loginEndpoint); - if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { - qDebug() << "POST" << url.toDisplayString(); - m_request.setUrl(url); - m_loginReply = m_manager.post(m_request, QByteArray()); - return true; - } + QUrl url = apiEndpointUrl(m_loginEndpoint); + qDebug() << "POST" << url.toDisplayString(); + if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { + setLoginStatus(LoginStatus::LoginFlowV2Initiating); + m_request.setUrl(url); + m_loginReply = m_manager.post(m_request, QByteArray()); + return true; + } + else { + qDebug() << "URL not valid!"; + setLoginStatus(LoginStatus::LoginFlowV2Failed); + abortFlowV2Login(); } return false; } @@ -200,14 +206,20 @@ void NotesApi::abortFlowV2Login() { m_loginReply->abort(); if (m_pollReply->isRunning()) m_pollReply->abort(); + setLoginStatus(LoginStatus::LoginUnknown); } void NotesApi::pollLoginUrl() { + qDebug() << "POST" << m_pollUrl.toDisplayString(); if (m_pollUrl.isValid() && !m_pollUrl.scheme().isEmpty() && !m_pollUrl.host().isEmpty() && !m_pollToken.isEmpty()) { - qDebug() << "POST" << m_pollUrl.toDisplayString(); m_request.setUrl(m_pollUrl); m_pollReply = m_manager.post(m_request, QByteArray("token=").append(m_pollToken)); } + else { + qDebug() << "URL not valid!"; + setLoginStatus(LoginStatus::LoginFlowV2Failed); + abortFlowV2Login(); + } } void NotesApi::getAllNotes(QStringList excludeFields) { @@ -279,10 +291,40 @@ void NotesApi::deleteNote(double noteId) { mp_model->removeNote(noteId); } +const QString NotesApi::errorMessage(ErrorCodes error) const { + QString message; + switch (error) { + case NoError: + break; + case NoConnectionError: + message = tr("No network connection available"); + break; + case CommunicationError: + message = tr("Failed to communicate with the Nextcloud server"); + break; + case LocalFileReadError: + message = tr("An error happened while reading from the local storage"); + break; + case LocalFileWriteError: + message = tr("An error happened while writing to the local storage"); + break; + case SslHandshakeError: + message = tr("An error occured while establishing an encrypted connection"); + break; + case AuthenticationError: + message = tr("Could not authenticate to the Nextcloud instance"); + break; + default: + message = tr("Unknown error"); + break; + } + return message; +} + void NotesApi::verifyUrl(QUrl url) { emit urlValidChanged(url.isValid()); if (m_url.isValid() && !m_url.scheme().isEmpty() && !m_url.host().isEmpty()) { - getStatus(); + getNcStatus(); } } @@ -335,7 +377,7 @@ void NotesApi::replyFinished(QNetworkReply *reply) { else if (reply == m_statusReply) { qDebug() << "Status reply"; if (json.isObject()) - updateStatus(json.object()); + updateNcStatus(json.object()); m_statusReply = NULL; } else if (m_notesReplies.contains(reply)) { @@ -363,17 +405,17 @@ void NotesApi::replyFinished(QNetworkReply *reply) { else { if (reply == m_loginReply) { m_loginReply = NULL; - m_loginStatus = RequestStatus::StatusError; + m_loginStatus = LoginStatus::LoginFailed; emit loginStatusChanged(m_loginStatus); } else if (reply == m_pollReply) { m_pollReply = NULL; - m_loginStatus = RequestStatus::StatusError; + m_loginStatus = LoginStatus::LoginFlowV2Polling; emit loginStatusChanged(m_loginStatus); } else if (reply == m_statusReply) { m_statusReply = NULL; - updateStatus(QJsonObject()); + updateNcStatus(QJsonObject()); //m_statusStatus = RequestStatus::StatusError; //emit statusStatusChanged(m_statusStatus); } @@ -412,7 +454,7 @@ QUrl NotesApi::apiEndpointUrl(const QString endpoint) const { return url; } -void NotesApi::updateStatus(const QJsonObject &status) { +void NotesApi::updateNcStatus(const QJsonObject &status) { if (m_status_installed != status.value("installed").toBool()) { m_status_installed = status.value("installed").toBool(); emit statusInstalledChanged(m_status_installed); @@ -446,10 +488,17 @@ void NotesApi::updateStatus(const QJsonObject &status) { emit statusExtendedSupportChanged(m_status_extendedSupport); } if (status.isEmpty()) - m_statusStatus = RequestStatus::StatusError; + setNcStatusStatus(NextcloudStatus::NextcloudFailed); else - m_statusStatus = RequestStatus::StatusFinished; - emit statusStatusChanged(m_statusStatus); + setNcStatusStatus(NextcloudStatus::NextcloudSuccess); +} + +void NotesApi::setNcStatusStatus(NextcloudStatus status, bool *changed) { + *changed = status != m_ncStatusStatus; + if (*changed) { + m_ncStatusStatus = status; + emit ncStatusStatusChanged(m_ncStatusStatus); + } } bool NotesApi::updateLoginFlow(const QJsonObject &login) { @@ -457,32 +506,29 @@ bool NotesApi::updateLoginFlow(const QJsonObject &login) { QString token; if (!login.isEmpty()) { QJsonObject poll = login.value("poll").toObject(); - if (!poll.isEmpty()) { - url = poll.value("endpoint").toString() ; + url = login.value("login").toString(); + if (!poll.isEmpty() && url.isValid()) { + if (url != m_loginUrl) { + m_loginUrl = url; + emit loginUrlChanged(m_loginUrl); + } + url = poll.value("endpoint").toString(); token = poll.value("token").toString(); if (url.isValid() && !token.isEmpty()) { m_pollUrl = url; qDebug() << "Poll URL: " << m_pollUrl; m_pollToken = token; qDebug() << "Poll Token: " << m_pollToken; - } - else { - qDebug() << "Invalid Poll URL:" << url; + setLoginStatus(LoginStatus::LoginFlowV2Polling); + m_loginPollTimer.start(); + return true; } } - - url = login.value("login").toString(); - if (m_loginUrl != url && url.isValid() && m_pollUrl.isValid() && !m_pollToken.isEmpty()) { - m_loginUrl = url; - m_loginStatus = RequestStatus::StatusBusy; - emit loginUrlChanged(m_loginUrl); - emit loginStatusChanged(m_loginStatus); - m_loginPollTimer.start(); - return true; - } - else { - abortFlowV2Login(); - } + } + else { + qDebug() << "Invalid Poll Data:" << login; + setLoginStatus(LoginStatus::LoginFlowV2Failed); + abortFlowV2Login(); } return false; } @@ -504,40 +550,17 @@ bool NotesApi::updateLoginCredentials(const QJsonObject &credentials) { } if (!serverAddr.isEmpty() && !loginName.isEmpty() && !appPassword.isEmpty()) { qDebug() << "Login successfull for user" << loginName << "on" << serverAddr; - m_loginStatus = RequestStatus::StatusFinished; - emit loginStatusChanged(m_loginStatus); + setLoginStatus(LoginStatus::LoginFlowV2Success); return true; } qDebug() << "Login failed for user" << loginName << "on" << serverAddr; return false; } -const QString NotesApi::errorMessage(int error) const { - QString message; - switch (error) { - case NoError: - break; - case NoConnectionError: - message = tr("No network connection available"); - break; - case CommunicationError: - message = tr("Failed to communicate with the Nextcloud server"); - break; - case LocalFileReadError: - message = tr("An error happened while reading from the local storage"); - break; - case LocalFileWriteError: - message = tr("An error happened while writing to the local storage"); - break; - case SslHandshakeError: - message = tr("An error occured while establishing an encrypted connection"); - break; - case AuthenticationError: - message = tr("Could not authenticate to the Nextcloud instance"); - break; - default: - message = tr("Unknown error"); - break; +void NotesApi::setLoginStatus(LoginStatus status, bool *changed) { + *changed = status != m_loginStatus; + if (*changed) { + m_loginStatus = status; + emit loginStatusChanged(m_loginStatus); } - return message; } diff --git a/src/notesapi.h b/src/notesapi.h index 9b63401..b7c8e1a 100644 --- a/src/notesapi.h +++ b/src/notesapi.h @@ -13,6 +13,7 @@ #define STATUS_ENDPOINT "/status.php" #define LOGIN_ENDPOINT "/index.php/login/v2" #define NOTES_ENDPOINT "/index.php/apps/notes/api/v0.2" +#define POLL_INTERVALL 5000 class NotesApi : public QObject { @@ -76,11 +77,27 @@ public: Q_PROPERTY(bool busy READ busy NOTIFY busyChanged) bool busy() const { return !m_notesReplies.empty();; } - enum RequestStatus { StatusNone, StatusInitiated, StatusBusy, StatusFinished, StatusError }; - Q_ENUM(RequestStatus) + enum NextcloudStatus { + NextcloudUnknown, // Nothing known about the nextcloud server + NextcloudBusy, // Getting information from the nextcloud server + NextcloudSuccess, // Got information about the nextcloud server + NextcloudFailed // Error getting information from the nextcloud server, see error() + }; + Q_ENUM(NextcloudStatus) + enum LoginStatus { + LoginUnknown, // Inital unknown state + LoginLegacyReady, // Ready for legacy login + LoginFlowV2Initiating, // Initiating login flow v2 + LoginFlowV2Polling, // Ready for login flow v2 + LoginFlowV2Success, // Finished login flow v2 + LoginFlowV2Failed, // An error in login flow v2 + LoginSuccess, // Login has been verified successfull + LoginFailed // Login has failed, see error() + }; + Q_ENUM(LoginStatus) - Q_PROPERTY(RequestStatus statusStatus READ statusStatus NOTIFY statusStatusChanged) - RequestStatus statusStatus() const { return m_statusStatus; } + Q_PROPERTY(NextcloudStatus ncStatusStatus READ ncStatusStatus NOTIFY ncStatusStatusChanged) + NextcloudStatus ncStatusStatus() const { return m_ncStatusStatus; } Q_PROPERTY(bool statusInstalled READ statusInstalled NOTIFY statusInstalledChanged) bool statusInstalled() const { return m_status_installed; } Q_PROPERTY(bool statusMaintenance READ statusMaintenance NOTIFY statusMaintenanceChanged) @@ -98,12 +115,12 @@ public: Q_PROPERTY(bool statusExtendedSupport READ statusExtendedSupport NOTIFY statusExtendedSupportChanged) bool statusExtendedSupport() const { return m_status_extendedSupport; } - Q_PROPERTY(RequestStatus loginStatus READ loginStatus NOTIFY loginStatusChanged) - RequestStatus loginStatus() const { return m_loginStatus; } + Q_PROPERTY(LoginStatus loginStatus READ loginStatus NOTIFY loginStatusChanged) + LoginStatus loginStatus() const { return m_loginStatus; } Q_PROPERTY(QUrl loginUrl READ loginUrl NOTIFY loginUrlChanged) QUrl loginUrl() const { return m_loginUrl; } - Q_INVOKABLE bool getStatus(); + Q_INVOKABLE bool getNcStatus(); Q_INVOKABLE bool initiateFlowV2Login(); Q_INVOKABLE void abortFlowV2Login(); Q_INVOKABLE void getAllNotes(QStringList excludeFields = QStringList()); @@ -122,7 +139,7 @@ public: SslHandshakeError, AuthenticationError }; - Q_INVOKABLE const QString errorMessage(int error) const; + Q_INVOKABLE const QString errorMessage(ErrorCodes error) const; signals: void sslVerifyChanged(bool verify); @@ -140,7 +157,7 @@ signals: void lastSyncChanged(QDateTime lastSync); void busyChanged(bool busy); - void statusStatusChanged(RequestStatus status); + void ncStatusStatusChanged(NextcloudStatus status); void statusInstalledChanged(bool installed); void statusMaintenanceChanged(bool maintenance); void statusNeedsDbUpgradeChanged(bool needsDbUpgrade); @@ -150,9 +167,9 @@ signals: void statusProductNameChanged(QString productName); void statusExtendedSupportChanged(bool extendedSupport); - void loginStatusChanged(RequestStatus status); + void loginStatusChanged(LoginStatus status); void loginUrlChanged(QUrl url); - void error(int error); + void error(ErrorCodes error); public slots: @@ -178,8 +195,9 @@ private: // Nextcloud status.php const QString m_statusEndpoint; QNetworkReply* m_statusReply; - void updateStatus(const QJsonObject &status); - RequestStatus m_statusStatus; + void updateNcStatus(const QJsonObject &status); + NextcloudStatus m_ncStatusStatus; + void setNcStatusStatus(NextcloudStatus status, bool *changed = NULL); bool m_status_installed; bool m_status_maintenance; bool m_status_needsDbUpgrade; @@ -195,7 +213,8 @@ private: QNetworkReply* m_pollReply; bool updateLoginFlow(const QJsonObject &login); bool updateLoginCredentials(const QJsonObject &credentials); - RequestStatus m_loginStatus; + LoginStatus m_loginStatus; + void setLoginStatus(LoginStatus status, bool *changed = NULL); QTimer m_loginPollTimer; QUrl m_loginUrl; QUrl m_pollUrl;