From 55c2ebf6c77cb6672d03452e51aa93cc0a0baf48 Mon Sep 17 00:00:00 2001 From: Kenneth Skovhede Date: Wed, 13 May 2026 13:30:39 +0200 Subject: [PATCH] Deprecate old SharePoint This PR deprecates the `mssp` and `od4b` backends as they stopped working when the APIs were turned off. The upgrade path is to use `sharepoint` instead (which used to be named "SharePoint v2"). This PR also adds a small amount of metadata to modules so they can be marked as "untested" or "deprecated", and the configuration for marking modules has been set up in `BackendModules.cs`. The backends that we currently cannot test (usualy due to not having public signups) are now marked as untested. --- Duplicati/Library/Backend/OneDrive/Strings.cs | 2 +- .../Library/Backend/SharePoint/Strings.cs | 8 +++---- Duplicati/Library/Backends/BackendModules.cs | 19 +++++++++++++++ Duplicati/Library/Main/Controller.cs | 11 ++++----- .../RestAPI/Serializable/ServerSettings.cs | 24 +++++++++---------- .../Interface/IDynamicModule.cs | 8 +++++++ 6 files changed, 47 insertions(+), 25 deletions(-) diff --git a/Duplicati/Library/Backend/OneDrive/Strings.cs b/Duplicati/Library/Backend/OneDrive/Strings.cs index 87c782d94..4528c73d5 100644 --- a/Duplicati/Library/Backend/OneDrive/Strings.cs +++ b/Duplicati/Library/Backend/OneDrive/Strings.cs @@ -45,7 +45,7 @@ namespace Duplicati.Library.Backend.Strings internal static class SharePointV2 { public static string Description(string mssadescription, string mssalink, string msopdescription, string msoplink) { return LC.L(@"Store files in a Microsoft SharePoint site via the Microsoft Graph API. Usage of this backend requires that you agree to the terms in {0} ({1}) and {2} ({3}).", mssadescription, mssalink, msopdescription, msoplink); } - public static string DisplayName { get { return LC.L(@"Microsoft SharePoint v2"); } } + public static string DisplayName { get { return LC.L(@"Microsoft SharePoint"); } } public static string SiteIdLong { get { return LC.L(@"ID of the site to store data in."); } } public static string SiteIdShort { get { return LC.L(@"ID of the site"); } } public static string MissingSiteId { get { return LC.L(@"No site ID was provided"); } } diff --git a/Duplicati/Library/Backend/SharePoint/Strings.cs b/Duplicati/Library/Backend/SharePoint/Strings.cs index 4e994e80f..64803e2cc 100644 --- a/Duplicati/Library/Backend/SharePoint/Strings.cs +++ b/Duplicati/Library/Backend/SharePoint/Strings.cs @@ -23,8 +23,8 @@ namespace Duplicati.Library.Backend.Strings { internal static class SharePoint { - public static string Description => LC.L(@"This backend can read and write data to a SharePoint server (including OneDrive for Business). Allowed formats are ""mssp://tennant.sharepoint.com/PathToWeb//BaseDocLibrary/subfolder"" and ""mssp://username:password@tennant.sharepoint.com/PathToWeb//BaseDocLibrary/subfolder"". Use a double slash '//' in the path to denote the web from the documents library."); - public static string DisplayName => LC.L(@"Microsoft SharePoint"); + public static string Description => LC.L(@"This backend is deprecated. Please use the SharePoint backend instead. This backend can read and write data to a SharePoint server (including OneDrive for Business). Allowed formats are ""mssp://tennant.sharepoint.com/PathToWeb//BaseDocLibrary/subfolder"" and ""mssp://username:password@tennant.sharepoint.com/PathToWeb//BaseDocLibrary/subfolder"". Use a double slash '//' in the path to denote the web from the documents library."); + public static string DisplayName => LC.L(@"Microsoft SharePoint using legacy API"); public static string DescriptionIntegratedAuthenticationLong => LC.L(@"If the server and client both supports integrated authentication, this option enables that authentication method. This is likely only available with windows servers and clients."); public static string DescriptionIntegratedAuthenticationShort => LC.L(@"Use windows integrated authentication to connect to the server"); public static string DescriptionUseRecyclerLong => LC.L(@"Use this option to have files moved to the recycle bin folder instead of removing them permanently when compacting or deleting backups."); @@ -46,7 +46,7 @@ namespace Duplicati.Library.Backend.Strings internal static class OneDriveForBusiness { - public static string Description => LC.L(@"This backend can read and write data to Microsoft OneDrive for Business. Allowed formats are ""od4b://tennant.sharepoint.com/personal/username_domain/Documents/subfolder"" and ""od4b://username:password@tennant.sharepoint.com/personal/username_domain/Documents/folder"". You can use a double slash '//' in the path to denote the base path from the documents folder."); - public static string DisplayName => LC.L(@"Microsoft OneDrive for Business"); + public static string Description => LC.L(@"This backend is deprecated. Please use the SharePoint backend instead. This backend can read and write data to Microsoft OneDrive for Business. Allowed formats are ""od4b://tennant.sharepoint.com/personal/username_domain/Documents/subfolder"" and ""od4b://username:password@tennant.sharepoint.com/personal/username_domain/Documents/folder"". You can use a double slash '//' in the path to denote the base path from the documents folder."); + public static string DisplayName => LC.L(@"Microsoft OneDrive for Business (deprecated)"); } } diff --git a/Duplicati/Library/Backends/BackendModules.cs b/Duplicati/Library/Backends/BackendModules.cs index bee551ea0..082840380 100644 --- a/Duplicati/Library/Backends/BackendModules.cs +++ b/Duplicati/Library/Backends/BackendModules.cs @@ -35,6 +35,25 @@ public static class BackendModules /// public static IReadOnlyList BuiltInBackendModules => SupportedBackends; + /// + /// List of backend modules that are not tested as it was not possible to get a test account + /// + public static IReadOnlySet UntestedBackendModules => new HashSet(StringComparer.OrdinalIgnoreCase) { + new Backend.AliyunOSS.OSS().ProtocolKey, + new Backend.TencentCOS.COS().ProtocolKey + }; + + /// + /// List of backend modules that are deprecated and should be migrated away from + /// + public static IReadOnlySet DeprecatedBackendModules => new HashSet(StringComparer.OrdinalIgnoreCase) { + new Backend.Mega.MegaBackend().ProtocolKey, + new Backend.OneDriveForBusinessBackend().ProtocolKey, + new Backend.SharePointBackend().ProtocolKey, + new Backend.CIFSBackend().ProtocolKey, + new Backend.Filejump().ProtocolKey + }; + /// /// Calculate list once and cache it /// diff --git a/Duplicati/Library/Main/Controller.cs b/Duplicati/Library/Main/Controller.cs index 63f099f64..d7f0b5979 100644 --- a/Duplicati/Library/Main/Controller.cs +++ b/Duplicati/Library/Main/Controller.cs @@ -30,6 +30,7 @@ using Duplicati.Library.Main.Database; using System.Threading.Tasks; using Duplicati.Library.Main.Operation.Common; using System.IO; +using Duplicati.Library.Backends; namespace Duplicati.Library.Main { @@ -1024,13 +1025,9 @@ namespace Duplicati.Library.Main throw new UserInformationException(Strings.Controller.BackendNotSupportedError(scheme, string.Join(", ", DynamicLoader.BackendLoader.Keys)), "BackendNotSupported"); } - //Inform the user about the deprecated Tardigrade-Backend. They should switch to Storj DCS instead. - if (string.Equals(scheme, "tardigrade", StringComparison.OrdinalIgnoreCase)) - Logging.Log.WriteWarningMessage(LOGTAG, "TardigradeRename", null, "The Tardigrade-backend got renamed to Storj DCS - please migrate your backups to the new configuration by changing the destination storage type to Storj DCS."); - - //Inform the user about the unmaintained Mega support library - if (string.Equals(scheme, "mega", StringComparison.OrdinalIgnoreCase)) - Logging.Log.WriteWarningMessage(LOGTAG, "MegaUnmaintained", null, "The Mega support library is currently unmaintained and may not work as expected. Mega has not published an official API so it may break at any moment. Please consider migrating to another backend."); + //Inform the user about unmaintained backends + if (BackendModules.DeprecatedBackendModules.Contains(scheme)) + Logging.Log.WriteWarningMessage(LOGTAG, "BackendDeprecated", null, $"The backend {scheme} is deprecated and should be migrated away from."); //TODO: Based on the action, see if all options are relevant } diff --git a/Duplicati/Library/RestAPI/Serializable/ServerSettings.cs b/Duplicati/Library/RestAPI/Serializable/ServerSettings.cs index 2275667c1..2f33620c7 100644 --- a/Duplicati/Library/RestAPI/Serializable/ServerSettings.cs +++ b/Duplicati/Library/RestAPI/Serializable/ServerSettings.cs @@ -44,6 +44,8 @@ namespace Duplicati.Server.Serializable this.Description = backend.Description; this.DisplayName = backend.DisplayName; this.Options = backend.SupportedCommands?.ToArray() ?? []; + this.IsDeprecated = Duplicati.Library.Backends.BackendModules.DeprecatedBackendModules.Contains(backend.ProtocolKey); + this.IsUntested = Duplicati.Library.Backends.BackendModules.UntestedBackendModules.Contains(backend.ProtocolKey); } /// @@ -124,27 +126,23 @@ namespace Duplicati.Server.Serializable this.Options = module.SupportedCommands?.ToArray() ?? []; } - /// - /// The module key - /// + /// public string Key { get; private set; } - /// - /// The localized module description - /// + /// public string Description { get; private set; } /// - /// Gets the localized display name - /// - /// The display name. + /// public string DisplayName { get; private set; } /// - /// The options supported by the module - /// + /// public Library.Interface.ICommandLineArgument[] Options { get; private set; } /// - /// The lookups supported by the module - /// + /// public IDictionary> Lookups { get; private set; } + /// + public bool IsDeprecated { get; private set; } + /// + public bool IsUntested { get; private set; } } /// diff --git a/Duplicati/Server/Duplicati.Server.Serialization/Interface/IDynamicModule.cs b/Duplicati/Server/Duplicati.Server.Serialization/Interface/IDynamicModule.cs index e174def22..6aae145ec 100644 --- a/Duplicati/Server/Duplicati.Server.Serialization/Interface/IDynamicModule.cs +++ b/Duplicati/Server/Duplicati.Server.Serialization/Interface/IDynamicModule.cs @@ -49,6 +49,14 @@ namespace Duplicati.Server.Serialization.Interface /// The lookups supported by the module /// IDictionary> Lookups { get; } + /// + /// Indicated the module is deprecated + /// + bool IsDeprecated { get; } + /// + /// Indicated the module is untested + /// + bool IsUntested { get; } } }