More state informations

This commit is contained in:
Scharel Clemens 2020-03-21 20:09:07 +01:00
parent 601985436d
commit 00921391a2
2 changed files with 134 additions and 92 deletions

View file

@ -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<int>)), this, SLOT(saveToFile(QModelIndex,QModelIndex,QVector<int>)));
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<QSslError>)), this, SLOT(sslError(QNetworkReply*,QList<QSslError>)));
disconnect(mp_model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)), this, SLOT(saveToFile(QModelIndex,QModelIndex,QVector<int>)));
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);
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
qDebug() << "POST" << url.toDisplayString();
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
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();
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() {
if (m_pollUrl.isValid() && !m_pollUrl.scheme().isEmpty() && !m_pollUrl.host().isEmpty() && !m_pollToken.isEmpty()) {
qDebug() << "POST" << m_pollUrl.toDisplayString();
if (m_pollUrl.isValid() && !m_pollUrl.scheme().isEmpty() && !m_pollUrl.host().isEmpty() && !m_pollToken.isEmpty()) {
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,33 +506,30 @@ 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;
}
}
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);
setLoginStatus(LoginStatus::LoginFlowV2Polling);
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;
}

View file

@ -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;