// Copyright (C) 2025, The Duplicati Team
// https://duplicati.com, hello@duplicati.com
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System.CommandLine;
using System.CommandLine.Binding;
using Duplicati.Library.AutoUpdater;
using Duplicati.Library.Main;
namespace Duplicati.CommandLine.ServerUtil;
///
/// Binds settings from command line options.
///
public class SettingsBinder : BinderBase
{
///
/// The password option.
///
public static readonly Option passwordOption = new Option("--password", description: "The password to use for connecting to the server", getDefaultValue: () => null);
///
/// The host URL option.
///
public static readonly Option hostUrlOption = new Option("--hosturl", description: "The host URL to use", getDefaultValue: () => new Uri("http://localhost:8200"));
///
/// The server datafolder option.
///
public static readonly Option serverDatafolderOption = new Option($"--{DataFolderManager.SERVER_DATAFOLDER_OPTION}", description: "The server datafolder to use for locating the database and storing configuration", getDefaultValue: () => new DirectoryInfo(DataFolderManager.DATAFOLDER));
///
/// The server portable mode option.
///
public static readonly Option portableModeOption = new Option($"--{DataFolderManager.PORTABLE_MODE_OPTION}", description: "Use portable mode for locating the database and storing configuration", getDefaultValue: () => DataFolderManager.PORTABLE_MODE);
///
/// The settings file option.
///
public static readonly Option settingsFileOption = new Option("--settings-file", description: "The settings file to use", getDefaultValue: () => null);
///
/// The settings file option.
///
public static readonly Option insecureOption = new Option("--insecure", description: "Accepts any TLS/SSL certificate (dangerous)", getDefaultValue: () => false);
///
/// The settings encryption key option.
///
public static readonly Option settingsEncryptionKeyOption = new Option("--settings-encryption-key", description: $"The encryption key to use for the settings file. Can also be supplied with environment variable {Library.Encryption.EncryptedFieldHelper.ENVIROMENT_VARIABLE_NAME}", getDefaultValue: () => null);
///
/// The secret provider option.
///
public static readonly Option secretProviderOption = new Option("--secret-provider", description: "The secret provider to use for reading secrets", getDefaultValue: () => null);
///
/// The secret provider cache option.
///
public static readonly Option secretProviderCacheOption = new Option("--secret-provider-cache", description: "The secret provider cache to use for reading secrets", getDefaultValue: () => SecretProviderHelper.CachingLevel.None);
///
/// The secret provider pattern option.
///
public static readonly Option secretProviderPatternOption = new Option("--secret-provider-pattern", description: "The pattern to use for the secret provider", getDefaultValue: () => SecretProviderHelper.DEFAULT_PATTERN);
/// The accepted host certificate option.
///
public static readonly Option acceptedHostCertificateOption = new Option("--host-cert", description: "The SHA1 hash of the host certificate to accept. Use * for any certificate, same as --insecure (dangerous)", getDefaultValue: () => string.Empty);
///
/// Adds global options to the root command.
///
/// The root command to add the options to.
/// The root command with the options added.
public static RootCommand AddGlobalOptions(RootCommand rootCommand)
{
rootCommand.AddGlobalOption(passwordOption);
rootCommand.AddGlobalOption(hostUrlOption);
rootCommand.AddGlobalOption(serverDatafolderOption);
rootCommand.AddGlobalOption(portableModeOption);
rootCommand.AddGlobalOption(settingsFileOption);
rootCommand.AddGlobalOption(insecureOption);
rootCommand.AddGlobalOption(settingsEncryptionKeyOption);
rootCommand.AddGlobalOption(secretProviderOption);
rootCommand.AddGlobalOption(secretProviderCacheOption);
rootCommand.AddGlobalOption(secretProviderPatternOption);
rootCommand.AddGlobalOption(acceptedHostCertificateOption);
return rootCommand;
}
///
/// Gets the settings instance from the binding context.
///
/// The binding context to get the settings from.
/// The settings instance.
public static Settings GetSettings(BindingContext bindingContext) =>
Settings.Load(
bindingContext.ParseResult.GetValueForOption(passwordOption),
bindingContext.ParseResult.GetValueForOption(hostUrlOption),
bindingContext.ParseResult.GetValueForOption(settingsFileOption)?.FullName ?? "settings.json",
bindingContext.ParseResult.GetValueForOption(insecureOption),
bindingContext.ParseResult.GetValueForOption(settingsEncryptionKeyOption) ?? Environment.GetEnvironmentVariable(Library.Encryption.EncryptedFieldHelper.ENVIROMENT_VARIABLE_NAME),
bindingContext.ParseResult.GetValueForOption(secretProviderOption),
bindingContext.ParseResult.GetValueForOption(secretProviderCacheOption),
bindingContext.ParseResult.GetValueForOption(secretProviderPatternOption) ?? SecretProviderHelper.DEFAULT_PATTERN,
bindingContext.ParseResult.GetValueForOption(acceptedHostCertificateOption)
);
///
/// Gets the bound value.
///
/// The binding context to get the value from.
/// The bound value.
protected override Settings GetBoundValue(BindingContext bindingContext) =>
GetSettings(bindingContext);
}