From 0794a3edf48eb232030560c4a03587cd77f38b65 Mon Sep 17 00:00:00 2001 From: bugfixin Date: Thu, 18 Apr 2019 21:47:19 +0000 Subject: Adjust detection of 'sample' in filenames to use regex boundaries --- .../Library/CoreResolutionIgnoreRule.cs | 11 +++-------- .../Library/Resolvers/Movies/MovieResolver.cs | 13 +++---------- 2 files changed, 6 insertions(+), 18 deletions(-) (limited to 'Emby.Server.Implementations/Library') diff --git a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs index c644d13ea..c9d4e4342 100644 --- a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs +++ b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Linq; +using System.Text.RegularExpressions; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Resolvers; @@ -148,15 +149,9 @@ namespace Emby.Server.Implementations.Library } // Ignore samples - var sampleFilename = " " + filename.Replace(".", " ", StringComparison.OrdinalIgnoreCase) - .Replace("-", " ", StringComparison.OrdinalIgnoreCase) - .Replace("_", " ", StringComparison.OrdinalIgnoreCase) - .Replace("!", " ", StringComparison.OrdinalIgnoreCase); + Match m = Regex.Match(filename,"\bsample\b",RegexOptions.IgnoreCase); - if (sampleFilename.IndexOf(" sample ", StringComparison.OrdinalIgnoreCase) != -1) - { - return true; - } + return m.Success; } return false; diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index 848563679..47c3e71d7 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text.RegularExpressions; using Emby.Naming.Video; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; @@ -167,17 +168,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies private static bool IsIgnored(string filename) { // Ignore samples - var sampleFilename = " " + filename.Replace(".", " ", StringComparison.OrdinalIgnoreCase) - .Replace("-", " ", StringComparison.OrdinalIgnoreCase) - .Replace("_", " ", StringComparison.OrdinalIgnoreCase) - .Replace("!", " ", StringComparison.OrdinalIgnoreCase); + Match m = Regex.Match(filename,@"\bsample\b",RegexOptions.IgnoreCase); - if (sampleFilename.IndexOf(" sample ", StringComparison.OrdinalIgnoreCase) != -1) - { - return true; - } - - return false; + return m.Success; } private bool ContainsFile(List result, FileSystemMetadata file) -- cgit v1.2.3 From da842d5a730ea8db2fab8e4c71a501191d54e46c Mon Sep 17 00:00:00 2001 From: bugfixin Date: Fri, 19 Apr 2019 18:35:28 +0000 Subject: Fix incorrect escaping in regex pattern --- Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations/Library') diff --git a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs index c9d4e4342..a70077163 100644 --- a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs +++ b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs @@ -149,7 +149,7 @@ namespace Emby.Server.Implementations.Library } // Ignore samples - Match m = Regex.Match(filename,"\bsample\b",RegexOptions.IgnoreCase); + Match m = Regex.Match(filename,@"\bsample\b",RegexOptions.IgnoreCase); return m.Success; } -- cgit v1.2.3 From 69ee49bee607d716a857a1525f503575ebf6db7f Mon Sep 17 00:00:00 2001 From: DrPandemic Date: Sat, 25 May 2019 13:46:55 -0400 Subject: Format correctly the PIN when updating it --- .../Library/DefaultAuthenticationProvider.cs | 28 ++++++++++++++++++++++ Emby.Server.Implementations/Library/UserManager.cs | 27 ++++----------------- .../Authentication/IAuthenticationProvider.cs | 3 +++ 3 files changed, 36 insertions(+), 22 deletions(-) (limited to 'Emby.Server.Implementations/Library') diff --git a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs b/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs index 0527464ff..fe09b07ff 100644 --- a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs +++ b/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs @@ -165,6 +165,34 @@ namespace Emby.Server.Implementations.Library return user.Password; } + public void ChangeEasyPassword(User user, string newPassword, string newPasswordHash) + { + ConvertPasswordFormat(user); + + if (newPassword != null) + { + newPasswordHash = string.Format("$SHA1${0}", GetHashedString(user, newPassword)); + } + + if (string.IsNullOrWhiteSpace(newPasswordHash)) + { + throw new ArgumentNullException(nameof(newPasswordHash)); + } + + user.EasyPassword = newPasswordHash; + } + + public string GetEasyPasswordHash(User user) + { + // This should be removed in the future. This was added to let user login after + // Jellyfin 10.3.3 failed to save a well formatted PIN. + ConvertPasswordFormat(user); + + return string.IsNullOrEmpty(user.EasyPassword) + ? null + : (new PasswordHash(user.EasyPassword)).Hash; + } + public string GetHashedStringChangeAuth(string newPassword, PasswordHash passwordHash) { passwordHash.HashBytes = Encoding.UTF8.GetBytes(newPassword); diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index b396ee51a..a0b8d4ba4 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -471,7 +471,7 @@ namespace Emby.Server.Implementations.Library if (password == null) { // legacy - success = string.Equals(_defaultAuthenticationProvider.GetPasswordHash(user), hashedPassword.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase); + success = string.Equals(GetAuthenticationProvider(user).GetPasswordHash(user), hashedPassword.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase); } else { @@ -497,11 +497,11 @@ namespace Emby.Server.Implementations.Library if (password == null) { // legacy - success = string.Equals(GetLocalPasswordHash(user), hashedPassword.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase); + success = string.Equals(GetAuthenticationProvider(user).GetEasyPasswordHash(user), hashedPassword.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase); } else { - success = string.Equals(GetLocalPasswordHash(user), _defaultAuthenticationProvider.GetHashedString(user, password), StringComparison.OrdinalIgnoreCase); + success = string.Equals(GetAuthenticationProvider(user).GetEasyPasswordHash(user), _defaultAuthenticationProvider.GetHashedString(user, password), StringComparison.OrdinalIgnoreCase); } } } @@ -546,13 +546,6 @@ namespace Emby.Server.Implementations.Library } } - private string GetLocalPasswordHash(User user) - { - return string.IsNullOrEmpty(user.EasyPassword) - ? null - : (new PasswordHash(user.EasyPassword)).Hash; - } - /// /// Loads the users from the repository /// @@ -596,7 +589,7 @@ namespace Emby.Server.Implementations.Library } bool hasConfiguredPassword = GetAuthenticationProvider(user).HasPassword(user).Result; - bool hasConfiguredEasyPassword = !string.IsNullOrEmpty(GetLocalPasswordHash(user)); + bool hasConfiguredEasyPassword = !string.IsNullOrEmpty(GetAuthenticationProvider(user).GetEasyPasswordHash(user)); bool hasPassword = user.Configuration.EnableLocalPassword && !string.IsNullOrEmpty(remoteEndPoint) && _networkManager.IsInLocalNetwork(remoteEndPoint) ? hasConfiguredEasyPassword : @@ -884,17 +877,7 @@ namespace Emby.Server.Implementations.Library throw new ArgumentNullException(nameof(user)); } - if (newPassword != null) - { - newPasswordHash = _defaultAuthenticationProvider.GetHashedString(user, newPassword); - } - - if (string.IsNullOrWhiteSpace(newPasswordHash)) - { - throw new ArgumentNullException(nameof(newPasswordHash)); - } - - user.EasyPassword = newPasswordHash; + GetAuthenticationProvider(user).ChangeEasyPassword(user, newPassword, newPasswordHash); UpdateUser(user); diff --git a/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs b/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs index b9f282bd2..2cf531eed 100644 --- a/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs +++ b/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs @@ -11,6 +11,9 @@ namespace MediaBrowser.Controller.Authentication Task Authenticate(string username, string password); Task HasPassword(User user); Task ChangePassword(User user, string newPassword); + void ChangeEasyPassword(User user, string newPassword, string newPasswordHash); + string GetPasswordHash(User user); + string GetEasyPasswordHash(User user); } public interface IRequiresResolvedUser -- cgit v1.2.3 From a6f9ceedd82fe34a8d1f088d91f7c217ee070aad Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Fri, 10 May 2019 20:37:42 +0200 Subject: Fix more warnings --- Emby.Naming/Audio/AlbumParser.cs | 26 ++++---- Emby.Naming/Audio/MultiPartResult.cs | 2 + Emby.Naming/AudioBook/AudioBookFileInfo.cs | 29 +++++++-- Emby.Naming/AudioBook/AudioBookFilePathParser.cs | 13 ++-- .../AudioBook/AudioBookFilePathParserResult.cs | 2 + Emby.Naming/AudioBook/AudioBookInfo.cs | 21 ++++-- Emby.Naming/AudioBook/AudioBookListResolver.cs | 2 +- Emby.Naming/AudioBook/AudioBookResolver.cs | 11 ++-- Emby.Naming/Common/EpisodeExpression.cs | 17 ++++- Emby.Naming/Common/MediaType.cs | 2 + Emby.Naming/Common/NamingOptions.cs | 75 +++++++++++----------- Emby.Naming/Emby.Naming.csproj | 18 +++++- Emby.Naming/Extensions/StringExtensions.cs | 1 + Emby.Naming/StringExtensions.cs | 30 --------- Emby.Naming/Subtitles/SubtitleInfo.cs | 3 + Emby.Naming/TV/EpisodeInfo.cs | 11 ++++ Emby.Naming/TV/EpisodePathParser.cs | 52 +++++++-------- Emby.Naming/TV/EpisodePathParserResult.cs | 7 ++ Emby.Naming/TV/EpisodeResolver.cs | 12 +++- Emby.Naming/TV/SeasonPathParser.cs | 42 ++++++------ Emby.Naming/TV/SeasonPathParserResult.cs | 2 + Emby.Naming/Video/CleanDateTimeParser.cs | 20 +++--- Emby.Naming/Video/ExtraResolver.cs | 2 - Emby.Naming/Video/FileStack.cs | 4 +- Emby.Naming/Video/Format3DParser.cs | 10 ++- Emby.Naming/Video/Format3DResult.cs | 12 ++-- Emby.Naming/Video/StackResolver.cs | 19 ++++-- Emby.Naming/Video/StubResolver.cs | 32 +++++---- Emby.Naming/Video/StubResult.cs | 1 + Emby.Naming/Video/StubTypeRule.cs | 1 + Emby.Naming/Video/VideoFileInfo.cs | 12 +++- Emby.Naming/Video/VideoInfo.cs | 4 ++ Emby.Naming/Video/VideoListResolver.cs | 40 ++++++------ Emby.Naming/Video/VideoResolver.cs | 12 ++-- .../Emby.Server.Implementations.csproj | 4 +- .../Library/LibraryManager.cs | 2 +- .../Library/Resolvers/TV/SeasonResolver.cs | 2 +- .../Library/Resolvers/TV/SeriesResolver.cs | 4 +- Jellyfin.Server/Jellyfin.Server.csproj | 6 +- Jellyfin.Server/Program.cs | 6 +- jellyfin.ruleset | 5 ++ 41 files changed, 339 insertions(+), 237 deletions(-) delete mode 100644 Emby.Naming/StringExtensions.cs (limited to 'Emby.Server.Implementations/Library') diff --git a/Emby.Naming/Audio/AlbumParser.cs b/Emby.Naming/Audio/AlbumParser.cs index 7d029a9f4..e8d765552 100644 --- a/Emby.Naming/Audio/AlbumParser.cs +++ b/Emby.Naming/Audio/AlbumParser.cs @@ -33,27 +33,29 @@ namespace Emby.Naming.Audio // Normalize // Remove whitespace - filename = filename.Replace("-", " "); - filename = filename.Replace(".", " "); - filename = filename.Replace("(", " "); - filename = filename.Replace(")", " "); + filename = filename.Replace('-', ' '); + filename = filename.Replace('.', ' '); + filename = filename.Replace('(', ' '); + filename = filename.Replace(')', ' '); filename = Regex.Replace(filename, @"\s+", " "); filename = filename.TrimStart(); foreach (var prefix in _options.AlbumStackingPrefixes) { - if (filename.IndexOf(prefix, StringComparison.OrdinalIgnoreCase) == 0) + if (filename.IndexOf(prefix, StringComparison.OrdinalIgnoreCase) != 0) { - var tmp = filename.Substring(prefix.Length); + continue; + } + + var tmp = filename.Substring(prefix.Length); - tmp = tmp.Trim().Split(' ').FirstOrDefault() ?? string.Empty; + tmp = tmp.Trim().Split(' ').FirstOrDefault() ?? string.Empty; - if (int.TryParse(tmp, NumberStyles.Integer, CultureInfo.InvariantCulture, out var val)) - { - result.IsMultiPart = true; - break; - } + if (int.TryParse(tmp, NumberStyles.Integer, CultureInfo.InvariantCulture, out _)) + { + result.IsMultiPart = true; + break; } } diff --git a/Emby.Naming/Audio/MultiPartResult.cs b/Emby.Naming/Audio/MultiPartResult.cs index b1fa6e563..00e4a9eb2 100644 --- a/Emby.Naming/Audio/MultiPartResult.cs +++ b/Emby.Naming/Audio/MultiPartResult.cs @@ -7,11 +7,13 @@ namespace Emby.Naming.Audio /// /// The name. public string Name { get; set; } + /// /// Gets or sets the part. /// /// The part. public string Part { get; set; } + /// /// Gets or sets a value indicating whether this instance is multi part. /// diff --git a/Emby.Naming/AudioBook/AudioBookFileInfo.cs b/Emby.Naming/AudioBook/AudioBookFileInfo.cs index de66a5402..326ea05ef 100644 --- a/Emby.Naming/AudioBook/AudioBookFileInfo.cs +++ b/Emby.Naming/AudioBook/AudioBookFileInfo.cs @@ -12,35 +12,56 @@ namespace Emby.Naming.AudioBook /// /// The path. public string Path { get; set; } + /// /// Gets or sets the container. /// /// The container. public string Container { get; set; } + /// /// Gets or sets the part number. /// /// The part number. public int? PartNumber { get; set; } + /// /// Gets or sets the chapter number. /// /// The chapter number. public int? ChapterNumber { get; set; } + /// /// Gets or sets the type. /// /// The type. public bool IsDirectory { get; set; } + /// public int CompareTo(AudioBookFileInfo other) { - if (ReferenceEquals(this, other)) return 0; - if (ReferenceEquals(null, other)) return 1; + if (ReferenceEquals(this, other)) + { + return 0; + } + + if (ReferenceEquals(null, other)) + { + return 1; + } + var chapterNumberComparison = Nullable.Compare(ChapterNumber, other.ChapterNumber); - if (chapterNumberComparison != 0) return chapterNumberComparison; + if (chapterNumberComparison != 0) + { + return chapterNumberComparison; + } + var partNumberComparison = Nullable.Compare(PartNumber, other.PartNumber); - if (partNumberComparison != 0) return partNumberComparison; + if (partNumberComparison != 0) + { + return partNumberComparison; + } + return string.Compare(Path, other.Path, StringComparison.Ordinal); } } diff --git a/Emby.Naming/AudioBook/AudioBookFilePathParser.cs b/Emby.Naming/AudioBook/AudioBookFilePathParser.cs index 590979794..ea7f06c8c 100644 --- a/Emby.Naming/AudioBook/AudioBookFilePathParser.cs +++ b/Emby.Naming/AudioBook/AudioBookFilePathParser.cs @@ -1,3 +1,4 @@ +using System; using System.Globalization; using System.IO; using System.Text.RegularExpressions; @@ -14,14 +15,13 @@ namespace Emby.Naming.AudioBook _options = options; } - public AudioBookFilePathParserResult Parse(string path, bool IsDirectory) + public AudioBookFilePathParserResult Parse(string path) { - var result = Parse(path); - return !result.Success ? new AudioBookFilePathParserResult() : result; - } + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } - private AudioBookFilePathParserResult Parse(string path) - { var result = new AudioBookFilePathParserResult(); var fileName = Path.GetFileNameWithoutExtension(path); foreach (var expression in _options.AudioBookPartsExpressions) @@ -40,6 +40,7 @@ namespace Emby.Naming.AudioBook } } } + if (!result.PartNumber.HasValue) { var value = match.Groups["part"]; diff --git a/Emby.Naming/AudioBook/AudioBookFilePathParserResult.cs b/Emby.Naming/AudioBook/AudioBookFilePathParserResult.cs index 3a8e3c31f..f845e8243 100644 --- a/Emby.Naming/AudioBook/AudioBookFilePathParserResult.cs +++ b/Emby.Naming/AudioBook/AudioBookFilePathParserResult.cs @@ -3,7 +3,9 @@ namespace Emby.Naming.AudioBook public class AudioBookFilePathParserResult { public int? PartNumber { get; set; } + public int? ChapterNumber { get; set; } + public bool Success { get; set; } } } diff --git a/Emby.Naming/AudioBook/AudioBookInfo.cs b/Emby.Naming/AudioBook/AudioBookInfo.cs index f6e1d5be4..600d3f05d 100644 --- a/Emby.Naming/AudioBook/AudioBookInfo.cs +++ b/Emby.Naming/AudioBook/AudioBookInfo.cs @@ -7,33 +7,40 @@ namespace Emby.Naming.AudioBook /// public class AudioBookInfo { + public AudioBookInfo() + { + Files = new List(); + Extras = new List(); + AlternateVersions = new List(); + } + /// /// Gets or sets the name. /// /// The name. public string Name { get; set; } + + /// + /// Gets or sets the year. + /// public int? Year { get; set; } + /// /// Gets or sets the files. /// /// The files. public List Files { get; set; } + /// /// Gets or sets the extras. /// /// The extras. public List Extras { get; set; } + /// /// Gets or sets the alternate versions. /// /// The alternate versions. public List AlternateVersions { get; set; } - - public AudioBookInfo() - { - Files = new List(); - Extras = new List(); - AlternateVersions = new List(); - } } } diff --git a/Emby.Naming/AudioBook/AudioBookListResolver.cs b/Emby.Naming/AudioBook/AudioBookListResolver.cs index 4e3ad7cac..414ef1183 100644 --- a/Emby.Naming/AudioBook/AudioBookListResolver.cs +++ b/Emby.Naming/AudioBook/AudioBookListResolver.cs @@ -15,7 +15,7 @@ namespace Emby.Naming.AudioBook _options = options; } - public IEnumerable Resolve(List files) + public IEnumerable Resolve(IEnumerable files) { var audioBookResolver = new AudioBookResolver(_options); diff --git a/Emby.Naming/AudioBook/AudioBookResolver.cs b/Emby.Naming/AudioBook/AudioBookResolver.cs index 67ab62e80..4a2b516d0 100644 --- a/Emby.Naming/AudioBook/AudioBookResolver.cs +++ b/Emby.Naming/AudioBook/AudioBookResolver.cs @@ -24,19 +24,21 @@ namespace Emby.Naming.AudioBook return Resolve(path, true); } - public AudioBookFileInfo Resolve(string path, bool IsDirectory = false) + public AudioBookFileInfo Resolve(string path, bool isDirectory = false) { if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException(nameof(path)); } - if (IsDirectory) // TODO + // TODO + if (isDirectory) { return null; } var extension = Path.GetExtension(path); + // Check supported extensions if (!_options.AudioFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) { @@ -45,8 +47,7 @@ namespace Emby.Naming.AudioBook var container = extension.TrimStart('.'); - var parsingResult = new AudioBookFilePathParser(_options) - .Parse(path, IsDirectory); + var parsingResult = new AudioBookFilePathParser(_options).Parse(path); return new AudioBookFileInfo { @@ -54,7 +55,7 @@ namespace Emby.Naming.AudioBook Container = container, PartNumber = parsingResult.PartNumber, ChapterNumber = parsingResult.ChapterNumber, - IsDirectory = IsDirectory + IsDirectory = isDirectory }; } } diff --git a/Emby.Naming/Common/EpisodeExpression.cs b/Emby.Naming/Common/EpisodeExpression.cs index fd85bf76a..136d8189d 100644 --- a/Emby.Naming/Common/EpisodeExpression.cs +++ b/Emby.Naming/Common/EpisodeExpression.cs @@ -6,17 +6,28 @@ namespace Emby.Naming.Common public class EpisodeExpression { private string _expression; - public string Expression { get => _expression; - set { _expression = value; _regex = null; } } + private Regex _regex; + + public string Expression + { + get => _expression; + set + { + _expression = value; + _regex = null; + } + } public bool IsByDate { get; set; } + public bool IsOptimistic { get; set; } + public bool IsNamed { get; set; } + public bool SupportsAbsoluteEpisodeNumbers { get; set; } public string[] DateTimeFormats { get; set; } - private Regex _regex; public Regex Regex => _regex ?? (_regex = new Regex(Expression, RegexOptions.IgnoreCase | RegexOptions.Compiled)); public EpisodeExpression(string expression, bool byDate) diff --git a/Emby.Naming/Common/MediaType.cs b/Emby.Naming/Common/MediaType.cs index 49cc9ee39..a7b08bf79 100644 --- a/Emby.Naming/Common/MediaType.cs +++ b/Emby.Naming/Common/MediaType.cs @@ -6,10 +6,12 @@ namespace Emby.Naming.Common /// The audio /// Audio = 0, + /// /// The photo /// Photo = 1, + /// /// The video /// diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs index 2ef0208ba..88a9b46e6 100644 --- a/Emby.Naming/Common/NamingOptions.cs +++ b/Emby.Naming/Common/NamingOptions.cs @@ -8,19 +8,25 @@ namespace Emby.Naming.Common public class NamingOptions { public string[] AudioFileExtensions { get; set; } + public string[] AlbumStackingPrefixes { get; set; } public string[] SubtitleFileExtensions { get; set; } + public char[] SubtitleFlagDelimiters { get; set; } public string[] SubtitleForcedFlags { get; set; } + public string[] SubtitleDefaultFlags { get; set; } public EpisodeExpression[] EpisodeExpressions { get; set; } + public string[] EpisodeWithoutSeasonExpressions { get; set; } + public string[] EpisodeMultiPartExpressions { get; set; } public string[] VideoFileExtensions { get; set; } + public string[] StubFileExtensions { get; set; } public string[] AudioBookPartsExpressions { get; set; } @@ -28,12 +34,14 @@ namespace Emby.Naming.Common public StubTypeRule[] StubTypes { get; set; } public char[] VideoFlagDelimiters { get; set; } + public Format3DRule[] Format3DRules { get; set; } public string[] VideoFileStackingExpressions { get; set; } + public string[] CleanDateTimes { get; set; } - public string[] CleanStrings { get; set; } + public string[] CleanStrings { get; set; } public EpisodeExpression[] MultipleEpisodeExpressions { get; set; } @@ -41,7 +49,7 @@ namespace Emby.Naming.Common public NamingOptions() { - VideoFileExtensions = new string[] + VideoFileExtensions = new[] { ".m4v", ".3gp", @@ -106,53 +114,53 @@ namespace Emby.Naming.Common { new StubTypeRule { - StubType = "dvd", - Token = "dvd" + StubType = "dvd", + Token = "dvd" }, new StubTypeRule { - StubType = "hddvd", - Token = "hddvd" + StubType = "hddvd", + Token = "hddvd" }, new StubTypeRule { - StubType = "bluray", - Token = "bluray" + StubType = "bluray", + Token = "bluray" }, new StubTypeRule { - StubType = "bluray", - Token = "brrip" + StubType = "bluray", + Token = "brrip" }, new StubTypeRule { - StubType = "bluray", - Token = "bd25" + StubType = "bluray", + Token = "bd25" }, new StubTypeRule { - StubType = "bluray", - Token = "bd50" + StubType = "bluray", + Token = "bd50" }, new StubTypeRule { - StubType = "vhs", - Token = "vhs" + StubType = "vhs", + Token = "vhs" }, new StubTypeRule { - StubType = "tv", - Token = "HDTV" + StubType = "tv", + Token = "HDTV" }, new StubTypeRule { - StubType = "tv", - Token = "PDTV" + StubType = "tv", + Token = "PDTV" }, new StubTypeRule { - StubType = "tv", - Token = "DSR" + StubType = "tv", + Token = "DSR" } }; @@ -286,7 +294,7 @@ namespace Emby.Naming.Common new EpisodeExpression(@"[\._ -]()[Ee][Pp]_?([0-9]+)([^\\/]*)$"), new EpisodeExpression("([0-9]{4})[\\.-]([0-9]{2})[\\.-]([0-9]{2})", true) { - DateTimeFormats = new [] + DateTimeFormats = new[] { "yyyy.MM.dd", "yyyy-MM-dd", @@ -295,7 +303,7 @@ namespace Emby.Naming.Common }, new EpisodeExpression("([0-9]{2})[\\.-]([0-9]{2})[\\.-]([0-9]{4})", true) { - DateTimeFormats = new [] + DateTimeFormats = new[] { "dd.MM.yyyy", "dd-MM-yyyy", @@ -348,9 +356,7 @@ namespace Emby.Naming.Common }, // "1-12 episode title" - new EpisodeExpression(@"([0-9]+)-([0-9]+)") - { - }, + new EpisodeExpression(@"([0-9]+)-([0-9]+)"), // "01 - blah.avi", "01-blah.avi" new EpisodeExpression(@".*(\\|\/)(?\d{1,3})(-(?\d{2,3}))*\s?-\s?[^\\\/]*$") @@ -427,7 +433,7 @@ namespace Emby.Naming.Common Token = "_trailer", MediaType = MediaType.Video }, - new ExtraRule + new ExtraRule { ExtraType = "trailer", RuleType = ExtraRuleType.Suffix, @@ -462,7 +468,7 @@ namespace Emby.Naming.Common Token = "_sample", MediaType = MediaType.Video }, - new ExtraRule + new ExtraRule { ExtraType = "sample", RuleType = ExtraRuleType.Suffix, @@ -476,7 +482,6 @@ namespace Emby.Naming.Common Token = "theme", MediaType = MediaType.Audio }, - new ExtraRule { ExtraType = "scene", @@ -526,8 +531,8 @@ namespace Emby.Naming.Common Token = "-short", MediaType = MediaType.Video } - }; + Format3DRules = new[] { // Kodi rules: @@ -648,12 +653,10 @@ namespace Emby.Naming.Common @".*(\\|\/)(?((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?\d{1,4})[xX](?\d{1,3}))(-[xX]?[eE]?(?\d{1,3}))+[^\\\/]*$", @".*(\\|\/)(?[^\\\/]*)[sS](?\d{1,4})[xX\.]?[eE](?\d{1,3})((-| - )?[xXeE](?\d{1,3}))+[^\\\/]*$", @".*(\\|\/)(?[^\\\/]*)[sS](?\d{1,4})[xX\.]?[eE](?\d{1,3})(-[xX]?[eE]?(?\d{1,3}))+[^\\\/]*$" - }.Select(i => new EpisodeExpression(i) - { - IsNamed = true - - }).ToArray(); + { + IsNamed = true + }).ToArray(); VideoFileExtensions = extensions .Distinct(StringComparer.OrdinalIgnoreCase) diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj index c448ec0ce..699d89325 100644 --- a/Emby.Naming/Emby.Naming.csproj +++ b/Emby.Naming/Emby.Naming.csproj @@ -1,4 +1,4 @@ - + netstandard2.0 @@ -18,6 +18,22 @@ Jellyfin.Naming https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt https://github.com/jellyfin/jellyfin + true + + + + true + + + + + + + + + + + ../jellyfin.ruleset diff --git a/Emby.Naming/Extensions/StringExtensions.cs b/Emby.Naming/Extensions/StringExtensions.cs index 26c09aeb4..5512127a8 100644 --- a/Emby.Naming/Extensions/StringExtensions.cs +++ b/Emby.Naming/Extensions/StringExtensions.cs @@ -5,6 +5,7 @@ namespace Emby.Naming.Extensions { public static class StringExtensions { + // TODO: @bond remove this when moving to netstandard2.1 public static string Replace(this string str, string oldValue, string newValue, StringComparison comparison) { var sb = new StringBuilder(); diff --git a/Emby.Naming/StringExtensions.cs b/Emby.Naming/StringExtensions.cs deleted file mode 100644 index 7c61922af..000000000 --- a/Emby.Naming/StringExtensions.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Text; - -namespace Emby.Naming -{ - internal static class StringExtensions - { - public static string Replace(this string str, string oldValue, string newValue, StringComparison comparison) - { - var sb = new StringBuilder(); - - var previousIndex = 0; - var index = str.IndexOf(oldValue, comparison); - - while (index != -1) - { - sb.Append(str.Substring(previousIndex, index - previousIndex)); - sb.Append(newValue); - index += oldValue.Length; - - previousIndex = index; - index = str.IndexOf(oldValue, index, comparison); - } - - sb.Append(str.Substring(previousIndex)); - - return sb.ToString(); - } - } -} diff --git a/Emby.Naming/Subtitles/SubtitleInfo.cs b/Emby.Naming/Subtitles/SubtitleInfo.cs index e4709dfbb..96fce04d7 100644 --- a/Emby.Naming/Subtitles/SubtitleInfo.cs +++ b/Emby.Naming/Subtitles/SubtitleInfo.cs @@ -7,16 +7,19 @@ namespace Emby.Naming.Subtitles /// /// The path. public string Path { get; set; } + /// /// Gets or sets the language. /// /// The language. public string Language { get; set; } + /// /// Gets or sets a value indicating whether this instance is default. /// /// true if this instance is default; otherwise, false. public bool IsDefault { get; set; } + /// /// Gets or sets a value indicating whether this instance is forced. /// diff --git a/Emby.Naming/TV/EpisodeInfo.cs b/Emby.Naming/TV/EpisodeInfo.cs index c8aca7a6f..de79b8bba 100644 --- a/Emby.Naming/TV/EpisodeInfo.cs +++ b/Emby.Naming/TV/EpisodeInfo.cs @@ -7,31 +7,37 @@ namespace Emby.Naming.TV /// /// The path. public string Path { get; set; } + /// /// Gets or sets the container. /// /// The container. public string Container { get; set; } + /// /// Gets or sets the name of the series. /// /// The name of the series. public string SeriesName { get; set; } + /// /// Gets or sets the format3 d. /// /// The format3 d. public string Format3D { get; set; } + /// /// Gets or sets a value indicating whether [is3 d]. /// /// true if [is3 d]; otherwise, false. public bool Is3D { get; set; } + /// /// Gets or sets a value indicating whether this instance is stub. /// /// true if this instance is stub; otherwise, false. public bool IsStub { get; set; } + /// /// Gets or sets the type of the stub. /// @@ -39,12 +45,17 @@ namespace Emby.Naming.TV public string StubType { get; set; } public int? SeasonNumber { get; set; } + public int? EpisodeNumber { get; set; } + public int? EndingEpsiodeNumber { get; set; } public int? Year { get; set; } + public int? Month { get; set; } + public int? Day { get; set; } + public bool IsByDate { get; set; } } } diff --git a/Emby.Naming/TV/EpisodePathParser.cs b/Emby.Naming/TV/EpisodePathParser.cs index a8f81a3b8..812bc970e 100644 --- a/Emby.Naming/TV/EpisodePathParser.cs +++ b/Emby.Naming/TV/EpisodePathParser.cs @@ -15,12 +15,12 @@ namespace Emby.Naming.TV _options = options; } - public EpisodePathParserResult Parse(string path, bool IsDirectory, bool? isNamed = null, bool? isOptimistic = null, bool? supportsAbsoluteNumbers = null, bool fillExtendedInfo = true) + public EpisodePathParserResult Parse(string path, bool isDirectory, bool? isNamed = null, bool? isOptimistic = null, bool? supportsAbsoluteNumbers = null, bool fillExtendedInfo = true) { // Added to be able to use regex patterns which require a file extension. // There were no failed tests without this block, but to be safe, we can keep it until // the regex which require file extensions are modified so that they don't need them. - if (IsDirectory) + if (isDirectory) { path += ".mp4"; } @@ -29,28 +29,20 @@ namespace Emby.Naming.TV foreach (var expression in _options.EpisodeExpressions) { - if (supportsAbsoluteNumbers.HasValue) + if (supportsAbsoluteNumbers.HasValue + && expression.SupportsAbsoluteEpisodeNumbers != supportsAbsoluteNumbers.Value) { - if (expression.SupportsAbsoluteEpisodeNumbers != supportsAbsoluteNumbers.Value) - { - continue; - } + continue; } - if (isNamed.HasValue) + if (isNamed.HasValue && expression.IsNamed != isNamed.Value) { - if (expression.IsNamed != isNamed.Value) - { - continue; - } + continue; } - if (isOptimistic.HasValue) + if (isOptimistic.HasValue && expression.IsOptimistic != isOptimistic.Value) { - if (expression.IsOptimistic != isOptimistic.Value) - { - continue; - } + continue; } var currentResult = Parse(path, expression); @@ -97,7 +89,8 @@ namespace Emby.Naming.TV DateTime date; if (expression.DateTimeFormats.Length > 0) { - if (DateTime.TryParseExact(match.Groups[0].Value, + if (DateTime.TryParseExact( + match.Groups[0].Value, expression.DateTimeFormats, CultureInfo.InvariantCulture, DateTimeStyles.None, @@ -109,17 +102,15 @@ namespace Emby.Naming.TV result.Success = true; } } - else + else if (DateTime.TryParse(match.Groups[0].Value, out date)) { - if (DateTime.TryParse(match.Groups[0].Value, out date)) - { - result.Year = date.Year; - result.Month = date.Month; - result.Day = date.Day; - result.Success = true; - } + result.Year = date.Year; + result.Month = date.Month; + result.Day = date.Day; + result.Success = true; } + // TODO: Only consider success if date successfully parsed? result.Success = true; } @@ -142,7 +133,8 @@ namespace Emby.Naming.TV // or a 'p' or 'i' as what you would get with a pixel resolution specification. // It avoids erroneous parsing of something like "series-s09e14-1080p.mkv" as a multi-episode from E14 to E108 int nextIndex = endingNumberGroup.Index + endingNumberGroup.Length; - if (nextIndex >= name.Length || "0123456789iIpP".IndexOf(name[nextIndex]) == -1) + if (nextIndex >= name.Length + || "0123456789iIpP".IndexOf(name[nextIndex]) == -1) { if (int.TryParse(endingNumberGroup.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out num)) { @@ -160,6 +152,7 @@ namespace Emby.Naming.TV { result.SeasonNumber = num; } + if (int.TryParse(match.Groups[2].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out num)) { result.EpisodeNumber = num; @@ -171,8 +164,11 @@ namespace Emby.Naming.TV // Invalidate match when the season is 200 through 1927 or above 2500 // because it is an error unless the TV show is intentionally using false season numbers. // It avoids erroneous parsing of something like "Series Special (1920x1080).mkv" as being season 1920 episode 1080. - if (result.SeasonNumber >= 200 && result.SeasonNumber < 1928 || result.SeasonNumber > 2500) + if ((result.SeasonNumber >= 200 && result.SeasonNumber < 1928) + || result.SeasonNumber > 2500) + { result.Success = false; + } result.IsByDate = expression.IsByDate; } diff --git a/Emby.Naming/TV/EpisodePathParserResult.cs b/Emby.Naming/TV/EpisodePathParserResult.cs index e1a48bfbc..996edfc50 100644 --- a/Emby.Naming/TV/EpisodePathParserResult.cs +++ b/Emby.Naming/TV/EpisodePathParserResult.cs @@ -3,14 +3,21 @@ namespace Emby.Naming.TV public class EpisodePathParserResult { public int? SeasonNumber { get; set; } + public int? EpisodeNumber { get; set; } + public int? EndingEpsiodeNumber { get; set; } + public string SeriesName { get; set; } + public bool Success { get; set; } public bool IsByDate { get; set; } + public int? Year { get; set; } + public int? Month { get; set; } + public int? Day { get; set; } } } diff --git a/Emby.Naming/TV/EpisodeResolver.cs b/Emby.Naming/TV/EpisodeResolver.cs index fccf9bdec..2d7bcb638 100644 --- a/Emby.Naming/TV/EpisodeResolver.cs +++ b/Emby.Naming/TV/EpisodeResolver.cs @@ -15,7 +15,13 @@ namespace Emby.Naming.TV _options = options; } - public EpisodeInfo Resolve(string path, bool IsDirectory, bool? isNamed = null, bool? isOptimistic = null, bool? supportsAbsoluteNumbers = null, bool fillExtendedInfo = true) + public EpisodeInfo Resolve( + string path, + bool isDirectory, + bool? isNamed = null, + bool? isOptimistic = null, + bool? supportsAbsoluteNumbers = null, + bool fillExtendedInfo = true) { if (string.IsNullOrEmpty(path)) { @@ -26,7 +32,7 @@ namespace Emby.Naming.TV string container = null; string stubType = null; - if (!IsDirectory) + if (!isDirectory) { var extension = Path.GetExtension(path); // Check supported extensions @@ -52,7 +58,7 @@ namespace Emby.Naming.TV var format3DResult = new Format3DParser(_options).Parse(flags); var parsingResult = new EpisodePathParser(_options) - .Parse(path, IsDirectory, isNamed, isOptimistic, supportsAbsoluteNumbers, fillExtendedInfo); + .Parse(path, isDirectory, isNamed, isOptimistic, supportsAbsoluteNumbers, fillExtendedInfo); return new EpisodeInfo { diff --git a/Emby.Naming/TV/SeasonPathParser.cs b/Emby.Naming/TV/SeasonPathParser.cs index f1dcc50b8..e81b2bb34 100644 --- a/Emby.Naming/TV/SeasonPathParser.cs +++ b/Emby.Naming/TV/SeasonPathParser.cs @@ -3,30 +3,24 @@ using System.Globalization; using System.IO; using System.Linq; using Emby.Naming.Common; +using Emby.Naming.Extensions; namespace Emby.Naming.TV { public class SeasonPathParser { - private readonly NamingOptions _options; - - public SeasonPathParser(NamingOptions options) - { - _options = options; - } - public SeasonPathParserResult Parse(string path, bool supportSpecialAliases, bool supportNumericSeasonFolders) { var result = new SeasonPathParserResult(); var seasonNumberInfo = GetSeasonNumberFromPath(path, supportSpecialAliases, supportNumericSeasonFolders); - result.SeasonNumber = seasonNumberInfo.Item1; + result.SeasonNumber = seasonNumberInfo.seasonNumber; if (result.SeasonNumber.HasValue) { result.Success = true; - result.IsSeasonFolder = seasonNumberInfo.Item2; + result.IsSeasonFolder = seasonNumberInfo.isSeasonFolder; } return result; @@ -35,7 +29,7 @@ namespace Emby.Naming.TV /// /// A season folder must contain one of these somewhere in the name /// - private static readonly string[] SeasonFolderNames = + private static readonly string[] _seasonFolderNames = { "season", "sæson", @@ -54,19 +48,23 @@ namespace Emby.Naming.TV /// if set to true [support special aliases]. /// if set to true [support numeric season folders]. /// System.Nullable{System.Int32}. - private Tuple GetSeasonNumberFromPath(string path, bool supportSpecialAliases, bool supportNumericSeasonFolders) + private static (int? seasonNumber, bool isSeasonFolder) GetSeasonNumberFromPath( + string path, + bool supportSpecialAliases, + bool supportNumericSeasonFolders) { - var filename = Path.GetFileName(path); + var filename = Path.GetFileName(path) ?? string.Empty; if (supportSpecialAliases) { if (string.Equals(filename, "specials", StringComparison.OrdinalIgnoreCase)) { - return new Tuple(0, true); + return (0, true); } + if (string.Equals(filename, "extras", StringComparison.OrdinalIgnoreCase)) { - return new Tuple(0, true); + return (0, true); } } @@ -74,7 +72,7 @@ namespace Emby.Naming.TV { if (int.TryParse(filename, NumberStyles.Integer, CultureInfo.InvariantCulture, out var val)) { - return new Tuple(val, true); + return (val, true); } } @@ -84,12 +82,12 @@ namespace Emby.Naming.TV if (int.TryParse(testFilename, NumberStyles.Integer, CultureInfo.InvariantCulture, out var val)) { - return new Tuple(val, true); + return (val, true); } } // Look for one of the season folder names - foreach (var name in SeasonFolderNames) + foreach (var name in _seasonFolderNames) { var index = filename.IndexOf(name, StringComparison.OrdinalIgnoreCase); @@ -107,10 +105,10 @@ namespace Emby.Naming.TV var parts = filename.Split(new[] { '.', '_', ' ', '-' }, StringSplitOptions.RemoveEmptyEntries); var resultNumber = parts.Select(GetSeasonNumberFromPart).FirstOrDefault(i => i.HasValue); - return new Tuple(resultNumber, true); + return (resultNumber, true); } - private int? GetSeasonNumberFromPart(string part) + private static int? GetSeasonNumberFromPart(string part) { if (part.Length < 2 || !part.StartsWith("s", StringComparison.OrdinalIgnoreCase)) { @@ -132,7 +130,7 @@ namespace Emby.Naming.TV /// /// The path. /// System.Nullable{System.Int32}. - private Tuple GetSeasonNumberFromPathSubstring(string path) + private static (int? seasonNumber, bool isSeasonFolder) GetSeasonNumberFromPathSubstring(string path) { var numericStart = -1; var length = 0; @@ -174,10 +172,10 @@ namespace Emby.Naming.TV if (numericStart == -1) { - return new Tuple(null, isSeasonFolder); + return (null, isSeasonFolder); } - return new Tuple(int.Parse(path.Substring(numericStart, length), CultureInfo.InvariantCulture), isSeasonFolder); + return (int.Parse(path.Substring(numericStart, length), CultureInfo.InvariantCulture), isSeasonFolder); } } } diff --git a/Emby.Naming/TV/SeasonPathParserResult.cs b/Emby.Naming/TV/SeasonPathParserResult.cs index eab27a4a5..548dbd5d2 100644 --- a/Emby.Naming/TV/SeasonPathParserResult.cs +++ b/Emby.Naming/TV/SeasonPathParserResult.cs @@ -7,11 +7,13 @@ namespace Emby.Naming.TV /// /// The season number. public int? SeasonNumber { get; set; } + /// /// Gets or sets a value indicating whether this is success. /// /// true if success; otherwise, false. public bool Success { get; set; } + public bool IsSeasonFolder { get; set; } } } diff --git a/Emby.Naming/Video/CleanDateTimeParser.cs b/Emby.Naming/Video/CleanDateTimeParser.cs index 74807ef53..25fa09c48 100644 --- a/Emby.Naming/Video/CleanDateTimeParser.cs +++ b/Emby.Naming/Video/CleanDateTimeParser.cs @@ -27,8 +27,8 @@ namespace Emby.Naming.Video { var extension = Path.GetExtension(name) ?? string.Empty; // Check supported extensions - if (!_options.VideoFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase) && - !_options.AudioFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) + if (!_options.VideoFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase) + && !_options.AudioFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) { // Dummy up a file extension because the expressions will fail without one // This is tricky because we can't just check Path.GetExtension for empty @@ -38,7 +38,6 @@ namespace Emby.Naming.Video } catch (ArgumentException) { - } var result = _options.CleanDateTimeRegexes.Select(i => Clean(name, i)) @@ -69,14 +68,15 @@ namespace Emby.Naming.Video var match = expression.Match(name); - if (match.Success && match.Groups.Count == 4) + if (match.Success + && match.Groups.Count == 4 + && match.Groups[1].Success + && match.Groups[2].Success + && int.TryParse(match.Groups[2].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var year)) { - if (match.Groups[1].Success && match.Groups[2].Success && int.TryParse(match.Groups[2].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var year)) - { - name = match.Groups[1].Value; - result.Year = year; - result.HasChanged = true; - } + name = match.Groups[1].Value; + result.Year = year; + result.HasChanged = true; } result.Name = name; diff --git a/Emby.Naming/Video/ExtraResolver.cs b/Emby.Naming/Video/ExtraResolver.cs index 3459b689a..9f70494d0 100644 --- a/Emby.Naming/Video/ExtraResolver.cs +++ b/Emby.Naming/Video/ExtraResolver.cs @@ -56,7 +56,6 @@ namespace Emby.Naming.Video result.Rule = rule; } } - else if (rule.RuleType == ExtraRuleType.Suffix) { var filename = Path.GetFileNameWithoutExtension(path); @@ -67,7 +66,6 @@ namespace Emby.Naming.Video result.Rule = rule; } } - else if (rule.RuleType == ExtraRuleType.Regex) { var filename = Path.GetFileName(path); diff --git a/Emby.Naming/Video/FileStack.cs b/Emby.Naming/Video/FileStack.cs index 2df1e9aed..584bdf2d2 100644 --- a/Emby.Naming/Video/FileStack.cs +++ b/Emby.Naming/Video/FileStack.cs @@ -15,9 +15,9 @@ namespace Emby.Naming.Video Files = new List(); } - public bool ContainsFile(string file, bool IsDirectory) + public bool ContainsFile(string file, bool isDirectory) { - if (IsDirectoryStack == IsDirectory) + if (IsDirectoryStack == isDirectory) { return Files.Contains(file, StringComparer.OrdinalIgnoreCase); } diff --git a/Emby.Naming/Video/Format3DParser.cs b/Emby.Naming/Video/Format3DParser.cs index e6f830c58..333a48641 100644 --- a/Emby.Naming/Video/Format3DParser.cs +++ b/Emby.Naming/Video/Format3DParser.cs @@ -15,10 +15,12 @@ namespace Emby.Naming.Video public Format3DResult Parse(string path) { - var delimeters = _options.VideoFlagDelimiters.ToList(); - delimeters.Add(' '); + int oldLen = _options.VideoFlagDelimiters.Length; + var delimeters = new char[oldLen + 1]; + _options.VideoFlagDelimiters.CopyTo(delimeters, 0); + delimeters[oldLen] = ' '; - return Parse(new FlagParser(_options).GetFlags(path, delimeters.ToArray())); + return Parse(new FlagParser(_options).GetFlags(path, delimeters)); } internal Format3DResult Parse(string[] videoFlags) @@ -66,8 +68,10 @@ namespace Emby.Naming.Video format = flag; result.Tokens.Add(rule.Token); } + break; } + foundPrefix = string.Equals(flag, rule.PreceedingToken, StringComparison.OrdinalIgnoreCase); } diff --git a/Emby.Naming/Video/Format3DResult.cs b/Emby.Naming/Video/Format3DResult.cs index e12494079..40fc31e08 100644 --- a/Emby.Naming/Video/Format3DResult.cs +++ b/Emby.Naming/Video/Format3DResult.cs @@ -4,25 +4,27 @@ namespace Emby.Naming.Video { public class Format3DResult { + public Format3DResult() + { + Tokens = new List(); + } + /// /// Gets or sets a value indicating whether [is3 d]. /// /// true if [is3 d]; otherwise, false. public bool Is3D { get; set; } + /// /// Gets or sets the format3 d. /// /// The format3 d. public string Format3D { get; set; } + /// /// Gets or sets the tokens. /// /// The tokens. public List Tokens { get; set; } - - public Format3DResult() - { - Tokens = new List(); - } } } diff --git a/Emby.Naming/Video/StackResolver.cs b/Emby.Naming/Video/StackResolver.cs index 4893002c1..b8ba42da4 100644 --- a/Emby.Naming/Video/StackResolver.cs +++ b/Emby.Naming/Video/StackResolver.cs @@ -40,17 +40,24 @@ namespace Emby.Naming.Video var result = new StackResult(); foreach (var directory in files.GroupBy(file => file.IsDirectory ? file.FullName : Path.GetDirectoryName(file.FullName))) { - var stack = new FileStack(); - stack.Name = Path.GetFileName(directory.Key); - stack.IsDirectoryStack = false; + var stack = new FileStack() + { + Name = Path.GetFileName(directory.Key), + IsDirectoryStack = false + }; foreach (var file in directory) { if (file.IsDirectory) + { continue; + } + stack.Files.Add(file.FullName); } + result.Stacks.Add(stack); } + return result; } @@ -114,16 +121,16 @@ namespace Emby.Naming.Video { if (!string.Equals(volume1, volume2, StringComparison.OrdinalIgnoreCase)) { - if (string.Equals(ignore1, ignore2, StringComparison.OrdinalIgnoreCase) && - string.Equals(extension1, extension2, StringComparison.OrdinalIgnoreCase)) + if (string.Equals(ignore1, ignore2, StringComparison.OrdinalIgnoreCase) + && string.Equals(extension1, extension2, StringComparison.OrdinalIgnoreCase)) { if (stack.Files.Count == 0) { stack.Name = title1 + ignore1; stack.IsDirectoryStack = file1.IsDirectory; - //stack.Name = title1 + ignore1 + extension1; stack.Files.Add(file1.FullName); } + stack.Files.Add(file2.FullName); } else diff --git a/Emby.Naming/Video/StubResolver.cs b/Emby.Naming/Video/StubResolver.cs index f86bcbdf0..b78244cb3 100644 --- a/Emby.Naming/Video/StubResolver.cs +++ b/Emby.Naming/Video/StubResolver.cs @@ -9,24 +9,32 @@ namespace Emby.Naming.Video { public static StubResult ResolveFile(string path, NamingOptions options) { - var result = new StubResult(); - var extension = Path.GetExtension(path) ?? string.Empty; + if (path == null) + { + return default(StubResult); + } + + var extension = Path.GetExtension(path); - if (options.StubFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) + if (!options.StubFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) { - result.IsStub = true; + return default(StubResult); + } - path = Path.GetFileNameWithoutExtension(path); + var result = new StubResult() + { + IsStub = true + }; - var token = (Path.GetExtension(path) ?? string.Empty).TrimStart('.'); + path = Path.GetFileNameWithoutExtension(path); + var token = Path.GetExtension(path).TrimStart('.'); - foreach (var rule in options.StubTypes) + foreach (var rule in options.StubTypes) + { + if (string.Equals(rule.Token, token, StringComparison.OrdinalIgnoreCase)) { - if (string.Equals(rule.Token, token, StringComparison.OrdinalIgnoreCase)) - { - result.StubType = rule.StubType; - break; - } + result.StubType = rule.StubType; + break; } } diff --git a/Emby.Naming/Video/StubResult.cs b/Emby.Naming/Video/StubResult.cs index 7f9509ca5..7a62e7b98 100644 --- a/Emby.Naming/Video/StubResult.cs +++ b/Emby.Naming/Video/StubResult.cs @@ -7,6 +7,7 @@ namespace Emby.Naming.Video /// /// true if this instance is stub; otherwise, false. public bool IsStub { get; set; } + /// /// Gets or sets the type of the stub. /// diff --git a/Emby.Naming/Video/StubTypeRule.cs b/Emby.Naming/Video/StubTypeRule.cs index b46050085..d76532150 100644 --- a/Emby.Naming/Video/StubTypeRule.cs +++ b/Emby.Naming/Video/StubTypeRule.cs @@ -7,6 +7,7 @@ namespace Emby.Naming.Video /// /// The token. public string Token { get; set; } + /// /// Gets or sets the type of the stub. /// diff --git a/Emby.Naming/Video/VideoFileInfo.cs b/Emby.Naming/Video/VideoFileInfo.cs index 6a29ada7e..78f688ca8 100644 --- a/Emby.Naming/Video/VideoFileInfo.cs +++ b/Emby.Naming/Video/VideoFileInfo.cs @@ -1,4 +1,3 @@ - namespace Emby.Naming.Video { /// @@ -11,56 +10,67 @@ namespace Emby.Naming.Video /// /// The path. public string Path { get; set; } + /// /// Gets or sets the container. /// /// The container. public string Container { get; set; } + /// /// Gets or sets the name. /// /// The name. public string Name { get; set; } + /// /// Gets or sets the year. /// /// The year. public int? Year { get; set; } + /// /// Gets or sets the type of the extra, e.g. trailer, theme song, behing the scenes, etc. /// /// The type of the extra. public string ExtraType { get; set; } + /// /// Gets or sets the extra rule. /// /// The extra rule. public ExtraRule ExtraRule { get; set; } + /// /// Gets or sets the format3 d. /// /// The format3 d. public string Format3D { get; set; } + /// /// Gets or sets a value indicating whether [is3 d]. /// /// true if [is3 d]; otherwise, false. public bool Is3D { get; set; } + /// /// Gets or sets a value indicating whether this instance is stub. /// /// true if this instance is stub; otherwise, false. public bool IsStub { get; set; } + /// /// Gets or sets the type of the stub. /// /// The type of the stub. public string StubType { get; set; } + /// /// Gets or sets the type. /// /// The type. public bool IsDirectory { get; set; } + /// /// Gets the file name without extension. /// diff --git a/Emby.Naming/Video/VideoInfo.cs b/Emby.Naming/Video/VideoInfo.cs index d96d0e757..2e456bda2 100644 --- a/Emby.Naming/Video/VideoInfo.cs +++ b/Emby.Naming/Video/VideoInfo.cs @@ -12,21 +12,25 @@ namespace Emby.Naming.Video /// /// The name. public string Name { get; set; } + /// /// Gets or sets the year. /// /// The year. public int? Year { get; set; } + /// /// Gets or sets the files. /// /// The files. public List Files { get; set; } + /// /// Gets or sets the extras. /// /// The extras. public List Extras { get; set; } + /// /// Gets or sets the alternate versions. /// diff --git a/Emby.Naming/Video/VideoListResolver.cs b/Emby.Naming/Video/VideoListResolver.cs index afedc30ef..5fa0041e0 100644 --- a/Emby.Naming/Video/VideoListResolver.cs +++ b/Emby.Naming/Video/VideoListResolver.cs @@ -53,7 +53,7 @@ namespace Emby.Naming.Video Name = stack.Name }; - info.Year = info.Files.First().Year; + info.Year = info.Files[0].Year; var extraBaseNames = new List { @@ -87,7 +87,7 @@ namespace Emby.Naming.Video Name = media.Name }; - info.Year = info.Files.First().Year; + info.Year = info.Files[0].Year; var extras = GetExtras(remainingFiles, new List { media.FileNameWithoutExtension }); @@ -115,7 +115,7 @@ namespace Emby.Naming.Video if (!string.IsNullOrEmpty(parentPath)) { - var folderName = Path.GetFileName(Path.GetDirectoryName(videoPath)); + var folderName = Path.GetFileName(parentPath); if (!string.IsNullOrEmpty(folderName)) { var extras = GetExtras(remainingFiles, new List { folderName }); @@ -163,9 +163,7 @@ namespace Emby.Naming.Video Year = i.Year })); - var orderedList = list.OrderBy(i => i.Name); - - return orderedList; + return list.OrderBy(i => i.Name); } private IEnumerable GetVideosGroupedByVersion(List videos) @@ -179,23 +177,21 @@ namespace Emby.Naming.Video var folderName = Path.GetFileName(Path.GetDirectoryName(videos[0].Files[0].Path)); - if (!string.IsNullOrEmpty(folderName) && folderName.Length > 1) + if (!string.IsNullOrEmpty(folderName) + && folderName.Length > 1 + && videos.All(i => i.Files.Count == 1 + && IsEligibleForMultiVersion(folderName, i.Files[0].Path)) + && HaveSameYear(videos)) { - if (videos.All(i => i.Files.Count == 1 && IsEligibleForMultiVersion(folderName, i.Files[0].Path))) - { - if (HaveSameYear(videos)) - { - var ordered = videos.OrderBy(i => i.Name).ToList(); + var ordered = videos.OrderBy(i => i.Name).ToList(); - list.Add(ordered[0]); + list.Add(ordered[0]); - list[0].AlternateVersions = ordered.Skip(1).Select(i => i.Files[0]).ToList(); - list[0].Name = folderName; - list[0].Extras.AddRange(ordered.Skip(1).SelectMany(i => i.Extras)); + list[0].AlternateVersions = ordered.Skip(1).Select(i => i.Files[0]).ToList(); + list[0].Name = folderName; + list[0].Extras.AddRange(ordered.Skip(1).SelectMany(i => i.Extras)); - return list; - } - } + return list; } return videos; @@ -213,9 +209,9 @@ namespace Emby.Naming.Video if (testFilename.StartsWith(folderName, StringComparison.OrdinalIgnoreCase)) { testFilename = testFilename.Substring(folderName.Length).Trim(); - return string.IsNullOrEmpty(testFilename) || - testFilename.StartsWith("-") || - string.IsNullOrWhiteSpace(Regex.Replace(testFilename, @"\[([^]]*)\]", string.Empty)) ; + return string.IsNullOrEmpty(testFilename) + || testFilename[0] == '-' + || string.IsNullOrWhiteSpace(Regex.Replace(testFilename, @"\[([^]]*)\]", string.Empty)); } return false; diff --git a/Emby.Naming/Video/VideoResolver.cs b/Emby.Naming/Video/VideoResolver.cs index a67315651..02a25c4b5 100644 --- a/Emby.Naming/Video/VideoResolver.cs +++ b/Emby.Naming/Video/VideoResolver.cs @@ -38,10 +38,11 @@ namespace Emby.Naming.Video /// Resolves the specified path. /// /// The path. - /// if set to true [is folder]. + /// if set to true [is folder]. + /// Whether or not the name should be parsed for info /// VideoFileInfo. /// path - public VideoFileInfo Resolve(string path, bool IsDirectory, bool parseName = true) + public VideoFileInfo Resolve(string path, bool isDirectory, bool parseName = true) { if (string.IsNullOrEmpty(path)) { @@ -52,9 +53,10 @@ namespace Emby.Naming.Video string container = null; string stubType = null; - if (!IsDirectory) + if (!isDirectory) { var extension = Path.GetExtension(path); + // Check supported extensions if (!_options.VideoFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) { @@ -79,7 +81,7 @@ namespace Emby.Naming.Video var extraResult = new ExtraResolver(_options).GetExtraInfo(path); - var name = IsDirectory + var name = isDirectory ? Path.GetFileName(path) : Path.GetFileNameWithoutExtension(path); @@ -108,7 +110,7 @@ namespace Emby.Naming.Video Is3D = format3DResult.Is3D, Format3D = format3DResult.Format3D, ExtraType = extraResult.ExtraType, - IsDirectory = IsDirectory, + IsDirectory = isDirectory, ExtraRule = extraResult.Rule }; } diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 2c7962452..d4e17c42a 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -52,8 +52,8 @@ - - + + diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 1673e3777..4b5063ada 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -2368,7 +2368,7 @@ namespace Emby.Server.Implementations.Library public int? GetSeasonNumberFromPath(string path) { - return new SeasonPathParser(GetNamingOptions()).Parse(path, true, true).SeasonNumber; + return new SeasonPathParser().Parse(path, true, true).SeasonNumber; } public bool FillMissingEpisodeNumbersFromPath(Episode episode, bool forceRefresh) diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs index ce1386e91..3b9e48d97 100644 --- a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs @@ -52,7 +52,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV var path = args.Path; - var seasonParserResult = new SeasonPathParser(namingOptions).Parse(path, true, true); + var seasonParserResult = new SeasonPathParser().Parse(path, true, true); var season = new Season { diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs index 5c95534ec..1f873d7c6 100644 --- a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs @@ -194,9 +194,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV /// true if [is season folder] [the specified path]; otherwise, false. private static bool IsSeasonFolder(string path, bool isTvContentType, ILibraryManager libraryManager) { - var namingOptions = ((LibraryManager)libraryManager).GetNamingOptions(); - - var seasonNumber = new SeasonPathParser(namingOptions).Parse(path, isTvContentType, isTvContentType).SeasonNumber; + var seasonNumber = new SeasonPathParser().Parse(path, isTvContentType, isTvContentType).SeasonNumber; return seasonNumber.HasValue; } diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 9346a2d25..81f145abf 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -12,7 +12,7 @@ latest - SA1600;SA1601;CS1591 + SA1600;SA1601;SA1629;CS1591 true @@ -26,8 +26,8 @@ - - + + diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index fab584bef..d8ca12117 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -122,8 +122,12 @@ namespace Jellyfin.Server // The default connection limit is 10 for ASP.NET hosted applications and 2 for all others. ServicePointManager.DefaultConnectionLimit = Math.Max(96, ServicePointManager.DefaultConnectionLimit); +// CA5359: Do Not Disable Certificate Validation +#pragma warning disable CA5359 + // Allow all https requests ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; }); +#pragma warning restore CA5359 var fileSystem = new ManagedFileSystem(_loggerFactory, appPaths); @@ -368,7 +372,7 @@ namespace Jellyfin.Server } catch (Exception ex) { - _logger.LogInformation(ex, "Skia not available. Will fallback to NullIMageEncoder. {0}"); + _logger.LogInformation(ex, "Skia not available. Will fallback to NullIMageEncoder."); } return new NullImageEncoder(); diff --git a/jellyfin.ruleset b/jellyfin.ruleset index 262121a32..0a60c8c7a 100644 --- a/jellyfin.ruleset +++ b/jellyfin.ruleset @@ -14,12 +14,17 @@ + + + + + -- cgit v1.2.3 From d78a55adb4f66b8a82449216a11657da1388ab12 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sat, 8 Jun 2019 22:54:31 -0400 Subject: Implement InvalidAuthProvider Implements the InvalidAuthProvider, which acts as a fallback if a configured authentication provider, e.g. LDAP, is unavailable due to a load failure or removal. Until the user or the authentication plugin is corrected, this will cause users with the missing provider to be locked out, while throwing errors in the logs about the issue. Fixes #1445 part 2 --- .../Library/InvalidAuthProvider.cs | 46 ++++++++++++++++++++++ Emby.Server.Implementations/Library/UserManager.cs | 20 +++++++--- 2 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 Emby.Server.Implementations/Library/InvalidAuthProvider.cs (limited to 'Emby.Server.Implementations/Library') diff --git a/Emby.Server.Implementations/Library/InvalidAuthProvider.cs b/Emby.Server.Implementations/Library/InvalidAuthProvider.cs new file mode 100644 index 000000000..ee2569562 --- /dev/null +++ b/Emby.Server.Implementations/Library/InvalidAuthProvider.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using MediaBrowser.Controller.Authentication; +using MediaBrowser.Controller.Entities; + +namespace Emby.Server.Implementations.Library +{ + public class InvalidAuthProvider : IAuthenticationProvider + { + public string Name => "InvalidorMissingAuthenticationProvider"; + + public bool IsEnabled => true; + + public Task Authenticate(string username, string password) + { + throw new Exception("User Account cannot login with this provider. The Normal provider for this user cannot be found"); + } + + public Task HasPassword(User user) + { + return Task.FromResult(true); + } + + public Task ChangePassword(User user, string newPassword) + { + return Task.FromResult(true); + } + + public void ChangeEasyPassword(User user, string newPassword, string newPasswordHash) + { + // Nothing here + } + + public string GetPasswordHash(User user) + { + return ""; + } + + public string GetEasyPasswordHash(User user) + { + return ""; + } + } +} diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index a0b8d4ba4..ca43f7aaa 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -79,6 +79,8 @@ namespace Emby.Server.Implementations.Library private IAuthenticationProvider[] _authenticationProviders; private DefaultAuthenticationProvider _defaultAuthenticationProvider; + private InvalidAuthProvider _invalidAuthProvider; + private IPasswordResetProvider[] _passwordResetProviders; private DefaultPasswordResetProvider _defaultPasswordResetProvider; @@ -141,6 +143,8 @@ namespace Emby.Server.Implementations.Library _defaultAuthenticationProvider = _authenticationProviders.OfType().First(); + _invalidAuthProvider = _authenticationProviders.OfType().First(); + _passwordResetProviders = passwordResetProviders.ToArray(); _defaultPasswordResetProvider = passwordResetProviders.OfType().First(); @@ -307,11 +311,14 @@ namespace Emby.Server.Implementations.Library user = Users .FirstOrDefault(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase)); - var hasNewUserPolicy = authenticationProvider as IHasNewUserPolicy; - if (hasNewUserPolicy != null) + if (authenticationProvider.GetType() != typeof(InvalidAuthProvider)) { - var policy = hasNewUserPolicy.GetNewUserPolicy(); - UpdateUserPolicy(user, policy, true); + var hasNewUserPolicy = authenticationProvider as IHasNewUserPolicy; + if (hasNewUserPolicy != null) + { + var policy = hasNewUserPolicy.GetNewUserPolicy(); + UpdateUserPolicy(user, policy, true); + } } } } @@ -400,7 +407,10 @@ namespace Emby.Server.Implementations.Library if (providers.Length == 0) { - providers = new IAuthenticationProvider[] { _defaultAuthenticationProvider }; + // this function used to assign any user without an auth provider to the default. + // we're going to have it use a new function now. + _logger.LogWarning($"The user {user.Name} was found but no Authentication Provider with ID: {user.Policy.AuthenticationProviderId} was found. Assigning user to InvalidAuthProvider temporarily"); + providers = new IAuthenticationProvider[] { _invalidAuthProvider }; } return providers; -- cgit v1.2.3 From 74ef3898798033a7cad987c4a869e7e72f57b229 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 9 Jun 2019 11:07:35 -0400 Subject: Add nicer log message and comment --- Emby.Server.Implementations/Library/UserManager.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations/Library') diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index ca43f7aaa..83584acf3 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -407,9 +407,8 @@ namespace Emby.Server.Implementations.Library if (providers.Length == 0) { - // this function used to assign any user without an auth provider to the default. - // we're going to have it use a new function now. - _logger.LogWarning($"The user {user.Name} was found but no Authentication Provider with ID: {user.Policy.AuthenticationProviderId} was found. Assigning user to InvalidAuthProvider temporarily"); + // Assign the user to the InvalidAuthProvider since no configured auth provider was valid/found + _logger.LogWarning("User {0} was found with invalid/missing Authentication Provider {1}. Assigning user to InvalidAuthProvider until this is corrected", user.Name, user.Policy.AuthenticationProviderId); providers = new IAuthenticationProvider[] { _invalidAuthProvider }; } -- cgit v1.2.3 From b70083f3b370055b2942e450291ce42345732cb7 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 9 Jun 2019 13:41:14 -0400 Subject: Apply suggestions from code review Co-Authored-By: Claus Vium Co-Authored-By: Bond-009 --- Emby.Server.Implementations/Library/InvalidAuthProvider.cs | 8 ++++---- Emby.Server.Implementations/Library/UserManager.cs | 5 ++--- 2 files changed, 6 insertions(+), 7 deletions(-) (limited to 'Emby.Server.Implementations/Library') diff --git a/Emby.Server.Implementations/Library/InvalidAuthProvider.cs b/Emby.Server.Implementations/Library/InvalidAuthProvider.cs index ee2569562..133864708 100644 --- a/Emby.Server.Implementations/Library/InvalidAuthProvider.cs +++ b/Emby.Server.Implementations/Library/InvalidAuthProvider.cs @@ -9,7 +9,7 @@ namespace Emby.Server.Implementations.Library { public class InvalidAuthProvider : IAuthenticationProvider { - public string Name => "InvalidorMissingAuthenticationProvider"; + public string Name => "InvalidOrMissingAuthenticationProvider"; public bool IsEnabled => true; @@ -25,7 +25,7 @@ namespace Emby.Server.Implementations.Library public Task ChangePassword(User user, string newPassword) { - return Task.FromResult(true); + return Task.CompletedTask; } public void ChangeEasyPassword(User user, string newPassword, string newPasswordHash) @@ -35,12 +35,12 @@ namespace Emby.Server.Implementations.Library public string GetPasswordHash(User user) { - return ""; + return string.Empty; } public string GetEasyPasswordHash(User user) { - return ""; + return string.Empty; } } } diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index 83584acf3..04abfc315 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -313,8 +313,7 @@ namespace Emby.Server.Implementations.Library if (authenticationProvider.GetType() != typeof(InvalidAuthProvider)) { - var hasNewUserPolicy = authenticationProvider as IHasNewUserPolicy; - if (hasNewUserPolicy != null) + if (authenticationProvider is IHasNewUserPolicy hasNewUserPolicy) { var policy = hasNewUserPolicy.GetNewUserPolicy(); UpdateUserPolicy(user, policy, true); @@ -408,7 +407,7 @@ namespace Emby.Server.Implementations.Library if (providers.Length == 0) { // Assign the user to the InvalidAuthProvider since no configured auth provider was valid/found - _logger.LogWarning("User {0} was found with invalid/missing Authentication Provider {1}. Assigning user to InvalidAuthProvider until this is corrected", user.Name, user.Policy.AuthenticationProviderId); + _logger.LogWarning("User {UserName} was found with invalid/missing Authentication Provider {AuthenticationProviderId}. Assigning user to InvalidAuthProvider until this is corrected", user.Name, user.Policy.AuthenticationProviderId); providers = new IAuthenticationProvider[] { _invalidAuthProvider }; } -- cgit v1.2.3 From 20e2cb2d8693e3f53749bcde5b5fd6367bd15007 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 9 Jun 2019 13:45:51 -0400 Subject: Use SecurityException for auth failure --- Emby.Server.Implementations/Library/InvalidAuthProvider.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations/Library') diff --git a/Emby.Server.Implementations/Library/InvalidAuthProvider.cs b/Emby.Server.Implementations/Library/InvalidAuthProvider.cs index 133864708..25d233137 100644 --- a/Emby.Server.Implementations/Library/InvalidAuthProvider.cs +++ b/Emby.Server.Implementations/Library/InvalidAuthProvider.cs @@ -4,6 +4,7 @@ using System.Text; using System.Threading.Tasks; using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Net; namespace Emby.Server.Implementations.Library { @@ -15,7 +16,7 @@ namespace Emby.Server.Implementations.Library public Task Authenticate(string username, string password) { - throw new Exception("User Account cannot login with this provider. The Normal provider for this user cannot be found"); + throw new SecurityException("User Account cannot login with this provider. The Normal provider for this user cannot be found"); } public Task HasPassword(User user) -- cgit v1.2.3 From c230d49d7c37d4fbe77676b835c3afd6c8cb56e7 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 9 Jun 2019 13:46:53 -0400 Subject: Don't set a default reset provider --- Emby.Server.Implementations/Library/UserManager.cs | 5 ----- 1 file changed, 5 deletions(-) (limited to 'Emby.Server.Implementations/Library') diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index 04abfc315..4233ea8f4 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -425,11 +425,6 @@ namespace Emby.Server.Implementations.Library providers = providers.Where(i => string.Equals(passwordResetProviderId, GetPasswordResetProviderId(i), StringComparison.OrdinalIgnoreCase)).ToArray(); } - if (providers.Length == 0) - { - providers = new IPasswordResetProvider[] { _defaultPasswordResetProvider }; - } - return providers; } -- cgit v1.2.3 From 4b8f735cb89901bd1004d590f4f2820c23e2493c Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 9 Jun 2019 13:57:49 -0400 Subject: Remove superfluous conditional This wasn't needed to prevent updating the policy on-disk from my tests and can be removed as suggested by @Bond-009 --- Emby.Server.Implementations/Library/UserManager.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'Emby.Server.Implementations/Library') diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index 4233ea8f4..16becbd52 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -311,13 +311,10 @@ namespace Emby.Server.Implementations.Library user = Users .FirstOrDefault(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase)); - if (authenticationProvider.GetType() != typeof(InvalidAuthProvider)) + if (authenticationProvider is IHasNewUserPolicy hasNewUserPolicy) { - if (authenticationProvider is IHasNewUserPolicy hasNewUserPolicy) - { - var policy = hasNewUserPolicy.GetNewUserPolicy(); - UpdateUserPolicy(user, policy, true); - } + var policy = hasNewUserPolicy.GetNewUserPolicy(); + UpdateUserPolicy(user, policy, true); } } } -- cgit v1.2.3 From 2946ae10092cddadade4c84cfa000129bf117e03 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 9 Jun 2019 15:27:38 -0400 Subject: Revert "Don't set a default reset provider" This reverts commit c230d49d7c37d4fbe77676b835c3afd6c8cb56e7. This reenables an edge case where an admin might want to reset, with the default auth provider, the password of an externally-provided user so they could "unlock" the account while it was failing. There might be minor security implications to this, but the malicious actor would need FS access to do it (as they would with any password resets) so it's probably best to keep it as-is. Removing this in the first place was due to a misunderstanding anyways so no harm. --- Emby.Server.Implementations/Library/UserManager.cs | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'Emby.Server.Implementations/Library') diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index 16becbd52..ff375e590 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -422,6 +422,11 @@ namespace Emby.Server.Implementations.Library providers = providers.Where(i => string.Equals(passwordResetProviderId, GetPasswordResetProviderId(i), StringComparison.OrdinalIgnoreCase)).ToArray(); } + if (providers.Length == 0) + { + providers = new IPasswordResetProvider[] { _defaultPasswordResetProvider }; + } + return providers; } -- cgit v1.2.3 From d961278b3dcab57910b260115cd45d9831e6443e Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 3 Apr 2019 18:15:04 +0200 Subject: Reduce amount of raw sql --- .../Data/SqliteUserRepository.cs | 23 ++++++------ Emby.Server.Implementations/Library/UserManager.cs | 42 +++++++++++----------- 2 files changed, 34 insertions(+), 31 deletions(-) (limited to 'Emby.Server.Implementations/Library') diff --git a/Emby.Server.Implementations/Data/SqliteUserRepository.cs b/Emby.Server.Implementations/Data/SqliteUserRepository.cs index cd364e7f4..de2354eef 100644 --- a/Emby.Server.Implementations/Data/SqliteUserRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteUserRepository.cs @@ -75,10 +75,8 @@ namespace Emby.Server.Implementations.Data private void RemoveEmptyPasswordHashes(ManagedConnection connection) { - foreach (var row in connection.Query("select id,guid,data from LocalUsersv2")) + foreach (var user in RetrieveAllUsers(connection)) { - var user = GetUser(row); - // If the user password is the sha1 hash of the empty string, remove it if (!string.Equals(user.Password, "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709", StringComparison.Ordinal) && !string.Equals(user.Password, "$SHA1$DA39A3EE5E6B4B0D3255BFEF95601890AFD80709", StringComparison.Ordinal)) @@ -198,17 +196,22 @@ namespace Emby.Server.Implementations.Data /// IEnumerable{User}. public List RetrieveAllUsers() { - var list = new List(); - using (var connection = GetConnection(true)) { - foreach (var row in connection.Query("select id,guid,data from LocalUsersv2")) - { - list.Add(GetUser(row)); - } + return new List(RetrieveAllUsers(connection)); } + } - return list; + /// + /// Retrieve all users from the database + /// + /// IEnumerable{User}. + private IEnumerable RetrieveAllUsers(ManagedConnection connection) + { + foreach (var row in connection.Query("select id,guid,data from LocalUsersv2")) + { + yield return GetUser(row); + } } /// diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index ff375e590..1701ced42 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -222,9 +222,8 @@ namespace Emby.Server.Implementations.Library public void Initialize() { - _users = LoadUsers(); - - var users = Users.ToList(); + var users = LoadUsers(); + _users = users.ToArray(); // If there are no local users with admin rights, make them all admins if (!users.Any(i => i.Policy.IsAdministrator)) @@ -555,35 +554,36 @@ namespace Emby.Server.Implementations.Library /// Loads the users from the repository /// /// IEnumerable{User}. - private User[] LoadUsers() + private List LoadUsers() { var users = UserRepository.RetrieveAllUsers(); // There always has to be at least one user. - if (users.Count == 0) + if (users.Count != 0) { - var defaultName = Environment.UserName; - if (string.IsNullOrWhiteSpace(defaultName)) - { - defaultName = "MyJellyfinUser"; - } - var name = MakeValidUsername(defaultName); + return users; + } - var user = InstantiateNewUser(name); + var defaultName = Environment.UserName; + if (string.IsNullOrWhiteSpace(defaultName)) + { + defaultName = "MyJellyfinUser"; + } - user.DateLastSaved = DateTime.UtcNow; + var name = MakeValidUsername(defaultName); - UserRepository.CreateUser(user); + var user = InstantiateNewUser(name); + + user.DateLastSaved = DateTime.UtcNow; - users.Add(user); + UserRepository.CreateUser(user); - user.Policy.IsAdministrator = true; - user.Policy.EnableContentDeletion = true; - user.Policy.EnableRemoteControlOfOtherUsers = true; - UpdateUserPolicy(user, user.Policy, false); - } + user.Policy.IsAdministrator = true; + user.Policy.EnableContentDeletion = true; + user.Policy.EnableRemoteControlOfOtherUsers = true; + UpdateUserPolicy(user, user.Policy, false); - return users.ToArray(); + return new List { user }; } public UserDto GetUserDto(User user, string remoteEndPoint = null) -- cgit v1.2.3 From 0f897589ed6349bb3c88919b06861daa80aec1be Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 21 May 2019 19:28:34 +0200 Subject: Streamline authentication proccess --- .../Cryptography/CryptographyProvider.cs | 86 ++++--- .../Library/DefaultAuthenticationProvider.cs | 115 +++++---- .../Library/DefaultPasswordResetProvider.cs | 257 ++++++++++----------- .../Library/InvalidAuthProvider.cs | 11 +- Emby.Server.Implementations/Library/UserManager.cs | 49 ++-- MediaBrowser.Api/LiveTv/LiveTvService.cs | 19 -- .../Authentication/AuthenticationException.cs | 28 +++ .../Authentication/IAuthenticationProvider.cs | 3 +- .../Authentication/IPasswordResetProvider.cs | 1 + MediaBrowser.Model/Cryptography/ICryptoProvider.cs | 6 +- MediaBrowser.Model/Cryptography/PasswordHash.cs | 152 ++++++------ 11 files changed, 365 insertions(+), 362 deletions(-) create mode 100644 MediaBrowser.Controller/Authentication/AuthenticationException.cs (limited to 'Emby.Server.Implementations/Library') diff --git a/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs b/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs index 6d7193ce2..f726dae2e 100644 --- a/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs +++ b/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs @@ -8,7 +8,7 @@ using MediaBrowser.Model.Cryptography; namespace Emby.Server.Implementations.Cryptography { - public class CryptographyProvider : ICryptoProvider + public class CryptographyProvider : ICryptoProvider, IDisposable { private static readonly HashSet _supportedHashMethods = new HashSet() { @@ -28,26 +28,28 @@ namespace Emby.Server.Implementations.Cryptography "System.Security.Cryptography.SHA512" }; - public string DefaultHashMethod => "PBKDF2"; - private RandomNumberGenerator _randomNumberGenerator; private const int _defaultIterations = 1000; + private bool _disposed = false; + public CryptographyProvider() { - //FIXME: When we get DotNet Standard 2.1 we need to revisit how we do the crypto - //Currently supported hash methods from https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptoconfig?view=netcore-2.1 - //there might be a better way to autogenerate this list as dotnet updates, but I couldn't find one - //Please note the default method of PBKDF2 is not included, it cannot be used to generate hashes cleanly as it is actually a pbkdf with sha1 + // FIXME: When we get DotNet Standard 2.1 we need to revisit how we do the crypto + // Currently supported hash methods from https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptoconfig?view=netcore-2.1 + // there might be a better way to autogenerate this list as dotnet updates, but I couldn't find one + // Please note the default method of PBKDF2 is not included, it cannot be used to generate hashes cleanly as it is actually a pbkdf with sha1 _randomNumberGenerator = RandomNumberGenerator.Create(); } + public string DefaultHashMethod => "PBKDF2"; + + [Obsolete("Use System.Security.Cryptography.MD5 directly")] public Guid GetMD5(string str) - { - return new Guid(ComputeMD5(Encoding.Unicode.GetBytes(str))); - } + => new Guid(ComputeMD5(Encoding.Unicode.GetBytes(str))); + [Obsolete("Use System.Security.Cryptography.SHA1 directly")] public byte[] ComputeSHA1(byte[] bytes) { using (var provider = SHA1.Create()) @@ -56,6 +58,7 @@ namespace Emby.Server.Implementations.Cryptography } } + [Obsolete("Use System.Security.Cryptography.MD5 directly")] public byte[] ComputeMD5(Stream str) { using (var provider = MD5.Create()) @@ -64,6 +67,7 @@ namespace Emby.Server.Implementations.Cryptography } } + [Obsolete("Use System.Security.Cryptography.MD5 directly")] public byte[] ComputeMD5(byte[] bytes) { using (var provider = MD5.Create()) @@ -73,9 +77,7 @@ namespace Emby.Server.Implementations.Cryptography } public IEnumerable GetSupportedHashMethods() - { - return _supportedHashMethods; - } + => _supportedHashMethods; private byte[] PBKDF2(string method, byte[] bytes, byte[] salt, int iterations) { @@ -93,14 +95,10 @@ namespace Emby.Server.Implementations.Cryptography } public byte[] ComputeHash(string hashMethod, byte[] bytes) - { - return ComputeHash(hashMethod, bytes, Array.Empty()); - } + => ComputeHash(hashMethod, bytes, Array.Empty()); public byte[] ComputeHashWithDefaultMethod(byte[] bytes) - { - return ComputeHash(DefaultHashMethod, bytes); - } + => ComputeHash(DefaultHashMethod, bytes); public byte[] ComputeHash(string hashMethod, byte[] bytes, byte[] salt) { @@ -125,37 +123,27 @@ namespace Emby.Server.Implementations.Cryptography } } } - else - { - throw new CryptographicException($"Requested hash method is not supported: {hashMethod}"); - } + + throw new CryptographicException($"Requested hash method is not supported: {hashMethod}"); + } public byte[] ComputeHashWithDefaultMethod(byte[] bytes, byte[] salt) - { - return PBKDF2(DefaultHashMethod, bytes, salt, _defaultIterations); - } + => PBKDF2(DefaultHashMethod, bytes, salt, _defaultIterations); public byte[] ComputeHash(PasswordHash hash) { int iterations = _defaultIterations; if (!hash.Parameters.ContainsKey("iterations")) { - hash.Parameters.Add("iterations", _defaultIterations.ToString(CultureInfo.InvariantCulture)); + hash.Parameters.Add("iterations", iterations.ToString(CultureInfo.InvariantCulture)); } - else + else if (!int.TryParse(hash.Parameters["iterations"], out iterations)) { - try - { - iterations = int.Parse(hash.Parameters["iterations"]); - } - catch (Exception e) - { - throw new InvalidDataException($"Couldn't successfully parse iterations value from string: {hash.Parameters["iterations"]}", e); - } + throw new InvalidDataException($"Couldn't successfully parse iterations value from string: {hash.Parameters["iterations"]}"); } - return PBKDF2(hash.Id, hash.HashBytes, hash.SaltBytes, iterations); + return PBKDF2(hash.Id, hash.Hash, hash.Salt, iterations); } public byte[] GenerateSalt() @@ -164,5 +152,29 @@ namespace Emby.Server.Implementations.Cryptography _randomNumberGenerator.GetBytes(salt); return salt; } + + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (_disposed) + { + return; + } + + if (disposing) + { + _randomNumberGenerator.Dispose(); + } + + _randomNumberGenerator = null; + + _disposed = true; + } } } diff --git a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs b/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs index fe09b07ff..b07244fda 100644 --- a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs +++ b/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs @@ -11,9 +11,9 @@ namespace Emby.Server.Implementations.Library public class DefaultAuthenticationProvider : IAuthenticationProvider, IRequiresResolvedUser { private readonly ICryptoProvider _cryptographyProvider; - public DefaultAuthenticationProvider(ICryptoProvider crypto) + public DefaultAuthenticationProvider(ICryptoProvider cryptographyProvider) { - _cryptographyProvider = crypto; + _cryptographyProvider = cryptographyProvider; } public string Name => "Default"; @@ -28,17 +28,17 @@ namespace Emby.Server.Implementations.Library throw new NotImplementedException(); } - // This is the verson that we need to use for local users. Because reasons. + // This is the version that we need to use for local users. Because reasons. public Task Authenticate(string username, string password, User resolvedUser) { bool success = false; if (resolvedUser == null) { - throw new Exception("Invalid username or password"); + throw new ArgumentNullException(nameof(resolvedUser)); } // As long as jellyfin supports passwordless users, we need this little block here to accomodate - if (IsPasswordEmpty(resolvedUser, password)) + if (!HasPassword(resolvedUser) && string.IsNullOrEmpty(password)) { return Task.FromResult(new ProviderAuthenticationResult { @@ -50,37 +50,24 @@ namespace Emby.Server.Implementations.Library byte[] passwordbytes = Encoding.UTF8.GetBytes(password); PasswordHash readyHash = new PasswordHash(resolvedUser.Password); - byte[] calculatedHash; - string calculatedHashString; - if (_cryptographyProvider.GetSupportedHashMethods().Contains(readyHash.Id) || _cryptographyProvider.DefaultHashMethod == readyHash.Id) + if (_cryptographyProvider.GetSupportedHashMethods().Contains(readyHash.Id) + || _cryptographyProvider.DefaultHashMethod == readyHash.Id) { - if (string.IsNullOrEmpty(readyHash.Salt)) - { - calculatedHash = _cryptographyProvider.ComputeHash(readyHash.Id, passwordbytes); - calculatedHashString = BitConverter.ToString(calculatedHash).Replace("-", string.Empty); - } - else - { - calculatedHash = _cryptographyProvider.ComputeHash(readyHash.Id, passwordbytes, readyHash.SaltBytes); - calculatedHashString = BitConverter.ToString(calculatedHash).Replace("-", string.Empty); - } + byte[] calculatedHash = _cryptographyProvider.ComputeHash(readyHash.Id, passwordbytes, readyHash.Salt); - if (calculatedHashString == readyHash.Hash) + if (calculatedHash.SequenceEqual(readyHash.Hash)) { success = true; - // throw new Exception("Invalid username or password"); } } else { - throw new Exception(string.Format($"Requested crypto method not available in provider: {readyHash.Id}")); + throw new AuthenticationException($"Requested crypto method not available in provider: {readyHash.Id}"); } - // var success = string.Equals(GetPasswordHash(resolvedUser), GetHashedString(resolvedUser, password), StringComparison.OrdinalIgnoreCase); - if (!success) { - throw new Exception("Invalid username or password"); + throw new AuthenticationException("Invalid username or password"); } return Task.FromResult(new ProviderAuthenticationResult @@ -98,29 +85,22 @@ namespace Emby.Server.Implementations.Library return; } - if (!user.Password.Contains("$")) + if (user.Password.IndexOf('$') == -1) { string hash = user.Password; user.Password = string.Format("$SHA1${0}", hash); } - if (user.EasyPassword != null && !user.EasyPassword.Contains("$")) + if (user.EasyPassword != null + && user.EasyPassword.IndexOf('$') == -1) { string hash = user.EasyPassword; user.EasyPassword = string.Format("$SHA1${0}", hash); } } - public Task HasPassword(User user) - { - var hasConfiguredPassword = !IsPasswordEmpty(user, GetPasswordHash(user)); - return Task.FromResult(hasConfiguredPassword); - } - - private bool IsPasswordEmpty(User user, string password) - { - return (string.IsNullOrEmpty(user.Password) && string.IsNullOrEmpty(password)); - } + public bool HasPassword(User user) + => !string.IsNullOrEmpty(user.Password); public Task ChangePassword(User user, string newPassword) { @@ -129,30 +109,24 @@ namespace Emby.Server.Implementations.Library if (string.IsNullOrEmpty(user.Password)) { PasswordHash newPasswordHash = new PasswordHash(_cryptographyProvider); - newPasswordHash.SaltBytes = _cryptographyProvider.GenerateSalt(); - newPasswordHash.Salt = PasswordHash.ConvertToByteString(newPasswordHash.SaltBytes); + newPasswordHash.Salt = _cryptographyProvider.GenerateSalt(); newPasswordHash.Id = _cryptographyProvider.DefaultHashMethod; - newPasswordHash.Hash = GetHashedStringChangeAuth(newPassword, newPasswordHash); + newPasswordHash.Hash = GetHashedChangeAuth(newPassword, newPasswordHash); user.Password = newPasswordHash.ToString(); return Task.CompletedTask; } PasswordHash passwordHash = new PasswordHash(user.Password); - if (passwordHash.Id == "SHA1" && string.IsNullOrEmpty(passwordHash.Salt)) + if (passwordHash.Id == "SHA1" + && passwordHash.Salt.Length == 0) { - passwordHash.SaltBytes = _cryptographyProvider.GenerateSalt(); - passwordHash.Salt = PasswordHash.ConvertToByteString(passwordHash.SaltBytes); + passwordHash.Salt = _cryptographyProvider.GenerateSalt(); passwordHash.Id = _cryptographyProvider.DefaultHashMethod; - passwordHash.Hash = GetHashedStringChangeAuth(newPassword, passwordHash); + passwordHash.Hash = GetHashedChangeAuth(newPassword, passwordHash); } else if (newPassword != null) { - passwordHash.Hash = GetHashedString(user, newPassword); - } - - if (string.IsNullOrWhiteSpace(passwordHash.Hash)) - { - throw new ArgumentNullException(nameof(passwordHash.Hash)); + passwordHash.Hash = GetHashed(user, newPassword); } user.Password = passwordHash.ToString(); @@ -160,11 +134,6 @@ namespace Emby.Server.Implementations.Library return Task.CompletedTask; } - public string GetPasswordHash(User user) - { - return user.Password; - } - public void ChangeEasyPassword(User user, string newPassword, string newPasswordHash) { ConvertPasswordFormat(user); @@ -190,13 +159,13 @@ namespace Emby.Server.Implementations.Library return string.IsNullOrEmpty(user.EasyPassword) ? null - : (new PasswordHash(user.EasyPassword)).Hash; + : PasswordHash.ConvertToByteString(new PasswordHash(user.EasyPassword).Hash); } - public string GetHashedStringChangeAuth(string newPassword, PasswordHash passwordHash) + internal byte[] GetHashedChangeAuth(string newPassword, PasswordHash passwordHash) { - passwordHash.HashBytes = Encoding.UTF8.GetBytes(newPassword); - return PasswordHash.ConvertToByteString(_cryptographyProvider.ComputeHash(passwordHash)); + passwordHash.Hash = Encoding.UTF8.GetBytes(newPassword); + return _cryptographyProvider.ComputeHash(passwordHash); } /// @@ -215,10 +184,10 @@ namespace Emby.Server.Implementations.Library passwordHash = new PasswordHash(user.Password); } - if (passwordHash.SaltBytes != null) + if (passwordHash.Salt != null) { // the password is modern format with PBKDF and we should take advantage of that - passwordHash.HashBytes = Encoding.UTF8.GetBytes(str); + passwordHash.Hash = Encoding.UTF8.GetBytes(str); return PasswordHash.ConvertToByteString(_cryptographyProvider.ComputeHash(passwordHash)); } else @@ -227,5 +196,31 @@ namespace Emby.Server.Implementations.Library return PasswordHash.ConvertToByteString(_cryptographyProvider.ComputeHash(passwordHash.Id, Encoding.UTF8.GetBytes(str))); } } + + public byte[] GetHashed(User user, string str) + { + PasswordHash passwordHash; + if (string.IsNullOrEmpty(user.Password)) + { + passwordHash = new PasswordHash(_cryptographyProvider); + } + else + { + ConvertPasswordFormat(user); + passwordHash = new PasswordHash(user.Password); + } + + if (passwordHash.Salt != null) + { + // the password is modern format with PBKDF and we should take advantage of that + passwordHash.Hash = Encoding.UTF8.GetBytes(str); + return _cryptographyProvider.ComputeHash(passwordHash); + } + else + { + // the password has no salt and should be called with the older method for safety + return _cryptographyProvider.ComputeHash(passwordHash.Id, Encoding.UTF8.GetBytes(str)); + } + } } } diff --git a/Emby.Server.Implementations/Library/DefaultPasswordResetProvider.cs b/Emby.Server.Implementations/Library/DefaultPasswordResetProvider.cs index e218749d9..c7044820c 100644 --- a/Emby.Server.Implementations/Library/DefaultPasswordResetProvider.cs +++ b/Emby.Server.Implementations/Library/DefaultPasswordResetProvider.cs @@ -1,132 +1,125 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using MediaBrowser.Common.Extensions; -using MediaBrowser.Controller.Authentication; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Cryptography; -using MediaBrowser.Model.Serialization; -using MediaBrowser.Model.Users; - -namespace Emby.Server.Implementations.Library -{ - public class DefaultPasswordResetProvider : IPasswordResetProvider - { - public string Name => "Default Password Reset Provider"; - - public bool IsEnabled => true; - - private readonly string _passwordResetFileBase; - private readonly string _passwordResetFileBaseDir; - private readonly string _passwordResetFileBaseName = "passwordreset"; - - private readonly IJsonSerializer _jsonSerializer; - private readonly IUserManager _userManager; - private readonly ICryptoProvider _crypto; - - public DefaultPasswordResetProvider(IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IUserManager userManager, ICryptoProvider cryptoProvider) - { - _passwordResetFileBaseDir = configurationManager.ApplicationPaths.ProgramDataPath; - _passwordResetFileBase = Path.Combine(_passwordResetFileBaseDir, _passwordResetFileBaseName); - _jsonSerializer = jsonSerializer; - _userManager = userManager; - _crypto = cryptoProvider; - } - - public async Task RedeemPasswordResetPin(string pin) - { - SerializablePasswordReset spr; - HashSet usersreset = new HashSet(); - foreach (var resetfile in Directory.EnumerateFiles(_passwordResetFileBaseDir, $"{_passwordResetFileBaseName}*")) - { - using (var str = File.OpenRead(resetfile)) - { - spr = await _jsonSerializer.DeserializeFromStreamAsync(str).ConfigureAwait(false); - } - - if (spr.ExpirationDate < DateTime.Now) - { - File.Delete(resetfile); - } - else if (spr.Pin.Replace("-", "").Equals(pin.Replace("-", ""), StringComparison.InvariantCultureIgnoreCase)) - { - var resetUser = _userManager.GetUserByName(spr.UserName); - if (resetUser == null) - { - throw new Exception($"User with a username of {spr.UserName} not found"); - } - - await _userManager.ChangePassword(resetUser, pin).ConfigureAwait(false); - usersreset.Add(resetUser.Name); - File.Delete(resetfile); - } - } - - if (usersreset.Count < 1) - { - throw new ResourceNotFoundException($"No Users found with a password reset request matching pin {pin}"); - } - else - { - return new PinRedeemResult - { - Success = true, - UsersReset = usersreset.ToArray() - }; - } - } - - public async Task StartForgotPasswordProcess(MediaBrowser.Controller.Entities.User user, bool isInNetwork) - { - string pin = string.Empty; - using (var cryptoRandom = System.Security.Cryptography.RandomNumberGenerator.Create()) - { - byte[] bytes = new byte[4]; - cryptoRandom.GetBytes(bytes); - pin = BitConverter.ToString(bytes); - } - - DateTime expireTime = DateTime.Now.AddMinutes(30); - string filePath = _passwordResetFileBase + user.InternalId + ".json"; - SerializablePasswordReset spr = new SerializablePasswordReset - { - ExpirationDate = expireTime, - Pin = pin, - PinFile = filePath, - UserName = user.Name - }; - - try - { - using (FileStream fileStream = File.OpenWrite(filePath)) - { - _jsonSerializer.SerializeToStream(spr, fileStream); - await fileStream.FlushAsync().ConfigureAwait(false); - } - } - catch (Exception e) - { - throw new Exception($"Error serializing or writing password reset for {user.Name} to location: {filePath}", e); - } - - return new ForgotPasswordResult - { - Action = ForgotPasswordAction.PinCode, - PinExpirationDate = expireTime, - PinFile = filePath - }; - } - - private class SerializablePasswordReset : PasswordPinCreationResult - { - public string Pin { get; set; } - - public string UserName { get; set; } - } - } -} +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Controller.Authentication; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Cryptography; +using MediaBrowser.Model.Serialization; +using MediaBrowser.Model.Users; + +namespace Emby.Server.Implementations.Library +{ + public class DefaultPasswordResetProvider : IPasswordResetProvider + { + public string Name => "Default Password Reset Provider"; + + public bool IsEnabled => true; + + private readonly string _passwordResetFileBase; + private readonly string _passwordResetFileBaseDir; + private readonly string _passwordResetFileBaseName = "passwordreset"; + + private readonly IJsonSerializer _jsonSerializer; + private readonly IUserManager _userManager; + private readonly ICryptoProvider _crypto; + + public DefaultPasswordResetProvider(IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IUserManager userManager, ICryptoProvider cryptoProvider) + { + _passwordResetFileBaseDir = configurationManager.ApplicationPaths.ProgramDataPath; + _passwordResetFileBase = Path.Combine(_passwordResetFileBaseDir, _passwordResetFileBaseName); + _jsonSerializer = jsonSerializer; + _userManager = userManager; + _crypto = cryptoProvider; + } + + public async Task RedeemPasswordResetPin(string pin) + { + SerializablePasswordReset spr; + HashSet usersreset = new HashSet(); + foreach (var resetfile in Directory.EnumerateFiles(_passwordResetFileBaseDir, $"{_passwordResetFileBaseName}*")) + { + using (var str = File.OpenRead(resetfile)) + { + spr = await _jsonSerializer.DeserializeFromStreamAsync(str).ConfigureAwait(false); + } + + if (spr.ExpirationDate < DateTime.Now) + { + File.Delete(resetfile); + } + else if (spr.Pin.Replace("-", "").Equals(pin.Replace("-", ""), StringComparison.InvariantCultureIgnoreCase)) + { + var resetUser = _userManager.GetUserByName(spr.UserName); + if (resetUser == null) + { + throw new Exception($"User with a username of {spr.UserName} not found"); + } + + await _userManager.ChangePassword(resetUser, pin).ConfigureAwait(false); + usersreset.Add(resetUser.Name); + File.Delete(resetfile); + } + } + + if (usersreset.Count < 1) + { + throw new ResourceNotFoundException($"No Users found with a password reset request matching pin {pin}"); + } + else + { + return new PinRedeemResult + { + Success = true, + UsersReset = usersreset.ToArray() + }; + } + } + + public async Task StartForgotPasswordProcess(MediaBrowser.Controller.Entities.User user, bool isInNetwork) + { + string pin = string.Empty; + using (var cryptoRandom = System.Security.Cryptography.RandomNumberGenerator.Create()) + { + byte[] bytes = new byte[4]; + cryptoRandom.GetBytes(bytes); + pin = BitConverter.ToString(bytes); + } + + DateTime expireTime = DateTime.Now.AddMinutes(30); + string filePath = _passwordResetFileBase + user.InternalId + ".json"; + SerializablePasswordReset spr = new SerializablePasswordReset + { + ExpirationDate = expireTime, + Pin = pin, + PinFile = filePath, + UserName = user.Name + }; + + using (FileStream fileStream = File.OpenWrite(filePath)) + { + _jsonSerializer.SerializeToStream(spr, fileStream); + await fileStream.FlushAsync().ConfigureAwait(false); + } + + return new ForgotPasswordResult + { + Action = ForgotPasswordAction.PinCode, + PinExpirationDate = expireTime, + PinFile = filePath + }; + } + + private class SerializablePasswordReset : PasswordPinCreationResult + { + public string Pin { get; set; } + + public string UserName { get; set; } + } + } +} diff --git a/Emby.Server.Implementations/Library/InvalidAuthProvider.cs b/Emby.Server.Implementations/Library/InvalidAuthProvider.cs index 25d233137..6956369dc 100644 --- a/Emby.Server.Implementations/Library/InvalidAuthProvider.cs +++ b/Emby.Server.Implementations/Library/InvalidAuthProvider.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Entities; @@ -16,12 +13,12 @@ namespace Emby.Server.Implementations.Library public Task Authenticate(string username, string password) { - throw new SecurityException("User Account cannot login with this provider. The Normal provider for this user cannot be found"); + throw new AuthenticationException("User Account cannot login with this provider. The Normal provider for this user cannot be found"); } - public Task HasPassword(User user) + public bool HasPassword(User user) { - return Task.FromResult(true); + return true; } public Task ChangePassword(User user, string newPassword) @@ -31,7 +28,7 @@ namespace Emby.Server.Implementations.Library public void ChangeEasyPassword(User user, string newPassword, string newPasswordHash) { - // Nothing here + // Nothing here } public string GetPasswordHash(User user) diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index 1701ced42..c8c8a108d 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -266,6 +266,7 @@ namespace Emby.Server.Implementations.Library builder.Append(c); } } + return builder.ToString(); } @@ -286,17 +287,17 @@ namespace Emby.Server.Implementations.Library if (user != null) { var authResult = await AuthenticateLocalUser(username, password, hashedPassword, user, remoteEndPoint).ConfigureAwait(false); - authenticationProvider = authResult.Item1; - updatedUsername = authResult.Item2; - success = authResult.Item3; + authenticationProvider = authResult.authenticationProvider; + updatedUsername = authResult.username; + success = authResult.success; } else { // user is null var authResult = await AuthenticateLocalUser(username, password, hashedPassword, null, remoteEndPoint).ConfigureAwait(false); - authenticationProvider = authResult.Item1; - updatedUsername = authResult.Item2; - success = authResult.Item3; + authenticationProvider = authResult.authenticationProvider; + updatedUsername = authResult.username; + success = authResult.success; if (success && authenticationProvider != null && !(authenticationProvider is DefaultAuthenticationProvider)) { @@ -331,22 +332,25 @@ namespace Emby.Server.Implementations.Library if (user == null) { - throw new SecurityException("Invalid username or password entered."); + throw new AuthenticationException("Invalid username or password entered."); } if (user.Policy.IsDisabled) { - throw new SecurityException(string.Format("The {0} account is currently disabled. Please consult with your administrator.", user.Name)); + throw new AuthenticationException(string.Format( + CultureInfo.InvariantCulture, + "The {0} account is currently disabled. Please consult with your administrator.", + user.Name)); } if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(remoteEndPoint)) { - throw new SecurityException("Forbidden."); + throw new AuthenticationException("Forbidden."); } if (!user.IsParentalScheduleAllowed()) { - throw new SecurityException("User is not allowed access at this time."); + throw new AuthenticationException("User is not allowed access at this time."); } // Update LastActivityDate and LastLoginDate, then save @@ -357,6 +361,7 @@ namespace Emby.Server.Implementations.Library user.LastActivityDate = user.LastLoginDate = DateTime.UtcNow; UpdateUser(user); } + UpdateInvalidLoginAttemptCount(user, 0); } else @@ -429,7 +434,7 @@ namespace Emby.Server.Implementations.Library return providers; } - private async Task> AuthenticateWithProvider(IAuthenticationProvider provider, string username, string password, User resolvedUser) + private async Task<(string username, bool success)> AuthenticateWithProvider(IAuthenticationProvider provider, string username, string password, User resolvedUser) { try { @@ -444,23 +449,23 @@ namespace Emby.Server.Implementations.Library authenticationResult = await provider.Authenticate(username, password).ConfigureAwait(false); } - if(authenticationResult.Username != username) + if (authenticationResult.Username != username) { _logger.LogDebug("Authentication provider provided updated username {1}", authenticationResult.Username); username = authenticationResult.Username; } - return new Tuple(username, true); + return (username, true); } - catch (Exception ex) + catch (AuthenticationException ex) { - _logger.LogError(ex, "Error authenticating with provider {provider}", provider.Name); + _logger.LogError(ex, "Error authenticating with provider {Provider}", provider.Name); - return new Tuple(username, false); + return (username, false); } } - private async Task> AuthenticateLocalUser(string username, string password, string hashedPassword, User user, string remoteEndPoint) + private async Task<(IAuthenticationProvider authenticationProvider, string username, bool success)> AuthenticateLocalUser(string username, string password, string hashedPassword, User user, string remoteEndPoint) { string updatedUsername = null; bool success = false; @@ -475,15 +480,15 @@ namespace Emby.Server.Implementations.Library if (password == null) { // legacy - success = string.Equals(GetAuthenticationProvider(user).GetPasswordHash(user), hashedPassword.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase); + success = string.Equals(user.Password, hashedPassword.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase); } else { foreach (var provider in GetAuthenticationProviders(user)) { var providerAuthResult = await AuthenticateWithProvider(provider, username, password, user).ConfigureAwait(false); - updatedUsername = providerAuthResult.Item1; - success = providerAuthResult.Item2; + updatedUsername = providerAuthResult.username; + success = providerAuthResult.success; if (success) { @@ -510,7 +515,7 @@ namespace Emby.Server.Implementations.Library } } - return new Tuple(authenticationProvider, username, success); + return (authenticationProvider, username, success); } private void UpdateInvalidLoginAttemptCount(User user, int newValue) @@ -593,7 +598,7 @@ namespace Emby.Server.Implementations.Library throw new ArgumentNullException(nameof(user)); } - bool hasConfiguredPassword = GetAuthenticationProvider(user).HasPassword(user).Result; + bool hasConfiguredPassword = GetAuthenticationProvider(user).HasPassword(user); bool hasConfiguredEasyPassword = !string.IsNullOrEmpty(GetAuthenticationProvider(user).GetEasyPasswordHash(user)); bool hasPassword = user.Configuration.EnableLocalPassword && !string.IsNullOrEmpty(remoteEndPoint) && _networkManager.IsInLocalNetwork(remoteEndPoint) ? diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index e41ad540a..8a4d6e216 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs @@ -599,7 +599,6 @@ namespace MediaBrowser.Api.LiveTv { public bool ValidateLogin { get; set; } public bool ValidateListings { get; set; } - public string Pw { get; set; } } [Route("/LiveTv/ListingProviders", "DELETE", Summary = "Deletes a listing provider")] @@ -867,28 +866,10 @@ namespace MediaBrowser.Api.LiveTv public async Task Post(AddListingProvider request) { - if (request.Pw != null) - { - request.Password = GetHashedString(request.Pw); - } - - request.Pw = null; - var result = await _liveTvManager.SaveListingProvider(request, request.ValidateLogin, request.ValidateListings).ConfigureAwait(false); return ToOptimizedResult(result); } - /// - /// Gets the hashed string. - /// - private string GetHashedString(string str) - { - // legacy - return BitConverter.ToString( - _cryptographyProvider.ComputeSHA1(Encoding.UTF8.GetBytes(str))) - .Replace("-", string.Empty).ToLowerInvariant(); - } - public void Delete(DeleteListingProvider request) { _liveTvManager.DeleteListingsProvider(request.Id); diff --git a/MediaBrowser.Controller/Authentication/AuthenticationException.cs b/MediaBrowser.Controller/Authentication/AuthenticationException.cs new file mode 100644 index 000000000..045cbcdae --- /dev/null +++ b/MediaBrowser.Controller/Authentication/AuthenticationException.cs @@ -0,0 +1,28 @@ +using System; +namespace MediaBrowser.Controller.Authentication +{ + /// + /// The exception that is thrown when an attempt to authenticate fails. + /// + public class AuthenticationException : Exception + { + /// + public AuthenticationException() : base() + { + + } + + /// + public AuthenticationException(string message) : base(message) + { + + } + + /// + public AuthenticationException(string message, Exception innerException) + : base(message, innerException) + { + + } + } +} diff --git a/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs b/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs index 2cf531eed..f5571065f 100644 --- a/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs +++ b/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs @@ -9,10 +9,9 @@ namespace MediaBrowser.Controller.Authentication string Name { get; } bool IsEnabled { get; } Task Authenticate(string username, string password); - Task HasPassword(User user); + bool HasPassword(User user); Task ChangePassword(User user, string newPassword); void ChangeEasyPassword(User user, string newPassword, string newPasswordHash); - string GetPasswordHash(User user); string GetEasyPasswordHash(User user); } diff --git a/MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs b/MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs index 9e5cd8816..2639960e7 100644 --- a/MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs +++ b/MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs @@ -12,6 +12,7 @@ namespace MediaBrowser.Controller.Authentication Task StartForgotPasswordProcess(User user, bool isInNetwork); Task RedeemPasswordResetPin(string pin); } + public class PasswordPinCreationResult { public string PinFile { get; set; } diff --git a/MediaBrowser.Model/Cryptography/ICryptoProvider.cs b/MediaBrowser.Model/Cryptography/ICryptoProvider.cs index 5988112c2..9e85beb43 100644 --- a/MediaBrowser.Model/Cryptography/ICryptoProvider.cs +++ b/MediaBrowser.Model/Cryptography/ICryptoProvider.cs @@ -6,9 +6,14 @@ namespace MediaBrowser.Model.Cryptography { public interface ICryptoProvider { + string DefaultHashMethod { get; } + [Obsolete("Use System.Security.Cryptography.MD5 directly")] Guid GetMD5(string str); + [Obsolete("Use System.Security.Cryptography.MD5 directly")] byte[] ComputeMD5(Stream str); + [Obsolete("Use System.Security.Cryptography.MD5 directly")] byte[] ComputeMD5(byte[] bytes); + [Obsolete("Use System.Security.Cryptography.SHA1 directly")] byte[] ComputeSHA1(byte[] bytes); IEnumerable GetSupportedHashMethods(); byte[] ComputeHash(string HashMethod, byte[] bytes); @@ -17,6 +22,5 @@ namespace MediaBrowser.Model.Cryptography byte[] ComputeHashWithDefaultMethod(byte[] bytes, byte[] salt); byte[] ComputeHash(PasswordHash hash); byte[] GenerateSalt(); - string DefaultHashMethod { get; } } } diff --git a/MediaBrowser.Model/Cryptography/PasswordHash.cs b/MediaBrowser.Model/Cryptography/PasswordHash.cs index f15b27d32..df32fdb00 100644 --- a/MediaBrowser.Model/Cryptography/PasswordHash.cs +++ b/MediaBrowser.Model/Cryptography/PasswordHash.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Text; namespace MediaBrowser.Model.Cryptography @@ -16,86 +17,71 @@ namespace MediaBrowser.Model.Cryptography private Dictionary _parameters = new Dictionary(); - private string _salt; + private byte[] _salt; - private byte[] _saltBytes; - - private string _hash; - - private byte[] _hashBytes; - - public string Id { get => _id; set => _id = value; } - - public Dictionary Parameters { get => _parameters; set => _parameters = value; } - - public string Salt { get => _salt; set => _salt = value; } - - public byte[] SaltBytes { get => _saltBytes; set => _saltBytes = value; } - - public string Hash { get => _hash; set => _hash = value; } - - public byte[] HashBytes { get => _hashBytes; set => _hashBytes = value; } + private byte[] _hash; public PasswordHash(string storageString) { string[] splitted = storageString.Split('$'); - _id = splitted[1]; - if (splitted[2].Contains("=")) + // The string should at least contain the hash function and the hash itself + if (splitted.Length < 3) + { + throw new ArgumentException("String doesn't contain enough segments", nameof(storageString)); + } + + // Start at 1, the first index shouldn't contain any data + int index = 1; + + // Name of the hash function + _id = splitted[index++]; + + // Optional parameters + if (splitted[index].IndexOf('=') != -1) { - foreach (string paramset in (splitted[2].Split(','))) + foreach (string paramset in splitted[index++].Split(',')) { - if (!string.IsNullOrEmpty(paramset)) + if (string.IsNullOrEmpty(paramset)) { - string[] fields = paramset.Split('='); - if (fields.Length == 2) - { - _parameters.Add(fields[0], fields[1]); - } - else - { - throw new Exception($"Malformed parameter in password hash string {paramset}"); - } + continue; } + + string[] fields = paramset.Split('='); + if (fields.Length != 2) + { + throw new InvalidDataException($"Malformed parameter in password hash string {paramset}"); + } + + _parameters.Add(fields[0], fields[1]); } - if (splitted.Length == 5) - { - _salt = splitted[3]; - _saltBytes = ConvertFromByteString(_salt); - _hash = splitted[4]; - _hashBytes = ConvertFromByteString(_hash); - } - else - { - _salt = string.Empty; - _hash = splitted[3]; - _hashBytes = ConvertFromByteString(_hash); - } + } + + // Check if the string also contains a salt + if (splitted.Length - index == 2) + { + _salt = ConvertFromByteString(splitted[index++]); + _hash = ConvertFromByteString(splitted[index++]); } else { - if (splitted.Length == 4) - { - _salt = splitted[2]; - _saltBytes = ConvertFromByteString(_salt); - _hash = splitted[3]; - _hashBytes = ConvertFromByteString(_hash); - } - else - { - _salt = string.Empty; - _hash = splitted[2]; - _hashBytes = ConvertFromByteString(_hash); - } - + _salt = Array.Empty(); + _hash = ConvertFromByteString(splitted[index++]); } - } + public string Id { get => _id; set => _id = value; } + + public Dictionary Parameters { get => _parameters; set => _parameters = value; } + + public byte[] Salt { get => _salt; set => _salt = value; } + + public byte[] Hash { get => _hash; set => _hash = value; } + public PasswordHash(ICryptoProvider cryptoProvider) { _id = cryptoProvider.DefaultHashMethod; - _saltBytes = cryptoProvider.GenerateSalt(); - _salt = ConvertToByteString(SaltBytes); + _salt = cryptoProvider.GenerateSalt(); + _hash = Array.Empty(); } public static byte[] ConvertFromByteString(string byteString) @@ -111,43 +97,45 @@ namespace MediaBrowser.Model.Cryptography } public static string ConvertToByteString(byte[] bytes) - { - return BitConverter.ToString(bytes).Replace("-", ""); - } + => BitConverter.ToString(bytes).Replace("-", string.Empty); - private string SerializeParameters() + private void SerializeParameters(StringBuilder stringBuilder) { - string returnString = string.Empty; - foreach (var KVP in _parameters) + if (_parameters.Count == 0) { - returnString += $",{KVP.Key}={KVP.Value}"; + return; } - if ((!string.IsNullOrEmpty(returnString)) && returnString[0] == ',') + stringBuilder.Append('$'); + foreach (var pair in _parameters) { - returnString = returnString.Remove(0, 1); + stringBuilder.Append(pair.Key); + stringBuilder.Append('='); + stringBuilder.Append(pair.Value); + stringBuilder.Append(','); } - return returnString; + // Remove last ',' + stringBuilder.Length -= 1; } public override string ToString() { - string outString = "$" + _id; - string paramstring = SerializeParameters(); - if (!string.IsNullOrEmpty(paramstring)) - { - outString += $"${paramstring}"; - } + var str = new StringBuilder(); + str.Append('$'); + str.Append(_id); + SerializeParameters(str); - if (!string.IsNullOrEmpty(_salt)) + if (_salt.Length == 0) { - outString += $"${_salt}"; + str.Append('$'); + str.Append(ConvertToByteString(_salt)); } - outString += $"${_hash}"; - return outString; + str.Append('$'); + str.Append(ConvertToByteString(_hash)); + + return str.ToString(); } } - } -- cgit v1.2.3 From ab7ef9c9cb951111eec5132e9c6a00aa0d1b111b Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 6 Jul 2019 16:15:38 +0200 Subject: Fix style issues --- .../Library/CoreResolutionIgnoreRule.cs | 69 +++++----------------- .../Library/Resolvers/Movies/MovieResolver.cs | 27 +++++---- .../Resolvers/IResolverIgnoreRule.cs | 6 ++ 3 files changed, 35 insertions(+), 67 deletions(-) (limited to 'Emby.Server.Implementations/Library') diff --git a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs index a70077163..f1ae2fc9c 100644 --- a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs +++ b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs @@ -5,7 +5,6 @@ using System.Text.RegularExpressions; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Resolvers; -using MediaBrowser.Model.Extensions; using MediaBrowser.Model.IO; namespace Emby.Server.Implementations.Library @@ -17,12 +16,10 @@ namespace Emby.Server.Implementations.Library { private readonly ILibraryManager _libraryManager; - private bool _ignoreDotPrefix; - /// - /// Any folder named in this list will be ignored - can be added to at runtime for extensibility + /// Any folder named in this list will be ignored /// - public static readonly string[] IgnoreFolders = + private static readonly string[] _ignoreFolders = { "metadata", "ps3_update", @@ -43,25 +40,14 @@ namespace Emby.Server.Implementations.Library "$RECYCLE.BIN", "System Volume Information", ".grab", - - // macos - ".AppleDouble" - }; public CoreResolutionIgnoreRule(ILibraryManager libraryManager) { _libraryManager = libraryManager; - - _ignoreDotPrefix = Environment.OSVersion.Platform != PlatformID.Win32NT; } - /// - /// Shoulds the ignore. - /// - /// The file information. - /// The parent. - /// true if XXXX, false otherwise + /// public bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem parent) { // Don't ignore top level folders @@ -73,46 +59,17 @@ namespace Emby.Server.Implementations.Library var filename = fileInfo.Name; var path = fileInfo.FullName; - // Handle mac .DS_Store - // https://github.com/MediaBrowser/MediaBrowser/issues/427 - if (_ignoreDotPrefix) + // Ignore hidden files on UNIX + if (Environment.OSVersion.Platform != PlatformID.Win32NT + && filename[0] == '.') { - if (filename.IndexOf('.') == 0) - { - return true; - } + return true; } - // Ignore hidden files and folders - //if (fileInfo.IsHidden) - //{ - // if (parent == null) - // { - // var parentFolderName = Path.GetFileName(_fileSystem.GetDirectoryName(path)); - - // if (string.Equals(parentFolderName, BaseItem.ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase)) - // { - // return false; - // } - // if (string.Equals(parentFolderName, BaseItem.ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase)) - // { - // return false; - // } - // } - - // // Sometimes these are marked hidden - // if (_fileSystem.IsRootPath(path)) - // { - // return false; - // } - - // return true; - //} - if (fileInfo.IsDirectory) { // Ignore any folders in our list - if (IgnoreFolders.Contains(filename, StringComparer.OrdinalIgnoreCase)) + if (_ignoreFolders.Contains(filename, StringComparer.OrdinalIgnoreCase)) { return true; } @@ -120,8 +77,9 @@ namespace Emby.Server.Implementations.Library if (parent != null) { // Ignore trailer folders but allow it at the collection level - if (string.Equals(filename, BaseItem.TrailerFolderName, StringComparison.OrdinalIgnoreCase) && - !(parent is AggregateFolder) && !(parent is UserRootFolder)) + if (string.Equals(filename, BaseItem.TrailerFolderName, StringComparison.OrdinalIgnoreCase) + && !(parent is AggregateFolder) + && !(parent is UserRootFolder)) { return true; } @@ -142,14 +100,15 @@ namespace Emby.Server.Implementations.Library if (parent != null) { // Don't resolve these into audio files - if (string.Equals(Path.GetFileNameWithoutExtension(filename), BaseItem.ThemeSongFilename) && _libraryManager.IsAudioFile(filename)) + if (string.Equals(Path.GetFileNameWithoutExtension(filename), BaseItem.ThemeSongFilename) + && _libraryManager.IsAudioFile(filename)) { return true; } } // Ignore samples - Match m = Regex.Match(filename,@"\bsample\b",RegexOptions.IgnoreCase); + Match m = Regex.Match(filename, @"\bsample\b", RegexOptions.IgnoreCase); return m.Success; } diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index 47c3e71d7..1b63b00a3 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -12,7 +12,6 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Resolvers; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Extensions; using MediaBrowser.Model.IO; namespace Emby.Server.Implementations.Library.Resolvers.Movies @@ -28,7 +27,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies /// The priority. public override ResolverPriority Priority => ResolverPriority.Third; - public MultiItemResolverResult ResolveMultiple(Folder parent, + public MultiItemResolverResult ResolveMultiple( + Folder parent, List files, string collectionType, IDirectoryService directoryService) @@ -46,7 +46,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies return result; } - private MultiItemResolverResult ResolveMultipleInternal(Folder parent, + private MultiItemResolverResult ResolveMultipleInternal( + Folder parent, List files, string collectionType, IDirectoryService directoryService) @@ -91,7 +92,13 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies return null; } - private MultiItemResolverResult ResolveVideos(Folder parent, IEnumerable fileSystemEntries, IDirectoryService directoryService, bool suppportMultiEditions, string collectionType, bool parseName) + private MultiItemResolverResult ResolveVideos( + Folder parent, + IEnumerable fileSystemEntries, + IDirectoryService directoryService, + bool suppportMultiEditions, + string collectionType, + bool parseName) where T : Video, new() { var files = new List(); @@ -104,8 +111,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies // This is a hack but currently no better way to resolve a sometimes ambiguous situation if (string.IsNullOrEmpty(collectionType)) { - if (string.Equals(child.Name, "tvshow.nfo", StringComparison.OrdinalIgnoreCase) || - string.Equals(child.Name, "season.nfo", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(child.Name, "tvshow.nfo", StringComparison.OrdinalIgnoreCase) + || string.Equals(child.Name, "season.nfo", StringComparison.OrdinalIgnoreCase)) { return null; } @@ -115,11 +122,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies { leftOver.Add(child); } - else if (IsIgnored(child.Name)) - { - - } - else + else if (!IsIgnored(child.Name)) { files.Add(child); } @@ -168,7 +171,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies private static bool IsIgnored(string filename) { // Ignore samples - Match m = Regex.Match(filename,@"\bsample\b",RegexOptions.IgnoreCase); + Match m = Regex.Match(filename, @"\bsample\b", RegexOptions.IgnoreCase); return m.Success; } diff --git a/MediaBrowser.Controller/Resolvers/IResolverIgnoreRule.cs b/MediaBrowser.Controller/Resolvers/IResolverIgnoreRule.cs index b40cc157a..bb80e6025 100644 --- a/MediaBrowser.Controller/Resolvers/IResolverIgnoreRule.cs +++ b/MediaBrowser.Controller/Resolvers/IResolverIgnoreRule.cs @@ -8,6 +8,12 @@ namespace MediaBrowser.Controller.Resolvers /// public interface IResolverIgnoreRule { + /// + /// Checks whether or not the file should be ignored. + /// + /// The file information. + /// The parent BaseItem. + /// True if the file should be ignored. bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem parent); } } -- cgit v1.2.3 From 6032f31aa660e3b0fe1936217109f9fb47853ba3 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Thu, 28 Feb 2019 23:22:57 +0100 Subject: Use CultureInvariant string conversion for Guids --- Emby.Dlna/Didl/DidlBuilder.cs | 4 ++-- Emby.Dlna/DlnaManager.cs | 5 ++-- Emby.Dlna/Eventing/EventManager.cs | 2 +- Emby.Dlna/Main/DlnaEntryPoint.cs | 3 ++- Emby.Dlna/PlayTo/PlayToManager.cs | 5 ++-- Emby.Drawing/ImageProcessor.cs | 6 ++--- Emby.Notifications/NotificationManager.cs | 5 ++-- .../Activity/ActivityLogEntryPoint.cs | 4 ++-- .../Activity/ActivityRepository.cs | 4 ++-- Emby.Server.Implementations/ApplicationHost.cs | 2 +- .../Channels/ChannelManager.cs | 17 +++++++------- .../Collections/CollectionManager.cs | 3 ++- .../Data/SqliteDisplayPreferencesRepository.cs | 3 ++- .../Data/SqliteItemRepository.cs | 19 +++++++-------- Emby.Server.Implementations/Devices/DeviceId.cs | 4 ++-- .../Devices/DeviceManager.cs | 4 ++-- Emby.Server.Implementations/Dto/DtoService.cs | 17 +++++++------- .../EntryPoints/LibraryChangedNotifier.cs | 16 ++++++------- .../EntryPoints/ServerEventNotifier.cs | 3 ++- .../EntryPoints/UserDataChangeNotifier.cs | 6 ++--- .../HttpClientManager/HttpClientManager.cs | 2 +- .../IO/ManagedFileSystem.cs | 3 ++- .../Images/BaseDynamicImageProvider.cs | 3 ++- .../Library/ExclusiveLiveStream.cs | 3 ++- .../Library/LibraryManager.cs | 14 +++++------ .../Library/LiveStreamHelper.cs | 2 +- .../Library/MediaSourceManager.cs | 6 ++--- .../Library/UserDataManager.cs | 2 +- .../Library/UserViewManager.cs | 9 ++++---- .../Library/Validators/ArtistsValidator.cs | 3 ++- .../Library/Validators/PeopleValidator.cs | 4 ++-- .../Library/Validators/StudiosValidator.cs | 3 ++- .../LiveTv/EmbyTV/EmbyTV.cs | 8 +++---- .../LiveTv/Listings/XmlTvListingsProvider.cs | 4 ++-- .../LiveTv/LiveTvDtoService.cs | 27 +++++++++++----------- .../LiveTv/LiveTvManager.cs | 23 +++++++++--------- .../LiveTv/LiveTvMediaSourceProvider.cs | 3 ++- .../LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs | 3 ++- .../LiveTv/TunerHosts/LiveStream.cs | 3 ++- .../LiveTv/TunerHosts/M3UTunerHost.cs | 7 +++--- .../LiveTv/TunerHosts/M3uParser.cs | 4 ++-- .../Playlists/PlaylistManager.cs | 7 +++--- .../ScheduledTasks/ScheduledTaskWorker.cs | 4 ++-- .../Security/AuthenticationRepository.cs | 2 +- .../Serialization/JsonSerializer.cs | 3 ++- .../Session/HttpSessionController.cs | 2 +- .../Session/SessionManager.cs | 14 ++++++----- Emby.Server.Implementations/TV/TVSeriesManager.cs | 3 ++- MediaBrowser.Api/Images/ImageService.cs | 3 ++- .../Library/LibraryStructureService.cs | 3 ++- MediaBrowser.Api/Movies/MoviesService.cs | 6 ++--- MediaBrowser.Api/Playback/BaseStreamingService.cs | 8 ++++++- MediaBrowser.Api/Playback/MediaInfoService.cs | 3 ++- MediaBrowser.Api/SearchService.cs | 5 ++-- MediaBrowser.Api/Session/SessionsService.cs | 3 ++- MediaBrowser.Api/Subtitles/SubtitleService.cs | 4 ++-- MediaBrowser.Api/TvShowsService.cs | 3 ++- MediaBrowser.Api/UserLibrary/ItemsService.cs | 2 +- MediaBrowser.Api/UserLibrary/UserViewsService.cs | 3 ++- MediaBrowser.Api/VideosService.cs | 3 ++- MediaBrowser.Controller/Channels/Channel.cs | 7 +++--- MediaBrowser.Controller/Entities/BaseItem.cs | 14 +++++------ MediaBrowser.Controller/Entities/Folder.cs | 5 ++-- MediaBrowser.Controller/Entities/LinkedChild.cs | 5 ++-- MediaBrowser.Controller/Entities/TV/Series.cs | 3 ++- MediaBrowser.Controller/Entities/User.cs | 3 ++- .../Entities/UserViewBuilder.cs | 3 ++- MediaBrowser.Controller/LiveTv/LiveTvChannel.cs | 4 ++-- MediaBrowser.Controller/LiveTv/LiveTvProgram.cs | 3 ++- MediaBrowser.Controller/Playlists/Playlist.cs | 3 ++- .../Providers/MetadataResult.cs | 3 ++- MediaBrowser.Providers/Manager/ProviderManager.cs | 8 +++---- .../MediaInfo/AudioImageProvider.cs | 7 +++--- .../Subtitles/SubtitleManager.cs | 5 ++-- jellyfin.ruleset | 4 ++++ 75 files changed, 240 insertions(+), 186 deletions(-) (limited to 'Emby.Server.Implementations/Library') diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs index a21aff9f9..26adfde83 100644 --- a/Emby.Dlna/Didl/DidlBuilder.cs +++ b/Emby.Dlna/Didl/DidlBuilder.cs @@ -1082,7 +1082,7 @@ namespace Emby.Dlna.Didl public static string GetClientId(Guid idValue, StubType? stubType) { - var id = idValue.ToString("N"); + var id = idValue.ToString("N", CultureInfo.InvariantCulture); if (stubType.HasValue) { @@ -1096,7 +1096,7 @@ namespace Emby.Dlna.Didl { var url = string.Format("{0}/Items/{1}/Images/{2}/0/{3}/{4}/{5}/{6}/0/0", _serverAddress, - info.ItemId.ToString("N"), + info.ItemId.ToString("N", CultureInfo.InvariantCulture), info.Type, info.ImageTag, format, diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index 2b76d2702..d5d788021 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Reflection; @@ -300,7 +301,7 @@ namespace Emby.Dlna profile = ReserializeProfile(tempProfile); - profile.Id = path.ToLowerInvariant().GetMD5().ToString("N"); + profile.Id = path.ToLowerInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture); _profiles[path] = new Tuple(GetInternalProfileInfo(_fileSystem.GetFileInfo(path), type), profile); @@ -352,7 +353,7 @@ namespace Emby.Dlna Info = new DeviceProfileInfo { - Id = file.FullName.ToLowerInvariant().GetMD5().ToString("N"), + Id = file.FullName.ToLowerInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture), Name = _fileSystem.GetFileNameWithoutExtension(file), Type = type } diff --git a/Emby.Dlna/Eventing/EventManager.cs b/Emby.Dlna/Eventing/EventManager.cs index b4ff3ec1d..4b542a820 100644 --- a/Emby.Dlna/Eventing/EventManager.cs +++ b/Emby.Dlna/Eventing/EventManager.cs @@ -55,7 +55,7 @@ namespace Emby.Dlna.Eventing public EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl) { var timeout = ParseTimeout(requestedTimeoutString) ?? 300; - var id = "uuid:" + Guid.NewGuid().ToString("N"); + var id = "uuid:" + Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); // Remove logging for now because some devices are sending this very frequently // TODO re-enable with dlna debug logging setting diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs index 206a873e1..77bde0ca2 100644 --- a/Emby.Dlna/Main/DlnaEntryPoint.cs +++ b/Emby.Dlna/Main/DlnaEntryPoint.cs @@ -1,5 +1,6 @@ using System; using System.Net.Sockets; +using System.Globalization; using System.Threading; using System.Threading.Tasks; using Emby.Dlna.PlayTo; @@ -307,7 +308,7 @@ namespace Emby.Dlna.Main { guid = text.GetMD5(); } - return guid.ToString("N"); + return guid.ToString("N", CultureInfo.InvariantCulture); } private void SetProperies(SsdpDevice device, string fullDeviceType) diff --git a/Emby.Dlna/PlayTo/PlayToManager.cs b/Emby.Dlna/PlayTo/PlayToManager.cs index c0a441871..a3a013096 100644 --- a/Emby.Dlna/PlayTo/PlayToManager.cs +++ b/Emby.Dlna/PlayTo/PlayToManager.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Linq; using System.Net; using System.Threading; @@ -141,7 +142,7 @@ namespace Emby.Dlna.PlayTo return usn; } - return usn.GetMD5().ToString("N"); + return usn.GetMD5().ToString("N", CultureInfo.InvariantCulture); } private async Task AddDevice(UpnpDeviceInfo info, string location, CancellationToken cancellationToken) @@ -156,7 +157,7 @@ namespace Emby.Dlna.PlayTo } else { - uuid = location.GetMD5().ToString("N"); + uuid = location.GetMD5().ToString("N", CultureInfo.InvariantCulture); } var sessionInfo = _sessionManager.LogSessionActivity("DLNA", _appHost.ApplicationVersion, uuid, null, uri.OriginalString, null); diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs index 6d209d8d0..a7d95eb20 100644 --- a/Emby.Drawing/ImageProcessor.cs +++ b/Emby.Drawing/ImageProcessor.cs @@ -454,14 +454,14 @@ namespace Emby.Drawing // Optimization if (imageEnhancers.Length == 0) { - return (originalImagePath + dateModified.Ticks).GetMD5().ToString("N"); + return (originalImagePath + dateModified.Ticks).GetMD5().ToString("N", CultureInfo.InvariantCulture); } // Cache name is created with supported enhancers combined with the last config change so we pick up new config changes var cacheKeys = imageEnhancers.Select(i => i.GetConfigurationCacheKey(item, imageType)).ToList(); cacheKeys.Add(originalImagePath + dateModified.Ticks); - return string.Join("|", cacheKeys).GetMD5().ToString("N"); + return string.Join("|", cacheKeys).GetMD5().ToString("N", CultureInfo.InvariantCulture); } private async Task<(string path, DateTime dateModified)> GetSupportedImage(string originalImagePath, DateTime dateModified) @@ -480,7 +480,7 @@ namespace Emby.Drawing { try { - string filename = (originalImagePath + dateModified.Ticks.ToString(UsCulture)).GetMD5().ToString("N"); + string filename = (originalImagePath + dateModified.Ticks.ToString(UsCulture)).GetMD5().ToString("N", CultureInfo.InvariantCulture); string cacheExtension = _mediaEncoder().SupportsEncoder("libwebp") ? ".webp" : ".png"; var outputPath = Path.Combine(_appPaths.ImageCachePath, "converted-images", filename + cacheExtension); diff --git a/Emby.Notifications/NotificationManager.cs b/Emby.Notifications/NotificationManager.cs index 3d1d4722d..a767e541e 100644 --- a/Emby.Notifications/NotificationManager.cs +++ b/Emby.Notifications/NotificationManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -101,7 +102,7 @@ namespace Emby.Notifications var config = GetConfiguration(); return _userManager.Users - .Where(i => config.IsEnabledToSendToUser(request.NotificationType, i.Id.ToString("N"), i.Policy)) + .Where(i => config.IsEnabledToSendToUser(request.NotificationType, i.Id.ToString("N", CultureInfo.InvariantCulture), i.Policy)) .Select(i => i.Id); } @@ -197,7 +198,7 @@ namespace Emby.Notifications return _services.Select(i => new NameIdPair { Name = i.Name, - Id = i.Name.GetMD5().ToString("N") + Id = i.Name.GetMD5().ToString("N", CultureInfo.InvariantCulture) }).OrderBy(i => i.Name); } diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs index 0530a251c..3e44c9c0a 100644 --- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs +++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -75,7 +76,6 @@ namespace Emby.Server.Implementations.Activity _sessionManager.AuthenticationFailed += OnAuthenticationFailed; _sessionManager.AuthenticationSucceeded += OnAuthenticationSucceeded; _sessionManager.SessionEnded += OnSessionEnded; - _sessionManager.PlaybackStart += OnPlaybackStart; _sessionManager.PlaybackStopped += OnPlaybackStopped; @@ -117,7 +117,7 @@ namespace Emby.Server.Implementations.Activity { Name = string.Format(_localization.GetLocalizedString("SubtitleDownloadFailureFromForItem"), e.Provider, Notifications.Notifications.GetItemName(e.Item)), Type = "SubtitleDownloadFailure", - ItemId = e.Item.Id.ToString("N"), + ItemId = e.Item.Id.ToString("N", CultureInfo.InvariantCulture), ShortOverview = e.Exception.Message }); } diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs index de46ab965..91371b833 100644 --- a/Emby.Server.Implementations/Activity/ActivityRepository.cs +++ b/Emby.Server.Implementations/Activity/ActivityRepository.cs @@ -102,7 +102,7 @@ namespace Emby.Server.Implementations.Activity } else { - statement.TryBind("@UserId", entry.UserId.ToString("N")); + statement.TryBind("@UserId", entry.UserId.ToString("N", CultureInfo.InvariantCulture)); } statement.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue()); @@ -141,7 +141,7 @@ namespace Emby.Server.Implementations.Activity } else { - statement.TryBind("@UserId", entry.UserId.ToString("N")); + statement.TryBind("@UserId", entry.UserId.ToString("N", CultureInfo.InvariantCulture)); } statement.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue()); diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index ef2f59d30..4ff90a04b 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1220,7 +1220,7 @@ namespace Emby.Server.Implementations // Generate self-signed cert var certHost = GetHostnameFromExternalDns(ServerConfigurationManager.Configuration.WanDdns); - var certPath = Path.Combine(ServerConfigurationManager.ApplicationPaths.ProgramDataPath, "ssl", "cert_" + (certHost + "2").GetMD5().ToString("N") + ".pfx"); + var certPath = Path.Combine(ServerConfigurationManager.ApplicationPaths.ProgramDataPath, "ssl", "cert_" + (certHost + "2").GetMD5().ToString("N", CultureInfo.InvariantCulture) + ".pfx"); const string Password = "embycert"; return new CertificateInfo diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index e9961e8bd..8e5f5b561 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Threading; @@ -206,7 +207,7 @@ namespace Emby.Server.Implementations.Channels try { - return GetChannelProvider(i).IsEnabledFor(user.Id.ToString("N")); + return GetChannelProvider(i).IsEnabledFor(user.Id.ToString("N", CultureInfo.InvariantCulture)); } catch { @@ -511,7 +512,7 @@ namespace Emby.Server.Implementations.Channels IncludeItemTypes = new[] { typeof(Channel).Name }, OrderBy = new ValueTuple[] { new ValueTuple(ItemSortBy.SortName, SortOrder.Ascending) } - }).Select(i => GetChannelFeatures(i.ToString("N"))).ToArray(); + }).Select(i => GetChannelFeatures(i.ToString("N", CultureInfo.InvariantCulture))).ToArray(); } public ChannelFeatures GetChannelFeatures(string id) @@ -552,7 +553,7 @@ namespace Emby.Server.Implementations.Channels SupportsSortOrderToggle = features.SupportsSortOrderToggle, SupportsLatestMedia = supportsLatest, Name = channel.Name, - Id = channel.Id.ToString("N"), + Id = channel.Id.ToString("N", CultureInfo.InvariantCulture), SupportsContentDownloading = features.SupportsContentDownloading, AutoRefreshLevels = features.AutoRefreshLevels }; @@ -740,7 +741,7 @@ namespace Emby.Server.Implementations.Channels bool sortDescending, CancellationToken cancellationToken) { - var userId = user == null ? null : user.Id.ToString("N"); + var userId = user == null ? null : user.Id.ToString("N", CultureInfo.InvariantCulture); var cacheLength = CacheLength; var cachePath = GetChannelDataCachePath(channel, userId, externalFolderId, sortField, sortDescending); @@ -836,7 +837,7 @@ namespace Emby.Server.Implementations.Channels ChannelItemSortField? sortField, bool sortDescending) { - var channelId = GetInternalChannelId(channel.Name).ToString("N"); + var channelId = GetInternalChannelId(channel.Name).ToString("N", CultureInfo.InvariantCulture); var userCacheKey = string.Empty; @@ -846,10 +847,10 @@ namespace Emby.Server.Implementations.Channels userCacheKey = hasCacheKey.GetCacheKey(userId) ?? string.Empty; } - var filename = string.IsNullOrEmpty(externalFolderId) ? "root" : externalFolderId.GetMD5().ToString("N"); + var filename = string.IsNullOrEmpty(externalFolderId) ? "root" : externalFolderId.GetMD5().ToString("N", CultureInfo.InvariantCulture); filename += userCacheKey; - var version = ((channel.DataVersion ?? string.Empty) + "2").GetMD5().ToString("N"); + var version = ((channel.DataVersion ?? string.Empty) + "2").GetMD5().ToString("N", CultureInfo.InvariantCulture); if (sortField.HasValue) { @@ -860,7 +861,7 @@ namespace Emby.Server.Implementations.Channels filename += "-sortDescending"; } - filename = filename.GetMD5().ToString("N"); + filename = filename.GetMD5().ToString("N", CultureInfo.InvariantCulture); return Path.Combine(_config.ApplicationPaths.CachePath, "channels", diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs index 2b99e0ddf..bb5057b1c 100644 --- a/Emby.Server.Implementations/Collections/CollectionManager.cs +++ b/Emby.Server.Implementations/Collections/CollectionManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Threading; @@ -182,7 +183,7 @@ namespace Emby.Server.Implementations.Collections public void AddToCollection(Guid collectionId, IEnumerable ids) { - AddToCollection(collectionId, ids.Select(i => i.ToString("N")), true, new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem))); + AddToCollection(collectionId, ids.Select(i => i.ToString("N", CultureInfo.InvariantCulture)), true, new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem))); } private void AddToCollection(Guid collectionId, IEnumerable ids, bool fireEvent, MetadataRefreshOptions refreshOptions) diff --git a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs index 01ef9851d..87096e72f 100644 --- a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Threading; using MediaBrowser.Common.Configuration; @@ -182,7 +183,7 @@ namespace Emby.Server.Implementations.Data return new DisplayPreferences { - Id = guidId.ToString("N") + Id = guidId.ToString("N", CultureInfo.InvariantCulture) }; } } diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 1cefcec7c..bb4c34f02 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -696,7 +696,7 @@ namespace Emby.Server.Implementations.Data saveItemStatement.TryBindNull("@EndDate"); } - saveItemStatement.TryBind("@ChannelId", item.ChannelId.Equals(Guid.Empty) ? null : item.ChannelId.ToString("N")); + saveItemStatement.TryBind("@ChannelId", item.ChannelId.Equals(Guid.Empty) ? null : item.ChannelId.ToString("N", CultureInfo.InvariantCulture)); if (item is IHasProgramAttributes hasProgramAttributes) { @@ -851,7 +851,7 @@ namespace Emby.Server.Implementations.Data } else { - saveItemStatement.TryBind("@TopParentId", topParent.Id.ToString("N")); + saveItemStatement.TryBind("@TopParentId", topParent.Id.ToString("N", CultureInfo.InvariantCulture)); } if (item is Trailer trailer && trailer.TrailerTypes.Length > 0) @@ -3548,12 +3548,12 @@ namespace Emby.Server.Implementations.Data whereClauses.Add("ChannelId=@ChannelId"); if (statement != null) { - statement.TryBind("@ChannelId", query.ChannelIds[0].ToString("N")); + statement.TryBind("@ChannelId", query.ChannelIds[0].ToString("N", CultureInfo.InvariantCulture)); } } else if (query.ChannelIds.Length > 1) { - var inClause = string.Join(",", query.ChannelIds.Select(i => "'" + i.ToString("N") + "'")); + var inClause = string.Join(",", query.ChannelIds.Select(i => "'" + i.ToString("N", CultureInfo.InvariantCulture) + "'")); whereClauses.Add($"ChannelId in ({inClause})"); } @@ -4537,12 +4537,12 @@ namespace Emby.Server.Implementations.Data } if (statement != null) { - statement.TryBind("@TopParentId", queryTopParentIds[0].ToString("N")); + statement.TryBind("@TopParentId", queryTopParentIds[0].ToString("N", CultureInfo.InvariantCulture)); } } else if (queryTopParentIds.Length > 1) { - var val = string.Join(",", queryTopParentIds.Select(i => "'" + i.ToString("N") + "'")); + var val = string.Join(",", queryTopParentIds.Select(i => "'" + i.ToString("N", CultureInfo.InvariantCulture) + "'")); if (enableItemsByName && includedItemByNameTypes.Count == 1) { @@ -4574,7 +4574,7 @@ namespace Emby.Server.Implementations.Data } if (query.AncestorIds.Length > 1) { - var inClause = string.Join(",", query.AncestorIds.Select(i => "'" + i.ToString("N") + "'")); + var inClause = string.Join(",", query.AncestorIds.Select(i => "'" + i.ToString("N", CultureInfo.InvariantCulture) + "'")); whereClauses.Add(string.Format("Guid in (select itemId from AncestorIds where AncestorIdText in ({0}))", inClause)); } if (!string.IsNullOrWhiteSpace(query.AncestorWithPresentationUniqueKey)) @@ -4637,7 +4637,7 @@ namespace Emby.Server.Implementations.Data foreach (var folderId in query.BoxSetLibraryFolders) { - folderIdQueries.Add("data like '%" + folderId.ToString("N") + "%'"); + folderIdQueries.Add("data like '%" + folderId.ToString("N", CultureInfo.InvariantCulture) + "%'"); } whereClauses.Add("(" + string.Join(" OR ", folderIdQueries) + ")"); @@ -5161,7 +5161,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type var ancestorId = ancestorIds[i]; statement.TryBind("@AncestorId" + index, ancestorId.ToGuidBlob()); - statement.TryBind("@AncestorIdText" + index, ancestorId.ToString("N")); + statement.TryBind("@AncestorIdText" + index, ancestorId.ToString("N", CultureInfo.InvariantCulture)); } statement.Reset(); @@ -5579,6 +5579,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type { counts.TrailerCount = value; } + counts.ItemCount += value; } diff --git a/Emby.Server.Implementations/Devices/DeviceId.cs b/Emby.Server.Implementations/Devices/DeviceId.cs index 495c3436a..7344dc72f 100644 --- a/Emby.Server.Implementations/Devices/DeviceId.cs +++ b/Emby.Server.Implementations/Devices/DeviceId.cs @@ -1,8 +1,8 @@ using System; +using System.Globalization; using System.IO; using System.Text; using MediaBrowser.Common.Configuration; -using MediaBrowser.Model.IO; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Devices @@ -67,7 +67,7 @@ namespace Emby.Server.Implementations.Devices private static string GetNewId() { - return Guid.NewGuid().ToString("N"); + return Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); } private string GetDeviceId() diff --git a/Emby.Server.Implementations/Devices/DeviceManager.cs b/Emby.Server.Implementations/Devices/DeviceManager.cs index 7d6529a67..d1704b373 100644 --- a/Emby.Server.Implementations/Devices/DeviceManager.cs +++ b/Emby.Server.Implementations/Devices/DeviceManager.cs @@ -1,11 +1,11 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Entities; @@ -195,7 +195,7 @@ namespace Emby.Server.Implementations.Devices private string GetDevicePath(string id) { - return Path.Combine(GetDevicesPath(), id.GetMD5().ToString("N")); + return Path.Combine(GetDevicesPath(), id.GetMD5().ToString("N", CultureInfo.InvariantCulture)); } public ContentUploadHistory GetCameraUploadHistory(string deviceId) diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index 2f1b60be9..6e7aa1313 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -213,7 +214,7 @@ namespace Emby.Server.Implementations.Dto if (options.ContainsField(ItemFields.DisplayPreferencesId)) { - dto.DisplayPreferencesId = item.DisplayPreferencesId.ToString("N"); + dto.DisplayPreferencesId = item.DisplayPreferencesId.ToString("N", CultureInfo.InvariantCulture); } if (user != null) @@ -444,7 +445,7 @@ namespace Emby.Server.Implementations.Dto /// item public string GetDtoId(BaseItem item) { - return item.Id.ToString("N"); + return item.Id.ToString("N", CultureInfo.InvariantCulture); } private static void SetBookProperties(BaseItemDto dto, Book item) @@ -608,7 +609,7 @@ namespace Emby.Server.Implementations.Dto if (dictionary.TryGetValue(person.Name, out Person entity)) { baseItemPerson.PrimaryImageTag = GetImageCacheTag(entity, ImageType.Primary); - baseItemPerson.Id = entity.Id.ToString("N"); + baseItemPerson.Id = entity.Id.ToString("N", CultureInfo.InvariantCulture); list.Add(baseItemPerson); } } @@ -893,7 +894,7 @@ namespace Emby.Server.Implementations.Dto //var artistItems = _libraryManager.GetArtists(new InternalItemsQuery //{ // EnableTotalRecordCount = false, - // ItemIds = new[] { item.Id.ToString("N") } + // ItemIds = new[] { item.Id.ToString("N", CultureInfo.InvariantCulture) } //}); //dto.ArtistItems = artistItems.Items @@ -903,7 +904,7 @@ namespace Emby.Server.Implementations.Dto // return new NameIdPair // { // Name = artist.Name, - // Id = artist.Id.ToString("N") + // Id = artist.Id.ToString("N", CultureInfo.InvariantCulture) // }; // }) // .ToList(); @@ -946,7 +947,7 @@ namespace Emby.Server.Implementations.Dto //var artistItems = _libraryManager.GetAlbumArtists(new InternalItemsQuery //{ // EnableTotalRecordCount = false, - // ItemIds = new[] { item.Id.ToString("N") } + // ItemIds = new[] { item.Id.ToString("N", CultureInfo.InvariantCulture) } //}); //dto.AlbumArtists = artistItems.Items @@ -956,7 +957,7 @@ namespace Emby.Server.Implementations.Dto // return new NameIdPair // { // Name = artist.Name, - // Id = artist.Id.ToString("N") + // Id = artist.Id.ToString("N", CultureInfo.InvariantCulture) // }; // }) // .ToList(); @@ -1044,7 +1045,7 @@ namespace Emby.Server.Implementations.Dto } else { - string id = item.Id.ToString("N"); + string id = item.Id.ToString("N", CultureInfo.InvariantCulture); mediaStreams = dto.MediaSources.Where(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase)) .SelectMany(i => i.MediaStreams) .ToArray(); diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index 8369f4f59..9c0db2cf5 100644 --- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs @@ -100,7 +100,7 @@ namespace Emby.Server.Implementations.EntryPoints _lastProgressMessageTimes[item.Id] = DateTime.UtcNow; var dict = new Dictionary(); - dict["ItemId"] = item.Id.ToString("N"); + dict["ItemId"] = item.Id.ToString("N", CultureInfo.InvariantCulture); dict["Progress"] = progress.ToString(CultureInfo.InvariantCulture); try @@ -116,7 +116,7 @@ namespace Emby.Server.Implementations.EntryPoints foreach (var collectionFolder in collectionFolders) { var collectionFolderDict = new Dictionary(); - collectionFolderDict["ItemId"] = collectionFolder.Id.ToString("N"); + collectionFolderDict["ItemId"] = collectionFolder.Id.ToString("N", CultureInfo.InvariantCulture); collectionFolderDict["Progress"] = (collectionFolder.GetRefreshProgress() ?? 0).ToString(CultureInfo.InvariantCulture); try @@ -378,15 +378,15 @@ namespace Emby.Server.Implementations.EntryPoints return new LibraryUpdateInfo { - ItemsAdded = itemsAdded.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToArray(), + ItemsAdded = itemsAdded.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)).Distinct().ToArray(), - ItemsUpdated = itemsUpdated.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToArray(), + ItemsUpdated = itemsUpdated.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)).Distinct().ToArray(), - ItemsRemoved = itemsRemoved.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user, true)).Select(i => i.Id.ToString("N")).Distinct().ToArray(), + ItemsRemoved = itemsRemoved.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user, true)).Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)).Distinct().ToArray(), - FoldersAddedTo = foldersAddedTo.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToArray(), + FoldersAddedTo = foldersAddedTo.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)).Distinct().ToArray(), - FoldersRemovedFrom = foldersRemovedFrom.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToArray(), + FoldersRemovedFrom = foldersRemovedFrom.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)).Distinct().ToArray(), CollectionFolders = GetTopParentIds(newAndRemoved, allUserRootChildren).ToArray() }; @@ -422,7 +422,7 @@ namespace Emby.Server.Implementations.EntryPoints var collectionFolders = _libraryManager.GetCollectionFolders(item, allUserRootChildren); foreach (var folder in allUserRootChildren) { - list.Add(folder.Id.ToString("N")); + list.Add(folder.Id.ToString("N", CultureInfo.InvariantCulture)); } } diff --git a/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs b/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs index 091dd6a45..141e72958 100644 --- a/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Plugins; @@ -134,7 +135,7 @@ namespace Emby.Server.Implementations.EntryPoints /// The e. void userManager_UserDeleted(object sender, GenericEventArgs e) { - SendMessageToUserSession(e.Argument, "UserDeleted", e.Argument.Id.ToString("N")); + SendMessageToUserSession(e.Argument, "UserDeleted", e.Argument.Id.ToString("N", CultureInfo.InvariantCulture)); } void _userManager_UserPolicyUpdated(object sender, GenericEventArgs e) diff --git a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs index a5badacee..bae3422ff 100644 --- a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -8,7 +9,6 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Extensions; using MediaBrowser.Model.Session; using Microsoft.Extensions.Logging; @@ -125,12 +125,12 @@ namespace Emby.Server.Implementations.EntryPoints .Select(i => { var dto = _userDataManager.GetUserDataDto(i, user); - dto.ItemId = i.Id.ToString("N"); + dto.ItemId = i.Id.ToString("N", CultureInfo.InvariantCulture); return dto; }) .ToArray(); - var userIdString = userId.ToString("N"); + var userIdString = userId.ToString("N", CultureInfo.InvariantCulture); return new UserDataChangeInfo { diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs index 331b5e29d..9ca33d7db 100644 --- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs +++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs @@ -195,7 +195,7 @@ namespace Emby.Server.Implementations.HttpClientManager } var url = options.Url; - var urlHash = url.ToLowerInvariant().GetMD5().ToString("N"); + var urlHash = url.ToLowerInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture); var responseCachePath = Path.Combine(_appPaths.CachePath, "httpclient", urlHash); diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index 8517abed6..ae8371a32 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Diagnostics; using System.IO; using System.Linq; @@ -555,7 +556,7 @@ namespace Emby.Server.Implementations.IO throw new ArgumentNullException(nameof(file2)); } - var temp1 = Path.Combine(_tempPath, Guid.NewGuid().ToString("N")); + var temp1 = Path.Combine(_tempPath, Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture)); // Copying over will fail against hidden files SetHidden(file1, false); diff --git a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs index 46f209b4b..d8faceadb 100644 --- a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs +++ b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Threading; @@ -89,7 +90,7 @@ namespace Emby.Server.Implementations.Images ImageType imageType, CancellationToken cancellationToken) { - var outputPathWithoutExtension = Path.Combine(ApplicationPaths.TempDirectory, Guid.NewGuid().ToString("N")); + var outputPathWithoutExtension = Path.Combine(ApplicationPaths.TempDirectory, Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture)); Directory.CreateDirectory(Path.GetDirectoryName(outputPathWithoutExtension)); string outputPath = CreateImage(item, itemsWithImages, outputPathWithoutExtension, imageType, 0); diff --git a/Emby.Server.Implementations/Library/ExclusiveLiveStream.cs b/Emby.Server.Implementations/Library/ExclusiveLiveStream.cs index 45a33a296..a3c879f12 100644 --- a/Emby.Server.Implementations/Library/ExclusiveLiveStream.cs +++ b/Emby.Server.Implementations/Library/ExclusiveLiveStream.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Library; @@ -26,7 +27,7 @@ namespace Emby.Server.Implementations.Library EnableStreamSharing = false; _closeFn = closeFn; ConsumerCount = 1; - UniqueId = Guid.NewGuid().ToString("N"); + UniqueId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); } public Task Close() diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 4b5063ada..30ff855cc 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -1187,12 +1187,12 @@ namespace Emby.Server.Implementations.Library if (libraryFolder != null && libraryFolder.HasImage(ImageType.Primary)) { - info.PrimaryImageItemId = libraryFolder.Id.ToString("N"); + info.PrimaryImageItemId = libraryFolder.Id.ToString("N", CultureInfo.InvariantCulture); } if (libraryFolder != null) { - info.ItemId = libraryFolder.Id.ToString("N"); + info.ItemId = libraryFolder.Id.ToString("N", CultureInfo.InvariantCulture); info.LibraryOptions = GetLibraryOptions(libraryFolder); if (refreshQueue != null) @@ -2135,12 +2135,12 @@ namespace Emby.Server.Implementations.Library string viewType, string sortName) { - var parentIdString = parentId.Equals(Guid.Empty) ? null : parentId.ToString("N"); - var idValues = "38_namedview_" + name + user.Id.ToString("N") + (parentIdString ?? string.Empty) + (viewType ?? string.Empty); + var parentIdString = parentId.Equals(Guid.Empty) ? null : parentId.ToString("N", CultureInfo.InvariantCulture); + var idValues = "38_namedview_" + name + user.Id.ToString("N", CultureInfo.InvariantCulture) + (parentIdString ?? string.Empty) + (viewType ?? string.Empty); var id = GetNewItemId(idValues, typeof(UserView)); - var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N")); + var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N", CultureInfo.InvariantCulture)); var item = GetItemById(id) as UserView; @@ -2271,7 +2271,7 @@ namespace Emby.Server.Implementations.Library throw new ArgumentNullException(nameof(name)); } - var parentIdString = parentId.Equals(Guid.Empty) ? null : parentId.ToString("N"); + var parentIdString = parentId.Equals(Guid.Empty) ? null : parentId.ToString("N", CultureInfo.InvariantCulture); var idValues = "37_namedview_" + name + (parentIdString ?? string.Empty) + (viewType ?? string.Empty); if (!string.IsNullOrEmpty(uniqueId)) { @@ -2280,7 +2280,7 @@ namespace Emby.Server.Implementations.Library var id = GetNewItemId(idValues, typeof(UserView)); - var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N")); + var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N", CultureInfo.InvariantCulture)); var item = GetItemById(id) as UserView; diff --git a/Emby.Server.Implementations/Library/LiveStreamHelper.cs b/Emby.Server.Implementations/Library/LiveStreamHelper.cs index c3082a78a..33e6f2434 100644 --- a/Emby.Server.Implementations/Library/LiveStreamHelper.cs +++ b/Emby.Server.Implementations/Library/LiveStreamHelper.cs @@ -40,7 +40,7 @@ namespace Emby.Server.Implementations.Library var now = DateTime.UtcNow; MediaInfo mediaInfo = null; - var cacheFilePath = string.IsNullOrEmpty(cacheKey) ? null : Path.Combine(_appPaths.CachePath, "mediainfo", cacheKey.GetMD5().ToString("N") + ".json"); + var cacheFilePath = string.IsNullOrEmpty(cacheKey) ? null : Path.Combine(_appPaths.CachePath, "mediainfo", cacheKey.GetMD5().ToString("N", CultureInfo.InvariantCulture) + ".json"); if (!string.IsNullOrEmpty(cacheKey)) { diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index 24ab8e761..d83e1fc02 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -269,7 +269,7 @@ namespace Emby.Server.Implementations.Library private static void SetKeyProperties(IMediaSourceProvider provider, MediaSourceInfo mediaSource) { - var prefix = provider.GetType().FullName.GetMD5().ToString("N") + LiveStreamIdDelimeter; + var prefix = provider.GetType().FullName.GetMD5().ToString("N", CultureInfo.InvariantCulture) + LiveStreamIdDelimeter; if (!string.IsNullOrEmpty(mediaSource.OpenToken) && !mediaSource.OpenToken.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) { @@ -626,7 +626,7 @@ namespace Emby.Server.Implementations.Library var now = DateTime.UtcNow; MediaInfo mediaInfo = null; - var cacheFilePath = string.IsNullOrEmpty(cacheKey) ? null : Path.Combine(_appPaths.CachePath, "mediainfo", cacheKey.GetMD5().ToString("N") + ".json"); + var cacheFilePath = string.IsNullOrEmpty(cacheKey) ? null : Path.Combine(_appPaths.CachePath, "mediainfo", cacheKey.GetMD5().ToString("N", CultureInfo.InvariantCulture) + ".json"); if (!string.IsNullOrEmpty(cacheKey)) { @@ -854,7 +854,7 @@ namespace Emby.Server.Implementations.Library var keys = key.Split(new[] { LiveStreamIdDelimeter }, 2); - var provider = _providers.FirstOrDefault(i => string.Equals(i.GetType().FullName.GetMD5().ToString("N"), keys[0], StringComparison.OrdinalIgnoreCase)); + var provider = _providers.FirstOrDefault(i => string.Equals(i.GetType().FullName.GetMD5().ToString("N", CultureInfo.InvariantCulture), keys[0], StringComparison.OrdinalIgnoreCase)); var splitIndex = key.IndexOf(LiveStreamIdDelimeter); var keyId = key.Substring(splitIndex + 1); diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs index dfa1edaff..36adc0b9c 100644 --- a/Emby.Server.Implementations/Library/UserDataManager.cs +++ b/Emby.Server.Implementations/Library/UserDataManager.cs @@ -152,7 +152,7 @@ namespace Emby.Server.Implementations.Library /// System.String. private static string GetCacheKey(long internalUserId, Guid itemId) { - return internalUserId.ToString(CultureInfo.InvariantCulture) + "-" + itemId.ToString("N"); + return internalUserId.ToString(CultureInfo.InvariantCulture) + "-" + itemId.ToString("N", CultureInfo.InvariantCulture); } public UserItemData GetUserData(User user, BaseItem item) diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs index e9ce682ee..71f16ac3e 100644 --- a/Emby.Server.Implementations/Library/UserViewManager.cs +++ b/Emby.Server.Implementations/Library/UserViewManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Threading; using MediaBrowser.Controller.Channels; @@ -117,7 +118,7 @@ namespace Emby.Server.Implementations.Library if (!query.IncludeHidden) { - list = list.Where(i => !user.Configuration.MyMediaExcludes.Contains(i.Id.ToString("N"))).ToList(); + list = list.Where(i => !user.Configuration.MyMediaExcludes.Contains(i.Id.ToString("N", CultureInfo.InvariantCulture))).ToList(); } var sorted = _libraryManager.Sort(list, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending).ToList(); @@ -127,7 +128,7 @@ namespace Emby.Server.Implementations.Library return list .OrderBy(i => { - var index = orders.IndexOf(i.Id.ToString("N")); + var index = orders.IndexOf(i.Id.ToString("N", CultureInfo.InvariantCulture)); if (index == -1) { @@ -136,7 +137,7 @@ namespace Emby.Server.Implementations.Library { if (!view.DisplayParentId.Equals(Guid.Empty)) { - index = orders.IndexOf(view.DisplayParentId.ToString("N")); + index = orders.IndexOf(view.DisplayParentId.ToString("N", CultureInfo.InvariantCulture)); } } } @@ -269,7 +270,7 @@ namespace Emby.Server.Implementations.Library { parents = _libraryManager.GetUserRootFolder().GetChildren(user, true) .Where(i => i is Folder) - .Where(i => !user.Configuration.LatestItemsExcludes.Contains(i.Id.ToString("N"))) + .Where(i => !user.Configuration.LatestItemsExcludes.Contains(i.Id.ToString("N", CultureInfo.InvariantCulture))) .ToList(); } diff --git a/Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs b/Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs index 294348660..b584cc649 100644 --- a/Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs +++ b/Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -91,7 +92,7 @@ namespace Emby.Server.Implementations.Library.Validators continue; } - _logger.LogInformation("Deleting dead {2} {0} {1}.", item.Id.ToString("N"), item.Name, item.GetType().Name); + _logger.LogInformation("Deleting dead {2} {0} {1}.", item.Id.ToString("N", CultureInfo.InvariantCulture), item.Name, item.GetType().Name); _libraryManager.DeleteItem(item, new DeleteOptions { diff --git a/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs b/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs index 7899cf01b..d00c6cde1 100644 --- a/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs +++ b/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs @@ -1,7 +1,7 @@ using System; +using System.Globalization; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; @@ -96,7 +96,7 @@ namespace Emby.Server.Implementations.Library.Validators foreach (var item in deadEntities) { - _logger.LogInformation("Deleting dead {2} {0} {1}.", item.Id.ToString("N"), item.Name, item.GetType().Name); + _logger.LogInformation("Deleting dead {2} {0} {1}.", item.Id.ToString("N", CultureInfo.InvariantCulture), item.Name, item.GetType().Name); _libraryManager.DeleteItem(item, new DeleteOptions { diff --git a/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs b/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs index da4645a11..93ded9e7b 100644 --- a/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs +++ b/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Entities; @@ -76,7 +77,7 @@ namespace Emby.Server.Implementations.Library.Validators foreach (var item in deadEntities) { - _logger.LogInformation("Deleting dead {2} {0} {1}.", item.Id.ToString("N"), item.Name, item.GetType().Name); + _logger.LogInformation("Deleting dead {2} {0} {1}.", item.Id.ToString("N", CultureInfo.InvariantCulture), item.Name, item.GetType().Name); _libraryManager.DeleteItem(item, new DeleteOptions { diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 7b210d231..d7411af50 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -681,7 +681,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV } } - timer.Id = Guid.NewGuid().ToString("N"); + timer.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); LiveTvProgram programInfo = null; @@ -713,7 +713,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV public async Task CreateSeriesTimer(SeriesTimerInfo info, CancellationToken cancellationToken) { - info.Id = Guid.NewGuid().ToString("N"); + info.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); // populate info.seriesID var program = GetProgramInfoFromCache(info.ProgramId); @@ -1059,7 +1059,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV var json = _jsonSerializer.SerializeToString(mediaSource); mediaSource = _jsonSerializer.DeserializeFromString(json); - mediaSource.Id = Guid.NewGuid().ToString("N") + "_" + mediaSource.Id; + mediaSource.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture) + "_" + mediaSource.Id; //if (mediaSource.DateLiveStreamOpened.HasValue && enableStreamSharing) //{ @@ -2529,7 +2529,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV var timer = new TimerInfo { ChannelId = channelId, - Id = (seriesTimer.Id + parent.ExternalId).GetMD5().ToString("N"), + Id = (seriesTimer.Id + parent.ExternalId).GetMD5().ToString("N", CultureInfo.InvariantCulture), StartDate = parent.StartDate, EndDate = parent.EndDate.Value, ProgramId = parent.ExternalId, diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs index 94225a0aa..88693f22a 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs @@ -211,7 +211,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings HasImage = program.Icon != null && !string.IsNullOrEmpty(program.Icon.Source), OfficialRating = program.Rating != null && !string.IsNullOrEmpty(program.Rating.Value) ? program.Rating.Value : null, CommunityRating = program.StarRating, - SeriesId = program.Episode == null ? null : program.Title.GetMD5().ToString("N") + SeriesId = program.Episode == null ? null : program.Title.GetMD5().ToString("N", CultureInfo.InvariantCulture) }; if (string.IsNullOrWhiteSpace(program.ProgramId)) @@ -227,7 +227,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings uniqueString = "-" + programInfo.EpisodeNumber.Value.ToString(CultureInfo.InvariantCulture); } - programInfo.ShowId = uniqueString.GetMD5().ToString("N"); + programInfo.ShowId = uniqueString.GetMD5().ToString("N", CultureInfo.InvariantCulture); // If we don't have valid episode info, assume it's a unique program, otherwise recordings might be skipped if (programInfo.IsSeries diff --git a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs index 1144c9ab1..e584664c9 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -52,7 +53,7 @@ namespace Emby.Server.Implementations.LiveTv ExternalId = info.Id, ChannelId = GetInternalChannelId(service.Name, info.ChannelId), Status = info.Status, - SeriesTimerId = string.IsNullOrEmpty(info.SeriesTimerId) ? null : GetInternalSeriesTimerId(info.SeriesTimerId).ToString("N"), + SeriesTimerId = string.IsNullOrEmpty(info.SeriesTimerId) ? null : GetInternalSeriesTimerId(info.SeriesTimerId).ToString("N", CultureInfo.InvariantCulture), PrePaddingSeconds = info.PrePaddingSeconds, PostPaddingSeconds = info.PostPaddingSeconds, IsPostPaddingRequired = info.IsPostPaddingRequired, @@ -69,7 +70,7 @@ namespace Emby.Server.Implementations.LiveTv if (!string.IsNullOrEmpty(info.ProgramId)) { - dto.ProgramId = GetInternalProgramId(info.ProgramId).ToString("N"); + dto.ProgramId = GetInternalProgramId(info.ProgramId).ToString("N", CultureInfo.InvariantCulture); } if (program != null) @@ -107,7 +108,7 @@ namespace Emby.Server.Implementations.LiveTv { var dto = new SeriesTimerInfoDto { - Id = GetInternalSeriesTimerId(info.Id).ToString("N"), + Id = GetInternalSeriesTimerId(info.Id).ToString("N", CultureInfo.InvariantCulture), Overview = info.Overview, EndDate = info.EndDate, Name = info.Name, @@ -139,7 +140,7 @@ namespace Emby.Server.Implementations.LiveTv if (!string.IsNullOrEmpty(info.ProgramId)) { - dto.ProgramId = GetInternalProgramId(info.ProgramId).ToString("N"); + dto.ProgramId = GetInternalProgramId(info.ProgramId).ToString("N", CultureInfo.InvariantCulture); } dto.DayPattern = info.Days == null ? null : GetDayPattern(info.Days.ToArray()); @@ -169,7 +170,7 @@ namespace Emby.Server.Implementations.LiveTv try { dto.ParentThumbImageTag = _imageProcessor.GetImageCacheTag(librarySeries, image); - dto.ParentThumbItemId = librarySeries.Id.ToString("N"); + dto.ParentThumbItemId = librarySeries.Id.ToString("N", CultureInfo.InvariantCulture); } catch (Exception ex) { @@ -185,7 +186,7 @@ namespace Emby.Server.Implementations.LiveTv { _imageProcessor.GetImageCacheTag(librarySeries, image) }; - dto.ParentBackdropItemId = librarySeries.Id.ToString("N"); + dto.ParentBackdropItemId = librarySeries.Id.ToString("N", CultureInfo.InvariantCulture); } catch (Exception ex) { @@ -213,7 +214,7 @@ namespace Emby.Server.Implementations.LiveTv try { dto.ParentPrimaryImageTag = _imageProcessor.GetImageCacheTag(program, image); - dto.ParentPrimaryImageItemId = program.Id.ToString("N"); + dto.ParentPrimaryImageItemId = program.Id.ToString("N", CultureInfo.InvariantCulture); } catch (Exception ex) { @@ -232,7 +233,7 @@ namespace Emby.Server.Implementations.LiveTv { _imageProcessor.GetImageCacheTag(program, image) }; - dto.ParentBackdropItemId = program.Id.ToString("N"); + dto.ParentBackdropItemId = program.Id.ToString("N", CultureInfo.InvariantCulture); } catch (Exception ex) { @@ -263,7 +264,7 @@ namespace Emby.Server.Implementations.LiveTv try { dto.ParentThumbImageTag = _imageProcessor.GetImageCacheTag(librarySeries, image); - dto.ParentThumbItemId = librarySeries.Id.ToString("N"); + dto.ParentThumbItemId = librarySeries.Id.ToString("N", CultureInfo.InvariantCulture); } catch (Exception ex) { @@ -279,7 +280,7 @@ namespace Emby.Server.Implementations.LiveTv { _imageProcessor.GetImageCacheTag(librarySeries, image) }; - dto.ParentBackdropItemId = librarySeries.Id.ToString("N"); + dto.ParentBackdropItemId = librarySeries.Id.ToString("N", CultureInfo.InvariantCulture); } catch (Exception ex) { @@ -320,7 +321,7 @@ namespace Emby.Server.Implementations.LiveTv try { dto.ParentPrimaryImageTag = _imageProcessor.GetImageCacheTag(program, image); - dto.ParentPrimaryImageItemId = program.Id.ToString("N"); + dto.ParentPrimaryImageItemId = program.Id.ToString("N", CultureInfo.InvariantCulture); } catch (Exception ex) { @@ -339,7 +340,7 @@ namespace Emby.Server.Implementations.LiveTv { _imageProcessor.GetImageCacheTag(program, image) }; - dto.ParentBackdropItemId = program.Id.ToString("N"); + dto.ParentBackdropItemId = program.Id.ToString("N", CultureInfo.InvariantCulture); } catch (Exception ex) { @@ -407,7 +408,7 @@ namespace Emby.Server.Implementations.LiveTv { var name = ServiceName + externalId + InternalVersionNumber; - return name.ToLowerInvariant().GetMD5().ToString("N"); + return name.ToLowerInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture); } public Guid GetInternalSeriesTimerId(string externalId) diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index 9093d9740..1e5198dd6 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -258,7 +259,7 @@ namespace Emby.Server.Implementations.LiveTv } info.RequiresClosing = true; - var idPrefix = service.GetType().FullName.GetMD5().ToString("N") + "_"; + var idPrefix = service.GetType().FullName.GetMD5().ToString("N", CultureInfo.InvariantCulture) + "_"; info.LiveStreamId = idPrefix + info.Id; @@ -820,7 +821,7 @@ namespace Emby.Server.Implementations.LiveTv if (!string.IsNullOrWhiteSpace(query.SeriesTimerId)) { var seriesTimers = await GetSeriesTimersInternal(new SeriesTimerQuery { }, cancellationToken).ConfigureAwait(false); - var seriesTimer = seriesTimers.Items.FirstOrDefault(i => string.Equals(_tvDtoService.GetInternalSeriesTimerId(i.Id).ToString("N"), query.SeriesTimerId, StringComparison.OrdinalIgnoreCase)); + var seriesTimer = seriesTimers.Items.FirstOrDefault(i => string.Equals(_tvDtoService.GetInternalSeriesTimerId(i.Id).ToString("N", CultureInfo.InvariantCulture), query.SeriesTimerId, StringComparison.OrdinalIgnoreCase)); if (seriesTimer != null) { internalQuery.ExternalSeriesId = seriesTimer.SeriesId; @@ -997,7 +998,7 @@ namespace Emby.Server.Implementations.LiveTv if (!string.IsNullOrEmpty(timer.SeriesTimerId)) { program.SeriesTimerId = _tvDtoService.GetInternalSeriesTimerId(timer.SeriesTimerId) - .ToString("N"); + .ToString("N", CultureInfo.InvariantCulture); foundSeriesTimer = true; } @@ -1018,7 +1019,7 @@ namespace Emby.Server.Implementations.LiveTv if (seriesTimer != null) { program.SeriesTimerId = _tvDtoService.GetInternalSeriesTimerId(seriesTimer.Id) - .ToString("N"); + .ToString("N", CultureInfo.InvariantCulture); } } } @@ -1472,7 +1473,7 @@ namespace Emby.Server.Implementations.LiveTv dto.SeriesTimerId = string.IsNullOrEmpty(info.SeriesTimerId) ? null - : _tvDtoService.GetInternalSeriesTimerId(info.SeriesTimerId).ToString("N"); + : _tvDtoService.GetInternalSeriesTimerId(info.SeriesTimerId).ToString("N", CultureInfo.InvariantCulture); dto.TimerId = string.IsNullOrEmpty(info.Id) ? null @@ -2027,7 +2028,7 @@ namespace Emby.Server.Implementations.LiveTv info.StartDate = program.StartDate; info.Name = program.Name; info.Overview = program.Overview; - info.ProgramId = programDto.Id.ToString("N"); + info.ProgramId = programDto.Id.ToString("N", CultureInfo.InvariantCulture); info.ExternalProgramId = program.ExternalId; if (program.EndDate.HasValue) @@ -2088,7 +2089,7 @@ namespace Emby.Server.Implementations.LiveTv if (service is ISupportsNewTimerIds supportsNewTimerIds) { newTimerId = await supportsNewTimerIds.CreateSeriesTimer(info, cancellationToken).ConfigureAwait(false); - newTimerId = _tvDtoService.GetInternalSeriesTimerId(newTimerId).ToString("N"); + newTimerId = _tvDtoService.GetInternalSeriesTimerId(newTimerId).ToString("N", CultureInfo.InvariantCulture); } else { @@ -2192,7 +2193,7 @@ namespace Emby.Server.Implementations.LiveTv info.EnabledUsers = _userManager.Users .Where(IsLiveTvEnabled) - .Select(i => i.Id.ToString("N")) + .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)) .ToArray(); return info; @@ -2219,7 +2220,7 @@ namespace Emby.Server.Implementations.LiveTv { var parts = id.Split(new[] { '_' }, 2); - var service = _services.FirstOrDefault(i => string.Equals(i.GetType().FullName.GetMD5().ToString("N"), parts[0], StringComparison.OrdinalIgnoreCase)); + var service = _services.FirstOrDefault(i => string.Equals(i.GetType().FullName.GetMD5().ToString("N", CultureInfo.InvariantCulture), parts[0], StringComparison.OrdinalIgnoreCase)); if (service == null) { @@ -2269,7 +2270,7 @@ namespace Emby.Server.Implementations.LiveTv if (index == -1 || string.IsNullOrWhiteSpace(info.Id)) { - info.Id = Guid.NewGuid().ToString("N"); + info.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); list.Add(info); config.TunerHosts = list.ToArray(); } @@ -2312,7 +2313,7 @@ namespace Emby.Server.Implementations.LiveTv if (index == -1 || string.IsNullOrWhiteSpace(info.Id)) { - info.Id = Guid.NewGuid().ToString("N"); + info.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); list.Add(info); config.ListingProviders = list.ToArray(); } diff --git a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs index cd1731de5..52d60c004 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -101,7 +102,7 @@ namespace Emby.Server.Implementations.LiveTv { var openKeys = new List(); openKeys.Add(item.GetType().Name); - openKeys.Add(item.Id.ToString("N")); + openKeys.Add(item.Id.ToString("N", CultureInfo.InvariantCulture)); openKeys.Add(source.Id ?? string.Empty); source.OpenToken = string.Join(StreamIdDelimeterString, openKeys.ToArray()); } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index ed524cae3..db016ec70 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Net; @@ -460,7 +461,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { id = "native"; } - id += "_" + channelId.GetMD5().ToString("N") + "_" + url.GetMD5().ToString("N"); + id += "_" + channelId.GetMD5().ToString("N", CultureInfo.InvariantCulture) + "_" + url.GetMD5().ToString("N", CultureInfo.InvariantCulture); var mediaSource = new MediaSourceInfo { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs index ece2cbd54..b4395e2e1 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Threading; @@ -42,7 +43,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts MediaSource = mediaSource; Logger = logger; EnableStreamSharing = true; - UniqueId = Guid.NewGuid().ToString("N"); + UniqueId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); if (tuner != null) { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs index 2d9bec53f..6c5c80827 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Threading; @@ -43,7 +44,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts private string GetFullChannelIdPrefix(TunerHostInfo info) { - return ChannelIdPrefix + info.Url.GetMD5().ToString("N"); + return ChannelIdPrefix + info.Url.GetMD5().ToString("N", CultureInfo.InvariantCulture); } protected override async Task> GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken) @@ -61,7 +62,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts Name = Name, SourceType = Type, Status = LiveTvTunerStatus.Available, - Id = i.Url.GetMD5().ToString("N"), + Id = i.Url.GetMD5().ToString("N", CultureInfo.InvariantCulture), Url = i.Url }) .ToList(); @@ -173,7 +174,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts ReadAtNativeFramerate = false, - Id = channel.Path.GetMD5().ToString("N"), + Id = channel.Path.GetMD5().ToString("N", CultureInfo.InvariantCulture), IsInfiniteStream = true, IsRemote = isRemote, diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs index 814031b12..e8cd129f5 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs @@ -92,11 +92,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts var channel = GetChannelnfo(extInf, tunerHostId, line); if (string.IsNullOrWhiteSpace(channel.Id)) { - channel.Id = channelIdPrefix + line.GetMD5().ToString("N"); + channel.Id = channelIdPrefix + line.GetMD5().ToString("N", CultureInfo.InvariantCulture); } else { - channel.Id = channelIdPrefix + channel.Id.GetMD5().ToString("N"); + channel.Id = channelIdPrefix + channel.Id.GetMD5().ToString("N", CultureInfo.InvariantCulture); } channel.Path = line; diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs index 29836e0bf..40b568b40 100644 --- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs +++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Threading; @@ -129,7 +130,7 @@ namespace Emby.Server.Implementations.Playlists { new Share { - UserId = options.UserId.Equals(Guid.Empty) ? null : options.UserId.ToString("N"), + UserId = options.UserId.Equals(Guid.Empty) ? null : options.UserId.ToString("N", CultureInfo.InvariantCulture), CanEdit = true } } @@ -144,7 +145,7 @@ namespace Emby.Server.Implementations.Playlists if (options.ItemIdList.Length > 0) { - AddToPlaylistInternal(playlist.Id.ToString("N"), options.ItemIdList, user, new DtoOptions(false) + AddToPlaylistInternal(playlist.Id.ToString("N", CultureInfo.InvariantCulture), options.ItemIdList, user, new DtoOptions(false) { EnableImages = true }); @@ -152,7 +153,7 @@ namespace Emby.Server.Implementations.Playlists return new PlaylistCreationResult { - Id = playlist.Id.ToString("N") + Id = playlist.Id.ToString("N", CultureInfo.InvariantCulture) }; } finally diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs index 08bb39578..83226b07f 100644 --- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs +++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs @@ -1,5 +1,5 @@ using System; -using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Threading; @@ -287,7 +287,7 @@ namespace Emby.Server.Implementations.ScheduledTasks { if (_id == null) { - _id = ScheduledTask.GetType().FullName.GetMD5().ToString("N"); + _id = ScheduledTask.GetType().FullName.GetMD5().ToString("N", CultureInfo.InvariantCulture); } return _id; diff --git a/Emby.Server.Implementations/Security/AuthenticationRepository.cs b/Emby.Server.Implementations/Security/AuthenticationRepository.cs index 545e11bf9..26a08cbe9 100644 --- a/Emby.Server.Implementations/Security/AuthenticationRepository.cs +++ b/Emby.Server.Implementations/Security/AuthenticationRepository.cs @@ -174,7 +174,7 @@ namespace Emby.Server.Implementations.Security if (!query.UserId.Equals(Guid.Empty)) { - statement.TryBind("@UserId", query.UserId.ToString("N")); + statement.TryBind("@UserId", query.UserId.ToString("N", CultureInfo.InvariantCulture)); } if (!string.IsNullOrEmpty(query.DeviceId)) diff --git a/Emby.Server.Implementations/Serialization/JsonSerializer.cs b/Emby.Server.Implementations/Serialization/JsonSerializer.cs index 8ae7fd90c..36196ee36 100644 --- a/Emby.Server.Implementations/Serialization/JsonSerializer.cs +++ b/Emby.Server.Implementations/Serialization/JsonSerializer.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.IO; using System.Threading.Tasks; using MediaBrowser.Model.IO; @@ -245,7 +246,7 @@ namespace Emby.Server.Implementations.Serialization return null; } - return guid.ToString("N"); + return guid.ToString("N", CultureInfo.InvariantCulture); } /// diff --git a/Emby.Server.Implementations/Session/HttpSessionController.cs b/Emby.Server.Implementations/Session/HttpSessionController.cs index 9281f82b3..1104a7a85 100644 --- a/Emby.Server.Implementations/Session/HttpSessionController.cs +++ b/Emby.Server.Implementations/Session/HttpSessionController.cs @@ -62,7 +62,7 @@ namespace Emby.Server.Implementations.Session { var dict = new Dictionary(); - dict["ItemIds"] = string.Join(",", command.ItemIds.Select(i => i.ToString("N")).ToArray()); + dict["ItemIds"] = string.Join(",", command.ItemIds.Select(i => i.ToString("N", CultureInfo.InvariantCulture)).ToArray()); if (command.StartPositionTicks.HasValue) { diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 53ed5fc22..7ee573da5 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -327,7 +327,7 @@ namespace Emby.Server.Implementations.Session { if (string.IsNullOrEmpty(info.MediaSourceId)) { - info.MediaSourceId = info.ItemId.ToString("N"); + info.MediaSourceId = info.ItemId.ToString("N", CultureInfo.InvariantCulture); } if (!info.ItemId.Equals(Guid.Empty) && info.Item == null && libraryItem != null) @@ -463,7 +463,7 @@ namespace Emby.Server.Implementations.Session Client = appName, DeviceId = deviceId, ApplicationVersion = appVersion, - Id = key.GetMD5().ToString("N"), + Id = key.GetMD5().ToString("N", CultureInfo.InvariantCulture), ServerId = _appHost.SystemId }; @@ -845,7 +845,7 @@ namespace Emby.Server.Implementations.Session // Normalize if (string.IsNullOrEmpty(info.MediaSourceId)) { - info.MediaSourceId = info.ItemId.ToString("N"); + info.MediaSourceId = info.ItemId.ToString("N", CultureInfo.InvariantCulture); } if (!info.ItemId.Equals(Guid.Empty) && info.Item == null && libraryItem != null) @@ -1029,7 +1029,7 @@ namespace Emby.Server.Implementations.Session private static async Task SendMessageToSession(SessionInfo session, string name, T data, CancellationToken cancellationToken) { var controllers = session.SessionControllers.ToArray(); - var messageId = Guid.NewGuid().ToString("N"); + var messageId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); foreach (var controller in controllers) { @@ -1234,7 +1234,7 @@ namespace Emby.Server.Implementations.Session AssertCanControl(session, controllingSession); if (!controllingSession.UserId.Equals(Guid.Empty)) { - command.ControllingUserId = controllingSession.UserId.ToString("N"); + command.ControllingUserId = controllingSession.UserId.ToString("N", CultureInfo.InvariantCulture); } } @@ -1484,7 +1484,7 @@ namespace Emby.Server.Implementations.Session DeviceId = deviceId, DeviceName = deviceName, UserId = user.Id, - AccessToken = Guid.NewGuid().ToString("N"), + AccessToken = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture), UserName = user.Name }; @@ -1822,6 +1822,7 @@ namespace Emby.Server.Implementations.Session CheckDisposed(); var sessions = Sessions.Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase)); + return SendMessageToSessions(sessions, name, data, cancellationToken); } @@ -1831,6 +1832,7 @@ namespace Emby.Server.Implementations.Session var sessions = Sessions .Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase) || IsAdminSession(i)); + return SendMessageToSessions(sessions, name, data, cancellationToken); } diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs index 630ef4893..4c2f24e6f 100644 --- a/Emby.Server.Implementations/TV/TVSeriesManager.cs +++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dto; @@ -73,7 +74,7 @@ namespace Emby.Server.Implementations.TV { parents = _libraryManager.GetUserRootFolder().GetChildren(user, true) .Where(i => i is Folder) - .Where(i => !user.Configuration.LatestItemsExcludes.Contains(i.Id.ToString("N"))) + .Where(i => !user.Configuration.LatestItemsExcludes.Contains(i.Id.ToString("N", CultureInfo.InvariantCulture))) .ToArray(); } diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index 10bbc9e5d..23c7339d2 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Threading; @@ -537,7 +538,7 @@ namespace MediaBrowser.Api.Images if (item == null) { - throw new ResourceNotFoundException(string.Format("Item {0} not found.", itemId.ToString("N"))); + throw new ResourceNotFoundException(string.Format("Item {0} not found.", itemId.ToString("N", CultureInfo.InvariantCulture))); } } diff --git a/MediaBrowser.Api/Library/LibraryStructureService.cs b/MediaBrowser.Api/Library/LibraryStructureService.cs index d6bcf7878..7266bf9f9 100644 --- a/MediaBrowser.Api/Library/LibraryStructureService.cs +++ b/MediaBrowser.Api/Library/LibraryStructureService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Threading; @@ -272,7 +273,7 @@ namespace MediaBrowser.Api.Library // Changing capitalization. Handle windows case insensitivity if (string.Equals(currentPath, newPath, StringComparison.OrdinalIgnoreCase)) { - var tempPath = Path.Combine(rootFolderPath, Guid.NewGuid().ToString("N")); + var tempPath = Path.Combine(rootFolderPath, Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture)); Directory.Move(currentPath, tempPath); currentPath = tempPath; } diff --git a/MediaBrowser.Api/Movies/MoviesService.cs b/MediaBrowser.Api/Movies/MoviesService.cs index 91766255f..d601fb500 100644 --- a/MediaBrowser.Api/Movies/MoviesService.cs +++ b/MediaBrowser.Api/Movies/MoviesService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Configuration; @@ -11,7 +12,6 @@ using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Extensions; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Services; @@ -268,7 +268,7 @@ namespace MediaBrowser.Api.Movies EnableGroupByMetadataKey = true, DtoOptions = dtoOptions - }).GroupBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString("N")) + }).GroupBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture)) .Select(x => x.First()) .Take(itemLimit) .ToList(); @@ -309,7 +309,7 @@ namespace MediaBrowser.Api.Movies EnableGroupByMetadataKey = true, DtoOptions = dtoOptions - }).GroupBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString("N")) + }).GroupBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture)) .Select(x => x.First()) .Take(itemLimit) .ToList(); diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 399401624..114d3f7a2 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -142,7 +142,7 @@ namespace MediaBrowser.Api.Playback data += "-" + (state.Request.DeviceId ?? string.Empty) + "-" + (state.Request.PlaySessionId ?? string.Empty); - var filename = data.GetMD5().ToString("N"); + var filename = data.GetMD5().ToString("N", CultureInfo.InvariantCulture); var ext = outputFileExtension.ToLowerInvariant(); var folder = ServerConfigurationManager.ApplicationPaths.TranscodingTempPath; @@ -215,6 +215,12 @@ namespace MediaBrowser.Api.Playback var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions(); +<<<<<<< HEAD +======= + var transcodingId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); + var commandLineArgs = GetCommandLineArguments(outputPath, encodingOptions, state, true); + +>>>>>>> Use CultureInvariant string conversion for Guids var process = new Process() { StartInfo = new ProcessStartInfo() diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index ab3994a63..da8f99a3d 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -306,7 +307,7 @@ namespace MediaBrowser.Api.Playback { result.MediaSources = Clone(result.MediaSources); - result.PlaySessionId = Guid.NewGuid().ToString("N"); + result.PlaySessionId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); } return result; diff --git a/MediaBrowser.Api/SearchService.cs b/MediaBrowser.Api/SearchService.cs index ecf07c912..6c67d4fb1 100644 --- a/MediaBrowser.Api/SearchService.cs +++ b/MediaBrowser.Api/SearchService.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Linq; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; @@ -305,7 +306,7 @@ namespace MediaBrowser.Api if (tag != null) { hint.ThumbImageTag = tag; - hint.ThumbImageItemId = itemWithImage.Id.ToString("N"); + hint.ThumbImageItemId = itemWithImage.Id.ToString("N", CultureInfo.InvariantCulture); } } } @@ -326,7 +327,7 @@ namespace MediaBrowser.Api if (tag != null) { hint.BackdropImageTag = tag; - hint.BackdropImageItemId = itemWithImage.Id.ToString("N"); + hint.BackdropImageItemId = itemWithImage.Id.ToString("N", CultureInfo.InvariantCulture); } } } diff --git a/MediaBrowser.Api/Session/SessionsService.cs b/MediaBrowser.Api/Session/SessionsService.cs index 4109b12bf..76392e27c 100644 --- a/MediaBrowser.Api/Session/SessionsService.cs +++ b/MediaBrowser.Api/Session/SessionsService.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -316,7 +317,7 @@ namespace MediaBrowser.Api.Session _authRepo.Create(new AuthenticationInfo { AppName = request.App, - AccessToken = Guid.NewGuid().ToString("N"), + AccessToken = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture), DateCreated = DateTime.UtcNow, DeviceId = _appHost.SystemId, DeviceName = _appHost.FriendlyName, diff --git a/MediaBrowser.Api/Subtitles/SubtitleService.cs b/MediaBrowser.Api/Subtitles/SubtitleService.cs index 08aa540a5..52043d3df 100644 --- a/MediaBrowser.Api/Subtitles/SubtitleService.cs +++ b/MediaBrowser.Api/Subtitles/SubtitleService.cs @@ -168,7 +168,7 @@ namespace MediaBrowser.Api.Subtitles builder.AppendLine("#EXT-X-MEDIA-SEQUENCE:0"); builder.AppendLine("#EXT-X-PLAYLIST-TYPE:VOD"); - long positionTicks = 0; + long positionTicks = 0; var accessToken = _authContext.GetAuthorizationInfo(Request).Token; @@ -206,7 +206,7 @@ namespace MediaBrowser.Api.Subtitles { var item = (Video)_libraryManager.GetItemById(request.Id); - var idString = request.Id.ToString("N"); + var idString = request.Id.ToString("N", CultureInfo.InvariantCulture); var mediaSource = _mediaSourceManager.GetStaticMediaSources(item, false, null) .First(i => string.Equals(i.Id, request.MediaSourceId ?? idString)); diff --git a/MediaBrowser.Api/TvShowsService.cs b/MediaBrowser.Api/TvShowsService.cs index b0900a554..2951fa6b4 100644 --- a/MediaBrowser.Api/TvShowsService.cs +++ b/MediaBrowser.Api/TvShowsService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Dto; @@ -470,7 +471,7 @@ namespace MediaBrowser.Api if (!string.IsNullOrWhiteSpace(request.StartItemId)) { - episodes = episodes.SkipWhile(i => !string.Equals(i.Id.ToString("N"), request.StartItemId, StringComparison.OrdinalIgnoreCase)).ToList(); + episodes = episodes.SkipWhile(i => !string.Equals(i.Id.ToString("N", CultureInfo.InvariantCulture), request.StartItemId, StringComparison.OrdinalIgnoreCase)).ToList(); } // This must be the last filter diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index f842230ee..c605cd396 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -99,7 +99,7 @@ namespace MediaBrowser.Api.UserLibrary { ancestorIds = _libraryManager.GetUserRootFolder().GetChildren(user, true) .Where(i => i is Folder) - .Where(i => !excludeFolderIds.Contains(i.Id.ToString("N"))) + .Where(i => !excludeFolderIds.Contains(i.Id.ToString("N", CultureInfo.InvariantCulture))) .Select(i => i.Id) .ToArray(); } diff --git a/MediaBrowser.Api/UserLibrary/UserViewsService.cs b/MediaBrowser.Api/UserLibrary/UserViewsService.cs index 1d61c5c1e..2fa5d8933 100644 --- a/MediaBrowser.Api/UserLibrary/UserViewsService.cs +++ b/MediaBrowser.Api/UserLibrary/UserViewsService.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Linq; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; @@ -116,7 +117,7 @@ namespace MediaBrowser.Api.UserLibrary .Select(i => new SpecialViewOption { Name = i.Name, - Id = i.Id.ToString("N") + Id = i.Id.ToString("N", CultureInfo.InvariantCulture) }) .OrderBy(i => i.Name) diff --git a/MediaBrowser.Api/VideosService.cs b/MediaBrowser.Api/VideosService.cs index 061f72438..474036f5c 100644 --- a/MediaBrowser.Api/VideosService.cs +++ b/MediaBrowser.Api/VideosService.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Linq; using System.Threading; using MediaBrowser.Controller.Configuration; @@ -168,7 +169,7 @@ namespace MediaBrowser.Api foreach (var item in items.Where(i => i.Id != primaryVersion.Id)) { - item.SetPrimaryVersionId(primaryVersion.Id.ToString("N")); + item.SetPrimaryVersionId(primaryVersion.Id.ToString("N", CultureInfo.InvariantCulture)); item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); diff --git a/MediaBrowser.Controller/Channels/Channel.cs b/MediaBrowser.Controller/Channels/Channel.cs index adf03fb66..89159973b 100644 --- a/MediaBrowser.Controller/Channels/Channel.cs +++ b/MediaBrowser.Controller/Channels/Channel.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Linq; using System.Threading; using MediaBrowser.Common.Progress; @@ -14,14 +15,14 @@ namespace MediaBrowser.Controller.Channels { if (user.Policy.BlockedChannels != null) { - if (user.Policy.BlockedChannels.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase)) + if (user.Policy.BlockedChannels.Contains(Id.ToString("N", CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase)) { return false; } } else { - if (!user.Policy.EnableAllChannels && !user.Policy.EnabledChannels.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase)) + if (!user.Policy.EnableAllChannels && !user.Policy.EnabledChannels.Contains(Id.ToString("N", CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase)) { return false; } @@ -60,7 +61,7 @@ namespace MediaBrowser.Controller.Channels public static string GetInternalMetadataPath(string basePath, Guid id) { - return System.IO.Path.Combine(basePath, "channels", id.ToString("N"), "metadata"); + return System.IO.Path.Combine(basePath, "channels", id.ToString("N", CultureInfo.InvariantCulture), "metadata"); } public override bool CanDelete() diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 10a603e42..2ae856b02 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -503,7 +503,7 @@ namespace MediaBrowser.Controller.Entities foreach (var folder in collectionFolders) { - if (allowed.Contains(folder.Id.ToString("N"), StringComparer.OrdinalIgnoreCase)) + if (allowed.Contains(folder.Id.ToString("N", CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase)) { return true; } @@ -664,10 +664,10 @@ namespace MediaBrowser.Controller.Entities { if (SourceType == SourceType.Channel) { - return System.IO.Path.Combine(basePath, "channels", ChannelId.ToString("N"), Id.ToString("N")); + return System.IO.Path.Combine(basePath, "channels", ChannelId.ToString("N", CultureInfo.InvariantCulture), Id.ToString("N", CultureInfo.InvariantCulture)); } - var idString = Id.ToString("N"); + var idString = Id.ToString("N", CultureInfo.InvariantCulture); basePath = System.IO.Path.Combine(basePath, "library"); @@ -1095,7 +1095,7 @@ namespace MediaBrowser.Controller.Entities var info = new MediaSourceInfo { - Id = item.Id.ToString("N"), + Id = item.Id.ToString("N", CultureInfo.InvariantCulture), Protocol = protocol ?? MediaProtocol.File, MediaStreams = MediaSourceManager.GetMediaStreams(item.Id), Name = GetMediaSourceName(item), @@ -1113,7 +1113,7 @@ namespace MediaBrowser.Controller.Entities if (info.Protocol == MediaProtocol.File) { - info.ETag = item.DateModified.Ticks.ToString(CultureInfo.InvariantCulture).GetMD5().ToString("N"); + info.ETag = item.DateModified.Ticks.ToString(CultureInfo.InvariantCulture).GetMD5().ToString("N", CultureInfo.InvariantCulture); } var video = item as Video; @@ -1626,7 +1626,7 @@ namespace MediaBrowser.Controller.Entities public virtual string CreatePresentationUniqueKey() { - return Id.ToString("N"); + return Id.ToString("N", CultureInfo.InvariantCulture); } [IgnoreDataMember] @@ -2736,7 +2736,7 @@ namespace MediaBrowser.Controller.Entities { var list = GetEtagValues(user); - return string.Join("|", list.ToArray()).GetMD5().ToString("N"); + return string.Join("|", list.ToArray()).GetMD5().ToString("N", CultureInfo.InvariantCulture); } protected virtual List GetEtagValues(User user) diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index c056bc0b4..d841b7ef8 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Threading; @@ -177,7 +178,7 @@ namespace MediaBrowser.Controller.Entities { if (user.Policy.BlockedMediaFolders != null) { - if (user.Policy.BlockedMediaFolders.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase) || + if (user.Policy.BlockedMediaFolders.Contains(Id.ToString("N", CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase) || // Backwards compatibility user.Policy.BlockedMediaFolders.Contains(Name, StringComparer.OrdinalIgnoreCase)) @@ -187,7 +188,7 @@ namespace MediaBrowser.Controller.Entities } else { - if (!user.Policy.EnableAllFolders && !user.Policy.EnabledFolders.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase)) + if (!user.Policy.EnableAllFolders && !user.Policy.EnabledFolders.Contains(Id.ToString("N", CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase)) { return false; } diff --git a/MediaBrowser.Controller/Entities/LinkedChild.cs b/MediaBrowser.Controller/Entities/LinkedChild.cs index bb2d03246..823060488 100644 --- a/MediaBrowser.Controller/Entities/LinkedChild.cs +++ b/MediaBrowser.Controller/Entities/LinkedChild.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.Serialization; @@ -29,7 +30,7 @@ namespace MediaBrowser.Controller.Entities if (string.IsNullOrEmpty(child.Path)) { - child.LibraryItemId = item.Id.ToString("N"); + child.LibraryItemId = item.Id.ToString("N", CultureInfo.InvariantCulture); } return child; @@ -37,7 +38,7 @@ namespace MediaBrowser.Controller.Entities public LinkedChild() { - Id = Guid.NewGuid().ToString("N"); + Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); } } diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index eae834f6f..1aacc13c9 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -91,7 +92,7 @@ namespace MediaBrowser.Controller.Entities.TV } var folders = LibraryManager.GetCollectionFolders(this) - .Select(i => i.Id.ToString("N")) + .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)) .ToArray(); if (folders.Length == 0) diff --git a/MediaBrowser.Controller/Entities/User.cs b/MediaBrowser.Controller/Entities/User.cs index 9952ba418..968d72579 100644 --- a/MediaBrowser.Controller/Entities/User.cs +++ b/MediaBrowser.Controller/Entities/User.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -230,7 +231,7 @@ namespace MediaBrowser.Controller.Entities // TODO: Remove idPath and just use usernamePath for future releases var usernamePath = System.IO.Path.Combine(parentPath, username); - var idPath = System.IO.Path.Combine(parentPath, Id.ToString("N")); + var idPath = System.IO.Path.Combine(parentPath, Id.ToString("N", CultureInfo.InvariantCulture)); if (!Directory.Exists(usernamePath) && Directory.Exists(idPath)) { Directory.Move(idPath, usernamePath); diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index e483c8f34..454bdc4ae 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.Movies; @@ -987,7 +988,7 @@ namespace MediaBrowser.Controller.Entities private UserView GetUserViewWithName(string name, string type, string sortName, BaseItem parent) { - return _userViewManager.GetUserSubView(parent.Id, parent.Id.ToString("N"), type, sortName); + return _userViewManager.GetUserSubView(parent.Id, parent.Id.ToString("N", CultureInfo.InvariantCulture), type, sortName); } private UserView GetUserView(string type, string localizationKey, string sortName, BaseItem parent) diff --git a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs index 55f47aae9..351662b29 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs @@ -89,7 +89,7 @@ namespace MediaBrowser.Controller.LiveTv var info = new MediaSourceInfo { - Id = Id.ToString("N"), + Id = Id.ToString("N", CultureInfo.InvariantCulture), Protocol = PathProtocol ?? MediaProtocol.File, MediaStreams = new List(), Name = Name, @@ -111,7 +111,7 @@ namespace MediaBrowser.Controller.LiveTv protected override string GetInternalMetadataPath(string basePath) { - return System.IO.Path.Combine(basePath, "livetv", Id.ToString("N"), "metadata"); + return System.IO.Path.Combine(basePath, "livetv", Id.ToString("N", CultureInfo.InvariantCulture), "metadata"); } public override bool CanDelete() diff --git a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs index 8bde6a5da..bdaf10d00 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Entities; @@ -188,7 +189,7 @@ namespace MediaBrowser.Controller.LiveTv protected override string GetInternalMetadataPath(string basePath) { - return System.IO.Path.Combine(basePath, "livetv", Id.ToString("N")); + return System.IO.Path.Combine(basePath, "livetv", Id.ToString("N", CultureInfo.InvariantCulture)); } public override bool CanDelete() diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs index e83260725..aff687f88 100644 --- a/MediaBrowser.Controller/Playlists/Playlist.cs +++ b/MediaBrowser.Controller/Playlists/Playlist.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -239,7 +240,7 @@ namespace MediaBrowser.Controller.Playlists return base.IsVisible(user); } - var userId = user.Id.ToString("N"); + var userId = user.Id.ToString("N", CultureInfo.InvariantCulture); foreach (var share in shares) { if (string.Equals(share.UserId, userId, StringComparison.OrdinalIgnoreCase)) diff --git a/MediaBrowser.Controller/Providers/MetadataResult.cs b/MediaBrowser.Controller/Providers/MetadataResult.cs index f4b915c06..ebff81b7f 100644 --- a/MediaBrowser.Controller/Providers/MetadataResult.cs +++ b/MediaBrowser.Controller/Providers/MetadataResult.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using MediaBrowser.Controller.Entities; namespace MediaBrowser.Controller.Providers @@ -55,7 +56,7 @@ namespace MediaBrowser.Controller.Providers foreach (var i in UserDataList) { - if (string.Equals(userId, i.UserId.ToString("N"), StringComparison.OrdinalIgnoreCase)) + if (string.Equals(userId, i.UserId.ToString("N", CultureInfo.InvariantCulture), StringComparison.OrdinalIgnoreCase)) { userData = i; } diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index 860ea13cf..a22eaaa51 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -934,7 +934,7 @@ namespace MediaBrowser.Providers.Manager public void OnRefreshStart(BaseItem item) { - //_logger.LogInformation("OnRefreshStart {0}", item.Id.ToString("N")); + //_logger.LogInformation("OnRefreshStart {0}", item.Id.ToString("N", CultureInfo.InvariantCulture)); var id = item.Id; lock (_activeRefreshes) @@ -947,7 +947,7 @@ namespace MediaBrowser.Providers.Manager public void OnRefreshComplete(BaseItem item) { - //_logger.LogInformation("OnRefreshComplete {0}", item.Id.ToString("N")); + //_logger.LogInformation("OnRefreshComplete {0}", item.Id.ToString("N", CultureInfo.InvariantCulture)); lock (_activeRefreshes) { _activeRefreshes.Remove(item.Id); @@ -971,7 +971,7 @@ namespace MediaBrowser.Providers.Manager public void OnRefreshProgress(BaseItem item, double progress) { - //_logger.LogInformation("OnRefreshProgress {0} {1}", item.Id.ToString("N"), progress); + //_logger.LogInformation("OnRefreshProgress {0} {1}", item.Id.ToString("N", CultureInfo.InvariantCulture), progress); var id = item.Id; lock (_activeRefreshes) @@ -985,7 +985,7 @@ namespace MediaBrowser.Providers.Manager else { // TODO: Need to hunt down the conditions for this happening - //throw new Exception(string.Format("Refresh for item {0} {1} is not in progress", item.GetType().Name, item.Id.ToString("N"))); + //throw new Exception(string.Format("Refresh for item {0} {1} is not in progress", item.GetType().Name, item.Id.ToString("N", CultureInfo.InvariantCulture))); } } } diff --git a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs index 61a8a122b..7023ef706 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Threading; @@ -99,11 +100,11 @@ namespace MediaBrowser.Providers.MediaInfo if (!string.IsNullOrWhiteSpace(item.Album) && !string.IsNullOrWhiteSpace(albumArtist)) { - filename = (item.Album + "-" + albumArtist).GetMD5().ToString("N"); + filename = (item.Album + "-" + albumArtist).GetMD5().ToString("N", CultureInfo.InvariantCulture); } else { - filename = item.Id.ToString("N"); + filename = item.Id.ToString("N", CultureInfo.InvariantCulture); } filename += ".jpg"; @@ -111,7 +112,7 @@ namespace MediaBrowser.Providers.MediaInfo else { // If it's an audio book or audio podcast, allow unique image per item - filename = item.Id.ToString("N") + ".jpg"; + filename = item.Id.ToString("N", CultureInfo.InvariantCulture) + ".jpg"; } var prefix = filename.Substring(0, 1); diff --git a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs index 7fc6909f5..b4a4c36e5 100644 --- a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs +++ b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs @@ -1,12 +1,11 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; -using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; @@ -296,7 +295,7 @@ namespace MediaBrowser.Providers.Subtitles private string GetProviderId(string name) { - return name.ToLowerInvariant().GetMD5().ToString("N"); + return name.ToLowerInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture); } private ISubtitleProvider GetProvider(string id) diff --git a/jellyfin.ruleset b/jellyfin.ruleset index 1249a60c0..e7e02a7d5 100644 --- a/jellyfin.ruleset +++ b/jellyfin.ruleset @@ -27,6 +27,10 @@ + + -- cgit v1.2.3 From 8d3b5c851ded74b9f34eb2cd963187761a3f6f61 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 9 Jun 2019 22:08:01 +0200 Subject: Improvements to UserManager --- Emby.Notifications/NotificationManager.cs | 2 +- Emby.Server.Implementations/ApplicationHost.cs | 3 +- .../Data/SqliteUserDataRepository.cs | 4 +- .../EntryPoints/RefreshUsersMetadata.cs | 4 +- .../Library/DefaultPasswordResetProvider.cs | 44 +-- Emby.Server.Implementations/Library/UserManager.cs | 358 ++++++++++----------- .../Session/SessionManager.cs | 8 +- MediaBrowser.Api/UserService.cs | 11 +- .../Authentication/AuthenticationException.cs | 1 + MediaBrowser.Controller/Entities/User.cs | 66 +--- MediaBrowser.Controller/Library/IUserManager.cs | 10 +- 11 files changed, 237 insertions(+), 274 deletions(-) (limited to 'Emby.Server.Implementations/Library') diff --git a/Emby.Notifications/NotificationManager.cs b/Emby.Notifications/NotificationManager.cs index a767e541e..eecbbea07 100644 --- a/Emby.Notifications/NotificationManager.cs +++ b/Emby.Notifications/NotificationManager.cs @@ -89,7 +89,7 @@ namespace Emby.Notifications return _userManager.Users.Where(i => i.Policy.IsAdministrator) .Select(i => i.Id); case SendToUserType.All: - return _userManager.Users.Select(i => i.Id); + return _userManager.UsersIds; case SendToUserType.Custom: return request.UserIds; default: diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 0b3b81f94..c390ba635 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -770,7 +770,8 @@ namespace Emby.Server.Implementations _userRepository = GetUserRepository(); - UserManager = new UserManager(LoggerFactory, ServerConfigurationManager, _userRepository, XmlSerializer, NetworkManager, () => ImageProcessor, () => DtoService, this, JsonSerializer, FileSystemManager); + UserManager = new UserManager(LoggerFactory.CreateLogger(), _userRepository, XmlSerializer, NetworkManager, () => ImageProcessor, () => DtoService, this, JsonSerializer, FileSystemManager); + serviceCollection.AddSingleton(UserManager); LibraryManager = new LibraryManager(this, LoggerFactory, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager, () => ProviderManager, () => UserViewManager); diff --git a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs index 4035bb99d..9d4855bcf 100644 --- a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs @@ -44,7 +44,7 @@ namespace Emby.Server.Implementations.Data var userDatasTableExists = TableExists(connection, "UserDatas"); var userDataTableExists = TableExists(connection, "userdata"); - var users = userDatasTableExists ? null : userManager.Users.ToArray(); + var users = userDatasTableExists ? null : userManager.Users; connection.RunInTransaction(db => { @@ -84,7 +84,7 @@ namespace Emby.Server.Implementations.Data } } - private void ImportUserIds(IDatabaseConnection db, User[] users) + private void ImportUserIds(IDatabaseConnection db, IEnumerable users) { var userIdsWithUserData = GetAllUserIdsWithUserData(db); diff --git a/Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs b/Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs index b7565adec..b2328121e 100644 --- a/Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs +++ b/Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs @@ -50,9 +50,7 @@ namespace Emby.Server.Implementations.EntryPoints public async Task Execute(CancellationToken cancellationToken, IProgress progress) { - var users = _userManager.Users.ToList(); - - foreach (var user in users) + foreach (var user in _userManager.Users) { cancellationToken.ThrowIfCancellationRequested(); diff --git a/Emby.Server.Implementations/Library/DefaultPasswordResetProvider.cs b/Emby.Server.Implementations/Library/DefaultPasswordResetProvider.cs index c7044820c..fa6bbcf91 100644 --- a/Emby.Server.Implementations/Library/DefaultPasswordResetProvider.cs +++ b/Emby.Server.Implementations/Library/DefaultPasswordResetProvider.cs @@ -1,15 +1,12 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.IO; -using System.Linq; -using System.Text; +using System.Security.Cryptography; using System.Threading.Tasks; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Users; @@ -17,32 +14,37 @@ namespace Emby.Server.Implementations.Library { public class DefaultPasswordResetProvider : IPasswordResetProvider { - public string Name => "Default Password Reset Provider"; + private const string BaseResetFileName = "passwordreset"; - public bool IsEnabled => true; + private readonly IJsonSerializer _jsonSerializer; + private readonly IUserManager _userManager; private readonly string _passwordResetFileBase; private readonly string _passwordResetFileBaseDir; - private readonly string _passwordResetFileBaseName = "passwordreset"; - private readonly IJsonSerializer _jsonSerializer; - private readonly IUserManager _userManager; - private readonly ICryptoProvider _crypto; - - public DefaultPasswordResetProvider(IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IUserManager userManager, ICryptoProvider cryptoProvider) + public DefaultPasswordResetProvider( + IServerConfigurationManager configurationManager, + IJsonSerializer jsonSerializer, + IUserManager userManager) { _passwordResetFileBaseDir = configurationManager.ApplicationPaths.ProgramDataPath; - _passwordResetFileBase = Path.Combine(_passwordResetFileBaseDir, _passwordResetFileBaseName); + _passwordResetFileBase = Path.Combine(_passwordResetFileBaseDir, BaseResetFileName); _jsonSerializer = jsonSerializer; _userManager = userManager; - _crypto = cryptoProvider; } + /// + public string Name => "Default Password Reset Provider"; + + /// + public bool IsEnabled => true; + + /// public async Task RedeemPasswordResetPin(string pin) { SerializablePasswordReset spr; - HashSet usersreset = new HashSet(); - foreach (var resetfile in Directory.EnumerateFiles(_passwordResetFileBaseDir, $"{_passwordResetFileBaseName}*")) + List usersreset = new List(); + foreach (var resetfile in Directory.EnumerateFiles(_passwordResetFileBaseDir, $"{BaseResetFileName}*")) { using (var str = File.OpenRead(resetfile)) { @@ -53,12 +55,15 @@ namespace Emby.Server.Implementations.Library { File.Delete(resetfile); } - else if (spr.Pin.Replace("-", "").Equals(pin.Replace("-", ""), StringComparison.InvariantCultureIgnoreCase)) + else if (string.Equals( + spr.Pin.Replace("-", string.Empty), + pin.Replace("-", string.Empty), + StringComparison.InvariantCultureIgnoreCase)) { var resetUser = _userManager.GetUserByName(spr.UserName); if (resetUser == null) { - throw new Exception($"User with a username of {spr.UserName} not found"); + throw new ResourceNotFoundException($"User with a username of {spr.UserName} not found"); } await _userManager.ChangePassword(resetUser, pin).ConfigureAwait(false); @@ -81,10 +86,11 @@ namespace Emby.Server.Implementations.Library } } + /// public async Task StartForgotPasswordProcess(MediaBrowser.Controller.Entities.User user, bool isInNetwork) { string pin = string.Empty; - using (var cryptoRandom = System.Security.Cryptography.RandomNumberGenerator.Create()) + using (var cryptoRandom = RandomNumberGenerator.Create()) { byte[] bytes = new byte[4]; cryptoRandom.GetBytes(bytes); diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index c8c8a108d..086527883 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; using System.IO; @@ -11,13 +12,11 @@ using MediaBrowser.Common.Events; using MediaBrowser.Common.Net; using MediaBrowser.Controller; using MediaBrowser.Controller.Authentication; -using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Providers; @@ -40,35 +39,20 @@ namespace Emby.Server.Implementations.Library /// public class UserManager : IUserManager { - /// - /// Gets the users. - /// - /// The users. - public IEnumerable Users => _users; - - private User[] _users; - /// /// The _logger /// private readonly ILogger _logger; - /// - /// Gets or sets the configuration manager. - /// - /// The configuration manager. - private IServerConfigurationManager ConfigurationManager { get; set; } + private readonly object _policySyncLock = new object(); /// /// Gets the active user repository /// /// The user repository. - private IUserRepository UserRepository { get; set; } - public event EventHandler> UserPasswordChanged; - + private readonly IUserRepository _userRepository; private readonly IXmlSerializer _xmlSerializer; private readonly IJsonSerializer _jsonSerializer; - private readonly INetworkManager _networkManager; private readonly Func _imageProcessorFactory; @@ -76,6 +60,8 @@ namespace Emby.Server.Implementations.Library private readonly IServerApplicationHost _appHost; private readonly IFileSystem _fileSystem; + private ConcurrentDictionary _users; + private IAuthenticationProvider[] _authenticationProviders; private DefaultAuthenticationProvider _defaultAuthenticationProvider; @@ -85,8 +71,7 @@ namespace Emby.Server.Implementations.Library private DefaultPasswordResetProvider _defaultPasswordResetProvider; public UserManager( - ILoggerFactory loggerFactory, - IServerConfigurationManager configurationManager, + ILogger logger, IUserRepository userRepository, IXmlSerializer xmlSerializer, INetworkManager networkManager, @@ -96,8 +81,8 @@ namespace Emby.Server.Implementations.Library IJsonSerializer jsonSerializer, IFileSystem fileSystem) { - _logger = loggerFactory.CreateLogger(nameof(UserManager)); - UserRepository = userRepository; + _logger = logger; + _userRepository = userRepository; _xmlSerializer = xmlSerializer; _networkManager = networkManager; _imageProcessorFactory = imageProcessorFactory; @@ -105,8 +90,51 @@ namespace Emby.Server.Implementations.Library _appHost = appHost; _jsonSerializer = jsonSerializer; _fileSystem = fileSystem; - ConfigurationManager = configurationManager; - _users = Array.Empty(); + _users = null; + } + + public event EventHandler> UserPasswordChanged; + + /// + /// Occurs when [user updated]. + /// + public event EventHandler> UserUpdated; + + public event EventHandler> UserPolicyUpdated; + + public event EventHandler> UserConfigurationUpdated; + + public event EventHandler> UserLockedOut; + + public event EventHandler> UserCreated; + + /// + /// Occurs when [user deleted]. + /// + public event EventHandler> UserDeleted; + + /// + public IEnumerable Users => _users.Values; + + /// + public IEnumerable UsersIds => _users.Keys; + + /// + /// Called when [user updated]. + /// + /// The user. + private void OnUserUpdated(User user) + { + UserUpdated?.Invoke(this, new GenericEventArgs { Argument = user }); + } + + /// + /// Called when [user deleted]. + /// + /// The user. + private void OnUserDeleted(User user) + { + UserDeleted?.Invoke(this, new GenericEventArgs { Argument = user }); } public NameIdPair[] GetAuthenticationProviders() @@ -137,7 +165,7 @@ namespace Emby.Server.Implementations.Library .ToArray(); } - public void AddParts(IEnumerable authenticationProviders,IEnumerable passwordResetProviders) + public void AddParts(IEnumerable authenticationProviders, IEnumerable passwordResetProviders) { _authenticationProviders = authenticationProviders.ToArray(); @@ -150,54 +178,21 @@ namespace Emby.Server.Implementations.Library _defaultPasswordResetProvider = passwordResetProviders.OfType().First(); } - #region UserUpdated Event /// - /// Occurs when [user updated]. - /// - public event EventHandler> UserUpdated; - public event EventHandler> UserPolicyUpdated; - public event EventHandler> UserConfigurationUpdated; - public event EventHandler> UserLockedOut; - - /// - /// Called when [user updated]. - /// - /// The user. - private void OnUserUpdated(User user) - { - UserUpdated?.Invoke(this, new GenericEventArgs { Argument = user }); - } - #endregion - - #region UserDeleted Event - /// - /// Occurs when [user deleted]. - /// - public event EventHandler> UserDeleted; - /// - /// Called when [user deleted]. - /// - /// The user. - private void OnUserDeleted(User user) - { - UserDeleted?.Invoke(this, new GenericEventArgs { Argument = user }); - } - #endregion - - /// - /// Gets a User by Id + /// Gets a User by Id. /// /// The id. /// User. - /// + /// public User GetUserById(Guid id) { if (id == Guid.Empty) { - throw new ArgumentException(nameof(id), "Guid can't be empty"); + throw new ArgumentException("Guid can't be empty", nameof(id)); } - return Users.FirstOrDefault(u => u.Id == id); + _users.TryGetValue(id, out User user); + return user; } /// @@ -206,15 +201,13 @@ namespace Emby.Server.Implementations.Library /// The identifier. /// User. public User GetUserById(string id) - { - return GetUserById(new Guid(id)); - } + => GetUserById(new Guid(id)); public User GetUserByName(string name) { if (string.IsNullOrWhiteSpace(name)) { - throw new ArgumentNullException(nameof(name)); + throw new ArgumentException("Invalid username", nameof(name)); } return Users.FirstOrDefault(u => string.Equals(u.Name, name, StringComparison.OrdinalIgnoreCase)); @@ -222,8 +215,9 @@ namespace Emby.Server.Implementations.Library public void Initialize() { - var users = LoadUsers(); - _users = users.ToArray(); + LoadUsers(); + + var users = Users; // If there are no local users with admin rights, make them all admins if (!users.Any(i => i.Policy.IsAdministrator)) @@ -240,14 +234,12 @@ namespace Emby.Server.Implementations.Library { // This is some regex that matches only on unicode "word" characters, as well as -, _ and @ // In theory this will cut out most if not all 'control' characters which should help minimize any weirdness - // Usernames can contain letters (a-z + whatever else unicode is cool with), numbers (0-9), at-signs (@), dashes (-), underscores (_), apostrophes ('), and periods (.) + // Usernames can contain letters (a-z + whatever else unicode is cool with), numbers (0-9), at-signs (@), dashes (-), underscores (_), apostrophes ('), and periods (.) return Regex.IsMatch(username, @"^[\w\-'._@]*$"); } private static bool IsValidUsernameCharacter(char i) - { - return IsValidUsername(i.ToString()); - } + => IsValidUsername(i.ToString(CultureInfo.InvariantCulture)); public string MakeValidUsername(string username) { @@ -277,8 +269,7 @@ namespace Emby.Server.Implementations.Library throw new ArgumentNullException(nameof(username)); } - var user = Users - .FirstOrDefault(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase)); + var user = Users.FirstOrDefault(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase)); var success = false; string updatedUsername = null; @@ -299,13 +290,12 @@ namespace Emby.Server.Implementations.Library updatedUsername = authResult.username; success = authResult.success; - if (success && authenticationProvider != null && !(authenticationProvider is DefaultAuthenticationProvider)) + if (success + && authenticationProvider != null + && !(authenticationProvider is DefaultAuthenticationProvider)) { // We should trust the user that the authprovider says, not what was typed - if (updatedUsername != username) - { - username = updatedUsername; - } + username = updatedUsername; // Search the database for the user again; the authprovider might have created it user = Users @@ -337,10 +327,11 @@ namespace Emby.Server.Implementations.Library if (user.Policy.IsDisabled) { - throw new AuthenticationException(string.Format( - CultureInfo.InvariantCulture, - "The {0} account is currently disabled. Please consult with your administrator.", - user.Name)); + throw new AuthenticationException( + string.Format( + CultureInfo.InvariantCulture, + "The {0} account is currently disabled. Please consult with your administrator.", + user.Name)); } if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(remoteEndPoint)) @@ -386,7 +377,7 @@ namespace Emby.Server.Implementations.Library private IAuthenticationProvider GetAuthenticationProvider(User user) { - return GetAuthenticationProviders(user).First(); + return GetAuthenticationProviders(user)[0]; } private IPasswordResetProvider GetPasswordResetProvider(User user) @@ -396,7 +387,7 @@ namespace Emby.Server.Implementations.Library private IAuthenticationProvider[] GetAuthenticationProviders(User user) { - var authenticationProviderId = user == null ? null : user.Policy.AuthenticationProviderId; + var authenticationProviderId = user?.Policy.AuthenticationProviderId; var providers = _authenticationProviders.Where(i => i.IsEnabled).ToArray(); @@ -438,16 +429,10 @@ namespace Emby.Server.Implementations.Library { try { - var requiresResolvedUser = provider as IRequiresResolvedUser; - ProviderAuthenticationResult authenticationResult = null; - if (requiresResolvedUser != null) - { - authenticationResult = await requiresResolvedUser.Authenticate(username, password, resolvedUser).ConfigureAwait(false); - } - else - { - authenticationResult = await provider.Authenticate(username, password).ConfigureAwait(false); - } + + var authenticationResult = provider is IRequiresResolvedUser requiresResolvedUser + ? await requiresResolvedUser.Authenticate(username, password, resolvedUser).ConfigureAwait(false) + : await provider.Authenticate(username, password).ConfigureAwait(false); if (authenticationResult.Username != username) { @@ -467,7 +452,6 @@ namespace Emby.Server.Implementations.Library private async Task<(IAuthenticationProvider authenticationProvider, string username, bool success)> AuthenticateLocalUser(string username, string password, string hashedPassword, User user, string remoteEndPoint) { - string updatedUsername = null; bool success = false; IAuthenticationProvider authenticationProvider = null; @@ -487,7 +471,7 @@ namespace Emby.Server.Implementations.Library foreach (var provider in GetAuthenticationProviders(user)) { var providerAuthResult = await AuthenticateWithProvider(provider, username, password, user).ConfigureAwait(false); - updatedUsername = providerAuthResult.username; + var updatedUsername = providerAuthResult.username; success = providerAuthResult.success; if (success) @@ -499,25 +483,32 @@ namespace Emby.Server.Implementations.Library } } - if (user != null) + if (user != null + && !success + && _networkManager.IsInLocalNetwork(remoteEndPoint) + && user.Configuration.EnableLocalPassword) { - if (!success && _networkManager.IsInLocalNetwork(remoteEndPoint) && user.Configuration.EnableLocalPassword) + if (password == null) { - if (password == null) - { - // legacy - success = string.Equals(GetAuthenticationProvider(user).GetEasyPasswordHash(user), hashedPassword.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase); - } - else - { - success = string.Equals(GetAuthenticationProvider(user).GetEasyPasswordHash(user), _defaultAuthenticationProvider.GetHashedString(user, password), StringComparison.OrdinalIgnoreCase); - } + // legacy + success = string.Equals(GetLocalPasswordHash(user), hashedPassword.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase); + } + else + { + success = string.Equals(GetLocalPasswordHash(user), _defaultAuthenticationProvider.GetHashedString(user, password), StringComparison.OrdinalIgnoreCase); } } return (authenticationProvider, username, success); } + private string GetLocalPasswordHash(User user) + { + return string.IsNullOrEmpty(user.EasyPassword) + ? null + : PasswordHash.ConvertToByteString(new PasswordHash(user.EasyPassword).Hash); + } + private void UpdateInvalidLoginAttemptCount(User user, int newValue) { if (user.Policy.InvalidLoginAttemptCount == newValue || newValue <= 0) @@ -556,17 +547,17 @@ namespace Emby.Server.Implementations.Library } /// - /// Loads the users from the repository + /// Loads the users from the repository. /// - /// IEnumerable{User}. - private List LoadUsers() + private void LoadUsers() { - var users = UserRepository.RetrieveAllUsers(); + var users = _userRepository.RetrieveAllUsers(); // There always has to be at least one user. if (users.Count != 0) { - return users; + _users = new ConcurrentDictionary( + users.Select(x => new KeyValuePair(x.Id, x))); } var defaultName = Environment.UserName; @@ -581,14 +572,15 @@ namespace Emby.Server.Implementations.Library user.DateLastSaved = DateTime.UtcNow; - UserRepository.CreateUser(user); + _userRepository.CreateUser(user); user.Policy.IsAdministrator = true; user.Policy.EnableContentDeletion = true; user.Policy.EnableRemoteControlOfOtherUsers = true; UpdateUserPolicy(user, user.Policy, false); - return new List { user }; + _users = new ConcurrentDictionary(); + _users[user.Id] = user; } public UserDto GetUserDto(User user, string remoteEndPoint = null) @@ -619,7 +611,7 @@ namespace Emby.Server.Implementations.Library Policy = user.Policy }; - if (!hasPassword && Users.Count() == 1) + if (!hasPassword && _users.Count == 1) { dto.EnableAutoLogin = true; } @@ -694,22 +686,26 @@ namespace Emby.Server.Implementations.Library throw new ArgumentNullException(nameof(user)); } - if (string.IsNullOrEmpty(newName)) + if (string.IsNullOrWhiteSpace(newName)) { - throw new ArgumentNullException(nameof(newName)); + throw new ArgumentException("Invalid username", nameof(newName)); } - if (Users.Any(u => u.Id != user.Id && u.Name.Equals(newName, StringComparison.OrdinalIgnoreCase))) + if (user.Name.Equals(newName, StringComparison.OrdinalIgnoreCase)) { - throw new ArgumentException(string.Format("A user with the name '{0}' already exists.", newName)); + throw new ArgumentException("The new and old names must be different."); } - if (user.Name.Equals(newName, StringComparison.Ordinal)) + if (Users.Any( + u => u.Id != user.Id && u.Name.Equals(newName, StringComparison.OrdinalIgnoreCase))) { - throw new ArgumentException("The new and old names must be different."); + throw new ArgumentException(string.Format( + CultureInfo.InvariantCulture, + "A user with the name '{0}' already exists.", + newName)); } - await user.Rename(newName); + await user.Rename(newName).ConfigureAwait(false); OnUserUpdated(user); } @@ -727,23 +723,30 @@ namespace Emby.Server.Implementations.Library throw new ArgumentNullException(nameof(user)); } - if (user.Id.Equals(Guid.Empty) || !Users.Any(u => u.Id.Equals(user.Id))) + if (user.Id == Guid.Empty) { - throw new ArgumentException(string.Format("User with name '{0}' and Id {1} does not exist.", user.Name, user.Id)); + throw new ArgumentException("Id can't be empty.", nameof(user)); + } + + if (!_users.ContainsKey(user.Id)) + { + throw new ArgumentException( + string.Format( + CultureInfo.InvariantCulture, + "A user '{0}' with Id {1} does not exist.", + user.Name, + user.Id), + nameof(user)); } user.DateModified = DateTime.UtcNow; user.DateLastSaved = DateTime.UtcNow; - UserRepository.UpdateUser(user); + _userRepository.UpdateUser(user); OnUserUpdated(user); } - public event EventHandler> UserCreated; - - private readonly SemaphoreSlim _userListLock = new SemaphoreSlim(1, 1); - /// /// Creates the user. /// @@ -751,7 +754,7 @@ namespace Emby.Server.Implementations.Library /// User. /// name /// - public async Task CreateUser(string name) + public User CreateUser(string name) { if (string.IsNullOrWhiteSpace(name)) { @@ -768,28 +771,17 @@ namespace Emby.Server.Implementations.Library throw new ArgumentException(string.Format("A user with the name '{0}' already exists.", name)); } - await _userListLock.WaitAsync(CancellationToken.None).ConfigureAwait(false); - - try - { - var user = InstantiateNewUser(name); + var user = InstantiateNewUser(name); - var list = Users.ToList(); - list.Add(user); - _users = list.ToArray(); + _users[user.Id] = user; - user.DateLastSaved = DateTime.UtcNow; + user.DateLastSaved = DateTime.UtcNow; - UserRepository.CreateUser(user); + _userRepository.CreateUser(user); - EventHelper.QueueEventIfNotNull(UserCreated, this, new GenericEventArgs { Argument = user }, _logger); + EventHelper.QueueEventIfNotNull(UserCreated, this, new GenericEventArgs { Argument = user }, _logger); - return user; - } - finally - { - _userListLock.Release(); - } + return user; } /// @@ -799,57 +791,59 @@ namespace Emby.Server.Implementations.Library /// Task. /// user /// - public async Task DeleteUser(User user) + public void DeleteUser(User user) { if (user == null) { throw new ArgumentNullException(nameof(user)); } - var allUsers = Users.ToList(); - - if (allUsers.FirstOrDefault(u => u.Id == user.Id) == null) + if (!_users.ContainsKey(user.Id)) { - throw new ArgumentException(string.Format("The user cannot be deleted because there is no user with the Name {0} and Id {1}.", user.Name, user.Id)); + throw new ArgumentException(string.Format( + CultureInfo.InvariantCulture, + "The user cannot be deleted because there is no user with the Name {0} and Id {1}.", + user.Name, + user.Id)); } - if (allUsers.Count == 1) + if (_users.Count == 1) { - throw new ArgumentException(string.Format("The user '{0}' cannot be deleted because there must be at least one user in the system.", user.Name)); + throw new ArgumentException(string.Format( + CultureInfo.InvariantCulture, + "The user '{0}' cannot be deleted because there must be at least one user in the system.", + user.Name)); } - if (user.Policy.IsAdministrator && allUsers.Count(i => i.Policy.IsAdministrator) == 1) + if (user.Policy.IsAdministrator + && Users.Count(i => i.Policy.IsAdministrator) == 1) { - throw new ArgumentException(string.Format("The user '{0}' cannot be deleted because there must be at least one admin user in the system.", user.Name)); + throw new ArgumentException( + string.Format( + CultureInfo.InvariantCulture, + "The user '{0}' cannot be deleted because there must be at least one admin user in the system.", + user.Name), + nameof(user)); } - await _userListLock.WaitAsync(CancellationToken.None).ConfigureAwait(false); + var configPath = GetConfigurationFilePath(user); + + _userRepository.DeleteUser(user); try { - var configPath = GetConfigurationFilePath(user); - - UserRepository.DeleteUser(user); - - try - { - _fileSystem.DeleteFile(configPath); - } - catch (IOException ex) - { - _logger.LogError(ex, "Error deleting file {path}", configPath); - } - - DeleteUserPolicy(user); - - _users = allUsers.Where(i => i.Id != user.Id).ToArray(); - - OnUserDeleted(user); + _fileSystem.DeleteFile(configPath); } - finally + catch (IOException ex) { - _userListLock.Release(); + _logger.LogError(ex, "Error deleting file {path}", configPath); } + + DeleteUserPolicy(user); + + _users.TryRemove(user.Id, out _); + + OnUserDeleted(user); } /// @@ -906,8 +900,7 @@ namespace Emby.Server.Implementations.Library Name = name, Id = Guid.NewGuid(), DateCreated = DateTime.UtcNow, - DateModified = DateTime.UtcNow, - UsesIdForConfigurationPath = true + DateModified = DateTime.UtcNow }; } @@ -989,7 +982,6 @@ namespace Emby.Server.Implementations.Library }; } - private readonly object _policySyncLock = new object(); public void UpdateUserPolicy(Guid userId, UserPolicy userPolicy) { var user = GetUserById(userId); diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 0347100a4..61329160a 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1375,16 +1375,14 @@ namespace Emby.Server.Implementations.Session CheckDisposed(); User user = null; - if (!request.UserId.Equals(Guid.Empty)) + if (request.UserId != Guid.Empty) { - user = _userManager.Users - .FirstOrDefault(i => i.Id == request.UserId); + user = _userManager.GetUserById(request.UserId); } if (user == null) { - user = _userManager.Users - .FirstOrDefault(i => string.Equals(request.Username, i.Name, StringComparison.OrdinalIgnoreCase)); + user = _userManager.GetUserByName(request.Username); } if (user != null) diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs index fa70a52aa..21a94a4e0 100644 --- a/MediaBrowser.Api/UserService.cs +++ b/MediaBrowser.Api/UserService.cs @@ -365,8 +365,8 @@ namespace MediaBrowser.Api } _sessionMananger.RevokeUserTokens(user.Id, null); - - return _userManager.DeleteUser(user); + _userManager.DeleteUser(user); + return Task.CompletedTask; } /// @@ -503,9 +503,14 @@ namespace MediaBrowser.Api } } + /// + /// Posts the specified request. + /// + /// The request. + /// System.Object. public async Task Post(CreateUserByName request) { - var newUser = await _userManager.CreateUser(request.Name).ConfigureAwait(false); + var newUser = _userManager.CreateUser(request.Name); // no need to authenticate password for new user if (request.Password != null) diff --git a/MediaBrowser.Controller/Authentication/AuthenticationException.cs b/MediaBrowser.Controller/Authentication/AuthenticationException.cs index 045cbcdae..62eca3ea9 100644 --- a/MediaBrowser.Controller/Authentication/AuthenticationException.cs +++ b/MediaBrowser.Controller/Authentication/AuthenticationException.cs @@ -1,4 +1,5 @@ using System; + namespace MediaBrowser.Controller.Authentication { /// diff --git a/MediaBrowser.Controller/Entities/User.cs b/MediaBrowser.Controller/Entities/User.cs index 968d72579..7d245d4aa 100644 --- a/MediaBrowser.Controller/Entities/User.cs +++ b/MediaBrowser.Controller/Entities/User.cs @@ -17,13 +17,6 @@ namespace MediaBrowser.Controller.Entities public class User : BaseItem { public static IUserManager UserManager { get; set; } - public static IXmlSerializer XmlSerializer { get; set; } - - /// - /// From now on all user paths will be Id-based. - /// This is for backwards compatibility. - /// - public bool UsesIdForConfigurationPath { get; set; } /// /// Gets or sets the password. @@ -31,7 +24,6 @@ namespace MediaBrowser.Controller.Entities /// The password. public string Password { get; set; } public string EasyPassword { get; set; } - public string Salt { get; set; } // Strictly to remove IgnoreDataMember public override ItemImageInfo[] ImageInfos @@ -148,46 +140,23 @@ namespace MediaBrowser.Controller.Entities /// public Task Rename(string newName) { - if (string.IsNullOrEmpty(newName)) - { - throw new ArgumentNullException(nameof(newName)); - } - - // If only the casing is changing, leave the file system alone - if (!UsesIdForConfigurationPath && !string.Equals(newName, Name, StringComparison.OrdinalIgnoreCase)) + if (string.IsNullOrWhiteSpace(newName)) { - UsesIdForConfigurationPath = true; - - // Move configuration - var newConfigDirectory = GetConfigurationDirectoryPath(newName); - var oldConfigurationDirectory = ConfigurationDirectoryPath; - - // Exceptions will be thrown if these paths already exist - if (Directory.Exists(newConfigDirectory)) - { - Directory.Delete(newConfigDirectory, true); - } - - if (Directory.Exists(oldConfigurationDirectory)) - { - Directory.Move(oldConfigurationDirectory, newConfigDirectory); - } - else - { - Directory.CreateDirectory(newConfigDirectory); - } + throw new ArgumentException("Username can't be empty", nameof(newName)); } Name = newName; - return RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(Logger, FileSystem)) - { - ReplaceAllMetadata = true, - ImageRefreshMode = MetadataRefreshMode.FullRefresh, - MetadataRefreshMode = MetadataRefreshMode.FullRefresh, - ForceSave = true + return RefreshMetadata( + new MetadataRefreshOptions(new DirectoryService(Logger, FileSystem)) + { + ReplaceAllMetadata = true, + ImageRefreshMode = MetadataRefreshMode.FullRefresh, + MetadataRefreshMode = MetadataRefreshMode.FullRefresh, + ForceSave = true - }, CancellationToken.None); + }, + CancellationToken.None); } public override void UpdateToRepository(ItemUpdateType updateReason, CancellationToken cancellationToken) @@ -216,19 +185,6 @@ namespace MediaBrowser.Controller.Entities { var parentPath = ConfigurationManager.ApplicationPaths.UserConfigurationDirectoryPath; - // Legacy - if (!UsesIdForConfigurationPath) - { - if (string.IsNullOrEmpty(username)) - { - throw new ArgumentNullException(nameof(username)); - } - - var safeFolderName = FileSystem.GetValidFilename(username); - - return System.IO.Path.Combine(ConfigurationManager.ApplicationPaths.UserConfigurationDirectoryPath, safeFolderName); - } - // TODO: Remove idPath and just use usernamePath for future releases var usernamePath = System.IO.Path.Combine(parentPath, username); var idPath = System.IO.Path.Combine(parentPath, Id.ToString("N", CultureInfo.InvariantCulture)); diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs index 7f7370893..bbedc0442 100644 --- a/MediaBrowser.Controller/Library/IUserManager.cs +++ b/MediaBrowser.Controller/Library/IUserManager.cs @@ -22,6 +22,12 @@ namespace MediaBrowser.Controller.Library /// The users. IEnumerable Users { get; } + /// + /// Gets the user ids. + /// + /// The users ids. + IEnumerable UsersIds { get; } + /// /// Occurs when [user updated]. /// @@ -92,7 +98,7 @@ namespace MediaBrowser.Controller.Library /// User. /// name /// - Task CreateUser(string name); + User CreateUser(string name); /// /// Deletes the user. @@ -101,7 +107,7 @@ namespace MediaBrowser.Controller.Library /// Task. /// user /// - Task DeleteUser(User user); + void DeleteUser(User user); /// /// Resets the password. -- cgit v1.2.3 From 24fac4b19172eae6a46208d712de09ac97e59d07 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 18 Aug 2019 20:12:25 +0200 Subject: Fix UserNotFoundError --- Emby.Server.Implementations/Library/UserManager.cs | 1 + 1 file changed, 1 insertion(+) (limited to 'Emby.Server.Implementations/Library') diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index 086527883..a7ea13ca6 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -558,6 +558,7 @@ namespace Emby.Server.Implementations.Library { _users = new ConcurrentDictionary( users.Select(x => new KeyValuePair(x.Id, x))); + return; } var defaultName = Environment.UserName; -- cgit v1.2.3