mirror of
https://github.com/duplicati/duplicati.git
synced 2025-11-27 19:10:29 +08:00
This commit adds the `DateTime` option to support validation of the `--time` parameter, which can both be an absolute date and time, or a relative value. This fixes #6107
176 lines
No EOL
7.2 KiB
C#
176 lines
No EOL
7.2 KiB
C#
// 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.
|
|
|
|
#nullable enable
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using Duplicati.Library.Interface;
|
|
|
|
namespace Duplicati.Library.Utility;
|
|
|
|
/// <summary>
|
|
/// Helper class for validating command line arguments
|
|
/// </summary>
|
|
public static class CommandLineArgumentValidator
|
|
{
|
|
/// <summary>
|
|
/// The log tag for this class
|
|
/// </summary>
|
|
private static readonly string LOGTAG = Logging.Log.LogTagFromType(typeof(CommandLineArgumentValidator));
|
|
|
|
/// <summary>
|
|
/// Validates arguments against the supported commands
|
|
/// </summary>
|
|
/// <param name="arguments">The arguments supported<</param>
|
|
public static void ValidateArguments(IEnumerable<ICommandLineArgument> arguments, IReadOnlyDictionary<string, string?> options, IReadOnlySet<string> knownDuplicateOptions, IReadOnlySet<string> IgnoredOptions)
|
|
{
|
|
var allOptionsGrouped = arguments
|
|
.SelectMany(option => (option.Aliases ?? []).Prepend(option.Name).Select(name => (Name: name, Option: option)))
|
|
.GroupBy(x => x.Name, StringComparer.OrdinalIgnoreCase);
|
|
|
|
#if DEBUG
|
|
foreach (var n in allOptionsGrouped.Where(x => !knownDuplicateOptions.Contains(x.Key) && x.Count() > 1))
|
|
Logging.Log.WriteErrorMessage(LOGTAG, "DuplicateOption", null, $"Duplicate option: {n.Key}");
|
|
#endif
|
|
|
|
var allOptions = allOptionsGrouped
|
|
.ToDictionary(x => x.Key, x => x.First().Option, StringComparer.OrdinalIgnoreCase);
|
|
|
|
foreach (var (key, value) in options.Where(x => !IgnoredOptions.Contains(x.Key)))
|
|
{
|
|
if (!allOptions.TryGetValue(key, out var option) || option == null)
|
|
{
|
|
Logging.Log.WriteWarningMessage(LOGTAG, "UnknownOption", null, $"Unknown option supplied: {key}");
|
|
continue;
|
|
}
|
|
|
|
var validationMessage = ValidateOptionValue(option, key, value);
|
|
if (!string.IsNullOrWhiteSpace(validationMessage))
|
|
Logging.Log.WriteWarningMessage(LOGTAG, "OptionValidationError", null, validationMessage);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if the value passed to an option is actually valid.
|
|
/// </summary>
|
|
/// <param name="arg">The argument being validated</param>
|
|
/// <param name="optionname">The name of the option to validate</param>
|
|
/// <param name="value">The value to check</param>
|
|
/// <returns>Null if no errors are found, an error message otherwise</returns>
|
|
public static string? ValidateOptionValue(ICommandLineArgument arg, string optionname, string? value)
|
|
{
|
|
if (arg.Type == CommandLineArgument.ArgumentType.Enumeration)
|
|
{
|
|
var found = false;
|
|
foreach (var v in arg.ValidValues ?? [])
|
|
if (string.Equals(v, value, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
|
|
if (!found)
|
|
return Strings.CommandLineArgumentValidator.UnsupportedEnumerationValue(optionname, value, arg.ValidValues ?? new string[0]);
|
|
|
|
}
|
|
else if (arg.Type == CommandLineArgument.ArgumentType.Flags)
|
|
{
|
|
var validatedAllFlags = false;
|
|
var flags = (value ?? string.Empty).ToLowerInvariant().Split(new[] { "," }, StringSplitOptions.None).Select(flag => flag.Trim()).Distinct();
|
|
var validFlags = arg.ValidValues ?? [];
|
|
|
|
foreach (var flag in flags)
|
|
{
|
|
if (!validFlags.Any(validFlag => string.Equals(validFlag, flag, StringComparison.OrdinalIgnoreCase)))
|
|
{
|
|
validatedAllFlags = false;
|
|
break;
|
|
}
|
|
|
|
validatedAllFlags = true;
|
|
}
|
|
|
|
if (!validatedAllFlags)
|
|
return Strings.CommandLineArgumentValidator.UnsupportedFlagsValue(optionname, value, validFlags);
|
|
}
|
|
else if (arg.Type == CommandLineArgument.ArgumentType.Boolean)
|
|
{
|
|
if (!string.IsNullOrEmpty(value) && Utility.ParseBool(value, true) != Utility.ParseBool(value, false))
|
|
return Strings.CommandLineArgumentValidator.UnsupportedBooleanValue(optionname, value);
|
|
}
|
|
else if (arg.Type == CommandLineArgument.ArgumentType.Integer)
|
|
{
|
|
if (!long.TryParse(value, out _))
|
|
return Strings.CommandLineArgumentValidator.UnsupportedIntegerValue(optionname, value);
|
|
}
|
|
else if (arg.Type == CommandLineArgument.ArgumentType.Path)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(value))
|
|
return Strings.CommandLineArgumentValidator.UnsupportedPathValue(optionname, value);
|
|
|
|
foreach (var p in value.Split(Path.DirectorySeparatorChar))
|
|
if (p.IndexOfAny(Path.GetInvalidPathChars()) >= 0)
|
|
return Strings.CommandLineArgumentValidator.UnsupportedPathValue(optionname, p);
|
|
}
|
|
else if (arg.Type == CommandLineArgument.ArgumentType.Size)
|
|
{
|
|
try
|
|
{
|
|
Sizeparser.ParseSize(value);
|
|
}
|
|
catch
|
|
{
|
|
return Strings.CommandLineArgumentValidator.UnsupportedSizeValue(optionname, value);
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(value) && char.IsDigit(value.Last()))
|
|
return Strings.CommandLineArgumentValidator.NonQualifiedSizeValue(optionname, value);
|
|
}
|
|
else if (arg.Type == CommandLineArgument.ArgumentType.Timespan)
|
|
{
|
|
try
|
|
{
|
|
Timeparser.ParseTimeSpan(value);
|
|
}
|
|
catch
|
|
{
|
|
return Strings.CommandLineArgumentValidator.UnsupportedTimeValue(optionname, value);
|
|
}
|
|
}
|
|
else if (arg.Type == CommandLineArgument.ArgumentType.DateTime)
|
|
{
|
|
try
|
|
{
|
|
Timeparser.ParseTimeInterval(value, DateTime.Now);
|
|
}
|
|
catch
|
|
{
|
|
return Strings.CommandLineArgumentValidator.UnsupportedTimeValue(optionname, value);
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
} |