From 7e1cc5cf1e99a67f92e7a2d5428b0f24b785eb7f Mon Sep 17 00:00:00 2001 From: Scharel Clemens Date: Mon, 1 Feb 2021 22:29:30 +0100 Subject: [PATCH] Some work on Generic Nextcloud API class --- src/nextcloudapi.cpp | 189 +++++++++++++++++++++++++++++++++++++------ src/nextcloudapi.h | 41 ++++++---- 2 files changed, 192 insertions(+), 38 deletions(-) diff --git a/src/nextcloudapi.cpp b/src/nextcloudapi.cpp index e1d86d1..5376050 100644 --- a/src/nextcloudapi.cpp +++ b/src/nextcloudapi.cpp @@ -1,12 +1,11 @@ #include "nextcloudapi.h" #include #include -#include NextcloudApi::NextcloudApi(QObject *parent) : QObject(parent) { // Initial status - setStatus(ApiCallStatus::ApiUnknown); + setStatusStatus(ApiCallStatus::ApiUnknown); setLoginStatus(LoginStatus::LoginUnknown); setCababilitiesStatus(ApiCallStatus::ApiUnknown); setUserListStatus(ApiCallStatus::ApiUnknown); @@ -245,11 +244,11 @@ bool NextcloudApi::del(const QString& endpoint, bool authenticated) { bool NextcloudApi::getStatus() { if (get(STATUS_ENDPOINT, false)) { - setStatus(ApiCallStatus::ApiBusy); + setStatusStatus(ApiCallStatus::ApiBusy); return true; } else { - setStatus(ApiCallStatus::ApiFailed); + setStatusStatus(ApiCallStatus::ApiFailed); } return false; } @@ -278,7 +277,7 @@ void NextcloudApi::abortFlowV2Login() { } bool NextcloudApi::verifyLogin() { - return get(USER_CAPABILITIES_ENDPOINT.arg(username()), true); + return get(USER_METADATA_ENDPOINT.arg(username()), true); } bool NextcloudApi::getAppPassword() { @@ -337,7 +336,7 @@ void NextcloudApi::replyFinished(QNetworkReply* reply) { QByteArray data = reply->readAll(); QJsonDocument json = QJsonDocument::fromJson(data); - qDebug() << data; + //qDebug() << data; switch (reply->error()) { case QNetworkReply::NoError: @@ -345,21 +344,27 @@ void NextcloudApi::replyFinished(QNetworkReply* reply) { switch (reply->operation()) { case QNetworkAccessManager::GetOperation: if (reply->url().toString().endsWith(STATUS_ENDPOINT)) { + qDebug() << "Nextcloud status.php"; updateStatus(json.object()); } else if (reply->url().toString().endsWith(GET_APPPASSWORD_ENDPOINT)) { + qDebug() << "App password received"; updateAppPassword(json.object()); } else if (reply->url().toString().endsWith(LIST_USERS_ENDPOINT)) { + qDebug() << "User list received"; updateUserList(json.object()); } else if (reply->url().toString().contains(USER_METADATA_ENDPOINT)) { + qDebug() << "User metadata for" << reply->url().toString().split('/').last() << "received"; updateUserMeta(json.object()); } else if (reply->url().toString().endsWith(CAPABILITIES_ENDPOINT)) { + qDebug() << "Capabilites received"; updateCapabilities(json.object()); } else { + qDebug() << "GET reply received"; emit getFinished(reply); break; } @@ -368,9 +373,10 @@ void NextcloudApi::replyFinished(QNetworkReply* reply) { break; case QNetworkAccessManager::PutOperation: if (reply->url().toString().endsWith(DIRECT_DOWNLOAD_ENDPOINT)) { - // TODO + qDebug() << "This function is not yet implemented!"; } else { + qDebug() << "PUT reply received"; emit putFinished(reply); break; } @@ -379,12 +385,15 @@ void NextcloudApi::replyFinished(QNetworkReply* reply) { break; case QNetworkAccessManager::PostOperation: if (reply->url().toString().endsWith(LOGIN_FLOWV2_ENDPOINT)) { - + qDebug() << "Login Flow v2 initiated."; + updateLoginFlow(json.object()); } else if (reply->url() == m_pollUrl) { - + qDebug() << "Login Flow v2 finished."; + updateLoginCredentials(json.object()); } else { + qDebug() << "POST reply received"; emit postFinished(reply); break; } @@ -393,9 +402,10 @@ void NextcloudApi::replyFinished(QNetworkReply* reply) { break; case QNetworkAccessManager::DeleteOperation: if (reply->url().toString().endsWith(DEL_APPPASSWORD_ENDPOINT)) { - + deleteAppPassword(json.object()); } else { + qDebug() << "DELETE reply received"; emit delFinished(reply); break; } @@ -403,62 +413,193 @@ void NextcloudApi::replyFinished(QNetworkReply* reply) { reply->deleteLater(); break; default: - qDebug() << "Unknown operation" << reply->operation() << reply->url(); + qDebug() << "Unknown reply received:" << reply->operation() << reply->url(); m_replies.removeOne(reply); reply->deleteLater(); break; } break; case QNetworkReply::AuthenticationRequiredError: + qDebug() << reply->errorString(); emit apiError(AuthenticationError); break; case QNetworkReply::ContentNotFoundError: if (reply->url() == m_pollUrl) { + qDebug() << "Login Flow v2 not finished yet."; emit apiError(NoError); } else { + qDebug() << reply->errorString(); emit apiError(CommunicationError); } break; default: + qDebug() << reply->errorString(); emit apiError(CommunicationError); break; } } -bool NextcloudApi::updateStatus(const QJsonObject &status) { - +bool NextcloudApi::updateStatus(const QJsonObject &json) { + bool tmpBool; + QString tmpString; + QVersionNumber tmpVersion; + if (!json.isEmpty()) { + setStatusStatus(ApiSuccess); + tmpBool = json.value("installed").toBool(); + if (m_status_installed != tmpBool) { + m_status_installed = tmpBool; + emit statusInstalledChanged(m_status_installed); + } + tmpBool = json.value("maintenance").toBool(); + if (m_status_maintenance != tmpBool) { + m_status_maintenance = tmpBool; + emit statusMaintenanceChanged(m_status_maintenance); + } + tmpBool = json.value("needsDbUpgrade").toBool(); + if (m_status_needsDbUpgrade != tmpBool) { + m_status_needsDbUpgrade = tmpBool; + emit statusNeedsDbUpgradeChanged(m_status_needsDbUpgrade); + } + tmpVersion = QVersionNumber::fromString(json.value("version").toString()); + if (m_status_version != tmpVersion) { + m_status_version = tmpVersion; + emit statusVersionChanged(m_status_version.toString()); + } + tmpString = json.value("versionstring").toString(); + if (m_status_versionstring != tmpString) { + m_status_versionstring = tmpString; + emit statusVersionStringChanged(m_status_versionstring); + } + tmpString = json.value("edition").toString(); + if (m_status_edition != tmpString) { + m_status_edition = tmpString; + emit statusEditionChanged(m_status_edition); + } + tmpString = json.value("productname").toString(); + if (m_status_productname != tmpString) { + m_status_productname = tmpString; + emit statusProductNameChanged(m_status_productname); + } + tmpBool = json.value("extendedSupport").toBool(); + if (m_status_extendedSupport != tmpBool) { + m_status_extendedSupport = tmpBool; + emit statusExtendedSupportChanged(m_status_extendedSupport); + } + return true; + } + else { + setStatusStatus(ApiFailed); + } + return false; } -void NextcloudApi::setStatus(ApiCallStatus status, bool *changed) { - +void NextcloudApi::setStatusStatus(ApiCallStatus status, bool *changed) { + if (status != m_statusStatus) { + m_statusStatus = status; + if (changed) *changed = true; + emit statusStatusChanged(m_statusStatus); + } } -bool NextcloudApi::updateLoginFlow(const QJsonObject &login) { +bool NextcloudApi::updateLoginFlow(const QJsonObject &json) { + if (!json.isEmpty()) { + QUrl loginUrl = json.value("login").toString(); + QJsonObject poll = json.value("poll").toObject(); + m_pollUrl = poll.value("endpoint").toString(); + m_pollToken = poll.value("token").toString(); + if (m_pollUrl.isValid() && !m_pollToken.isEmpty() && loginUrl.isValid()) { + if (m_loginUrl != loginUrl) { + m_loginUrl = loginUrl; + emit loginUrlChanged(m_loginUrl); + } + setLoginStatus(LoginStatus::LoginFlowV2Polling); + m_loginPollTimer.start(); + return true; + } + } + else { + setLoginStatus(LoginStatus::LoginFlowV2Failed); + abortFlowV2Login(); + } + return false; } -bool NextcloudApi::updateLoginCredentials(const QJsonObject &credentials) { - +bool NextcloudApi::updateLoginCredentials(const QJsonObject &json) { + QString serverAddr; + QString loginName; + QString appPassword; + if (!json.isEmpty()) { + serverAddr = json.value("server").toString(); + if (!serverAddr.isEmpty()) setServer(serverAddr); + loginName = json.value("loginName").toString(); + if (!loginName.isEmpty()) setUsername(loginName); + appPassword = json.value("appPassword").toString(); + if (!appPassword.isEmpty()) setPassword(appPassword); + } + if (!serverAddr.isEmpty() && !loginName.isEmpty() && !appPassword.isEmpty()) { + abortFlowV2Login(); + qDebug() << "Login successfull for user" << loginName << "on" << serverAddr; + setLoginStatus(LoginStatus::LoginFlowV2Success); + return true; + } + qDebug() << "Login Flow v2 failed!"; + return false; } -bool NextcloudApi::updateAppPassword(const QJsonObject &password) { +bool NextcloudApi::updateAppPassword(const QJsonObject &json) { + QJsonObject ocs = json.value("ocs").toObject(); + QJsonObject data = ocs.value("data").toObject(); + QJsonValue password = data.value("apppassword"); + if (password.isString()) { + setPassword(password.toString()); + return true; + } + return false; +} +bool NextcloudApi::deleteAppPassword(const QJsonObject &json) { + setPassword(QString()); } void NextcloudApi::setLoginStatus(LoginStatus status, bool *changed) { - + if (status != m_loginStatus) { + m_loginStatus = status; + if (changed) *changed = true; + emit loginStatusChanged(m_loginStatus); + } } -bool NextcloudApi::updateUserList(const QJsonObject &users) { - +bool NextcloudApi::updateUserList(const QJsonObject &json) { + QJsonObject ocs = json.value("ocs").toObject(); + QJsonObject data = ocs.value("data").toObject(); + QJsonValue list = data.value("users"); + if (list.isArray()) { + QJsonArray array = list.toArray(); + QStringList userList; + for (int i = 0; i < array.size(); ++i) { + QString user = array.at(0).toString(); + if (!m_userList.contains(user) && !user.isEmpty()) { + userList << user; + } + } + if (userList != m_userList && !userList.isEmpty()) { + setUserListStatus(ApiSuccess); + m_userList = userList; + emit userListChanged(m_userList); + } + return true; + } + setUserListStatus(ApiFailed); + return false; } void NextcloudApi::setUserListStatus(ApiCallStatus status, bool *changed) { } -bool NextcloudApi::updateUserMeta(const QJsonObject &userMeta) { +bool NextcloudApi::updateUserMeta(const QJsonObject &json) { } @@ -466,7 +607,7 @@ void NextcloudApi::setUserMetaStatus(ApiCallStatus status, bool *changed) { } -bool NextcloudApi::updateCapabilities(const QJsonObject &capabilities) { +bool NextcloudApi::updateCapabilities(const QJsonObject &json) { } diff --git a/src/nextcloudapi.h b/src/nextcloudapi.h index 4dcf353..671577e 100644 --- a/src/nextcloudapi.h +++ b/src/nextcloudapi.h @@ -2,7 +2,9 @@ #define NEXTCLOUDAPI_H #include +#include #include +#include #include #include #include @@ -62,13 +64,15 @@ class NextcloudApi : public QObject Q_PROPERTY(QString statusEdition READ statusEdition NOTIFY statusEditionChanged) Q_PROPERTY(QString statusProductName READ statusProductName NOTIFY statusProductNameChanged) Q_PROPERTY(bool statusExtendedSupport READ statusExtendedSupport NOTIFY statusExtendedSupportChanged) - Q_PROPERTY(bool loginFlowV2Possible READ loginFlowV2Possible NOTIFY loginFlowV2PossibleChanged) // Login status + Q_PROPERTY(bool loginFlowV2Possible READ loginFlowV2Possible NOTIFY loginFlowV2PossibleChanged) + Q_PROPERTY(QUrl loginUrl READ loginUrl NOTIFY loginUrlChanged) Q_PROPERTY(ApiCallStatus loginStatus READ loginStatus NOTIFY loginStatusChanged) // User(s) status Q_PROPERTY(ApiCallStatus userListStatus READ userListStatus NOTIFY userListStatusChanged) + Q_PROPERTY(QStringList userList READ userList NOTIFY userListChanged) Q_PROPERTY(ApiCallStatus userMetaStatus READ userMetaStatus NOTIFY userMetaStatusChanged) // Nextcloud capabilities @@ -142,13 +146,15 @@ public: QString statusEdition() const { return m_status_edition; } QString statusProductName() const { return m_status_productname; } bool statusExtendedSupport() const { return m_status_extendedSupport; } - bool loginFlowV2Possible() const { return QVersionNumber::fromString(statusVersion()) >= QVersionNumber(LOGIN_FLOWV2_MIN_VERSION); } // Login status + bool loginFlowV2Possible() const { return QVersionNumber::fromString(statusVersion()) >= QVersionNumber(LOGIN_FLOWV2_MIN_VERSION); } + QUrl loginUrl() const { return m_loginUrl; } LoginStatus loginStatus() const { return m_loginStatus; } // User(s) status ApiCallStatus userListStatus() const { return m_userListStatus; } + QStringList userList() const { return m_userList; } ApiCallStatus userMetaStatus() const { return m_userMetaStatus; } // Nextcloud capabilities @@ -200,9 +206,6 @@ signals: void networkAccessibleChanged(bool accessible); void busyChanged(bool busy); - // Nextcloud capabilities - void capabilitiesStatusChanged(ApiCallStatus status); - // Nextcloud status (status.php) void statusStatusChanged(ApiCallStatus status); void statusInstalledChanged(bool installed); @@ -215,8 +218,17 @@ signals: void statusExtendedSupportChanged(bool extendedSupport); // Login status - void loginStatusChanged(LoginStatus status); void loginFlowV2PossibleChanged(bool loginV2possible); + void loginUrlChanged(QUrl url); + void loginStatusChanged(LoginStatus status); + + // User(s) status + void userListStatusChanged(ApiCallStatus status); + void userListChanged(QStringList users); + void userMetaStatusChanged(ApiCallStatus status); + + // Nextcloud capabilities + void capabilitiesStatusChanged(ApiCallStatus status); // API helper updates void getFinished(QNetworkReply* reply); @@ -241,8 +253,8 @@ private: QNetworkRequest m_authenticatedRequest; // Nextcloud status.php - bool updateStatus(const QJsonObject &status); - void setStatus(ApiCallStatus status, bool *changed = NULL); + bool updateStatus(const QJsonObject &json); + void setStatusStatus(ApiCallStatus status, bool *changed = NULL); ApiCallStatus m_statusStatus; bool m_status_installed; bool m_status_maintenance; @@ -254,28 +266,29 @@ private: bool m_status_extendedSupport; // Nextcloud capabilities - bool updateCapabilities(const QJsonObject &capabilities); + bool updateCapabilities(const QJsonObject &json); void setCababilitiesStatus(ApiCallStatus status, bool *changed = NULL); ApiCallStatus m_capabilitiesStatus; QJsonObject m_capabilities; // Nextcloud users list - bool updateUserList(const QJsonObject &users); + bool updateUserList(const QJsonObject &json); void setUserListStatus(ApiCallStatus status, bool *changed = NULL); ApiCallStatus m_userListStatus; QStringList m_userList; // Nextcloud user metadata - bool updateUserMeta(const QJsonObject &userMeta); + bool updateUserMeta(const QJsonObject &json); void setUserMetaStatus(ApiCallStatus status, bool *changed = NULL); ApiCallStatus m_userMetaStatus; QHash m_userMeta; // Nextcloud Login Flow v2 // https://docs.nextcloud.com/server/latest/developer_manual/client_apis/LoginFlow/index.html#login-flow-v2 - bool updateLoginFlow(const QJsonObject &login); - bool updateLoginCredentials(const QJsonObject &credentials); - bool updateAppPassword(const QJsonObject &password); + bool updateLoginFlow(const QJsonObject &json); + bool updateLoginCredentials(const QJsonObject &json); + bool updateAppPassword(const QJsonObject &json); + bool deleteAppPassword(const QJsonObject &json); void setLoginStatus(LoginStatus status, bool *changed = NULL); LoginStatus m_loginStatus; QTimer m_loginPollTimer;