using System.Text.Json;
using Duplicati.Library.Encryption;
using Duplicati.Library.Main;
namespace Duplicati.CommandLine.ServerUtil;
///
/// Settings instance for the server utility.
///
/// The commandline password
/// The saved refresh token
/// The host url to connect to
/// The server datafolder for password-free connections
/// The settings file where data is loaded/saved
/// Whether to disable TLS/SSL certificate trust check
public sealed record Settings(
string? Password,
string? RefreshToken,
Uri HostUrl,
string? ServerDatafolder,
string SettingsFile,
bool Insecure
)
{
///
/// The JSON serialized settings for a single host
///
/// Encrypted refresh token
/// The host url to connect to
/// The server datafolder, if any
private sealed record PersistedSettings(
string? RefreshToken,
Uri HostUrl,
string? ServerDatafolder
);
private static string GetDefaultStorageFolder(string filename)
{
var folder = DatabaseLocator.GetDefaultStorageFolderWithDebugSupport(filename);
if (!Directory.Exists(folder))
Directory.CreateDirectory(folder);
return folder;
}
///
/// Loads the settings from the settings file
///
/// The password to use
/// The host URL to use
/// The server data folder to use
/// The settings file to use
/// Whether to disable TLS/SSL certificate trust check
/// The loaded settings
public static Settings Load(string? password, Uri? hostUrl, string? serverDataFolder, string settingsFile, bool insecure)
{
hostUrl ??= new Uri("http://localhost:8200");
if (string.IsNullOrWhiteSpace(serverDataFolder))
serverDataFolder = GetDefaultStorageFolder("Duplicati-server.sqlite");
if (!string.IsNullOrWhiteSpace(settingsFile) && !Path.IsPathRooted(settingsFile))
settingsFile = Path.Combine(GetDefaultStorageFolder(settingsFile), settingsFile);
var persistedSettings = LoadSettings(settingsFile)
.FirstOrDefault(x => x.HostUrl == hostUrl);
return new Settings(
password,
persistedSettings?.RefreshToken,
hostUrl,
serverDataFolder,
settingsFile,
insecure
);
}
///
/// Saves the settings to the settings file
///
public void Save()
{
if (!string.IsNullOrWhiteSpace(RefreshToken))
{
if (!EncryptedFieldHelper.HasValidDefaultKey)
Console.WriteLine("Warning: The encryption key is missing, saving login token without encryption");
else if (EncryptedFieldHelper.IsDefaultKeyBlacklisted)
Console.WriteLine("Warning: The current encryption key is blacklisted and cannot be used, saving login token without encryption");
}
File.WriteAllText(SettingsFile, JsonSerializer.Serialize(LoadSettings(SettingsFile)
.Where(x => x.HostUrl != HostUrl)
.Append(new PersistedSettings(RefreshToken, HostUrl, ServerDatafolder))
.Select(x => x with
{
RefreshToken = string.IsNullOrWhiteSpace(x.RefreshToken) || EncryptedFieldHelper.IsDefaultKeyBlacklisted || !EncryptedFieldHelper.HasValidDefaultKey
? x.RefreshToken
: EncryptedFieldHelper.Encrypt(x.RefreshToken)
})
));
}
///
/// Gets a connection to the server
///
/// The connection
public Task GetConnection()
{
return Connection.Connect(this);
}
///
/// Loads the settings from the settings file
///
/// The filename to load
/// The loaded settings
private static List LoadSettings(string filename)
{
if (File.Exists(filename))
return (JsonSerializer.Deserialize>(File.ReadAllText(filename)) ?? [])
.Select(x => x with { RefreshToken = EncryptedFieldHelper.Decrypt(x.RefreshToken) })
.ToList();
return [];
}
}