Fixed: Prevent conflicts with reserved device names

Closes #4595
This commit is contained in:
Mark McDowall 2021-08-01 16:45:23 -07:00
parent 2031da05f6
commit dc7f46027a
2 changed files with 105 additions and 5 deletions

View File

@ -0,0 +1,86 @@
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.MediaInfo;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
{
[TestFixture]
public class ReservedDeviceNameFixture : CoreTest<FileNameBuilder>
{
private Series _series;
private Episode _episode1;
private EpisodeFile _episodeFile;
private NamingConfig _namingConfig;
[SetUp]
public void Setup()
{
_series = Builder<Series>
.CreateNew()
.With(s => s.Title = "South Park")
.Build();
_namingConfig = NamingConfig.Default;
_namingConfig.RenameEpisodes = true;
Mocker.GetMock<INamingConfigService>()
.Setup(c => c.GetConfig()).Returns(_namingConfig);
_episode1 = Builder<Episode>.CreateNew()
.With(e => e.Title = "City Sushi")
.With(e => e.SeasonNumber = 15)
.With(e => e.EpisodeNumber = 6)
.With(e => e.AbsoluteEpisodeNumber = 100)
.Build();
_episodeFile = new EpisodeFile { Quality = new QualityModel(Quality.HDTV720p), ReleaseGroup = "SonarrTest" };
Mocker.GetMock<IQualityDefinitionService>()
.Setup(v => v.Get(Moq.It.IsAny<Quality>()))
.Returns<Quality>(v => Quality.DefaultQualityDefinitions.First(c => c.Quality == v));
}
[Test]
public void should_replace_reserved_device_name_in_series_folder()
{
_series.Title = "Con Man";
_namingConfig.SeriesFolderFormat = "{Series.Title}";
Subject.GetSeriesFolder(_series).Should().Be("Con_Man");
}
[Test]
public void should_replace_reserved_device_name_in_season_folder()
{
_series.Title = "Con Man";
_namingConfig.SeasonFolderFormat = "{Series.Title} - Season {Season:00}";
Subject.GetSeasonFolder(_series, 1).Should().Be("Con_Man - Season 01");
}
[Test]
public void should_replace_reserved_device_name_in_file_name()
{
_series.Title = "Con Man";
_namingConfig.StandardEpisodeFormat = "{Series.Title} - S{Season:00}E{Episode:00}";
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile).Should().Be("Con_Man - S15E06");
}
}
}

View File

@ -4,7 +4,6 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using FluentMigrator.Builders.Create.Column;
using NLog;
using NzbDrone.Common.Cache;
using NzbDrone.Common.Disk;
@ -13,7 +12,6 @@ using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.MediaInfo;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Profiles.Releases;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv;
@ -82,6 +80,8 @@ namespace NzbDrone.Core.Organizer
private static readonly Regex YearRegex = new Regex(@"\(\d{4}\)$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex ReservedDeviceNamesRegex = new Regex(@"^(?:aux|com1|com2|com3|com4|com5|com6|com7|com8|com9|con|lpt1|lpt2|lpt3|lpt4|lpt5|lpt6|lpt7|lpt8|lpt9|nul|prn)\.", RegexOptions.Compiled | RegexOptions.IgnoreCase);
public FileNameBuilder(INamingConfigService namingConfigService,
IQualityDefinitionService qualityDefinitionService,
ICacheManager cacheManager,
@ -176,6 +176,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);
components.Add(component);
}
@ -274,7 +275,11 @@ namespace NzbDrone.Core.Organizer
AddIdTokens(tokenHandlers, series);
var folderName = ReplaceTokens(namingConfig.SeriesFolderFormat, tokenHandlers, namingConfig);
return CleanFolderName(folderName);
folderName = CleanFolderName(folderName);
folderName = ReplaceReservedDeviceNames(folderName);
return folderName;
}
public string GetSeasonFolder(Series series, int seasonNumber, NamingConfig namingConfig = null)
@ -291,9 +296,12 @@ namespace NzbDrone.Core.Organizer
AddSeasonTokens(tokenHandlers, seasonNumber);
var format = seasonNumber == 0 ? namingConfig.SpecialsFolderFormat : namingConfig.SeasonFolderFormat;
var folderName = ReplaceTokens(format, tokenHandlers, namingConfig);
return CleanFolderName(folderName);
folderName = CleanFolderName(folderName);
folderName = ReplaceReservedDeviceNames(folderName);
return folderName;
}
public static string CleanTitle(string title)
@ -1048,6 +1056,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(".", "_"));
}
}
internal sealed class TokenMatch