using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using FluentValidation; using NzbDrone.Common.Extensions; using NzbDrone.Core.Annotations; using NzbDrone.Core.Languages; using NzbDrone.Core.Validation; namespace NzbDrone.Core.Indexers.Newznab { public class NewznabSettingsValidator : AbstractValidator { private static readonly string[] ApiKeyWhiteList = { "nzbs.org", "nzb.su", "dognzb.cr", "nzbplanet.net", "nzbid.org", "nzbndx.com", "nzbindex.in" }; private static bool ShouldHaveApiKey(NewznabSettings settings) { if (settings.BaseUrl == null) { return false; } return ApiKeyWhiteList.Any(c => settings.BaseUrl.ToLowerInvariant().Contains(c)); } private static readonly Regex AdditionalParametersRegex = new Regex(@"(&.+?\=.+?)+", RegexOptions.Compiled); public NewznabSettingsValidator() { RuleFor(c => c).Custom((c, context) => { if (c.Categories.Empty()) { context.AddFailure("'Categories' must be provided"); } }); RuleFor(c => c.BaseUrl).ValidRootUrl(); RuleFor(c => c.ApiPath).ValidUrlBase("/api"); RuleFor(c => c.ApiKey).NotEmpty().When(ShouldHaveApiKey); RuleFor(c => c.AdditionalParameters).Matches(AdditionalParametersRegex) .When(c => !c.AdditionalParameters.IsNullOrWhiteSpace()); } } public class NewznabSettings : IIndexerSettings { private static readonly NewznabSettingsValidator Validator = new NewznabSettingsValidator(); public NewznabSettings() { ApiPath = "/api"; Categories = new[] { 2000, 2010, 2020, 2030, 2040, 2045, 2050, 2060 }; MultiLanguages = new List(); } [FieldDefinition(0, Label = "URL")] public string BaseUrl { get; set; } [FieldDefinition(1, Label = "API Path", HelpText = "Path to the api, usually /api", Advanced = true)] public string ApiPath { get; set; } [FieldDefinition(1, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "Multi Languages", HelpText = "What languages are normally in a multi release on this indexer?", Advanced = true)] public IEnumerable MultiLanguages { get; set; } [FieldDefinition(2, Label = "API Key", Privacy = PrivacyLevel.ApiKey)] public string ApiKey { get; set; } [FieldDefinition(3, Label = "Categories", Type = FieldType.Select, SelectOptionsProviderAction = "newznabCategories", HelpText = "Drop down list, leave blank to disable all categories", Advanced = true)] public IEnumerable Categories { get; set; } [FieldDefinition(5, Label = "Additional Parameters", HelpText = "Additional Newznab parameters", Advanced = true)] public string AdditionalParameters { get; set; } [FieldDefinition(6, Label = "Remove year from search string", HelpText = "Should Radarr remove the year after the title when searching this indexer?", Advanced = true, Type = FieldType.Checkbox)] public bool RemoveYear { get; set; } // Field 8 is used by TorznabSettings MinimumSeeders // If you need to add another field here, update TorznabSettings as well and this comment public virtual NzbDroneValidationResult Validate() { return new NzbDroneValidationResult(Validator.Validate(this)); } } }