From d6ba7575851079ac5b7bf91aeca84484e7c421df Mon Sep 17 00:00:00 2001 From: Matthieu Gallien Date: Mon, 21 Oct 2024 18:25:36 +0200 Subject: [PATCH 1/2] detect the terms of service app is requiring the user to sign them will query the sign state for the terms of service and automatically pause or resume sync Signed-off-by: Matthieu Gallien --- src/gui/accountsettings.cpp | 3 +++ src/gui/accountstate.cpp | 11 +++++++++++ src/gui/accountstate.h | 6 +++++- src/gui/application.cpp | 2 ++ src/gui/connectionvalidator.cpp | 31 ++++++++++++++++++++++++++++++- src/gui/connectionvalidator.h | 5 ++++- src/gui/folder.cpp | 6 ++++++ src/gui/owncloudgui.cpp | 10 ++++++++++ src/gui/owncloudgui.h | 2 +- 9 files changed, 72 insertions(+), 4 deletions(-) diff --git a/src/gui/accountsettings.cpp b/src/gui/accountsettings.cpp index 6446876cd0f19..b882c15620079 100644 --- a/src/gui/accountsettings.cpp +++ b/src/gui/accountsettings.cpp @@ -1320,6 +1320,9 @@ void AccountSettings::slotAccountStateChanged() // we can't end up here as the whole block is ifdeffed Q_UNREACHABLE(); break; + case AccountState::NeedToSignTermsOfService: + showConnectionLabel(tr("You need to accept the terms of service")); + break; } } else { // ownCloud is not yet configured. diff --git a/src/gui/accountstate.cpp b/src/gui/accountstate.cpp index d1a0957791936..a69f5ef17356c 100644 --- a/src/gui/accountstate.cpp +++ b/src/gui/accountstate.cpp @@ -159,6 +159,8 @@ QString AccountState::stateString(State state) return tr("Configuration error"); case AskingCredentials: return tr("Asking Credentials"); + case NeedToSignTermsOfService: + return tr("Need the user to accept the terms of service"); } return tr("Unknown account state"); } @@ -346,6 +348,12 @@ void AccountState::slotConnectionValidatorResult(ConnectionValidator::Status sta _lastConnectionValidatorStatus = status; + if ((_lastConnectionValidatorStatus == ConnectionValidator::NeedToSignTermsOfService && status == ConnectionValidator::Connected) || + status == ConnectionValidator::NeedToSignTermsOfService) { + + emit termsOfServiceChanged(_account); + } + // Come online gradually from 503, captive portal(redirection) or maintenance mode if (status == ConnectionValidator::Connected && (_connectionStatus == ConnectionValidator::ServiceUnavailable @@ -424,6 +432,9 @@ void AccountState::slotConnectionValidatorResult(ConnectionValidator::Status sta setState(NetworkError); updateRetryCount(); break; + case ConnectionValidator::NeedToSignTermsOfService: + setState(NeedToSignTermsOfService); + break; } } diff --git a/src/gui/accountstate.h b/src/gui/accountstate.h index 77caa1b4051ca..019b96d70cfd3 100644 --- a/src/gui/accountstate.h +++ b/src/gui/accountstate.h @@ -82,7 +82,10 @@ class AccountState : public QObject, public QSharedData ConfigurationError, /// We are currently asking the user for credentials - AskingCredentials + AskingCredentials, + + /// Need to sign terms of service by going to web UI + NeedToSignTermsOfService, }; /// The actual current connectivity status. @@ -192,6 +195,7 @@ public slots: void hasFetchedNavigationApps(); void statusChanged(); void desktopNotificationsAllowedChanged(); + void termsOfServiceChanged(OCC::AccountPtr account); protected Q_SLOTS: void slotConnectionValidatorResult(OCC::ConnectionValidator::Status status, const QStringList &errors); diff --git a/src/gui/application.cpp b/src/gui/application.cpp index 171acc75cfa8a..9dff3dcbfd7a3 100644 --- a/src/gui/application.cpp +++ b/src/gui/application.cpp @@ -627,6 +627,8 @@ void Application::slotAccountStateAdded(AccountState *accountState) _gui.data(), &ownCloudGui::slotAccountStateChanged); connect(accountState->account().data(), &Account::serverVersionChanged, _gui.data(), &ownCloudGui::slotTrayMessageIfServerUnsupported); + connect(accountState, &AccountState::termsOfServiceChanged, + _gui.data(), &ownCloudGui::slotNeedToAcceptTermsOfService); connect(accountState, &AccountState::stateChanged, _folderManager.data(), &FolderMan::slotAccountStateChanged); connect(accountState->account().data(), &Account::serverVersionChanged, diff --git a/src/gui/connectionvalidator.cpp b/src/gui/connectionvalidator.cpp index 87473c2ed05b4..3d7c974df0ae8 100644 --- a/src/gui/connectionvalidator.cpp +++ b/src/gui/connectionvalidator.cpp @@ -267,7 +267,20 @@ void ConnectionValidator::slotCapabilitiesRecieved(const QJsonDocument &json) QString directEditingETag = caps["files"].toObject()["directEditing"].toObject()["etag"].toString(); _account->fetchDirectEditors(directEditingURL, directEditingETag); - fetchUser(); + checkServerTermsOfService(); +} + +void ConnectionValidator::checkServerTermsOfService() +{ + // The main flow now needs the capabilities + auto *job = new JsonApiJob(_account, QLatin1String("ocs/v2.php/apps/terms_of_service/terms"), this); + job->setTimeout(timeoutToUseMsec); + QObject::connect(job, &JsonApiJob::jsonReceived, this, &ConnectionValidator::slotServerTermsOfServiceRecieved); + QObject::connect(job, &JsonApiJob::networkError, this, [] (QNetworkReply *reply) + { + qCInfo(lcConnectionValidator()) << "network error" << reply->error(); + }); + job->start(); } void ConnectionValidator::fetchUser() @@ -319,6 +332,22 @@ void ConnectionValidator::slotUserFetched(UserInfo *userInfo) #endif } +void ConnectionValidator::slotServerTermsOfServiceRecieved(const QJsonDocument &reply) +{ + qCDebug(lcConnectionValidator) << "Terms of service status" << reply; + + if (reply.object().contains("ocs")) { + const auto hasSigned = reply.object().value("ocs").toObject().value("data").toObject().value("hasSigned").toBool(false); + + if (!hasSigned) { + reportResult(NeedToSignTermsOfService); + return; + } + } + + fetchUser(); +} + #ifndef TOKEN_AUTH_ONLY void ConnectionValidator::reportConnected() { reportResult(Connected); diff --git a/src/gui/connectionvalidator.h b/src/gui/connectionvalidator.h index ecff3b98b4544..788a30da2b816 100644 --- a/src/gui/connectionvalidator.h +++ b/src/gui/connectionvalidator.h @@ -95,7 +95,8 @@ class ConnectionValidator : public QObject StatusRedirect, // 204 URL received one of redirect HTTP codes (301-307), possibly a captive portal ServiceUnavailable, // 503 on authed request MaintenanceMode, // maintenance enabled in status.php - Timeout // actually also used for other errors on the authed request + Timeout, // actually also used for other errors on the authed request + NeedToSignTermsOfService, }; Q_ENUM(Status); @@ -129,6 +130,7 @@ protected slots: void slotCapabilitiesRecieved(const QJsonDocument &); void slotUserFetched(OCC::UserInfo *userInfo); + void slotServerTermsOfServiceRecieved(const QJsonDocument &reply); private: #ifndef TOKEN_AUTH_ONLY @@ -136,6 +138,7 @@ protected slots: #endif void reportResult(Status status); void checkServerCapabilities(); + void checkServerTermsOfService(); void fetchUser(); /** Sets the account's server version diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index 066d655c4d56c..e35be0168f82d 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -93,6 +93,12 @@ Folder::Folder(const FolderDefinition &definition, if (!reloadExcludes()) qCWarning(lcFolder, "Could not read system exclude file"); + connect(_accountState.data(), &AccountState::termsOfServiceChanged, + this, [this] () + { + setSyncPaused(_accountState->state() == AccountState::NeedToSignTermsOfService); + }); + connect(_accountState.data(), &AccountState::isConnectedChanged, this, &Folder::canSyncChanged); connect(_engine.data(), &SyncEngine::rootEtag, this, &Folder::etagRetrievedFromSyncEngine); diff --git a/src/gui/owncloudgui.cpp b/src/gui/owncloudgui.cpp index 60138a494943b..abd1b6ec9cacc 100644 --- a/src/gui/owncloudgui.cpp +++ b/src/gui/owncloudgui.cpp @@ -285,6 +285,16 @@ void ownCloudGui::slotTrayMessageIfServerUnsupported(Account *account) } } +void ownCloudGui::slotNeedToAcceptTermsOfService(OCC::AccountPtr account) +{ + slotShowTrayMessage( + tr("Terms of service"), + tr("Your account %1 requires you to accept the terms of service of your server. " + "You will be redirected to %2 to acknowledge that you have read it and agrees with it.") + .arg(account->displayName(), account->url().toString())); + QDesktopServices::openUrl(account->url()); +} + void ownCloudGui::slotComputeOverallSyncStatus() { bool allSignedOut = true; diff --git a/src/gui/owncloudgui.h b/src/gui/owncloudgui.h index 8315fe228caa6..1cfc44876d525 100644 --- a/src/gui/owncloudgui.h +++ b/src/gui/owncloudgui.h @@ -94,7 +94,7 @@ public slots: void slotOpenPath(const QString &path); void slotAccountStateChanged(); void slotTrayMessageIfServerUnsupported(OCC::Account *account); - + void slotNeedToAcceptTermsOfService(OCC::AccountPtr account); /** * Open a share dialog for a file or folder. From 4a1538b6c4f10949c0dcf4f4773ef2ba19dcfa33 Mon Sep 17 00:00:00 2001 From: Matthieu Gallien Date: Tue, 22 Oct 2024 14:49:51 +0200 Subject: [PATCH 2/2] content access denied error during discovery: verify server access will trigger a check of the server connectivity in case of content access denied reporting when listing folders during discovery should allow discovering early that terms of service need to be signed Signed-off-by: Matthieu Gallien --- src/gui/accountstate.cpp | 4 ++++ src/libsync/account.h | 1 + src/libsync/discoveryphase.cpp | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/src/gui/accountstate.cpp b/src/gui/accountstate.cpp index a69f5ef17356c..59f26fe7cc54e 100644 --- a/src/gui/accountstate.cpp +++ b/src/gui/accountstate.cpp @@ -64,6 +64,10 @@ AccountState::AccountState(const AccountPtr &account) this, &AccountState::slotPushNotificationsReady); connect(account.data(), &Account::serverUserStatusChanged, this, &AccountState::slotServerUserStatusChanged); + connect(account.data(), &Account::termsOfServiceNeedToBeChecked, + this, [this] () { + checkConnectivity(); + }); connect(this, &AccountState::isConnectedChanged, [=]{ // Get the Apps available on the server if we're now connected. diff --git a/src/libsync/account.h b/src/libsync/account.h index 4afd9f654dc37..92a132ef6c48d 100644 --- a/src/libsync/account.h +++ b/src/libsync/account.h @@ -464,6 +464,7 @@ public slots: void downloadLimitSettingChanged(); void uploadLimitChanged(); void downloadLimitChanged(); + void termsOfServiceNeedToBeChecked(); protected Q_SLOTS: void slotCredentialsFetched(); diff --git a/src/libsync/discoveryphase.cpp b/src/libsync/discoveryphase.cpp index 38e4a424c5a73..4cb604b9aa7da 100644 --- a/src/libsync/discoveryphase.cpp +++ b/src/libsync/discoveryphase.cpp @@ -652,6 +652,10 @@ void DiscoverySingleDirectoryJob::lsJobFinishedWithErrorSlot(QNetworkReply *r) msg = tr("Server error: PROPFIND reply is not XML formatted!"); } + if (r->error() == QNetworkReply::ContentAccessDenied) { + emit _account->termsOfServiceNeedToBeChecked(); + } + emit finished(HttpError{ httpCode, msg }); deleteLater(); }