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) : QObject(parent), m_statusEndpoint(statusEndpoint), m_loginEndpoint(loginEndpoint), m_notesEndpoint(notesEndpoint)
{ {
// TODO verify connections (also in destructor) // TODO verify connections (also in destructor)
m_loginPollTimer.setInterval(5000); m_loginPollTimer.setInterval(POLL_INTERVALL);
connect(&m_loginPollTimer, SIGNAL(timeout()), this, SLOT(pollLoginUrl())); connect(&m_loginPollTimer, SIGNAL(timeout()), this, SLOT(pollLoginUrl()));
m_statusReply = NULL; m_statusReply = NULL;
m_loginReply = NULL; m_loginReply = NULL;
m_pollReply = NULL; m_pollReply = NULL;
m_statusStatus = RequestStatus::StatusNone; setNcStatusStatus(NextcloudStatus::NextcloudUnknown);
m_loginStatus = RequestStatus::StatusNone; setLoginStatus(LoginStatus::LoginUnknown);
m_ncStatusStatus = NextcloudStatus::NextcloudUnknown;
m_loginStatus = LoginStatus::LoginUnknown;
mp_model = new NotesModel(this); mp_model = new NotesModel(this);
mp_modelProxy = new NotesProxyModel(this); mp_modelProxy = new NotesProxyModel(this);
mp_modelProxy->setSourceModel(mp_model); mp_modelProxy->setSourceModel(mp_model);
@ -37,12 +39,13 @@ NotesApi::NotesApi(const QString statusEndpoint, const QString loginEndpoint, co
} }
NotesApi::~NotesApi() { 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(this, SIGNAL(urlChanged(QUrl)), this, SLOT(verifyUrl(QUrl)));
disconnect(&m_manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), this, SLOT(requireAuthentication(QNetworkReply*,QAuthenticator*))); 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(networkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility)), this, SLOT(onNetworkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility)));
disconnect(&m_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*))); 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(&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(); m_jsonFile.close();
if (mp_modelProxy) if (mp_modelProxy)
delete mp_modelProxy; delete mp_modelProxy;
@ -157,35 +160,38 @@ void NotesApi::setDataFile(QString dataFile) {
} }
} }
bool NotesApi::getStatus() { bool NotesApi::getNcStatus() {
if (m_statusStatus != RequestStatus::StatusBusy) {
m_statusStatus = RequestStatus::StatusBusy;
emit statusStatusChanged(m_statusStatus);
}
QUrl url = apiEndpointUrl(m_statusEndpoint); QUrl url = apiEndpointUrl(m_statusEndpoint);
qDebug() << "POST" << url.toDisplayString();
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
qDebug() << "POST" << url.toDisplayString(); setNcStatusStatus(NextcloudStatus::NextcloudBusy);
m_request.setUrl(url); m_request.setUrl(url);
m_statusReply = m_manager.post(m_request, QByteArray()); m_statusReply = m_manager.post(m_request, QByteArray());
return true; return true;
} }
else {
qDebug() << "URL not valid!";
setNcStatusStatus(NextcloudStatus::NextcloudUnknown);
}
return false; return false;
} }
bool NotesApi::initiateFlowV2Login() { bool NotesApi::initiateFlowV2Login() {
if (m_loginStatus == RequestStatus::StatusInitiated || m_loginStatus == RequestStatus::StatusBusy) { if (m_loginStatus == LoginStatus::LoginFlowV2Initiating || m_loginStatus == LoginStatus::LoginFlowV2Polling) {
abortFlowV2Login(); abortFlowV2Login();
} }
if (m_loginStatus != RequestStatus::StatusInitiated) { QUrl url = apiEndpointUrl(m_loginEndpoint);
m_loginStatus = RequestStatus::StatusInitiated; qDebug() << "POST" << url.toDisplayString();
emit loginStatusChanged(m_loginStatus); if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
QUrl url = apiEndpointUrl(m_loginEndpoint); setLoginStatus(LoginStatus::LoginFlowV2Initiating);
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { m_request.setUrl(url);
qDebug() << "POST" << url.toDisplayString(); m_loginReply = m_manager.post(m_request, QByteArray());
m_request.setUrl(url); return true;
m_loginReply = m_manager.post(m_request, QByteArray()); }
return true; else {
} qDebug() << "URL not valid!";
setLoginStatus(LoginStatus::LoginFlowV2Failed);
abortFlowV2Login();
} }
return false; return false;
} }
@ -200,14 +206,20 @@ void NotesApi::abortFlowV2Login() {
m_loginReply->abort(); m_loginReply->abort();
if (m_pollReply->isRunning()) if (m_pollReply->isRunning())
m_pollReply->abort(); m_pollReply->abort();
setLoginStatus(LoginStatus::LoginUnknown);
} }
void NotesApi::pollLoginUrl() { void NotesApi::pollLoginUrl() {
qDebug() << "POST" << m_pollUrl.toDisplayString();
if (m_pollUrl.isValid() && !m_pollUrl.scheme().isEmpty() && !m_pollUrl.host().isEmpty() && !m_pollToken.isEmpty()) { 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_request.setUrl(m_pollUrl);
m_pollReply = m_manager.post(m_request, QByteArray("token=").append(m_pollToken)); 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) { void NotesApi::getAllNotes(QStringList excludeFields) {
@ -279,10 +291,40 @@ void NotesApi::deleteNote(double noteId) {
mp_model->removeNote(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) { void NotesApi::verifyUrl(QUrl url) {
emit urlValidChanged(url.isValid()); emit urlValidChanged(url.isValid());
if (m_url.isValid() && !m_url.scheme().isEmpty() && !m_url.host().isEmpty()) { 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) { else if (reply == m_statusReply) {
qDebug() << "Status reply"; qDebug() << "Status reply";
if (json.isObject()) if (json.isObject())
updateStatus(json.object()); updateNcStatus(json.object());
m_statusReply = NULL; m_statusReply = NULL;
} }
else if (m_notesReplies.contains(reply)) { else if (m_notesReplies.contains(reply)) {
@ -363,17 +405,17 @@ void NotesApi::replyFinished(QNetworkReply *reply) {
else { else {
if (reply == m_loginReply) { if (reply == m_loginReply) {
m_loginReply = NULL; m_loginReply = NULL;
m_loginStatus = RequestStatus::StatusError; m_loginStatus = LoginStatus::LoginFailed;
emit loginStatusChanged(m_loginStatus); emit loginStatusChanged(m_loginStatus);
} }
else if (reply == m_pollReply) { else if (reply == m_pollReply) {
m_pollReply = NULL; m_pollReply = NULL;
m_loginStatus = RequestStatus::StatusError; m_loginStatus = LoginStatus::LoginFlowV2Polling;
emit loginStatusChanged(m_loginStatus); emit loginStatusChanged(m_loginStatus);
} }
else if (reply == m_statusReply) { else if (reply == m_statusReply) {
m_statusReply = NULL; m_statusReply = NULL;
updateStatus(QJsonObject()); updateNcStatus(QJsonObject());
//m_statusStatus = RequestStatus::StatusError; //m_statusStatus = RequestStatus::StatusError;
//emit statusStatusChanged(m_statusStatus); //emit statusStatusChanged(m_statusStatus);
} }
@ -412,7 +454,7 @@ QUrl NotesApi::apiEndpointUrl(const QString endpoint) const {
return url; return url;
} }
void NotesApi::updateStatus(const QJsonObject &status) { void NotesApi::updateNcStatus(const QJsonObject &status) {
if (m_status_installed != status.value("installed").toBool()) { if (m_status_installed != status.value("installed").toBool()) {
m_status_installed = status.value("installed").toBool(); m_status_installed = status.value("installed").toBool();
emit statusInstalledChanged(m_status_installed); emit statusInstalledChanged(m_status_installed);
@ -446,10 +488,17 @@ void NotesApi::updateStatus(const QJsonObject &status) {
emit statusExtendedSupportChanged(m_status_extendedSupport); emit statusExtendedSupportChanged(m_status_extendedSupport);
} }
if (status.isEmpty()) if (status.isEmpty())
m_statusStatus = RequestStatus::StatusError; setNcStatusStatus(NextcloudStatus::NextcloudFailed);
else else
m_statusStatus = RequestStatus::StatusFinished; setNcStatusStatus(NextcloudStatus::NextcloudSuccess);
emit statusStatusChanged(m_statusStatus); }
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) { bool NotesApi::updateLoginFlow(const QJsonObject &login) {
@ -457,32 +506,29 @@ bool NotesApi::updateLoginFlow(const QJsonObject &login) {
QString token; QString token;
if (!login.isEmpty()) { if (!login.isEmpty()) {
QJsonObject poll = login.value("poll").toObject(); QJsonObject poll = login.value("poll").toObject();
if (!poll.isEmpty()) { url = login.value("login").toString();
url = poll.value("endpoint").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(); token = poll.value("token").toString();
if (url.isValid() && !token.isEmpty()) { if (url.isValid() && !token.isEmpty()) {
m_pollUrl = url; m_pollUrl = url;
qDebug() << "Poll URL: " << m_pollUrl; qDebug() << "Poll URL: " << m_pollUrl;
m_pollToken = token; m_pollToken = token;
qDebug() << "Poll Token: " << m_pollToken; qDebug() << "Poll Token: " << m_pollToken;
} setLoginStatus(LoginStatus::LoginFlowV2Polling);
else { m_loginPollTimer.start();
qDebug() << "Invalid Poll URL:" << url; return true;
} }
} }
}
url = login.value("login").toString(); else {
if (m_loginUrl != url && url.isValid() && m_pollUrl.isValid() && !m_pollToken.isEmpty()) { qDebug() << "Invalid Poll Data:" << login;
m_loginUrl = url; setLoginStatus(LoginStatus::LoginFlowV2Failed);
m_loginStatus = RequestStatus::StatusBusy; abortFlowV2Login();
emit loginUrlChanged(m_loginUrl);
emit loginStatusChanged(m_loginStatus);
m_loginPollTimer.start();
return true;
}
else {
abortFlowV2Login();
}
} }
return false; return false;
} }
@ -504,40 +550,17 @@ bool NotesApi::updateLoginCredentials(const QJsonObject &credentials) {
} }
if (!serverAddr.isEmpty() && !loginName.isEmpty() && !appPassword.isEmpty()) { if (!serverAddr.isEmpty() && !loginName.isEmpty() && !appPassword.isEmpty()) {
qDebug() << "Login successfull for user" << loginName << "on" << serverAddr; qDebug() << "Login successfull for user" << loginName << "on" << serverAddr;
m_loginStatus = RequestStatus::StatusFinished; setLoginStatus(LoginStatus::LoginFlowV2Success);
emit loginStatusChanged(m_loginStatus);
return true; return true;
} }
qDebug() << "Login failed for user" << loginName << "on" << serverAddr; qDebug() << "Login failed for user" << loginName << "on" << serverAddr;
return false; return false;
} }
const QString NotesApi::errorMessage(int error) const { void NotesApi::setLoginStatus(LoginStatus status, bool *changed) {
QString message; *changed = status != m_loginStatus;
switch (error) { if (*changed) {
case NoError: m_loginStatus = status;
break; emit loginStatusChanged(m_loginStatus);
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;
} }

View file

@ -13,6 +13,7 @@
#define STATUS_ENDPOINT "/status.php" #define STATUS_ENDPOINT "/status.php"
#define LOGIN_ENDPOINT "/index.php/login/v2" #define LOGIN_ENDPOINT "/index.php/login/v2"
#define NOTES_ENDPOINT "/index.php/apps/notes/api/v0.2" #define NOTES_ENDPOINT "/index.php/apps/notes/api/v0.2"
#define POLL_INTERVALL 5000
class NotesApi : public QObject class NotesApi : public QObject
{ {
@ -76,11 +77,27 @@ public:
Q_PROPERTY(bool busy READ busy NOTIFY busyChanged) Q_PROPERTY(bool busy READ busy NOTIFY busyChanged)
bool busy() const { return !m_notesReplies.empty();; } bool busy() const { return !m_notesReplies.empty();; }
enum RequestStatus { StatusNone, StatusInitiated, StatusBusy, StatusFinished, StatusError }; enum NextcloudStatus {
Q_ENUM(RequestStatus) 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) Q_PROPERTY(NextcloudStatus ncStatusStatus READ ncStatusStatus NOTIFY ncStatusStatusChanged)
RequestStatus statusStatus() const { return m_statusStatus; } NextcloudStatus ncStatusStatus() const { return m_ncStatusStatus; }
Q_PROPERTY(bool statusInstalled READ statusInstalled NOTIFY statusInstalledChanged) Q_PROPERTY(bool statusInstalled READ statusInstalled NOTIFY statusInstalledChanged)
bool statusInstalled() const { return m_status_installed; } bool statusInstalled() const { return m_status_installed; }
Q_PROPERTY(bool statusMaintenance READ statusMaintenance NOTIFY statusMaintenanceChanged) Q_PROPERTY(bool statusMaintenance READ statusMaintenance NOTIFY statusMaintenanceChanged)
@ -98,12 +115,12 @@ public:
Q_PROPERTY(bool statusExtendedSupport READ statusExtendedSupport NOTIFY statusExtendedSupportChanged) Q_PROPERTY(bool statusExtendedSupport READ statusExtendedSupport NOTIFY statusExtendedSupportChanged)
bool statusExtendedSupport() const { return m_status_extendedSupport; } bool statusExtendedSupport() const { return m_status_extendedSupport; }
Q_PROPERTY(RequestStatus loginStatus READ loginStatus NOTIFY loginStatusChanged) Q_PROPERTY(LoginStatus loginStatus READ loginStatus NOTIFY loginStatusChanged)
RequestStatus loginStatus() const { return m_loginStatus; } LoginStatus loginStatus() const { return m_loginStatus; }
Q_PROPERTY(QUrl loginUrl READ loginUrl NOTIFY loginUrlChanged) Q_PROPERTY(QUrl loginUrl READ loginUrl NOTIFY loginUrlChanged)
QUrl loginUrl() const { return m_loginUrl; } QUrl loginUrl() const { return m_loginUrl; }
Q_INVOKABLE bool getStatus(); Q_INVOKABLE bool getNcStatus();
Q_INVOKABLE bool initiateFlowV2Login(); Q_INVOKABLE bool initiateFlowV2Login();
Q_INVOKABLE void abortFlowV2Login(); Q_INVOKABLE void abortFlowV2Login();
Q_INVOKABLE void getAllNotes(QStringList excludeFields = QStringList()); Q_INVOKABLE void getAllNotes(QStringList excludeFields = QStringList());
@ -122,7 +139,7 @@ public:
SslHandshakeError, SslHandshakeError,
AuthenticationError AuthenticationError
}; };
Q_INVOKABLE const QString errorMessage(int error) const; Q_INVOKABLE const QString errorMessage(ErrorCodes error) const;
signals: signals:
void sslVerifyChanged(bool verify); void sslVerifyChanged(bool verify);
@ -140,7 +157,7 @@ signals:
void lastSyncChanged(QDateTime lastSync); void lastSyncChanged(QDateTime lastSync);
void busyChanged(bool busy); void busyChanged(bool busy);
void statusStatusChanged(RequestStatus status); void ncStatusStatusChanged(NextcloudStatus status);
void statusInstalledChanged(bool installed); void statusInstalledChanged(bool installed);
void statusMaintenanceChanged(bool maintenance); void statusMaintenanceChanged(bool maintenance);
void statusNeedsDbUpgradeChanged(bool needsDbUpgrade); void statusNeedsDbUpgradeChanged(bool needsDbUpgrade);
@ -150,9 +167,9 @@ signals:
void statusProductNameChanged(QString productName); void statusProductNameChanged(QString productName);
void statusExtendedSupportChanged(bool extendedSupport); void statusExtendedSupportChanged(bool extendedSupport);
void loginStatusChanged(RequestStatus status); void loginStatusChanged(LoginStatus status);
void loginUrlChanged(QUrl url); void loginUrlChanged(QUrl url);
void error(int error); void error(ErrorCodes error);
public slots: public slots:
@ -178,8 +195,9 @@ private:
// Nextcloud status.php // Nextcloud status.php
const QString m_statusEndpoint; const QString m_statusEndpoint;
QNetworkReply* m_statusReply; QNetworkReply* m_statusReply;
void updateStatus(const QJsonObject &status); void updateNcStatus(const QJsonObject &status);
RequestStatus m_statusStatus; NextcloudStatus m_ncStatusStatus;
void setNcStatusStatus(NextcloudStatus status, bool *changed = NULL);
bool m_status_installed; bool m_status_installed;
bool m_status_maintenance; bool m_status_maintenance;
bool m_status_needsDbUpgrade; bool m_status_needsDbUpgrade;
@ -195,7 +213,8 @@ private:
QNetworkReply* m_pollReply; QNetworkReply* m_pollReply;
bool updateLoginFlow(const QJsonObject &login); bool updateLoginFlow(const QJsonObject &login);
bool updateLoginCredentials(const QJsonObject &credentials); bool updateLoginCredentials(const QJsonObject &credentials);
RequestStatus m_loginStatus; LoginStatus m_loginStatus;
void setLoginStatus(LoginStatus status, bool *changed = NULL);
QTimer m_loginPollTimer; QTimer m_loginPollTimer;
QUrl m_loginUrl; QUrl m_loginUrl;
QUrl m_pollUrl; QUrl m_pollUrl;