Some work on Generic Nextcloud API class

This commit is contained in:
Scharel Clemens 2021-02-01 22:29:30 +01:00
parent ffe821d8be
commit 7e1cc5cf1e
2 changed files with 192 additions and 38 deletions

View file

@ -1,12 +1,11 @@
#include "nextcloudapi.h" #include "nextcloudapi.h"
#include <QGuiApplication> #include <QGuiApplication>
#include <QAuthenticator> #include <QAuthenticator>
#include <QJsonDocument>
NextcloudApi::NextcloudApi(QObject *parent) : QObject(parent) NextcloudApi::NextcloudApi(QObject *parent) : QObject(parent)
{ {
// Initial status // Initial status
setStatus(ApiCallStatus::ApiUnknown); setStatusStatus(ApiCallStatus::ApiUnknown);
setLoginStatus(LoginStatus::LoginUnknown); setLoginStatus(LoginStatus::LoginUnknown);
setCababilitiesStatus(ApiCallStatus::ApiUnknown); setCababilitiesStatus(ApiCallStatus::ApiUnknown);
setUserListStatus(ApiCallStatus::ApiUnknown); setUserListStatus(ApiCallStatus::ApiUnknown);
@ -245,11 +244,11 @@ bool NextcloudApi::del(const QString& endpoint, bool authenticated) {
bool NextcloudApi::getStatus() { bool NextcloudApi::getStatus() {
if (get(STATUS_ENDPOINT, false)) { if (get(STATUS_ENDPOINT, false)) {
setStatus(ApiCallStatus::ApiBusy); setStatusStatus(ApiCallStatus::ApiBusy);
return true; return true;
} }
else { else {
setStatus(ApiCallStatus::ApiFailed); setStatusStatus(ApiCallStatus::ApiFailed);
} }
return false; return false;
} }
@ -278,7 +277,7 @@ void NextcloudApi::abortFlowV2Login() {
} }
bool NextcloudApi::verifyLogin() { bool NextcloudApi::verifyLogin() {
return get(USER_CAPABILITIES_ENDPOINT.arg(username()), true); return get(USER_METADATA_ENDPOINT.arg(username()), true);
} }
bool NextcloudApi::getAppPassword() { bool NextcloudApi::getAppPassword() {
@ -337,7 +336,7 @@ void NextcloudApi::replyFinished(QNetworkReply* reply) {
QByteArray data = reply->readAll(); QByteArray data = reply->readAll();
QJsonDocument json = QJsonDocument::fromJson(data); QJsonDocument json = QJsonDocument::fromJson(data);
qDebug() << data; //qDebug() << data;
switch (reply->error()) { switch (reply->error()) {
case QNetworkReply::NoError: case QNetworkReply::NoError:
@ -345,21 +344,27 @@ void NextcloudApi::replyFinished(QNetworkReply* reply) {
switch (reply->operation()) { switch (reply->operation()) {
case QNetworkAccessManager::GetOperation: case QNetworkAccessManager::GetOperation:
if (reply->url().toString().endsWith(STATUS_ENDPOINT)) { if (reply->url().toString().endsWith(STATUS_ENDPOINT)) {
qDebug() << "Nextcloud status.php";
updateStatus(json.object()); updateStatus(json.object());
} }
else if (reply->url().toString().endsWith(GET_APPPASSWORD_ENDPOINT)) { else if (reply->url().toString().endsWith(GET_APPPASSWORD_ENDPOINT)) {
qDebug() << "App password received";
updateAppPassword(json.object()); updateAppPassword(json.object());
} }
else if (reply->url().toString().endsWith(LIST_USERS_ENDPOINT)) { else if (reply->url().toString().endsWith(LIST_USERS_ENDPOINT)) {
qDebug() << "User list received";
updateUserList(json.object()); updateUserList(json.object());
} }
else if (reply->url().toString().contains(USER_METADATA_ENDPOINT)) { else if (reply->url().toString().contains(USER_METADATA_ENDPOINT)) {
qDebug() << "User metadata for" << reply->url().toString().split('/').last() << "received";
updateUserMeta(json.object()); updateUserMeta(json.object());
} }
else if (reply->url().toString().endsWith(CAPABILITIES_ENDPOINT)) { else if (reply->url().toString().endsWith(CAPABILITIES_ENDPOINT)) {
qDebug() << "Capabilites received";
updateCapabilities(json.object()); updateCapabilities(json.object());
} }
else { else {
qDebug() << "GET reply received";
emit getFinished(reply); emit getFinished(reply);
break; break;
} }
@ -368,9 +373,10 @@ void NextcloudApi::replyFinished(QNetworkReply* reply) {
break; break;
case QNetworkAccessManager::PutOperation: case QNetworkAccessManager::PutOperation:
if (reply->url().toString().endsWith(DIRECT_DOWNLOAD_ENDPOINT)) { if (reply->url().toString().endsWith(DIRECT_DOWNLOAD_ENDPOINT)) {
// TODO qDebug() << "This function is not yet implemented!";
} }
else { else {
qDebug() << "PUT reply received";
emit putFinished(reply); emit putFinished(reply);
break; break;
} }
@ -379,12 +385,15 @@ void NextcloudApi::replyFinished(QNetworkReply* reply) {
break; break;
case QNetworkAccessManager::PostOperation: case QNetworkAccessManager::PostOperation:
if (reply->url().toString().endsWith(LOGIN_FLOWV2_ENDPOINT)) { if (reply->url().toString().endsWith(LOGIN_FLOWV2_ENDPOINT)) {
qDebug() << "Login Flow v2 initiated.";
updateLoginFlow(json.object());
} }
else if (reply->url() == m_pollUrl) { else if (reply->url() == m_pollUrl) {
qDebug() << "Login Flow v2 finished.";
updateLoginCredentials(json.object());
} }
else { else {
qDebug() << "POST reply received";
emit postFinished(reply); emit postFinished(reply);
break; break;
} }
@ -393,9 +402,10 @@ void NextcloudApi::replyFinished(QNetworkReply* reply) {
break; break;
case QNetworkAccessManager::DeleteOperation: case QNetworkAccessManager::DeleteOperation:
if (reply->url().toString().endsWith(DEL_APPPASSWORD_ENDPOINT)) { if (reply->url().toString().endsWith(DEL_APPPASSWORD_ENDPOINT)) {
deleteAppPassword(json.object());
} }
else { else {
qDebug() << "DELETE reply received";
emit delFinished(reply); emit delFinished(reply);
break; break;
} }
@ -403,62 +413,193 @@ void NextcloudApi::replyFinished(QNetworkReply* reply) {
reply->deleteLater(); reply->deleteLater();
break; break;
default: default:
qDebug() << "Unknown operation" << reply->operation() << reply->url(); qDebug() << "Unknown reply received:" << reply->operation() << reply->url();
m_replies.removeOne(reply); m_replies.removeOne(reply);
reply->deleteLater(); reply->deleteLater();
break; break;
} }
break; break;
case QNetworkReply::AuthenticationRequiredError: case QNetworkReply::AuthenticationRequiredError:
qDebug() << reply->errorString();
emit apiError(AuthenticationError); emit apiError(AuthenticationError);
break; break;
case QNetworkReply::ContentNotFoundError: case QNetworkReply::ContentNotFoundError:
if (reply->url() == m_pollUrl) { if (reply->url() == m_pollUrl) {
qDebug() << "Login Flow v2 not finished yet.";
emit apiError(NoError); emit apiError(NoError);
} }
else { else {
qDebug() << reply->errorString();
emit apiError(CommunicationError); emit apiError(CommunicationError);
} }
break; break;
default: default:
qDebug() << reply->errorString();
emit apiError(CommunicationError); emit apiError(CommunicationError);
break; 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) { 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) { 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) {
} }

View file

@ -2,7 +2,9 @@
#define NEXTCLOUDAPI_H #define NEXTCLOUDAPI_H
#include <QObject> #include <QObject>
#include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
#include <QJsonArray>
#include <QVersionNumber> #include <QVersionNumber>
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QNetworkRequest> #include <QNetworkRequest>
@ -62,13 +64,15 @@ class NextcloudApi : public QObject
Q_PROPERTY(QString statusEdition READ statusEdition NOTIFY statusEditionChanged) Q_PROPERTY(QString statusEdition READ statusEdition NOTIFY statusEditionChanged)
Q_PROPERTY(QString statusProductName READ statusProductName NOTIFY statusProductNameChanged) Q_PROPERTY(QString statusProductName READ statusProductName NOTIFY statusProductNameChanged)
Q_PROPERTY(bool statusExtendedSupport READ statusExtendedSupport NOTIFY statusExtendedSupportChanged) Q_PROPERTY(bool statusExtendedSupport READ statusExtendedSupport NOTIFY statusExtendedSupportChanged)
Q_PROPERTY(bool loginFlowV2Possible READ loginFlowV2Possible NOTIFY loginFlowV2PossibleChanged)
// Login status // 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) Q_PROPERTY(ApiCallStatus loginStatus READ loginStatus NOTIFY loginStatusChanged)
// User(s) status // User(s) status
Q_PROPERTY(ApiCallStatus userListStatus READ userListStatus NOTIFY userListStatusChanged) Q_PROPERTY(ApiCallStatus userListStatus READ userListStatus NOTIFY userListStatusChanged)
Q_PROPERTY(QStringList userList READ userList NOTIFY userListChanged)
Q_PROPERTY(ApiCallStatus userMetaStatus READ userMetaStatus NOTIFY userMetaStatusChanged) Q_PROPERTY(ApiCallStatus userMetaStatus READ userMetaStatus NOTIFY userMetaStatusChanged)
// Nextcloud capabilities // Nextcloud capabilities
@ -142,13 +146,15 @@ public:
QString statusEdition() const { return m_status_edition; } QString statusEdition() const { return m_status_edition; }
QString statusProductName() const { return m_status_productname; } QString statusProductName() const { return m_status_productname; }
bool statusExtendedSupport() const { return m_status_extendedSupport; } bool statusExtendedSupport() const { return m_status_extendedSupport; }
bool loginFlowV2Possible() const { return QVersionNumber::fromString(statusVersion()) >= QVersionNumber(LOGIN_FLOWV2_MIN_VERSION); }
// Login status // 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; } LoginStatus loginStatus() const { return m_loginStatus; }
// User(s) status // User(s) status
ApiCallStatus userListStatus() const { return m_userListStatus; } ApiCallStatus userListStatus() const { return m_userListStatus; }
QStringList userList() const { return m_userList; }
ApiCallStatus userMetaStatus() const { return m_userMetaStatus; } ApiCallStatus userMetaStatus() const { return m_userMetaStatus; }
// Nextcloud capabilities // Nextcloud capabilities
@ -200,9 +206,6 @@ signals:
void networkAccessibleChanged(bool accessible); void networkAccessibleChanged(bool accessible);
void busyChanged(bool busy); void busyChanged(bool busy);
// Nextcloud capabilities
void capabilitiesStatusChanged(ApiCallStatus status);
// Nextcloud status (status.php) // Nextcloud status (status.php)
void statusStatusChanged(ApiCallStatus status); void statusStatusChanged(ApiCallStatus status);
void statusInstalledChanged(bool installed); void statusInstalledChanged(bool installed);
@ -215,8 +218,17 @@ signals:
void statusExtendedSupportChanged(bool extendedSupport); void statusExtendedSupportChanged(bool extendedSupport);
// Login status // Login status
void loginStatusChanged(LoginStatus status);
void loginFlowV2PossibleChanged(bool loginV2possible); 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 // API helper updates
void getFinished(QNetworkReply* reply); void getFinished(QNetworkReply* reply);
@ -241,8 +253,8 @@ private:
QNetworkRequest m_authenticatedRequest; QNetworkRequest m_authenticatedRequest;
// Nextcloud status.php // Nextcloud status.php
bool updateStatus(const QJsonObject &status); bool updateStatus(const QJsonObject &json);
void setStatus(ApiCallStatus status, bool *changed = NULL); void setStatusStatus(ApiCallStatus status, bool *changed = NULL);
ApiCallStatus m_statusStatus; ApiCallStatus m_statusStatus;
bool m_status_installed; bool m_status_installed;
bool m_status_maintenance; bool m_status_maintenance;
@ -254,28 +266,29 @@ private:
bool m_status_extendedSupport; bool m_status_extendedSupport;
// Nextcloud capabilities // Nextcloud capabilities
bool updateCapabilities(const QJsonObject &capabilities); bool updateCapabilities(const QJsonObject &json);
void setCababilitiesStatus(ApiCallStatus status, bool *changed = NULL); void setCababilitiesStatus(ApiCallStatus status, bool *changed = NULL);
ApiCallStatus m_capabilitiesStatus; ApiCallStatus m_capabilitiesStatus;
QJsonObject m_capabilities; QJsonObject m_capabilities;
// Nextcloud users list // Nextcloud users list
bool updateUserList(const QJsonObject &users); bool updateUserList(const QJsonObject &json);
void setUserListStatus(ApiCallStatus status, bool *changed = NULL); void setUserListStatus(ApiCallStatus status, bool *changed = NULL);
ApiCallStatus m_userListStatus; ApiCallStatus m_userListStatus;
QStringList m_userList; QStringList m_userList;
// Nextcloud user metadata // Nextcloud user metadata
bool updateUserMeta(const QJsonObject &userMeta); bool updateUserMeta(const QJsonObject &json);
void setUserMetaStatus(ApiCallStatus status, bool *changed = NULL); void setUserMetaStatus(ApiCallStatus status, bool *changed = NULL);
ApiCallStatus m_userMetaStatus; ApiCallStatus m_userMetaStatus;
QHash<QString, QJsonObject> m_userMeta; QHash<QString, QJsonObject> m_userMeta;
// Nextcloud Login Flow v2 // Nextcloud Login Flow v2
// https://docs.nextcloud.com/server/latest/developer_manual/client_apis/LoginFlow/index.html#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 updateLoginFlow(const QJsonObject &json);
bool updateLoginCredentials(const QJsonObject &credentials); bool updateLoginCredentials(const QJsonObject &json);
bool updateAppPassword(const QJsonObject &password); bool updateAppPassword(const QJsonObject &json);
bool deleteAppPassword(const QJsonObject &json);
void setLoginStatus(LoginStatus status, bool *changed = NULL); void setLoginStatus(LoginStatus status, bool *changed = NULL);
LoginStatus m_loginStatus; LoginStatus m_loginStatus;
QTimer m_loginPollTimer; QTimer m_loginPollTimer;