From be3ec7ddb83aa3f7a64fbb7712bdda4ce417561f Mon Sep 17 00:00:00 2001 From: kayone Date: Sun, 1 Dec 2013 13:33:53 -0800 Subject: [PATCH] new: smarter validation for newznab indexer settings --- NzbDrone.sln.DotSettings | 3 +- .../NewznabTests/NewznabSettingFixture.cs | 41 +++++++++++++++++++ .../NzbDrone.Core.Test.csproj | 1 + .../Indexers/Newznab/NewznabParser.cs | 24 +++++++++++ .../Indexers/Newznab/NewznabPreProcessor.cs | 11 ++++- .../Indexers/Newznab/NewznabSettings.cs | 17 ++++++++ .../Indexers/NewznabTestService.cs | 4 +- src/NzbDrone.Core/Indexers/RssParserBase.cs | 4 +- 8 files changed, 98 insertions(+), 7 deletions(-) create mode 100644 src/NzbDrone.Core.Test/IndexerTests/NewznabTests/NewznabSettingFixture.cs diff --git a/NzbDrone.sln.DotSettings b/NzbDrone.sln.DotSettings index e7d45a2a8..4657be0e3 100644 --- a/NzbDrone.sln.DotSettings +++ b/NzbDrone.sln.DotSettings @@ -1 +1,2 @@ - \ No newline at end of file + + True \ No newline at end of file diff --git a/src/NzbDrone.Core.Test/IndexerTests/NewznabTests/NewznabSettingFixture.cs b/src/NzbDrone.Core.Test/IndexerTests/NewznabTests/NewznabSettingFixture.cs new file mode 100644 index 000000000..7ef91def5 --- /dev/null +++ b/src/NzbDrone.Core.Test/IndexerTests/NewznabTests/NewznabSettingFixture.cs @@ -0,0 +1,41 @@ +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.Indexers.Newznab; +using NzbDrone.Core.Test.Framework; + +namespace NzbDrone.Core.Test.IndexerTests.NewznabTests +{ + public class NewznabSettingFixture : CoreTest + { + + [TestCase("http://nzbs.org")] + [TestCase("http:///www.nzbplanet.net")] + public void requires_apikey(string url) + { + var setting = new NewznabSettings() + { + ApiKey = "", + Url = url + }; + + + setting.Validate().IsValid.Should().BeFalse(); + setting.Validate().Errors.Should().Contain(c => c.PropertyName == "ApiKey"); + + } + + + [TestCase("http://nzbs2.org")] + public void doesnt_requires_apikey(string url) + { + var setting = new NewznabSettings() + { + ApiKey = "", + Url = url + }; + + + setting.Validate().IsValid.Should().BeTrue(); + } + } +} \ No newline at end of file diff --git a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index 6f0904eda..313d67d3a 100644 --- a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -138,6 +138,7 @@ + diff --git a/src/NzbDrone.Core/Indexers/Newznab/NewznabParser.cs b/src/NzbDrone.Core/Indexers/Newznab/NewznabParser.cs index 451c6222b..85fe2d4e4 100644 --- a/src/NzbDrone.Core/Indexers/Newznab/NewznabParser.cs +++ b/src/NzbDrone.Core/Indexers/Newznab/NewznabParser.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Xml.Linq; using NzbDrone.Core.Parser.Model; @@ -7,6 +8,12 @@ namespace NzbDrone.Core.Indexers.Newznab { public class NewznabParser : RssParserBase { + + private static readonly string[] IgnoredErrors = + { + "Request limit reached", + }; + protected override string GetNzbInfoUrl(XElement item) { return item.Comments().Replace("#comments", ""); @@ -25,6 +32,23 @@ protected override long GetSize(XElement item) return ParseSize(item.Description()); } + public override IEnumerable Process(string xml, string url) + { + try + { + return base.Process(xml, url); + } + catch (NewznabException e) + { + if (!IgnoredErrors.Any(ignoredError => e.Message.Contains(ignoredError))) + { + throw; + } + _logger.Error(e.Message); + return new List(); + } + } + protected override ReleaseInfo PostProcessor(XElement item, ReleaseInfo currentResult) { if (currentResult != null) diff --git a/src/NzbDrone.Core/Indexers/Newznab/NewznabPreProcessor.cs b/src/NzbDrone.Core/Indexers/Newznab/NewznabPreProcessor.cs index 3977a2c75..4cd4d66c8 100644 --- a/src/NzbDrone.Core/Indexers/Newznab/NewznabPreProcessor.cs +++ b/src/NzbDrone.Core/Indexers/Newznab/NewznabPreProcessor.cs @@ -15,10 +15,17 @@ public static void Process(string source, string url) if (error == null) return; var code = Convert.ToInt32(error.Attribute("code").Value); + var errorMessage = error.Attribute("description").Value; - if (code >= 100 && code <= 199) throw new ApiKeyException("Invalid API key: {0}"); - throw new NewznabException("Newznab error detected: {0}", error.Attribute("description").Value); + if (code >= 100 && code <= 199) throw new ApiKeyException("Invalid API key"); + + if (!url.Contains("apikey=") && errorMessage == "Missing parameter") + { + throw new ApiKeyException("Indexer requires and API key"); + } + + throw new NewznabException("Newznab error detected: {0}", errorMessage); } } } diff --git a/src/NzbDrone.Core/Indexers/Newznab/NewznabSettings.cs b/src/NzbDrone.Core/Indexers/Newznab/NewznabSettings.cs index c66dcf56e..c5a0eb230 100644 --- a/src/NzbDrone.Core/Indexers/Newznab/NewznabSettings.cs +++ b/src/NzbDrone.Core/Indexers/Newznab/NewznabSettings.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using FluentValidation; using FluentValidation.Results; using NzbDrone.Core.Annotations; @@ -10,9 +11,25 @@ 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", + }; + + private static bool ShouldHaveApiKey(NewznabSettings settings) + { + return ApiKeyWhiteList.Any(c => settings.Url.ToLowerInvariant().Contains(c)); + } + public NewznabSettingsValidator() { RuleFor(c => c.Url).ValidRootUrl(); + RuleFor(c => c.ApiKey).NotEmpty().When(ShouldHaveApiKey); } } diff --git a/src/NzbDrone.Core/Indexers/NewznabTestService.cs b/src/NzbDrone.Core/Indexers/NewznabTestService.cs index c56b9b464..9055aa5e7 100644 --- a/src/NzbDrone.Core/Indexers/NewznabTestService.cs +++ b/src/NzbDrone.Core/Indexers/NewznabTestService.cs @@ -41,14 +41,14 @@ public void Test(IIndexer indexer) NewznabPreProcessor.Process(xml, url); } - catch (ApiKeyException apiKeyException) + catch (ApiKeyException) { _logger.Warn("Indexer returned result for Newznab RSS URL, API Key appears to be invalid"); var apiKeyFailure = new ValidationFailure("ApiKey", "Invalid API Key"); throw new ValidationException(new List { apiKeyFailure }.ToArray()); } - catch (Exception ex) + catch (Exception) { _logger.Warn("Indexer doesn't appear to be Newznab based"); diff --git a/src/NzbDrone.Core/Indexers/RssParserBase.cs b/src/NzbDrone.Core/Indexers/RssParserBase.cs index 7eae9a117..1065631aa 100644 --- a/src/NzbDrone.Core/Indexers/RssParserBase.cs +++ b/src/NzbDrone.Core/Indexers/RssParserBase.cs @@ -15,7 +15,7 @@ namespace NzbDrone.Core.Indexers { public abstract class RssParserBase : IParseFeed { - private readonly Logger _logger; + protected readonly Logger _logger; protected virtual ReleaseInfo CreateNewReleaseInfo() { @@ -27,7 +27,7 @@ protected RssParserBase() _logger = NzbDroneLogger.GetLogger(this); } - public IEnumerable Process(string xml, string url) + public virtual IEnumerable Process(string xml, string url) { PreProcess(xml, url);