New: Option to treat downloads with non-media extensions as failed

(cherry picked from commit 776143cc813ec1b5fa31fbf8667c3ab174b71f5c)

New: Treat .scr as dangerous file

(cherry picked from commit 103ccd74f30830944e9e9f06d02be096f476ae34)

New: .arj and .lzh extensions are potentially dangerous

(cherry picked from commit a72288a14e67f62b00f921dbeb1a0f57a61e5ba7)

Fixed: Failing dangerous and executable single file downloads

(cherry picked from commit e37684e045310ca543aa6a22b38a325cd8a8e84d)

Fixed: Rejected Imports with no associated release or indexer

(cherry picked from commit 31e02bdeada8c85d67a75b69e57d3e7ea46989c6)

Fixed: Don't return warning in title field for rejected downloads

(cherry picked from commit 1fa532dd3eaaee01ac6a049e43fcdbd44357d617)

Fixed: Improve rejected download handling

(cherry picked from commit 4db43882361232eb8fe9ee5331c3d77ea3aa8dfa)
This commit is contained in:
Mark McDowall 2024-11-13 20:13:00 -08:00 committed by Bogdan
parent 91f08a83cd
commit 48075e33ac
24 changed files with 294 additions and 58 deletions

View file

@ -1,8 +1,10 @@
using FluentAssertions;
using System;
using System.Collections.Generic;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.Torznab;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework;
@ -14,13 +16,13 @@ namespace NzbDrone.Core.Test.IndexerTests
[Test]
public void should_not_return_config_for_non_existent_indexer()
{
Mocker.GetMock<IIndexerFactory>()
.Setup(v => v.Get(It.IsAny<int>()))
.Throws(new ModelNotFoundException(typeof(IndexerDefinition), 0));
Mocker.GetMock<ICachedIndexerSettingsProvider>()
.Setup(v => v.GetSettings(It.IsAny<int>()))
.Returns<CachedIndexerSettings>(null);
var result = Subject.GetSeedConfiguration(new RemoteMovie
{
Release = new ReleaseInfo()
Release = new ReleaseInfo
{
DownloadProtocol = DownloadProtocol.Torrent,
IndexerId = 0
@ -29,5 +31,33 @@ namespace NzbDrone.Core.Test.IndexerTests
result.Should().BeNull();
}
[Test]
public void should_return_seed_time_for_movies()
{
var settings = new TorznabSettings();
settings.SeedCriteria.SeedTime = 10;
Mocker.GetMock<ICachedIndexerSettingsProvider>()
.Setup(v => v.GetSettings(It.IsAny<int>()))
.Returns(new CachedIndexerSettings
{
FailDownloads = new HashSet<FailDownloads> { FailDownloads.Executables },
SeedCriteriaSettings = settings.SeedCriteria
});
var result = Subject.GetSeedConfiguration(new RemoteMovie
{
Release = new ReleaseInfo
{
DownloadProtocol = DownloadProtocol.Torrent,
IndexerId = 1
},
ParsedMovieInfo = new ParsedMovieInfo()
});
result.Should().NotBeNull();
result.SeedTime.Should().Be(TimeSpan.FromMinutes(10));
}
}
}

View file

@ -15,5 +15,6 @@ namespace NzbDrone.Core.Test.IndexerTests
public string BaseUrl { get; set; }
public IEnumerable<int> MultiLanguages { get; set; }
public IEnumerable<int> FailDownloads { get; set; }
}
}

View file

@ -33,6 +33,7 @@ namespace NzbDrone.Core.Download
private readonly IParsingService _parsingService;
private readonly IMovieService _movieService;
private readonly ITrackedDownloadAlreadyImported _trackedDownloadAlreadyImported;
private readonly IRejectedImportService _rejectedImportService;
private readonly Logger _logger;
public CompletedDownloadService(IEventAggregator eventAggregator,
@ -42,6 +43,7 @@ namespace NzbDrone.Core.Download
IParsingService parsingService,
IMovieService movieService,
ITrackedDownloadAlreadyImported trackedDownloadAlreadyImported,
IRejectedImportService rejectedImportService,
Logger logger)
{
_eventAggregator = eventAggregator;
@ -51,6 +53,7 @@ namespace NzbDrone.Core.Download
_parsingService = parsingService;
_movieService = movieService;
_trackedDownloadAlreadyImported = trackedDownloadAlreadyImported;
_rejectedImportService = rejectedImportService;
_logger = logger;
}
@ -159,10 +162,8 @@ namespace NzbDrone.Core.Download
{
var firstResult = importResults.First();
if (firstResult.Result == ImportResultType.Rejected && firstResult.ImportDecision.LocalMovie == null)
if (_rejectedImportService.Process(trackedDownload, firstResult))
{
trackedDownload.Warn(new TrackedDownloadStatusMessage(firstResult.Errors.First(), new List<string>()));
return;
}
}

View file

@ -55,14 +55,18 @@ namespace NzbDrone.Core.Download
{
try
{
// Process completed items followed by failed, this allows failed imports to have
// their state changed and be processed immediately instead of the next execution.
if (enableCompletedDownloadHandling && trackedDownload.State == TrackedDownloadState.ImportPending)
{
_completedDownloadService.Import(trackedDownload);
}
if (trackedDownload.State == TrackedDownloadState.FailedPending)
{
_failedDownloadService.ProcessFailed(trackedDownload);
}
else if (enableCompletedDownloadHandling && trackedDownload.State == TrackedDownloadState.ImportPending)
{
_completedDownloadService.Import(trackedDownload);
}
}
catch (Exception e)
{

View file

@ -0,0 +1,60 @@
using System.Linq;
using NLog;
using NzbDrone.Core.Download.TrackedDownloads;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.MediaFiles.MovieImport;
namespace NzbDrone.Core.Download;
public interface IRejectedImportService
{
bool Process(TrackedDownload trackedDownload, ImportResult importResult);
}
public class RejectedImportService : IRejectedImportService
{
private readonly ICachedIndexerSettingsProvider _cachedIndexerSettingsProvider;
private readonly Logger _logger;
public RejectedImportService(ICachedIndexerSettingsProvider cachedIndexerSettingsProvider, Logger logger)
{
_cachedIndexerSettingsProvider = cachedIndexerSettingsProvider;
_logger = logger;
}
public bool Process(TrackedDownload trackedDownload, ImportResult importResult)
{
if (importResult.Result != ImportResultType.Rejected || trackedDownload.RemoteMovie?.Release == null)
{
return false;
}
var indexerSettings = _cachedIndexerSettingsProvider.GetSettings(trackedDownload.RemoteMovie.Release.IndexerId);
var rejectionReason = importResult.ImportDecision.Rejections.FirstOrDefault()?.Reason;
if (indexerSettings == null)
{
trackedDownload.Warn(new TrackedDownloadStatusMessage(trackedDownload.DownloadItem.Title, importResult.Errors));
return true;
}
if (rejectionReason == ImportRejectionReason.DangerousFile &&
indexerSettings.FailDownloads.Contains(FailDownloads.PotentiallyDangerous))
{
_logger.Trace("Download '{0}' contains potentially dangerous file, marking as failed", trackedDownload.DownloadItem.Title);
trackedDownload.Fail();
}
else if (rejectionReason == ImportRejectionReason.ExecutableFile &&
indexerSettings.FailDownloads.Contains(FailDownloads.Executables))
{
_logger.Trace("Download '{0}' contains executable file, marking as failed", trackedDownload.DownloadItem.Title);
trackedDownload.Fail();
}
else
{
trackedDownload.Warn(new TrackedDownloadStatusMessage(trackedDownload.DownloadItem.Title, importResult.Errors));
}
return true;
}
}

View file

@ -35,6 +35,12 @@ namespace NzbDrone.Core.Download.TrackedDownloads
Status = TrackedDownloadStatus.Warning;
StatusMessages = statusMessages;
}
public void Fail()
{
Status = TrackedDownloadStatus.Error;
State = TrackedDownloadState.FailedPending;
}
}
public enum TrackedDownloadState

View file

@ -166,6 +166,11 @@ namespace NzbDrone.Core.Download.TrackedDownloads
{
trackedDownload.RemoteMovie.Release.IndexerFlags = flags;
}
if (downloadHistory != null)
{
trackedDownload.RemoteMovie.Release.IndexerId = downloadHistory.IndexerId;
}
}
}

View file

@ -0,0 +1,69 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Common.Cache;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.ThingiProvider.Events;
namespace NzbDrone.Core.Indexers;
public interface ICachedIndexerSettingsProvider
{
CachedIndexerSettings GetSettings(int indexerId);
}
public class CachedIndexerSettingsProvider : ICachedIndexerSettingsProvider, IHandle<ProviderUpdatedEvent<IIndexer>>
{
private readonly IIndexerFactory _indexerFactory;
private readonly ICached<CachedIndexerSettings> _cache;
public CachedIndexerSettingsProvider(IIndexerFactory indexerFactory, ICacheManager cacheManager)
{
_indexerFactory = indexerFactory;
_cache = cacheManager.GetRollingCache<CachedIndexerSettings>(GetType(), "settingsByIndexer", TimeSpan.FromHours(1));
}
public CachedIndexerSettings GetSettings(int indexerId)
{
if (indexerId == 0)
{
return null;
}
return _cache.Get(indexerId.ToString(), () => FetchIndexerSettings(indexerId));
}
private CachedIndexerSettings FetchIndexerSettings(int indexerId)
{
var indexer = _indexerFactory.Get(indexerId);
var indexerSettings = indexer.Settings as IIndexerSettings;
if (indexerSettings == null)
{
return null;
}
var settings = new CachedIndexerSettings
{
FailDownloads = indexerSettings.FailDownloads.Select(f => (FailDownloads)f).ToHashSet()
};
if (indexer.Settings is ITorrentIndexerSettings torrentIndexerSettings)
{
settings.SeedCriteriaSettings = torrentIndexerSettings.SeedCriteria;
}
return settings;
}
public void Handle(ProviderUpdatedEvent<IIndexer> message)
{
_cache.Clear();
}
}
public class CachedIndexerSettings
{
public HashSet<FailDownloads> FailDownloads { get; set; }
public SeedCriteriaSettings SeedCriteriaSettings { get; set; }
}

View file

@ -0,0 +1,12 @@
using NzbDrone.Core.Annotations;
namespace NzbDrone.Core.Indexers;
public enum FailDownloads
{
[FieldOption(Label = "Executables")]
Executables = 0,
[FieldOption(Label = "Potentially Dangerous")]
PotentiallyDangerous = 1
}

View file

@ -38,6 +38,7 @@ namespace NzbDrone.Core.Indexers.FileList
};
MultiLanguages = Array.Empty<int>();
FailDownloads = Array.Empty<int>();
RequiredFlags = Array.Empty<int>();
}
@ -65,7 +66,10 @@ namespace NzbDrone.Core.Indexers.FileList
[FieldDefinition(7, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "IndexerSettingsMultiLanguageRelease", HelpText = "IndexerSettingsMultiLanguageReleaseHelpText", Advanced = true)]
public IEnumerable<int> MultiLanguages { get; set; }
[FieldDefinition(8, Type = FieldType.Select, SelectOptions = typeof(IndexerFlags), Label = "IndexerSettingsRequiredFlags", HelpText = "IndexerSettingsRequiredFlagsHelpText", HelpLink = "https://wiki.servarr.com/radarr/settings#indexer-flags", Advanced = true)]
[FieldDefinition(8, Type = FieldType.Select, SelectOptions = typeof(FailDownloads), Label = "IndexerSettingsFailDownloads", HelpText = "IndexerSettingsFailDownloadsHelpText", Advanced = true)]
public IEnumerable<int> FailDownloads { get; set; }
[FieldDefinition(9, Type = FieldType.Select, SelectOptions = typeof(IndexerFlags), Label = "IndexerSettingsRequiredFlags", HelpText = "IndexerSettingsRequiredFlagsHelpText", HelpLink = "https://wiki.servarr.com/radarr/settings#indexer-flags", Advanced = true)]
public IEnumerable<int> RequiredFlags { get; set; }
public NzbDroneValidationResult Validate()

View file

@ -33,6 +33,7 @@ namespace NzbDrone.Core.Indexers.HDBits
Codecs = Array.Empty<int>();
Mediums = Array.Empty<int>();
MultiLanguages = Array.Empty<int>();
FailDownloads = Array.Empty<int>();
RequiredFlags = Array.Empty<int>();
}
@ -66,7 +67,10 @@ namespace NzbDrone.Core.Indexers.HDBits
[FieldDefinition(9, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "IndexerSettingsMultiLanguageRelease", HelpText = "IndexerSettingsMultiLanguageReleaseHelpText", Advanced = true)]
public IEnumerable<int> MultiLanguages { get; set; }
[FieldDefinition(10, Type = FieldType.Select, SelectOptions = typeof(IndexerFlags), Label = "IndexerSettingsRequiredFlags", HelpText = "IndexerSettingsRequiredFlagsHelpText", HelpLink = "https://wiki.servarr.com/radarr/settings#indexer-flags", Advanced = true)]
[FieldDefinition(10, Type = FieldType.Select, SelectOptions = typeof(FailDownloads), Label = "IndexerSettingsFailDownloads", HelpText = "IndexerSettingsFailDownloadsHelpText", Advanced = true)]
public IEnumerable<int> FailDownloads { get; set; }
[FieldDefinition(11, Type = FieldType.Select, SelectOptions = typeof(IndexerFlags), Label = "IndexerSettingsRequiredFlags", HelpText = "IndexerSettingsRequiredFlagsHelpText", HelpLink = "https://wiki.servarr.com/radarr/settings#indexer-flags", Advanced = true)]
public IEnumerable<int> RequiredFlags { get; set; }
public NzbDroneValidationResult Validate()

View file

@ -9,5 +9,7 @@ namespace NzbDrone.Core.Indexers
// TODO: Need to Create UI field for this and turn functionality back on per indexer.
IEnumerable<int> MultiLanguages { get; set; }
IEnumerable<int> FailDownloads { get; set; }
}
}

View file

@ -36,6 +36,7 @@ namespace NzbDrone.Core.Indexers.IPTorrents
BaseUrl = string.Empty;
MinimumSeeders = IndexerDefaults.MINIMUM_SEEDERS;
MultiLanguages = Array.Empty<int>();
FailDownloads = Array.Empty<int>();
RequiredFlags = Array.Empty<int>();
}
@ -54,7 +55,10 @@ namespace NzbDrone.Core.Indexers.IPTorrents
[FieldDefinition(4, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "IndexerSettingsMultiLanguageRelease", HelpText = "IndexerSettingsMultiLanguageReleaseHelpText", Advanced = true)]
public IEnumerable<int> MultiLanguages { get; set; }
[FieldDefinition(5, Type = FieldType.Select, SelectOptions = typeof(IndexerFlags), Label = "IndexerSettingsRequiredFlags", HelpText = "IndexerSettingsRequiredFlagsHelpText", HelpLink = "https://wiki.servarr.com/radarr/settings#indexer-flags", Advanced = true)]
[FieldDefinition(5, Type = FieldType.Select, SelectOptions = typeof(FailDownloads), Label = "IndexerSettingsFailDownloads", HelpText = "IndexerSettingsFailDownloadsHelpText", Advanced = true)]
public IEnumerable<int> FailDownloads { get; set; }
[FieldDefinition(6, Type = FieldType.Select, SelectOptions = typeof(IndexerFlags), Label = "IndexerSettingsRequiredFlags", HelpText = "IndexerSettingsRequiredFlagsHelpText", HelpLink = "https://wiki.servarr.com/radarr/settings#indexer-flags", Advanced = true)]
public IEnumerable<int> RequiredFlags { get; set; }
public NzbDroneValidationResult Validate()

View file

@ -58,6 +58,7 @@ namespace NzbDrone.Core.Indexers.Newznab
ApiPath = "/api";
Categories = new[] { 2000, 2010, 2020, 2030, 2040, 2045, 2050, 2060 };
MultiLanguages = Array.Empty<int>();
FailDownloads = Array.Empty<int>();
}
[FieldDefinition(0, Label = "URL")]
@ -79,7 +80,10 @@ namespace NzbDrone.Core.Indexers.Newznab
[FieldDefinition(5, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "IndexerSettingsMultiLanguageRelease", HelpText = "IndexerSettingsMultiLanguageReleaseHelpText", Advanced = true)]
public IEnumerable<int> MultiLanguages { get; set; }
[FieldDefinition(6, Type = FieldType.Checkbox, Label = "IndexerSettingsRemoveYear", HelpText = "IndexerSettingsRemoveYearHelpText", Advanced = true)]
[FieldDefinition(6, Type = FieldType.Select, SelectOptions = typeof(FailDownloads), Label = "IndexerSettingsFailDownloads", HelpText = "IndexerSettingsFailDownloadsHelpText", Advanced = true)]
public IEnumerable<int> FailDownloads { get; set; }
[FieldDefinition(7, Type = FieldType.Checkbox, Label = "IndexerSettingsRemoveYear", HelpText = "IndexerSettingsRemoveYearHelpText", Advanced = true)]
public bool RemoveYear { get; set; }
// Field 8 is used by TorznabSettings MinimumSeeders

View file

@ -31,6 +31,7 @@ namespace NzbDrone.Core.Indexers.Nyaa
AdditionalParameters = "&cats=1_0&filter=1";
MinimumSeeders = IndexerDefaults.MINIMUM_SEEDERS;
MultiLanguages = Array.Empty<int>();
FailDownloads = Array.Empty<int>();
RequiredFlags = Array.Empty<int>();
}
@ -52,7 +53,10 @@ namespace NzbDrone.Core.Indexers.Nyaa
[FieldDefinition(5, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "IndexerSettingsMultiLanguageRelease", HelpText = "IndexerSettingsMultiLanguageReleaseHelpText", Advanced = true)]
public IEnumerable<int> MultiLanguages { get; set; }
[FieldDefinition(6, Type = FieldType.Select, SelectOptions = typeof(IndexerFlags), Label = "IndexerSettingsRequiredFlags", HelpText = "IndexerSettingsRequiredFlagsHelpText", HelpLink = "https://wiki.servarr.com/radarr/settings#indexer-flags", Advanced = true)]
[FieldDefinition(6, Type = FieldType.Select, SelectOptions = typeof(FailDownloads), Label = "IndexerSettingsFailDownloads", HelpText = "IndexerSettingsFailDownloadsHelpText", Advanced = true)]
public IEnumerable<int> FailDownloads { get; set; }
[FieldDefinition(7, Type = FieldType.Select, SelectOptions = typeof(IndexerFlags), Label = "IndexerSettingsRequiredFlags", HelpText = "IndexerSettingsRequiredFlagsHelpText", HelpLink = "https://wiki.servarr.com/radarr/settings#indexer-flags", Advanced = true)]
public IEnumerable<int> RequiredFlags { get; set; }
public NzbDroneValidationResult Validate()

View file

@ -30,6 +30,7 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn
BaseUrl = "https://passthepopcorn.me";
MinimumSeeders = IndexerDefaults.MINIMUM_SEEDERS;
MultiLanguages = Array.Empty<int>();
FailDownloads = Array.Empty<int>();
RequiredFlags = Array.Empty<int>();
}
@ -54,7 +55,10 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn
[FieldDefinition(6, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "IndexerSettingsMultiLanguageRelease", HelpText = "IndexerSettingsMultiLanguageReleaseHelpText", Advanced = true)]
public IEnumerable<int> MultiLanguages { get; set; }
[FieldDefinition(7, Type = FieldType.Select, SelectOptions = typeof(IndexerFlags), Label = "IndexerSettingsRequiredFlags", HelpText = "IndexerSettingsRequiredFlagsHelpText", HelpLink = "https://wiki.servarr.com/radarr/settings#indexer-flags", Advanced = true)]
[FieldDefinition(7, Type = FieldType.Select, SelectOptions = typeof(FailDownloads), Label = "IndexerSettingsFailDownloads", HelpText = "IndexerSettingsFailDownloadsHelpText", Advanced = true)]
public IEnumerable<int> FailDownloads { get; set; }
[FieldDefinition(8, Type = FieldType.Select, SelectOptions = typeof(IndexerFlags), Label = "IndexerSettingsRequiredFlags", HelpText = "IndexerSettingsRequiredFlagsHelpText", HelpLink = "https://wiki.servarr.com/radarr/settings#indexer-flags", Advanced = true)]
public IEnumerable<int> RequiredFlags { get; set; }
public NzbDroneValidationResult Validate()

View file

@ -1,10 +1,6 @@
using System;
using NzbDrone.Common.Cache;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Download.Clients;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.ThingiProvider.Events;
namespace NzbDrone.Core.Indexers
{
@ -14,15 +10,13 @@ namespace NzbDrone.Core.Indexers
TorrentSeedConfiguration GetSeedConfiguration(int indexerId);
}
public class SeedConfigProvider : ISeedConfigProvider, IHandle<ProviderUpdatedEvent<IIndexer>>
public class SeedConfigProvider : ISeedConfigProvider
{
private readonly IIndexerFactory _indexerFactory;
private readonly ICached<SeedCriteriaSettings> _cache;
private readonly ICachedIndexerSettingsProvider _cachedIndexerSettingsProvider;
public SeedConfigProvider(IIndexerFactory indexerFactory, ICacheManager cacheManager)
public SeedConfigProvider(ICachedIndexerSettingsProvider cachedIndexerSettingsProvider)
{
_indexerFactory = indexerFactory;
_cache = cacheManager.GetRollingCache<SeedCriteriaSettings>(GetType(), "criteriaByIndexer", TimeSpan.FromHours(1));
_cachedIndexerSettingsProvider = cachedIndexerSettingsProvider;
}
public TorrentSeedConfiguration GetSeedConfiguration(RemoteMovie remoteMovie)
@ -47,7 +41,8 @@ namespace NzbDrone.Core.Indexers
return null;
}
var seedCriteria = _cache.Get(indexerId.ToString(), () => FetchSeedCriteria(indexerId));
var settings = _cachedIndexerSettingsProvider.GetSettings(indexerId);
var seedCriteria = settings?.SeedCriteriaSettings;
if (seedCriteria == null)
{
@ -68,25 +63,5 @@ namespace NzbDrone.Core.Indexers
return seedConfig;
}
private SeedCriteriaSettings FetchSeedCriteria(int indexerId)
{
try
{
var indexer = _indexerFactory.Get(indexerId);
var torrentIndexerSettings = indexer.Settings as ITorrentIndexerSettings;
return torrentIndexerSettings?.SeedCriteria;
}
catch (ModelNotFoundException)
{
return null;
}
}
public void Handle(ProviderUpdatedEvent<IIndexer> message)
{
_cache.Clear();
}
}
}

View file

@ -28,6 +28,7 @@ namespace NzbDrone.Core.Indexers.TorrentPotato
BaseUrl = "http://127.0.0.1";
MinimumSeeders = IndexerDefaults.MINIMUM_SEEDERS;
MultiLanguages = Array.Empty<int>();
FailDownloads = Array.Empty<int>();
RequiredFlags = Array.Empty<int>();
}
@ -52,7 +53,10 @@ namespace NzbDrone.Core.Indexers.TorrentPotato
[FieldDefinition(6, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "IndexerSettingsMultiLanguageRelease", HelpText = "IndexerSettingsMultiLanguageReleaseHelpText", Advanced = true)]
public IEnumerable<int> MultiLanguages { get; set; }
[FieldDefinition(7, Type = FieldType.Select, SelectOptions = typeof(IndexerFlags), Label = "IndexerSettingsRequiredFlags", HelpText = "IndexerSettingsRequiredFlagsHelpText", HelpLink = "https://wiki.servarr.com/radarr/settings#indexer-flags", Advanced = true)]
[FieldDefinition(7, Type = FieldType.Select, SelectOptions = typeof(FailDownloads), Label = "IndexerSettingsFailDownloads", HelpText = "IndexerSettingsFailDownloadsHelpText", Advanced = true)]
public IEnumerable<int> FailDownloads { get; set; }
[FieldDefinition(8, Type = FieldType.Select, SelectOptions = typeof(IndexerFlags), Label = "IndexerSettingsRequiredFlags", HelpText = "IndexerSettingsRequiredFlagsHelpText", HelpLink = "https://wiki.servarr.com/radarr/settings#indexer-flags", Advanced = true)]
public IEnumerable<int> RequiredFlags { get; set; }
public NzbDroneValidationResult Validate()

View file

@ -29,6 +29,7 @@ namespace NzbDrone.Core.Indexers.TorrentRss
AllowZeroSize = false;
MinimumSeeders = IndexerDefaults.MINIMUM_SEEDERS;
MultiLanguages = Array.Empty<int>();
FailDownloads = Array.Empty<int>();
RequiredFlags = Array.Empty<int>();
}
@ -53,7 +54,10 @@ namespace NzbDrone.Core.Indexers.TorrentRss
[FieldDefinition(6, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "IndexerSettingsMultiLanguageRelease", HelpText = "IndexerSettingsMultiLanguageReleaseHelpText", Advanced = true)]
public IEnumerable<int> MultiLanguages { get; set; }
[FieldDefinition(7, Type = FieldType.Select, SelectOptions = typeof(IndexerFlags), Label = "IndexerSettingsRequiredFlags", HelpText = "IndexerSettingsRequiredFlagsHelpText", HelpLink = "https://wiki.servarr.com/radarr/settings#indexer-flags", Advanced = true)]
[FieldDefinition(7, Type = FieldType.Select, SelectOptions = typeof(FailDownloads), Label = "IndexerSettingsFailDownloads", HelpText = "IndexerSettingsFailDownloadsHelpText", Advanced = true)]
public IEnumerable<int> FailDownloads { get; set; }
[FieldDefinition(8, Type = FieldType.Select, SelectOptions = typeof(IndexerFlags), Label = "IndexerSettingsRequiredFlags", HelpText = "IndexerSettingsRequiredFlagsHelpText", HelpLink = "https://wiki.servarr.com/radarr/settings#indexer-flags", Advanced = true)]
public IEnumerable<int> RequiredFlags { get; set; }
public NzbDroneValidationResult Validate()

View file

@ -55,16 +55,16 @@ namespace NzbDrone.Core.Indexers.Torznab
RequiredFlags = Array.Empty<int>();
}
[FieldDefinition(8, Type = FieldType.Number, Label = "IndexerSettingsMinimumSeeders", HelpText = "IndexerSettingsMinimumSeedersHelpText", Advanced = true)]
[FieldDefinition(9, Type = FieldType.Number, Label = "IndexerSettingsMinimumSeeders", HelpText = "IndexerSettingsMinimumSeedersHelpText", Advanced = true)]
public int MinimumSeeders { get; set; }
[FieldDefinition(9)]
[FieldDefinition(10)]
public SeedCriteriaSettings SeedCriteria { get; set; } = new ();
[FieldDefinition(10, Type = FieldType.Checkbox, Label = "IndexerSettingsRejectBlocklistedTorrentHashes", HelpText = "IndexerSettingsRejectBlocklistedTorrentHashesHelpText", Advanced = true)]
[FieldDefinition(11, Type = FieldType.Checkbox, Label = "IndexerSettingsRejectBlocklistedTorrentHashes", HelpText = "IndexerSettingsRejectBlocklistedTorrentHashesHelpText", Advanced = true)]
public bool RejectBlocklistedTorrentHashesWhileGrabbing { get; set; }
[FieldDefinition(11, Type = FieldType.Select, SelectOptions = typeof(IndexerFlags), Label = "IndexerSettingsRequiredFlags", HelpText = "IndexerSettingsRequiredFlagsHelpText", HelpLink = "https://wiki.servarr.com/radarr/settings#indexer-flags", Advanced = true)]
[FieldDefinition(12, Type = FieldType.Select, SelectOptions = typeof(IndexerFlags), Label = "IndexerSettingsRequiredFlags", HelpText = "IndexerSettingsRequiredFlagsHelpText", HelpLink = "https://wiki.servarr.com/radarr/settings#indexer-flags", Advanced = true)]
public IEnumerable<int> RequiredFlags { get; set; }
public override NzbDroneValidationResult Validate()

View file

@ -889,6 +889,8 @@
"IndexerSettingsCategories": "Categories",
"IndexerSettingsCookie": "Cookie",
"IndexerSettingsCookieHelpText": "If your site requires a login cookie to access the RSS, you'll have to retrieve it via a browser.",
"IndexerSettingsFailDownloads": "Fail Downloads",
"IndexerSettingsFailDownloadsHelpText": "While processing completed downloads {appName} will treat selected errors preventing importing as failed downloads.",
"IndexerSettingsMinimumSeeders": "Minimum Seeders",
"IndexerSettingsMinimumSeedersHelpText": "Minimum number of seeders required.",
"IndexerSettingsMultiLanguageRelease": "Multi Languages",

View file

@ -277,6 +277,26 @@ namespace NzbDrone.Core.MediaFiles
var extension = Path.GetExtension(fileInfo.Name);
if (FileExtensions.DangerousExtensions.Contains(extension))
{
return new List<ImportResult>
{
new ImportResult(new ImportDecision(new LocalMovie { Path = fileInfo.FullName },
new ImportRejection(ImportRejectionReason.DangerousFile, $"Caution: Found potentially dangerous file with extension: {extension}")),
$"Caution: Found potentially dangerous file with extension: {extension}")
};
}
if (FileExtensions.ExecutableExtensions.Contains(extension))
{
return new List<ImportResult>
{
new ImportResult(new ImportDecision(new LocalMovie { Path = fileInfo.FullName },
new ImportRejection(ImportRejectionReason.ExecutableFile, $"Caution: Found executable file with extension: '{extension}'")),
$"Caution: Found executable file with extension: '{extension}'")
};
}
if (extension.IsNullOrWhiteSpace() || !MediaFileExtensions.Extensions.Contains(extension))
{
_logger.Debug("[{0}] has an unsupported extension: '{1}'", fileInfo.FullName, extension);
@ -335,6 +355,11 @@ namespace NzbDrone.Core.MediaFiles
{
var files = _diskProvider.GetFiles(folder, true);
if (files.Any(file => FileExtensions.DangerousExtensions.Contains(Path.GetExtension(file))))
{
return RejectionResult(ImportRejectionReason.DangerousFile, "Caution: Found potentially dangerous file");
}
if (files.Any(file => FileExtensions.ExecutableExtensions.Contains(Path.GetExtension(file))))
{
return RejectionResult(ImportRejectionReason.ExecutableFile, "Caution: Found executable file");

View file

@ -18,19 +18,30 @@ namespace NzbDrone.Core.MediaFiles
".tb2",
".tbz2",
".tgz",
".zip",
".zip"
};
private static List<string> _dangerousExtensions = new List<string>
{
".arj",
".lnk",
".lzh",
".ps1",
".scr",
".vbs",
".zipx"
};
private static List<string> _executableExtensions = new List<string>
{
".exe",
".bat",
".cmd",
".exe",
".sh"
};
public static HashSet<string> ArchiveExtensions => new HashSet<string>(_archiveExtensions, StringComparer.OrdinalIgnoreCase);
public static HashSet<string> DangerousExtensions => new HashSet<string>(_dangerousExtensions, StringComparer.OrdinalIgnoreCase);
public static HashSet<string> ExecutableExtensions => new HashSet<string>(_executableExtensions, StringComparer.OrdinalIgnoreCase);
}
}

View file

@ -5,6 +5,7 @@ public enum ImportRejectionReason
Unknown,
FileLocked,
UnknownMovie,
DangerousFile,
ExecutableFile,
ArchiveFile,
MovieFolder,