From 70b258df831412a54abca18c3eec570c17c3928a Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 7 May 2025 10:42:31 +0800 Subject: [PATCH 1/9] feat(shell_integration/macOS/FileProviderExt): Add config setting for preventing deletion of trashed items Signed-off-by: Claudio Cambra --- .../FileProviderExt/FileProviderConfig.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderConfig.swift b/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderConfig.swift index 46d8724c2c..dd59ed52dd 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderConfig.swift +++ b/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderConfig.swift @@ -12,6 +12,7 @@ import Foundation struct FileProviderConfig { private enum ConfigKey: String { case fastEnumerationEnabled = "fastEnumerationEnabled" + case trashDeletionEnabled = "trashDeletionEnabled" } let domainIdentifier: NSFileProviderDomainIdentifier @@ -32,6 +33,11 @@ struct FileProviderConfig { } } + var trashDeletionEnabled: Bool { + get { internalConfig[ConfigKey.trashDeletionEnabled.rawValue] as? Bool ?? true } + set { internalConfig[ConfigKey.trashDeletionEnabled.rawValue] = newValue } + } + var fastEnumerationEnabled: Bool { get { internalConfig[ConfigKey.fastEnumerationEnabled.rawValue] as? Bool ?? true } set { internalConfig[ConfigKey.fastEnumerationEnabled.rawValue] = newValue } From aab2728a84761100947d11af316e0240a08229be Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 7 May 2025 10:47:20 +0800 Subject: [PATCH 2/9] feat(shell_integration/macOS/FileProviderExt): Prevent deletion of trashed items when config flag is set Signed-off-by: Claudio Cambra --- .../FileProviderExt/FileProviderExtension.swift | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExtension.swift b/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExtension.swift index 3e8e8ee4b5..166ec5a604 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExtension.swift +++ b/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExtension.swift @@ -422,6 +422,17 @@ import OSLog } let progress = Progress(totalUnitCount: 1) + guard config.trashDeletionEnabled || item.parentItemIdentifier != .trashContainer else { + Logger.fileProviderExtension.warning( + """ + System requested deletion of item in trash, but deleting trash items is disabled. + item: \(item.filename) + """ + ) + completionHandler(NSFileProviderError(.deletionRejected)) + return progress + } + Task { let error = await item.delete(dbManager: dbManager) if error != nil { From 2c1f4be6a66e381a9fcf247b3a2ecd97bdd2113a Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 7 May 2025 11:20:10 +0800 Subject: [PATCH 3/9] feat(shell_integration/macOS/FileProviderExt): Add config entry to check if trash deletion enabled setting has been set Signed-off-by: Claudio Cambra --- .../FileProviderExt/FileProviderConfig.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderConfig.swift b/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderConfig.swift index dd59ed52dd..93c8caae76 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderConfig.swift +++ b/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderConfig.swift @@ -38,6 +38,8 @@ struct FileProviderConfig { set { internalConfig[ConfigKey.trashDeletionEnabled.rawValue] = newValue } } + lazy var trashDeletionSet = internalConfig[ConfigKey.trashDeletionEnabled.rawValue] != nil + var fastEnumerationEnabled: Bool { get { internalConfig[ConfigKey.fastEnumerationEnabled.rawValue] as? Bool ?? true } set { internalConfig[ConfigKey.fastEnumerationEnabled.rawValue] = newValue } From a8bff243df7714cdd8297828c09bbfb4029ed19d Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 7 May 2025 11:20:38 +0800 Subject: [PATCH 4/9] feat(shell_integration/macOS/FileProviderExt): Add ClientCommunicationProtocol method to get/set trash deletion enablement Signed-off-by: Claudio Cambra --- .../Services/ClientCommunicationProtocol.h | 2 ++ .../Services/ClientCommunicationService.swift | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/Services/ClientCommunicationProtocol.h b/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/Services/ClientCommunicationProtocol.h index 9c48841825..a4f11a5507 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/Services/ClientCommunicationProtocol.h +++ b/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/Services/ClientCommunicationProtocol.h @@ -20,6 +20,8 @@ - (void)createDebugLogStringWithCompletionHandler:(void(^)(NSString *debugLogString, NSError *error))completionHandler; - (void)getFastEnumerationStateWithCompletionHandler:(void(^)(BOOL enabled, BOOL set))completionHandler; - (void)setFastEnumerationEnabled:(BOOL)enabled; +- (void)getTrashDeletionEnabledStateWithCompletionHandler:(void(^)(BOOL enabled, BOOL set))completionHandler; +- (void)setTrashDeletionEnabled:(BOOL)enabled; @end diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/Services/ClientCommunicationService.swift b/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/Services/ClientCommunicationService.swift index 8f8638bab0..03e261ee66 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/Services/ClientCommunicationService.swift +++ b/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/Services/ClientCommunicationService.swift @@ -101,4 +101,17 @@ class ClientCommunicationService: NSObject, NSFileProviderServiceSource, NSXPCLi } } } + + func getTrashDeletionEnabledState(completionHandler: @escaping (Bool, Bool) -> Void) { + let enabled = fpExtension.config.trashDeletionEnabled + let set = fpExtension.config.trashDeletionSet + completionHandler(enabled, set) + } + + func setTrashDeletionEnabled(_ enabled: Bool) { + fpExtension.config.trashDeletionEnabled = enabled + Logger.fileProviderExtension.info( + "Trash deletion setting changed to: \(enabled, privacy: .public)" + ) + } } From edc4c490ab0d65965100705c4f5a5026c195ac86 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 7 May 2025 11:22:06 +0800 Subject: [PATCH 5/9] feat(gui/macOS): Add methods to get/set trash deletion state over XPC for File Provider module Signed-off-by: Claudio Cambra --- src/gui/macOS/fileproviderxpc.h | 3 +++ src/gui/macOS/fileproviderxpc_mac.mm | 34 ++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/gui/macOS/fileproviderxpc.h b/src/gui/macOS/fileproviderxpc.h index 54e6dc1555..46dc8a8183 100644 --- a/src/gui/macOS/fileproviderxpc.h +++ b/src/gui/macOS/fileproviderxpc.h @@ -31,6 +31,8 @@ public: // Returns enabled and set state of fast enumeration for the given extension [[nodiscard]] std::optional> fastEnumerationStateForExtension(const QString &extensionAccountId) const; + [[nodiscard]] std::optional> trashDeletionEnabledStateForExtension(const QString &extensionAccountId) const; + public slots: void connectToExtensions(); void configureExtensions(); @@ -39,6 +41,7 @@ public slots: void createDebugArchiveForExtension(const QString &extensionAccountId, const QString &filename); void setFastEnumerationEnabledForExtension(const QString &extensionAccountId, bool enabled) const; + void setTrashDeletionEnabledForExtension(const QString &extensionAccountId, bool enabled) const; private slots: void slotAccountStateChanged(AccountState::State state) const; diff --git a/src/gui/macOS/fileproviderxpc_mac.mm b/src/gui/macOS/fileproviderxpc_mac.mm index dd7400919d..a4bf589da2 100644 --- a/src/gui/macOS/fileproviderxpc_mac.mm +++ b/src/gui/macOS/fileproviderxpc_mac.mm @@ -235,4 +235,38 @@ void FileProviderXPC::setFastEnumerationEnabledForExtension(const QString &exten [service setFastEnumerationEnabled:enabled]; } +std::optional> FileProviderXPC::trashDeletionEnabledStateForExtension(const QString &extensionAccountId) const +{ + qCInfo(lcFileProviderXPC) << "Checking if fast enumeration is enabled for extension" << extensionAccountId; + const auto service = (NSObject *)_clientCommServices.value(extensionAccountId); + if (service == nil) { + qCWarning(lcFileProviderXPC) << "Could not get service for extension" << extensionAccountId; + return std::nullopt; + } + + __block BOOL receivedTrashDeletionEnabled = YES; // What is the value of the setting being used by the extension? + __block BOOL receivedTrashDeletionEnabledSet = NO; // Has the setting been set by the user? + __block BOOL receivedResponse = NO; + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + [service getTrashDeletionEnabledStateWithCompletionHandler:^(BOOL enabled, BOOL set) { + receivedTrashDeletionEnabled = enabled; + receivedTrashDeletionEnabledSet = set; + receivedResponse = YES; + dispatch_semaphore_signal(semaphore); + }]; + dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, semaphoreWaitDelta)); + if (!receivedResponse) { + qCWarning(lcFileProviderXPC) << "Did not receive response for fast enumeration state"; + return std::nullopt; + } + return std::optional>{{receivedTrashDeletionEnabled, receivedTrashDeletionEnabledSet}}; +} + +void FileProviderXPC::setTrashDeletionEnabledForExtension(const QString &extensionAccountId, bool enabled) const +{ + qCInfo(lcFileProviderXPC) << "Setting trash deletion enabled for extension" << extensionAccountId << "to" << enabled; + const auto service = (NSObject *)_clientCommServices.value(extensionAccountId); + [service setTrashDeletionEnabled:enabled]; +} + } // namespace OCC::Mac From 3f2591799cf07d66496b1e4b54f028b49369db14 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 7 May 2025 11:22:55 +0800 Subject: [PATCH 6/9] feat(gui/macOS): Add trash deletion related components to File Provider settings controller Signed-off-by: Claudio Cambra --- .../macOS/fileprovidersettingscontroller.h | 5 +++ .../fileprovidersettingscontroller_mac.mm | 38 +++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/src/gui/macOS/fileprovidersettingscontroller.h b/src/gui/macOS/fileprovidersettingscontroller.h index 40d04c528a..e6aac92979 100644 --- a/src/gui/macOS/fileprovidersettingscontroller.h +++ b/src/gui/macOS/fileprovidersettingscontroller.h @@ -37,6 +37,8 @@ public: [[nodiscard]] Q_INVOKABLE float remoteStorageUsageGbForAccount(const QString &userIdAtHost) const; [[nodiscard]] Q_INVOKABLE bool fastEnumerationEnabledForAccount(const QString &userIdAtHost) const; [[nodiscard]] Q_INVOKABLE bool fastEnumerationSetForAccount(const QString &userIdAtHost) const; + [[nodiscard]] Q_INVOKABLE bool trashDeletionEnabledForAccount(const QString &userIdAtHost) const; + [[nodiscard]] Q_INVOKABLE bool trashDeletionSetForAccount(const QString &userIdAtHost) const; [[nodiscard]] Q_INVOKABLE QAbstractListModel *materialisedItemsModelForAccount(const QString &userIdAtHost); [[nodiscard]] Q_INVOKABLE FileProviderDomainSyncStatus *domainSyncStatusForAccount(const QString &userIdAtHost) const; @@ -44,6 +46,7 @@ public: public slots: void setVfsEnabledForAccount(const QString &userIdAtHost, const bool setEnabled); void setFastEnumerationEnabledForAccount(const QString &userIdAtHost, const bool setEnabled); + void setTrashDeletionEnabledForAccount(const QString &userIdAtHost, const bool setEnabled); void createEvictionWindowForAccount(const QString &userIdAtHost); void refreshMaterialisedItemsForAccount(const QString &userIdAtHost); @@ -57,6 +60,8 @@ signals: void materialisedItemsForAccountChanged(const QString &userIdAtHost); void fastEnumerationEnabledForAccountChanged(const QString &userIdAtHost); void fastEnumerationSetForAccountChanged(const QString &userIdAtHost); + void trashDeletionEnabledForAccountChanged(const QString &userIdAtHost); + void trashDeletionSetForAccountChanged(const QString &userIdAtHost); private: explicit FileProviderSettingsController(QObject *parent = nullptr); diff --git a/src/gui/macOS/fileprovidersettingscontroller_mac.mm b/src/gui/macOS/fileprovidersettingscontroller_mac.mm index 7158346e41..174ef039dd 100644 --- a/src/gui/macOS/fileprovidersettingscontroller_mac.mm +++ b/src/gui/macOS/fileprovidersettingscontroller_mac.mm @@ -412,6 +412,44 @@ void FileProviderSettingsController::setFastEnumerationEnabledForAccount(const Q emit fastEnumerationSetForAccountChanged(userIdAtHost); } +bool FileProviderSettingsController::trashDeletionEnabledForAccount(const QString &userIdAtHost) const +{ + const auto xpc = FileProvider::instance()->xpc(); + if (!xpc) { + return true; + } + if (const auto trashDeletionState = xpc->trashDeletionEnabledStateForExtension(userIdAtHost)) { + return trashDeletionState->first; + } + return true; +} + +bool FileProviderSettingsController::trashDeletionSetForAccount(const QString &userIdAtHost) const +{ + const auto xpc = FileProvider::instance()->xpc(); + if (!xpc) { + return false; + } + if (const auto state = xpc->trashDeletionEnabledStateForExtension(userIdAtHost)) { + return state->second; + } + return false; +} + +void FileProviderSettingsController::setTrashDeletionEnabledForAccount(const QString &userIdAtHost, const bool setEnabled) +{ + const auto xpc = FileProvider::instance()->xpc(); + if (!xpc) { + // Reset state of UI elements + emit trashDeletionEnabledForAccountChanged(userIdAtHost); + emit trashDeletionSetForAccountChanged(userIdAtHost); + return; + } + xpc->setTrashDeletionEnabledForExtension(userIdAtHost, setEnabled); + emit trashDeletionEnabledForAccountChanged(userIdAtHost); + emit trashDeletionSetForAccountChanged(userIdAtHost); +} + unsigned long long FileProviderSettingsController::localStorageUsageForAccount(const QString &userIdAtHost) const { return d->localStorageUsageForAccount(userIdAtHost); From f1535d438cd59f8d7d7eb4186aec2a6fbcb817ae Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 7 May 2025 11:26:36 +0800 Subject: [PATCH 7/9] feat(gui/macOS): Add checkbox to toggle permission to delete trashed items Signed-off-by: Claudio Cambra --- src/gui/macOS/ui/FileProviderSettings.qml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/gui/macOS/ui/FileProviderSettings.qml b/src/gui/macOS/ui/FileProviderSettings.qml index 52aba614a2..c5245e8d57 100644 --- a/src/gui/macOS/ui/FileProviderSettings.qml +++ b/src/gui/macOS/ui/FileProviderSettings.qml @@ -131,6 +131,12 @@ Page { } } } + + CheckBox { + text: qsTr("Allow deletion of items in Trash") + checked: root.controller.trashDeletionEnabledForAccount(root.accountUserIdAtHost) + onClicked: root.controller.setTrashDeletionEnabledForAccount(root.accountUserIdAtHost, checked) + } } } } From 8a7fd5651bfc466cc6d287ceb30c3c86178ac8bb Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 7 May 2025 12:24:10 +0800 Subject: [PATCH 8/9] fix(shell_integration/macOS/FileProviderExt): Make item filename public during rejected deletion Signed-off-by: Claudio Cambra --- .../FileProviderExt/FileProviderExtension.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExtension.swift b/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExtension.swift index 166ec5a604..5d5ba68808 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExtension.swift +++ b/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExtension.swift @@ -426,7 +426,7 @@ import OSLog Logger.fileProviderExtension.warning( """ System requested deletion of item in trash, but deleting trash items is disabled. - item: \(item.filename) + item: \(item.filename, privacy: .public) """ ) completionHandler(NSFileProviderError(.deletionRejected)) From c6254b57fb9ae7801e4420cfe5a1bcaa33c01f6c Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 7 May 2025 12:24:41 +0800 Subject: [PATCH 9/9] fix(shell_integration/macOS/FileProviderExt): Use NSError-based deletion rejected builder method Signed-off-by: Claudio Cambra --- .../FileProviderExt/FileProviderExtension.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExtension.swift b/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExtension.swift index 5d5ba68808..2594f35b90 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExtension.swift +++ b/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExtension.swift @@ -429,7 +429,7 @@ import OSLog item: \(item.filename, privacy: .public) """ ) - completionHandler(NSFileProviderError(.deletionRejected)) + completionHandler(NSError.fileProviderErrorForRejectedDeletion(of: item)) return progress }