From efa0a53f1cb11b14fb4b099934332dbf748a55e9 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sun, 1 Aug 2021 16:45:23 -0700 Subject: [PATCH] Fixed: Prevent conflicts with reserved device names Closes #2346 Closes #2349 (cherry picked from commit dc7f46027aebf33b77d258a63c2ae973788cedd0) --- .../ReservedDeviceNameFixture.cs | 97 +++++++++++++++++++ .../Organizer/FileNameBuilder.cs | 10 ++ 2 files changed, 107 insertions(+) create mode 100644 src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/ReservedDeviceNameFixture.cs diff --git a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/ReservedDeviceNameFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/ReservedDeviceNameFixture.cs new file mode 100644 index 000000000..eceb0bf2a --- /dev/null +++ b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/ReservedDeviceNameFixture.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using FizzWare.NBuilder; +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.MediaFiles; +using NzbDrone.Core.Music; +using NzbDrone.Core.Organizer; +using NzbDrone.Core.Qualities; +using NzbDrone.Core.Test.Framework; + +namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests +{ + [TestFixture] + + public class ReservedDeviceNameFixture : CoreTest + { + private Artist _artist; + private Album _album; + private Track _track1; + private Medium _medium; + private AlbumRelease _release; + private TrackFile _trackFile; + private NamingConfig _namingConfig; + + [SetUp] + public void Setup() + { + _artist = Builder + .CreateNew() + .With(s => s.Name = "Tim Park") + .Build(); + + _medium = Builder + .CreateNew() + .With(m => m.Number = 3) + .Build(); + + _release = Builder + .CreateNew() + .With(s => s.Media = new List { _medium }) + .With(s => s.Monitored = true) + .Build(); + + _album = Builder + .CreateNew() + .With(s => s.Title = "Hybrid Theory") + .With(s => s.AlbumType = "Album") + .With(s => s.Disambiguation = "The Best Album") + .With(s => s.Genres = new List { "Rock" }) + .With(s => s.ForeignAlbumId = Guid.NewGuid().ToString()) + .Build(); + + _namingConfig = NamingConfig.Default; + _namingConfig.RenameTracks = true; + + Mocker.GetMock() + .Setup(c => c.GetConfig()).Returns(_namingConfig); + + _track1 = Builder.CreateNew() + .With(e => e.Title = "City Sushi") + .With(e => e.TrackNumber = "6") + .With(e => e.AbsoluteTrackNumber = 6) + .With(e => e.AlbumRelease = _release) + .With(e => e.MediumNumber = _medium.Number) + .With(e => e.ArtistMetadata = _artist.Metadata) + .Build(); + + _trackFile = new TrackFile { Quality = new QualityModel(Quality.FLAC), ReleaseGroup = "SonarrTest" }; + + Mocker.GetMock() + .Setup(v => v.Get(Moq.It.IsAny())) + .Returns(v => Quality.DefaultQualityDefinitions.First(c => c.Quality == v)); + } + + [TestCase("Con Game", "Con_Game")] + [TestCase("Com1 Sat", "Com1_Sat")] + public void should_replace_reserved_device_name_in_artist_folder(string title, string expected) + { + _artist.Name = title; + _namingConfig.ArtistFolderFormat = "{Artist.Name}"; + + Subject.GetArtistFolder(_artist).Should().Be(expected); + } + + [TestCase("Con Game", "Con_Game")] + [TestCase("Com1 Sat", "Com1_Sat")] + public void should_replace_reserved_device_name_in_file_name(string title, string expected) + { + _artist.Name = title; + _namingConfig.StandardTrackFormat = "{Artist.Name} - {Track Title}"; + + Subject.BuildTrackFileName(new List { _track1 }, _artist, _album, _trackFile).Should().Be($"{expected} - City Sushi"); + } + } +} diff --git a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs index 2f0202674..e2c7e6b2d 100644 --- a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs +++ b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs @@ -69,6 +69,8 @@ namespace NzbDrone.Core.Organizer private static readonly Regex TitlePrefixRegex = new Regex(@"^(The|An|A) (.*?)((?: *\([^)]+\))*)$", RegexOptions.Compiled | RegexOptions.IgnoreCase); + private static readonly Regex ReservedDeviceNamesRegex = new Regex(@"^(?:aux|com[1-9]|con|lpt[1-9]|nul|prn)\.", RegexOptions.Compiled | RegexOptions.IgnoreCase); + public FileNameBuilder(INamingConfigService namingConfigService, IQualityDefinitionService qualityDefinitionService, ICacheManager cacheManager, @@ -144,6 +146,7 @@ namespace NzbDrone.Core.Organizer component = FileNameCleanupRegex.Replace(component, match => match.Captures[0].Value[0].ToString()); component = TrimSeparatorsRegex.Replace(component, string.Empty); component = component.Replace("{ellipsis}", "..."); + component = ReplaceReservedDeviceNames(component); if (component.IsNotNullOrWhiteSpace()) { @@ -236,6 +239,7 @@ namespace NzbDrone.Core.Organizer var component = ReplaceTokens(splitPattern, tokenHandlers, namingConfig); component = CleanFolderName(component); + component = ReplaceReservedDeviceNames(component); if (component.IsNotNullOrWhiteSpace()) { @@ -644,6 +648,12 @@ namespace NzbDrone.Core.Organizer return result.GetByteCount(); } + private string ReplaceReservedDeviceNames(string input) + { + // Replace reserved windows device names with an alternative + return ReservedDeviceNamesRegex.Replace(input, match => match.Value.Replace(".", "_")); + } + private static string CleanFileName(string name, NamingConfig namingConfig) { var result = name;