Browse Source

Merge branch 'master' into master

whooo 6 years ago
parent
commit
9556561a77
100 changed files with 1142 additions and 4561 deletions
  1. 1 1
      .ci/azure-pipelines.yml
  2. 0 20
      .github/ISSUE_TEMPLATE/enhancement-request.md
  3. 0 14
      .github/ISSUE_TEMPLATE/feature_request.md
  4. 22 0
      .github/stale.yml
  5. 1 0
      CONTRIBUTORS.md
  6. 3 3
      Dockerfile
  7. 3 3
      Dockerfile.arm
  8. 3 3
      Dockerfile.arm64
  9. 7 27
      Emby.Dlna/Didl/DidlBuilder.cs
  10. 3 2
      Emby.Dlna/DlnaManager.cs
  11. 1 1
      Emby.Dlna/Eventing/EventManager.cs
  12. 4 2
      Emby.Dlna/Main/DlnaEntryPoint.cs
  13. 5 4
      Emby.Dlna/PlayTo/PlayToManager.cs
  14. 1 1
      Emby.Dlna/Profiles/DishHopperJoeyProfile.cs
  15. 1 1
      Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml
  16. 3 3
      Emby.Drawing/ImageProcessor.cs
  17. 3 2
      Emby.Notifications/NotificationManager.cs
  18. 2 2
      Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
  19. 2 2
      Emby.Server.Implementations/Activity/ActivityRepository.cs
  20. 20 17
      Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
  21. 51 41
      Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs
  22. 69 47
      Emby.Server.Implementations/ApplicationHost.cs
  23. 9 8
      Emby.Server.Implementations/Channels/ChannelManager.cs
  24. 2 1
      Emby.Server.Implementations/Collections/CollectionManager.cs
  25. 2 2
      Emby.Server.Implementations/ConfigurationOptions.cs
  26. 49 37
      Emby.Server.Implementations/Cryptography/CryptographyProvider.cs
  27. 2 1
      Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs
  28. 10 9
      Emby.Server.Implementations/Data/SqliteItemRepository.cs
  29. 2 2
      Emby.Server.Implementations/Devices/DeviceId.cs
  30. 2 2
      Emby.Server.Implementations/Devices/DeviceManager.cs
  31. 9 8
      Emby.Server.Implementations/Dto/DtoService.cs
  32. 2 0
      Emby.Server.Implementations/Emby.Server.Implementations.csproj
  33. 8 8
      Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
  34. 2 1
      Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs
  35. 3 3
      Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
  36. 2 1
      Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
  37. 107 67
      Emby.Server.Implementations/HttpServer/FileWriter.cs
  38. 63 91
      Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
  39. 15 11
      Emby.Server.Implementations/HttpServer/ResponseFilter.cs
  40. 23 25
      Emby.Server.Implementations/HttpServer/Security/AuthService.cs
  41. 2 1
      Emby.Server.Implementations/IO/ManagedFileSystem.cs
  42. 2 1
      Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
  43. 14 55
      Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
  44. 55 60
      Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs
  45. 125 132
      Emby.Server.Implementations/Library/DefaultPasswordResetProvider.cs
  46. 2 1
      Emby.Server.Implementations/Library/ExclusiveLiveStream.cs
  47. 4 7
      Emby.Server.Implementations/Library/InvalidAuthProvider.cs
  48. 7 7
      Emby.Server.Implementations/Library/LibraryManager.cs
  49. 1 1
      Emby.Server.Implementations/Library/LiveStreamHelper.cs
  50. 3 3
      Emby.Server.Implementations/Library/MediaSourceManager.cs
  51. 15 12
      Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
  52. 1 1
      Emby.Server.Implementations/Library/UserDataManager.cs
  53. 27 22
      Emby.Server.Implementations/Library/UserManager.cs
  54. 5 4
      Emby.Server.Implementations/Library/UserViewManager.cs
  55. 2 1
      Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs
  56. 2 2
      Emby.Server.Implementations/Library/Validators/PeopleValidator.cs
  57. 2 1
      Emby.Server.Implementations/Library/Validators/StudiosValidator.cs
  58. 4 4
      Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
  59. 2 2
      Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
  60. 14 13
      Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
  61. 12 11
      Emby.Server.Implementations/LiveTv/LiveTvManager.cs
  62. 2 1
      Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
  63. 6 6
      Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
  64. 8 8
      Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
  65. 14 3
      Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
  66. 2 1
      Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs
  67. 4 3
      Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
  68. 3 2
      Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
  69. 4 4
      Emby.Server.Implementations/Localization/Core/da.json
  70. 2 2
      Emby.Server.Implementations/Localization/Core/hu.json
  71. 14 14
      Emby.Server.Implementations/Localization/Core/pl.json
  72. 7 7
      Emby.Server.Implementations/Localization/Core/pt-BR.json
  73. 6 8
      Emby.Server.Implementations/Net/SocketFactory.cs
  74. 9 22
      Emby.Server.Implementations/Net/UdpSocket.cs
  75. 0 167
      Emby.Server.Implementations/Networking/IPNetwork/BigIntegerExt.cs
  76. 0 94
      Emby.Server.Implementations/Networking/IPNetwork/IPAddressCollection.cs
  77. 0 2008
      Emby.Server.Implementations/Networking/IPNetwork/IPNetwork.cs
  78. 0 129
      Emby.Server.Implementations/Networking/IPNetwork/IPNetworkCollection.cs
  79. 0 24
      Emby.Server.Implementations/Networking/IPNetwork/LICENSE.txt
  80. 65 212
      Emby.Server.Implementations/Networking/NetworkManager.cs
  81. 4 3
      Emby.Server.Implementations/Playlists/PlaylistManager.cs
  82. 2 2
      Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
  83. 3 3
      Emby.Server.Implementations/Security/AuthenticationRepository.cs
  84. 2 1
      Emby.Server.Implementations/Serialization/JsonSerializer.cs
  85. 14 13
      Emby.Server.Implementations/ServerApplicationPaths.cs
  86. 5 4
      Emby.Server.Implementations/Services/HttpResult.cs
  87. 15 16
      Emby.Server.Implementations/Services/ResponseHelper.cs
  88. 0 4
      Emby.Server.Implementations/Services/ServiceController.cs
  89. 1 1
      Emby.Server.Implementations/Services/ServiceExec.cs
  90. 38 69
      Emby.Server.Implementations/Services/ServiceHandler.cs
  91. 1 1
      Emby.Server.Implementations/Session/HttpSessionController.cs
  92. 9 7
      Emby.Server.Implementations/Session/SessionManager.cs
  93. 0 647
      Emby.Server.Implementations/SocketSharp/RequestMono.cs
  94. 58 140
      Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs
  95. 0 98
      Emby.Server.Implementations/SocketSharp/WebSocketSharpResponse.cs
  96. 2 1
      Emby.Server.Implementations/TV/TVSeriesManager.cs
  97. 8 7
      Emby.Server.Implementations/Udp/UdpServer.cs
  98. 6 7
      Jellyfin.Server/Program.cs
  99. 8 7
      MediaBrowser.Api/Devices/DeviceService.cs
  100. 2 1
      MediaBrowser.Api/Images/ImageService.cs

+ 1 - 1
.ci/azure-pipelines.yml

@@ -190,7 +190,7 @@ jobs:
     - task: CmdLine@2
     - task: CmdLine@2
       displayName: Execute ABI compatibility check tool
       displayName: Execute ABI compatibility check tool
       inputs:
       inputs:
-        script: 'dotnet tools/CompatibilityCheckerCoreCLI.dll current-release/$(AssemblyFileName) new-release/$(AssemblyFileName)'
+        script: 'dotnet tools/CompatibilityCheckerCoreCLI.dll current-release/$(AssemblyFileName) new-release/$(AssemblyFileName) --azure-pipelines'
         workingDirectory: $(System.ArtifactsDirectory) # Optional
         workingDirectory: $(System.ArtifactsDirectory) # Optional
         #failOnStderr: false # Optional
         #failOnStderr: false # Optional
 
 

+ 0 - 20
.github/ISSUE_TEMPLATE/enhancement-request.md

@@ -1,20 +0,0 @@
----
-name: Enhancement request
-about: Suggest an modification to an existing feature
-title: ''
-labels: enhancement
-assignees: ''
-
----
-
-**Is your feature request related to a problem? Please describe.**
-<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
-
-**Describe the solution you'd like**
-<!-- A clear and concise description of what you want to happen. -->
-
-**Describe alternatives you've considered**
-<!-- A clear and concise description of any alternative solutions or features you've considered. -->
-
-**Additional context**
-<!-- Add any other context or screenshots about the feature request here. -->

+ 0 - 14
.github/ISSUE_TEMPLATE/feature_request.md

@@ -1,14 +0,0 @@
----
-name: Feature request
-about: Suggest a new feature
-title: ''
-labels: feature
-assignees: ''
-
----
-
-**Describe the feature you'd like**
-<!-- A clear and concise description of what you want to happen. -->
-
-**Additional context**
-<!-- Add any other context or screenshots about the feature request here. -->

+ 22 - 0
.github/stale.yml

@@ -0,0 +1,22 @@
+# Number of days of inactivity before an issue becomes stale
+daysUntilStale: 90
+# Number of days of inactivity before a stale issue is closed
+daysUntilClose: 14
+# Issues with these labels will never be considered stale
+exemptLabels:
+  - regression
+  - security
+  - dotnet-3.0-future
+  - roadmap
+  - future
+  - feature
+  - enhancement
+# Label to use when marking an issue as stale
+staleLabel: stale
+# Comment to post when marking an issue as stale. Set to `false` to disable
+markComment: >
+  Issues go stale after 90d of inactivity. Mark the issue as fresh by adding a comment or commit. Stale issues close after an additional 14d of inactivity.
+  If this issue is safe to close now please do so.
+  If you have any questions you can reach us on [Matrix or Social Media](https://jellyfin.readthedocs.io/en/latest/getting-help/).
+# Comment to post when closing a stale issue. Set to `false` to disable
+closeComment: false

+ 1 - 0
CONTRIBUTORS.md

@@ -27,6 +27,7 @@
  - [pjeanjean](https://github.com/pjeanjean)
  - [pjeanjean](https://github.com/pjeanjean)
  - [DrPandemic](https://github.com/drpandemic)
  - [DrPandemic](https://github.com/drpandemic)
  - [joern-h](https://github.com/joern-h)
  - [joern-h](https://github.com/joern-h)
+ - [Khinenw](https://github.com/HelloWorld017)
 
 
 # Emby Contributors
 # Emby Contributors
 
 

+ 3 - 3
Dockerfile

@@ -21,10 +21,10 @@ RUN apt-get update \
 COPY --from=ffmpeg / /
 COPY --from=ffmpeg / /
 COPY --from=builder /jellyfin /jellyfin
 COPY --from=builder /jellyfin /jellyfin
 
 
-ARG JELLYFIN_WEB_VERSION=10.3.7
-RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
+ARG JELLYFIN_WEB_VERSION=v10.3.7
+RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
  && rm -rf /jellyfin/jellyfin-web \
  && rm -rf /jellyfin/jellyfin-web \
- && mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
+ && mv jellyfin-web-* /jellyfin/jellyfin-web
 
 
 EXPOSE 8096
 EXPOSE 8096
 VOLUME /cache /config /media
 VOLUME /cache /config /media

+ 3 - 3
Dockerfile.arm

@@ -26,10 +26,10 @@ RUN apt-get update \
  && chmod 777 /cache /config /media
  && chmod 777 /cache /config /media
 COPY --from=builder /jellyfin /jellyfin
 COPY --from=builder /jellyfin /jellyfin
 
 
-ARG JELLYFIN_WEB_VERSION=10.3.7
-RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
+ARG JELLYFIN_WEB_VERSION=v10.3.7
+RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
  && rm -rf /jellyfin/jellyfin-web \
  && rm -rf /jellyfin/jellyfin-web \
- && mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
+ && mv jellyfin-web-* /jellyfin/jellyfin-web
 
 
 EXPOSE 8096
 EXPOSE 8096
 VOLUME /cache /config /media
 VOLUME /cache /config /media

+ 3 - 3
Dockerfile.arm64

@@ -26,10 +26,10 @@ RUN apt-get update \
  && chmod 777 /cache /config /media
  && chmod 777 /cache /config /media
 COPY --from=builder /jellyfin /jellyfin
 COPY --from=builder /jellyfin /jellyfin
 
 
-ARG JELLYFIN_WEB_VERSION=10.3.7
-RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
+ARG JELLYFIN_WEB_VERSION=v10.3.7
+RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
  && rm -rf /jellyfin/jellyfin-web \
  && rm -rf /jellyfin/jellyfin-web \
- && mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
+ && mv jellyfin-web-* /jellyfin/jellyfin-web
 
 
 EXPOSE 8096
 EXPOSE 8096
 VOLUME /cache /config /media
 VOLUME /cache /config /media

+ 7 - 27
Emby.Dlna/Didl/DidlBuilder.cs

@@ -181,19 +181,6 @@ namespace Emby.Dlna.Didl
             writer.WriteFullEndElement();
             writer.WriteFullEndElement();
         }
         }
 
 
-        private string GetMimeType(string input)
-        {
-            var mime = MimeTypes.GetMimeType(input);
-
-            // TODO: Instead of being hard-coded here, this should probably be moved into all of the existing profiles
-            if (string.Equals(mime, "video/mp2t", StringComparison.OrdinalIgnoreCase))
-            {
-                mime = "video/mpeg";
-            }
-
-            return mime;
-        }
-
         private void AddVideoResource(DlnaOptions options, XmlWriter writer, BaseItem video, string deviceId, Filter filter, StreamInfo streamInfo = null)
         private void AddVideoResource(DlnaOptions options, XmlWriter writer, BaseItem video, string deviceId, Filter filter, StreamInfo streamInfo = null)
         {
         {
             if (streamInfo == null)
             if (streamInfo == null)
@@ -384,7 +371,7 @@ namespace Emby.Dlna.Didl
             var filename = url.Substring(0, url.IndexOf('?'));
             var filename = url.Substring(0, url.IndexOf('?'));
 
 
             var mimeType = mediaProfile == null || string.IsNullOrEmpty(mediaProfile.MimeType)
             var mimeType = mediaProfile == null || string.IsNullOrEmpty(mediaProfile.MimeType)
-               ? GetMimeType(filename)
+               ? MimeTypes.GetMimeType(filename)
                : mediaProfile.MimeType;
                : mediaProfile.MimeType;
 
 
             writer.WriteAttributeString("protocolInfo", string.Format(
             writer.WriteAttributeString("protocolInfo", string.Format(
@@ -520,7 +507,7 @@ namespace Emby.Dlna.Didl
             var filename = url.Substring(0, url.IndexOf('?'));
             var filename = url.Substring(0, url.IndexOf('?'));
 
 
             var mimeType = mediaProfile == null || string.IsNullOrEmpty(mediaProfile.MimeType)
             var mimeType = mediaProfile == null || string.IsNullOrEmpty(mediaProfile.MimeType)
-                ? GetMimeType(filename)
+                ? MimeTypes.GetMimeType(filename)
                 : mediaProfile.MimeType;
                 : mediaProfile.MimeType;
 
 
             var contentFeatures = new ContentFeatureBuilder(_profile).BuildAudioHeader(streamInfo.Container,
             var contentFeatures = new ContentFeatureBuilder(_profile).BuildAudioHeader(streamInfo.Container,
@@ -545,17 +532,10 @@ namespace Emby.Dlna.Didl
         }
         }
 
 
         public static bool IsIdRoot(string id)
         public static bool IsIdRoot(string id)
-        {
-            if (string.IsNullOrWhiteSpace(id)
+            => string.IsNullOrWhiteSpace(id)
                 || string.Equals(id, "0", StringComparison.OrdinalIgnoreCase)
                 || string.Equals(id, "0", StringComparison.OrdinalIgnoreCase)
                 // Samsung sometimes uses 1 as root
                 // Samsung sometimes uses 1 as root
-                || string.Equals(id, "1", StringComparison.OrdinalIgnoreCase))
-            {
-                return true;
-            }
-
-            return false;
-        }
+                || string.Equals(id, "1", StringComparison.OrdinalIgnoreCase);
 
 
         public void WriteFolderElement(XmlWriter writer, BaseItem folder, StubType? stubType, BaseItem context, int childCount, Filter filter, string requestedId = null)
         public void WriteFolderElement(XmlWriter writer, BaseItem folder, StubType? stubType, BaseItem context, int childCount, Filter filter, string requestedId = null)
         {
         {
@@ -971,7 +951,7 @@ namespace Emby.Dlna.Didl
 
 
             writer.WriteAttributeString("protocolInfo", string.Format(
             writer.WriteAttributeString("protocolInfo", string.Format(
                 "http-get:*:{0}:{1}",
                 "http-get:*:{0}:{1}",
-                GetMimeType("file." + format),
+                MimeTypes.GetMimeType("file." + format),
                 contentFeatures
                 contentFeatures
                 ));
                 ));
 
 
@@ -1102,7 +1082,7 @@ namespace Emby.Dlna.Didl
 
 
         public static string GetClientId(Guid idValue, StubType? stubType)
         public static string GetClientId(Guid idValue, StubType? stubType)
         {
         {
-            var id = idValue.ToString("N");
+            var id = idValue.ToString("N", CultureInfo.InvariantCulture);
 
 
             if (stubType.HasValue)
             if (stubType.HasValue)
             {
             {
@@ -1116,7 +1096,7 @@ namespace Emby.Dlna.Didl
         {
         {
             var url = string.Format("{0}/Items/{1}/Images/{2}/0/{3}/{4}/{5}/{6}/0/0",
             var url = string.Format("{0}/Items/{1}/Images/{2}/0/{3}/{4}/{5}/{6}/0/0",
                 _serverAddress,
                 _serverAddress,
-                info.ItemId.ToString("N"),
+                info.ItemId.ToString("N", CultureInfo.InvariantCulture),
                 info.Type,
                 info.Type,
                 info.ImageTag,
                 info.ImageTag,
                 format,
                 format,

+ 3 - 2
Emby.Dlna/DlnaManager.cs

@@ -1,5 +1,6 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Reflection;
 using System.Reflection;
@@ -300,7 +301,7 @@ namespace Emby.Dlna
 
 
                     profile = ReserializeProfile(tempProfile);
                     profile = ReserializeProfile(tempProfile);
 
 
-                    profile.Id = path.ToLowerInvariant().GetMD5().ToString("N");
+                    profile.Id = path.ToLowerInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture);
 
 
                     _profiles[path] = new Tuple<InternalProfileInfo, DeviceProfile>(GetInternalProfileInfo(_fileSystem.GetFileInfo(path), type), profile);
                     _profiles[path] = new Tuple<InternalProfileInfo, DeviceProfile>(GetInternalProfileInfo(_fileSystem.GetFileInfo(path), type), profile);
 
 
@@ -352,7 +353,7 @@ namespace Emby.Dlna
 
 
                 Info = new DeviceProfileInfo
                 Info = new DeviceProfileInfo
                 {
                 {
-                    Id = file.FullName.ToLowerInvariant().GetMD5().ToString("N"),
+                    Id = file.FullName.ToLowerInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture),
                     Name = _fileSystem.GetFileNameWithoutExtension(file),
                     Name = _fileSystem.GetFileNameWithoutExtension(file),
                     Type = type
                     Type = type
                 }
                 }

+ 1 - 1
Emby.Dlna/Eventing/EventManager.cs

@@ -55,7 +55,7 @@ namespace Emby.Dlna.Eventing
         public EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl)
         public EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl)
         {
         {
             var timeout = ParseTimeout(requestedTimeoutString) ?? 300;
             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
             // Remove logging for now because some devices are sending this very frequently
             // TODO re-enable with dlna debug logging setting
             // TODO re-enable with dlna debug logging setting

+ 4 - 2
Emby.Dlna/Main/DlnaEntryPoint.cs

@@ -1,4 +1,6 @@
 using System;
 using System;
+using System.Net.Sockets;
+using System.Globalization;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using Emby.Dlna.PlayTo;
 using Emby.Dlna.PlayTo;
@@ -247,7 +249,7 @@ namespace Emby.Dlna.Main
 
 
             foreach (var address in addresses)
             foreach (var address in addresses)
             {
             {
-                if (address.AddressFamily == IpAddressFamily.InterNetworkV6)
+                if (address.AddressFamily == AddressFamily.InterNetworkV6)
                 {
                 {
                    // Not support IPv6 right now
                    // Not support IPv6 right now
                    continue;
                    continue;
@@ -306,7 +308,7 @@ namespace Emby.Dlna.Main
             {
             {
                 guid = text.GetMD5();
                 guid = text.GetMD5();
             }
             }
-            return guid.ToString("N");
+            return guid.ToString("N", CultureInfo.InvariantCulture);
         }
         }
 
 
         private void SetProperies(SsdpDevice device, string fullDeviceType)
         private void SetProperies(SsdpDevice device, string fullDeviceType)

+ 5 - 4
Emby.Dlna/PlayTo/PlayToManager.cs

@@ -1,5 +1,7 @@
 using System;
 using System;
+using System.Globalization;
 using System.Linq;
 using System.Linq;
+using System.Net;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Extensions;
@@ -14,7 +16,6 @@ using MediaBrowser.Controller.Session;
 using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Events;
 using MediaBrowser.Model.Events;
 using MediaBrowser.Model.Globalization;
 using MediaBrowser.Model.Globalization;
-using MediaBrowser.Model.Net;
 using MediaBrowser.Model.Session;
 using MediaBrowser.Model.Session;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
 
 
@@ -141,7 +142,7 @@ namespace Emby.Dlna.PlayTo
                 return usn;
                 return usn;
             }
             }
 
 
-            return usn.GetMD5().ToString("N");
+            return usn.GetMD5().ToString("N", CultureInfo.InvariantCulture);
         }
         }
 
 
         private async Task AddDevice(UpnpDeviceInfo info, string location, CancellationToken cancellationToken)
         private async Task AddDevice(UpnpDeviceInfo info, string location, CancellationToken cancellationToken)
@@ -156,7 +157,7 @@ namespace Emby.Dlna.PlayTo
             }
             }
             else
             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);
             var sessionInfo = _sessionManager.LogSessionActivity("DLNA", _appHost.ApplicationVersion, uuid, null, uri.OriginalString, null);
@@ -172,7 +173,7 @@ namespace Emby.Dlna.PlayTo
                 _sessionManager.UpdateDeviceName(sessionInfo.Id, deviceName);
                 _sessionManager.UpdateDeviceName(sessionInfo.Id, deviceName);
 
 
                 string serverAddress;
                 string serverAddress;
-                if (info.LocalIpAddress == null || info.LocalIpAddress.Equals(IpAddressInfo.Any) || info.LocalIpAddress.Equals(IpAddressInfo.IPv6Any))
+                if (info.LocalIpAddress == null || info.LocalIpAddress.Equals(IPAddress.Any) || info.LocalIpAddress.Equals(IPAddress.IPv6Any))
                 {
                 {
                     serverAddress = await _appHost.GetLocalApiUrl(cancellationToken).ConfigureAwait(false);
                     serverAddress = await _appHost.GetLocalApiUrl(cancellationToken).ConfigureAwait(false);
                 }
                 }

+ 1 - 1
Emby.Dlna/Profiles/DishHopperJoeyProfile.cs

@@ -9,7 +9,7 @@ namespace Emby.Dlna.Profiles
         {
         {
             Name = "Dish Hopper-Joey";
             Name = "Dish Hopper-Joey";
 
 
-            ProtocolInfo = "http-get:*:video/mp2t:*,http-get:*:video/MP1S:*,http-get:*:video/mpeg2:*,http-get:*:video/mp4:*,http-get:*:video/x-matroska:*,http-get:*:audio/mpeg:*,http-get:*:audio/mpeg3:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/mp4a-latm:*,http-get:*:image/jpeg:*";
+            ProtocolInfo = "http-get:*:video/mp2t:*,http-get:*:video/mpeg:*,http-get:*:video/MP1S:*,http-get:*:video/mpeg2:*,http-get:*:video/mp4:*,http-get:*:video/x-matroska:*,http-get:*:audio/mpeg:*,http-get:*:audio/mpeg3:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/mp4a-latm:*,http-get:*:image/jpeg:*";
 
 
             Identification = new DeviceIdentification
             Identification = new DeviceIdentification
             {
             {

+ 1 - 1
Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml

@@ -28,7 +28,7 @@
   <MaxStaticBitrate>140000000</MaxStaticBitrate>
   <MaxStaticBitrate>140000000</MaxStaticBitrate>
   <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
   <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
   <MaxStaticMusicBitrate xsi:nil="true" />
   <MaxStaticMusicBitrate xsi:nil="true" />
-  <ProtocolInfo>http-get:*:video/mp2t:*,http-get:*:video/MP1S:*,http-get:*:video/mpeg2:*,http-get:*:video/mp4:*,http-get:*:video/x-matroska:*,http-get:*:audio/mpeg:*,http-get:*:audio/mpeg3:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/mp4a-latm:*,http-get:*:image/jpeg:*</ProtocolInfo>
+  <ProtocolInfo>http-get:*:video/mp2t:http-get:*:video/mpeg:*,http-get:*:video/MP1S:*,http-get:*:video/mpeg2:*,http-get:*:video/mp4:*,http-get:*:video/x-matroska:*,http-get:*:audio/mpeg:*,http-get:*:audio/mpeg3:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/mp4a-latm:*,http-get:*:image/jpeg:*</ProtocolInfo>
   <TimelineOffsetSeconds>0</TimelineOffsetSeconds>
   <TimelineOffsetSeconds>0</TimelineOffsetSeconds>
   <RequiresPlainVideoItems>false</RequiresPlainVideoItems>
   <RequiresPlainVideoItems>false</RequiresPlainVideoItems>
   <RequiresPlainFolders>false</RequiresPlainFolders>
   <RequiresPlainFolders>false</RequiresPlainFolders>

+ 3 - 3
Emby.Drawing/ImageProcessor.cs

@@ -454,14 +454,14 @@ namespace Emby.Drawing
             // Optimization
             // Optimization
             if (imageEnhancers.Length == 0)
             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
             // 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();
             var cacheKeys = imageEnhancers.Select(i => i.GetConfigurationCacheKey(item, imageType)).ToList();
             cacheKeys.Add(originalImagePath + dateModified.Ticks);
             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)
         private async Task<(string path, DateTime dateModified)> GetSupportedImage(string originalImagePath, DateTime dateModified)
@@ -480,7 +480,7 @@ namespace Emby.Drawing
             {
             {
                 try
                 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";
                     string cacheExtension = _mediaEncoder().SupportsEncoder("libwebp") ? ".webp" : ".png";
                     var outputPath = Path.Combine(_appPaths.ImageCachePath, "converted-images", filename + cacheExtension);
                     var outputPath = Path.Combine(_appPaths.ImageCachePath, "converted-images", filename + cacheExtension);

+ 3 - 2
Emby.Notifications/NotificationManager.cs

@@ -1,5 +1,6 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Globalization;
 using System.Linq;
 using System.Linq;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
@@ -101,7 +102,7 @@ namespace Emby.Notifications
                 var config = GetConfiguration();
                 var config = GetConfiguration();
 
 
                 return _userManager.Users
                 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);
                     .Select(i => i.Id);
             }
             }
 
 
@@ -197,7 +198,7 @@ namespace Emby.Notifications
             return _services.Select(i => new NameIdPair
             return _services.Select(i => new NameIdPair
             {
             {
                 Name = i.Name,
                 Name = i.Name,
-                Id = i.Name.GetMD5().ToString("N")
+                Id = i.Name.GetMD5().ToString("N", CultureInfo.InvariantCulture)
 
 
             }).OrderBy(i => i.Name);
             }).OrderBy(i => i.Name);
         }
         }

+ 2 - 2
Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs

@@ -1,5 +1,6 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Globalization;
 using System.Linq;
 using System.Linq;
 using System.Text;
 using System.Text;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
@@ -75,7 +76,6 @@ namespace Emby.Server.Implementations.Activity
             _sessionManager.AuthenticationFailed += OnAuthenticationFailed;
             _sessionManager.AuthenticationFailed += OnAuthenticationFailed;
             _sessionManager.AuthenticationSucceeded += OnAuthenticationSucceeded;
             _sessionManager.AuthenticationSucceeded += OnAuthenticationSucceeded;
             _sessionManager.SessionEnded += OnSessionEnded;
             _sessionManager.SessionEnded += OnSessionEnded;
-
             _sessionManager.PlaybackStart += OnPlaybackStart;
             _sessionManager.PlaybackStart += OnPlaybackStart;
             _sessionManager.PlaybackStopped += OnPlaybackStopped;
             _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)),
                 Name = string.Format(_localization.GetLocalizedString("SubtitleDownloadFailureFromForItem"), e.Provider, Notifications.Notifications.GetItemName(e.Item)),
                 Type = "SubtitleDownloadFailure",
                 Type = "SubtitleDownloadFailure",
-                ItemId = e.Item.Id.ToString("N"),
+                ItemId = e.Item.Id.ToString("N", CultureInfo.InvariantCulture),
                 ShortOverview = e.Exception.Message
                 ShortOverview = e.Exception.Message
             });
             });
         }
         }

+ 2 - 2
Emby.Server.Implementations/Activity/ActivityRepository.cs

@@ -102,7 +102,7 @@ namespace Emby.Server.Implementations.Activity
                         }
                         }
                         else
                         else
                         {
                         {
-                            statement.TryBind("@UserId", entry.UserId.ToString("N"));
+                            statement.TryBind("@UserId", entry.UserId.ToString("N", CultureInfo.InvariantCulture));
                         }
                         }
 
 
                         statement.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue());
                         statement.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue());
@@ -141,7 +141,7 @@ namespace Emby.Server.Implementations.Activity
                         }
                         }
                         else
                         else
                         {
                         {
-                            statement.TryBind("@UserId", entry.UserId.ToString("N"));
+                            statement.TryBind("@UserId", entry.UserId.ToString("N", CultureInfo.InvariantCulture));
                         }
                         }
 
 
                         statement.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue());
                         statement.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue());

+ 20 - 17
Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs

@@ -10,6 +10,8 @@ namespace Emby.Server.Implementations.AppBase
     /// </summary>
     /// </summary>
     public abstract class BaseApplicationPaths : IApplicationPaths
     public abstract class BaseApplicationPaths : IApplicationPaths
     {
     {
+        private string _dataPath;
+
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="BaseApplicationPaths"/> class.
         /// Initializes a new instance of the <see cref="BaseApplicationPaths"/> class.
         /// </summary>
         /// </summary>
@@ -30,27 +32,27 @@ namespace Emby.Server.Implementations.AppBase
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Gets the path to the program data folder
+        /// Gets the path to the program data folder.
         /// </summary>
         /// </summary>
         /// <value>The program data path.</value>
         /// <value>The program data path.</value>
-        public string ProgramDataPath { get; private set; }
+        public string ProgramDataPath { get; }
 
 
         /// <summary>
         /// <summary>
-        /// Gets the path to the web UI resources folder
+        /// Gets the path to the web UI resources folder.
         /// </summary>
         /// </summary>
         /// <value>The web UI resources path.</value>
         /// <value>The web UI resources path.</value>
-        public string WebPath { get; set; }
+        public string WebPath { get; }
 
 
         /// <summary>
         /// <summary>
-        /// Gets the path to the system folder
+        /// Gets the path to the system folder.
         /// </summary>
         /// </summary>
+        /// <value>The path to the system folder.</value>
         public string ProgramSystemPath { get; } = AppContext.BaseDirectory;
         public string ProgramSystemPath { get; } = AppContext.BaseDirectory;
 
 
         /// <summary>
         /// <summary>
-        /// Gets the folder path to the data directory
+        /// Gets the folder path to the data directory.
         /// </summary>
         /// </summary>
         /// <value>The data directory.</value>
         /// <value>The data directory.</value>
-        private string _dataPath;
         public string DataPath
         public string DataPath
         {
         {
             get => _dataPath;
             get => _dataPath;
@@ -58,8 +60,9 @@ namespace Emby.Server.Implementations.AppBase
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Gets the magic strings used for virtual path manipulation.
+        /// Gets the magic string used for virtual path manipulation.
         /// </summary>
         /// </summary>
+        /// <value>The magic string used for virtual path manipulation.</value>
         public string VirtualDataPath { get; } = "%AppDataPath%";
         public string VirtualDataPath { get; } = "%AppDataPath%";
 
 
         /// <summary>
         /// <summary>
@@ -69,43 +72,43 @@ namespace Emby.Server.Implementations.AppBase
         public string ImageCachePath => Path.Combine(CachePath, "images");
         public string ImageCachePath => Path.Combine(CachePath, "images");
 
 
         /// <summary>
         /// <summary>
-        /// Gets the path to the plugin directory
+        /// Gets the path to the plugin directory.
         /// </summary>
         /// </summary>
         /// <value>The plugins path.</value>
         /// <value>The plugins path.</value>
         public string PluginsPath => Path.Combine(ProgramDataPath, "plugins");
         public string PluginsPath => Path.Combine(ProgramDataPath, "plugins");
 
 
         /// <summary>
         /// <summary>
-        /// Gets the path to the plugin configurations directory
+        /// Gets the path to the plugin configurations directory.
         /// </summary>
         /// </summary>
         /// <value>The plugin configurations path.</value>
         /// <value>The plugin configurations path.</value>
         public string PluginConfigurationsPath => Path.Combine(PluginsPath, "configurations");
         public string PluginConfigurationsPath => Path.Combine(PluginsPath, "configurations");
 
 
         /// <summary>
         /// <summary>
-        /// Gets the path to the log directory
+        /// Gets the path to the log directory.
         /// </summary>
         /// </summary>
         /// <value>The log directory path.</value>
         /// <value>The log directory path.</value>
-        public string LogDirectoryPath { get; private set; }
+        public string LogDirectoryPath { get; }
 
 
         /// <summary>
         /// <summary>
-        /// Gets the path to the application configuration root directory
+        /// Gets the path to the application configuration root directory.
         /// </summary>
         /// </summary>
         /// <value>The configuration directory path.</value>
         /// <value>The configuration directory path.</value>
-        public string ConfigurationDirectoryPath { get; private set; }
+        public string ConfigurationDirectoryPath { get; }
 
 
         /// <summary>
         /// <summary>
-        /// Gets the path to the system configuration file
+        /// Gets the path to the system configuration file.
         /// </summary>
         /// </summary>
         /// <value>The system configuration file path.</value>
         /// <value>The system configuration file path.</value>
         public string SystemConfigurationFilePath => Path.Combine(ConfigurationDirectoryPath, "system.xml");
         public string SystemConfigurationFilePath => Path.Combine(ConfigurationDirectoryPath, "system.xml");
 
 
         /// <summary>
         /// <summary>
-        /// Gets the folder path to the cache directory
+        /// Gets or sets the folder path to the cache directory.
         /// </summary>
         /// </summary>
         /// <value>The cache directory.</value>
         /// <value>The cache directory.</value>
         public string CachePath { get; set; }
         public string CachePath { get; set; }
 
 
         /// <summary>
         /// <summary>
-        /// Gets the folder path to the temp directory within the cache folder
+        /// Gets the folder path to the temp directory within the cache folder.
         /// </summary>
         /// </summary>
         /// <value>The temp directory.</value>
         /// <value>The temp directory.</value>
         public string TempDirectory => Path.Combine(CachePath, "temp");
         public string TempDirectory => Path.Combine(CachePath, "temp");

+ 51 - 41
Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs

@@ -1,6 +1,7 @@
 using System;
 using System;
 using System.Collections.Concurrent;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Threading;
 using System.Threading;
@@ -19,11 +20,44 @@ namespace Emby.Server.Implementations.AppBase
     /// </summary>
     /// </summary>
     public abstract class BaseConfigurationManager : IConfigurationManager
     public abstract class BaseConfigurationManager : IConfigurationManager
     {
     {
+        private readonly IFileSystem _fileSystem;
+
+        private readonly ConcurrentDictionary<string, object> _configurations = new ConcurrentDictionary<string, object>();
+
+        private ConfigurationStore[] _configurationStores = Array.Empty<ConfigurationStore>();
+        private IConfigurationFactory[] _configurationFactories = Array.Empty<IConfigurationFactory>();
+
         /// <summary>
         /// <summary>
-        /// Gets the type of the configuration.
+        /// The _configuration loaded.
         /// </summary>
         /// </summary>
-        /// <value>The type of the configuration.</value>
-        protected abstract Type ConfigurationType { get; }
+        private bool _configurationLoaded;
+
+        /// <summary>
+        /// The _configuration sync lock.
+        /// </summary>
+        private object _configurationSyncLock = new object();
+
+        /// <summary>
+        /// The _configuration.
+        /// </summary>
+        private BaseApplicationConfiguration _configuration;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BaseConfigurationManager" /> class.
+        /// </summary>
+        /// <param name="applicationPaths">The application paths.</param>
+        /// <param name="loggerFactory">The logger factory.</param>
+        /// <param name="xmlSerializer">The XML serializer.</param>
+        /// <param name="fileSystem">The file system</param>
+        protected BaseConfigurationManager(IApplicationPaths applicationPaths, ILoggerFactory loggerFactory, IXmlSerializer xmlSerializer, IFileSystem fileSystem)
+        {
+            CommonApplicationPaths = applicationPaths;
+            XmlSerializer = xmlSerializer;
+            _fileSystem = fileSystem;
+            Logger = loggerFactory.CreateLogger(GetType().Name);
+
+            UpdateCachePath();
+        }
 
 
         /// <summary>
         /// <summary>
         /// Occurs when [configuration updated].
         /// Occurs when [configuration updated].
@@ -40,6 +74,12 @@ namespace Emby.Server.Implementations.AppBase
         /// </summary>
         /// </summary>
         public event EventHandler<ConfigurationUpdateEventArgs> NamedConfigurationUpdated;
         public event EventHandler<ConfigurationUpdateEventArgs> NamedConfigurationUpdated;
 
 
+        /// <summary>
+        /// Gets the type of the configuration.
+        /// </summary>
+        /// <value>The type of the configuration.</value>
+        protected abstract Type ConfigurationType { get; }
+
         /// <summary>
         /// <summary>
         /// Gets the logger.
         /// Gets the logger.
         /// </summary>
         /// </summary>
@@ -56,20 +96,7 @@ namespace Emby.Server.Implementations.AppBase
         /// </summary>
         /// </summary>
         /// <value>The application paths.</value>
         /// <value>The application paths.</value>
         public IApplicationPaths CommonApplicationPaths { get; private set; }
         public IApplicationPaths CommonApplicationPaths { get; private set; }
-        public readonly IFileSystem FileSystem;
 
 
-        /// <summary>
-        /// The _configuration loaded
-        /// </summary>
-        private bool _configurationLoaded;
-        /// <summary>
-        /// The _configuration sync lock
-        /// </summary>
-        private object _configurationSyncLock = new object();
-        /// <summary>
-        /// The _configuration
-        /// </summary>
-        private BaseApplicationConfiguration _configuration;
         /// <summary>
         /// <summary>
         /// Gets the system configuration
         /// Gets the system configuration
         /// </summary>
         /// </summary>
@@ -90,26 +117,6 @@ namespace Emby.Server.Implementations.AppBase
             }
             }
         }
         }
 
 
-        private ConfigurationStore[] _configurationStores = { };
-        private IConfigurationFactory[] _configurationFactories = { };
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="BaseConfigurationManager" /> class.
-        /// </summary>
-        /// <param name="applicationPaths">The application paths.</param>
-        /// <param name="loggerFactory">The logger factory.</param>
-        /// <param name="xmlSerializer">The XML serializer.</param>
-        /// <param name="fileSystem">The file system</param>
-        protected BaseConfigurationManager(IApplicationPaths applicationPaths, ILoggerFactory loggerFactory, IXmlSerializer xmlSerializer, IFileSystem fileSystem)
-        {
-            CommonApplicationPaths = applicationPaths;
-            XmlSerializer = xmlSerializer;
-            FileSystem = fileSystem;
-            Logger = loggerFactory.CreateLogger(GetType().Name);
-
-            UpdateCachePath();
-        }
-
         public virtual void AddParts(IEnumerable<IConfigurationFactory> factories)
         public virtual void AddParts(IEnumerable<IConfigurationFactory> factories)
         {
         {
             _configurationFactories = factories.ToArray();
             _configurationFactories = factories.ToArray();
@@ -171,6 +178,7 @@ namespace Emby.Server.Implementations.AppBase
         private void UpdateCachePath()
         private void UpdateCachePath()
         {
         {
             string cachePath;
             string cachePath;
+
             // If the configuration file has no entry (i.e. not set in UI)
             // If the configuration file has no entry (i.e. not set in UI)
             if (string.IsNullOrWhiteSpace(CommonConfiguration.CachePath))
             if (string.IsNullOrWhiteSpace(CommonConfiguration.CachePath))
             {
             {
@@ -207,12 +215,16 @@ namespace Emby.Server.Implementations.AppBase
             var newPath = newConfig.CachePath;
             var newPath = newConfig.CachePath;
 
 
             if (!string.IsNullOrWhiteSpace(newPath)
             if (!string.IsNullOrWhiteSpace(newPath)
-                && !string.Equals(CommonConfiguration.CachePath ?? string.Empty, newPath))
+                && !string.Equals(CommonConfiguration.CachePath ?? string.Empty, newPath, StringComparison.Ordinal))
             {
             {
                 // Validate
                 // Validate
                 if (!Directory.Exists(newPath))
                 if (!Directory.Exists(newPath))
                 {
                 {
-                    throw new FileNotFoundException(string.Format("{0} does not exist.", newPath));
+                    throw new FileNotFoundException(
+                        string.Format(
+                            CultureInfo.InvariantCulture,
+                            "{0} does not exist.",
+                            newPath));
                 }
                 }
 
 
                 EnsureWriteAccess(newPath);
                 EnsureWriteAccess(newPath);
@@ -223,11 +235,9 @@ namespace Emby.Server.Implementations.AppBase
         {
         {
             var file = Path.Combine(path, Guid.NewGuid().ToString());
             var file = Path.Combine(path, Guid.NewGuid().ToString());
             File.WriteAllText(file, string.Empty);
             File.WriteAllText(file, string.Empty);
-            FileSystem.DeleteFile(file);
+            _fileSystem.DeleteFile(file);
         }
         }
 
 
-        private readonly ConcurrentDictionary<string, object> _configurations = new ConcurrentDictionary<string, object>();
-
         private string GetConfigurationFile(string key)
         private string GetConfigurationFile(string key)
         {
         {
             return Path.Combine(CommonApplicationPaths.ConfigurationDirectoryPath, key.ToLowerInvariant() + ".xml");
             return Path.Combine(CommonApplicationPaths.ConfigurationDirectoryPath, key.ToLowerInvariant() + ".xml");

+ 69 - 47
Emby.Server.Implementations/ApplicationHost.cs

@@ -7,6 +7,7 @@ using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Net;
 using System.Net;
 using System.Net.Http;
 using System.Net.Http;
+using System.Net.Sockets;
 using System.Reflection;
 using System.Reflection;
 using System.Runtime.InteropServices;
 using System.Runtime.InteropServices;
 using System.Security.Cryptography.X509Certificates;
 using System.Security.Cryptography.X509Certificates;
@@ -107,9 +108,9 @@ using Microsoft.AspNetCore.Hosting;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Http.Extensions;
 using Microsoft.AspNetCore.Http.Extensions;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.DependencyInjection.Extensions;
 using Microsoft.Extensions.DependencyInjection.Extensions;
+using Microsoft.Extensions.Logging;
 using ServiceStack;
 using ServiceStack;
 using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
 using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
 
 
@@ -385,7 +386,7 @@ namespace Emby.Server.Implementations
 
 
             fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem));
             fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem));
 
 
-            NetworkManager.NetworkChanged += NetworkManager_NetworkChanged;
+            NetworkManager.NetworkChanged += OnNetworkChanged;
         }
         }
 
 
         public string ExpandVirtualPath(string path)
         public string ExpandVirtualPath(string path)
@@ -409,7 +410,7 @@ namespace Emby.Server.Implementations
             return ServerConfigurationManager.Configuration.LocalNetworkSubnets;
             return ServerConfigurationManager.Configuration.LocalNetworkSubnets;
         }
         }
 
 
-        private void NetworkManager_NetworkChanged(object sender, EventArgs e)
+        private void OnNetworkChanged(object sender, EventArgs e)
         {
         {
             _validAddressResults.Clear();
             _validAddressResults.Clear();
         }
         }
@@ -417,10 +418,10 @@ namespace Emby.Server.Implementations
         public string ApplicationVersion { get; } = typeof(ApplicationHost).Assembly.GetName().Version.ToString(3);
         public string ApplicationVersion { get; } = typeof(ApplicationHost).Assembly.GetName().Version.ToString(3);
 
 
         /// <summary>
         /// <summary>
-        /// Gets the current application user agent
+        /// Gets the current application user agent.
         /// </summary>
         /// </summary>
         /// <value>The application user agent.</value>
         /// <value>The application user agent.</value>
-        public string ApplicationUserAgent => Name.Replace(' ','-') + '/' + ApplicationVersion;
+        public string ApplicationUserAgent => Name.Replace(' ', '-') + "/" + ApplicationVersion;
 
 
         /// <summary>
         /// <summary>
         /// Gets the email address for use within a comment section of a user agent field.
         /// Gets the email address for use within a comment section of a user agent field.
@@ -428,14 +429,11 @@ namespace Emby.Server.Implementations
         /// </summary>
         /// </summary>
         public string ApplicationUserAgentAddress { get; } = "team@jellyfin.org";
         public string ApplicationUserAgentAddress { get; } = "team@jellyfin.org";
 
 
-        private string _productName;
-
         /// <summary>
         /// <summary>
-        /// Gets the current application name
+        /// Gets the current application name.
         /// </summary>
         /// </summary>
         /// <value>The application name.</value>
         /// <value>The application name.</value>
-        public string ApplicationProductName
-            => _productName ?? (_productName = FileVersionInfo.GetVersionInfo(Assembly.GetEntryAssembly().Location).ProductName);
+        public string ApplicationProductName { get; } = FileVersionInfo.GetVersionInfo(Assembly.GetEntryAssembly().Location).ProductName;
 
 
         private DeviceId _deviceId;
         private DeviceId _deviceId;
 
 
@@ -469,8 +467,8 @@ namespace Emby.Server.Implementations
         /// <summary>
         /// <summary>
         /// Creates an instance of type and resolves all constructor dependencies
         /// Creates an instance of type and resolves all constructor dependencies
         /// </summary>
         /// </summary>
-        /// /// <typeparam name="T">The type</typeparam>
-        /// <returns>T</returns>
+        /// /// <typeparam name="T">The type.</typeparam>
+        /// <returns>T.</returns>
         public T CreateInstance<T>()
         public T CreateInstance<T>()
             => ActivatorUtilities.CreateInstance<T>(_serviceProvider);
             => ActivatorUtilities.CreateInstance<T>(_serviceProvider);
 
 
@@ -603,10 +601,15 @@ namespace Emby.Server.Implementations
 
 
                 foreach (var plugin in Plugins)
                 foreach (var plugin in Plugins)
                 {
                 {
-                    pluginBuilder.AppendLine(string.Format("{0} {1}", plugin.Name, plugin.Version));
+                    pluginBuilder.AppendLine(
+                        string.Format(
+                            CultureInfo.InvariantCulture,
+                            "{0} {1}",
+                            plugin.Name,
+                            plugin.Version));
                 }
                 }
 
 
-                Logger.LogInformation("Plugins: {plugins}", pluginBuilder.ToString());
+                Logger.LogInformation("Plugins: {Plugins}", pluginBuilder.ToString());
             }
             }
 
 
             DiscoverTypes();
             DiscoverTypes();
@@ -628,7 +631,7 @@ namespace Emby.Server.Implementations
 
 
                     if (EnableHttps && Certificate != null)
                     if (EnableHttps && Certificate != null)
                     {
                     {
-                        options.ListenAnyIP(HttpsPort, listenOptions => { listenOptions.UseHttps(Certificate); });
+                        options.ListenAnyIP(HttpsPort, listenOptions => listenOptions.UseHttps(Certificate));
                     }
                     }
                 })
                 })
                 .UseContentRoot(contentRoot)
                 .UseContentRoot(contentRoot)
@@ -642,6 +645,7 @@ namespace Emby.Server.Implementations
                     app.UseWebSockets();
                     app.UseWebSockets();
 
 
                     app.UseResponseCompression();
                     app.UseResponseCompression();
+
                     // TODO app.UseMiddleware<WebSocketMiddleware>();
                     // TODO app.UseMiddleware<WebSocketMiddleware>();
                     app.Use(ExecuteWebsocketHandlerAsync);
                     app.Use(ExecuteWebsocketHandlerAsync);
                     app.Use(ExecuteHttpHandlerAsync);
                     app.Use(ExecuteHttpHandlerAsync);
@@ -675,7 +679,7 @@ namespace Emby.Server.Implementations
             var localPath = context.Request.Path.ToString();
             var localPath = context.Request.Path.ToString();
 
 
             var req = new WebSocketSharpRequest(request, response, request.Path, Logger);
             var req = new WebSocketSharpRequest(request, response, request.Path, Logger);
-            await HttpServer.RequestHandler(req, request.GetDisplayUrl(), request.Host.ToString(), localPath, CancellationToken.None).ConfigureAwait(false);
+            await HttpServer.RequestHandler(req, request.GetDisplayUrl(), request.Host.ToString(), localPath, context.RequestAborted).ConfigureAwait(false);
         }
         }
 
 
         public static IStreamHelper StreamHelper { get; set; }
         public static IStreamHelper StreamHelper { get; set; }
@@ -784,7 +788,7 @@ namespace Emby.Server.Implementations
 
 
             HttpServer = new HttpListenerHost(
             HttpServer = new HttpListenerHost(
                 this,
                 this,
-                LoggerFactory,
+                LoggerFactory.CreateLogger<HttpListenerHost>(),
                 ServerConfigurationManager,
                 ServerConfigurationManager,
                 _configuration,
                 _configuration,
                 NetworkManager,
                 NetworkManager,
@@ -872,7 +876,7 @@ namespace Emby.Server.Implementations
             serviceCollection.AddSingleton<IAuthorizationContext>(authContext);
             serviceCollection.AddSingleton<IAuthorizationContext>(authContext);
             serviceCollection.AddSingleton<ISessionContext>(new SessionContext(UserManager, authContext, SessionManager));
             serviceCollection.AddSingleton<ISessionContext>(new SessionContext(UserManager, authContext, SessionManager));
 
 
-            AuthService = new AuthService(UserManager, authContext, ServerConfigurationManager, SessionManager, NetworkManager);
+            AuthService = new AuthService(authContext, ServerConfigurationManager, SessionManager, NetworkManager);
             serviceCollection.AddSingleton(AuthService);
             serviceCollection.AddSingleton(AuthService);
 
 
             SubtitleEncoder = new MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder(LibraryManager, LoggerFactory, ApplicationPaths, FileSystemManager, MediaEncoder, JsonSerializer, HttpClient, MediaSourceManager, ProcessFactory);
             SubtitleEncoder = new MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder(LibraryManager, LoggerFactory, ApplicationPaths, FileSystemManager, MediaEncoder, JsonSerializer, HttpClient, MediaSourceManager, ProcessFactory);
@@ -1043,8 +1047,8 @@ namespace Emby.Server.Implementations
                 .Cast<IServerEntryPoint>()
                 .Cast<IServerEntryPoint>()
                 .ToList();
                 .ToList();
 
 
-            await Task.WhenAll(StartEntryPoints(entries, true));
-            await Task.WhenAll(StartEntryPoints(entries, false));
+            await Task.WhenAll(StartEntryPoints(entries, true)).ConfigureAwait(false);
+            await Task.WhenAll(StartEntryPoints(entries, false)).ConfigureAwait(false);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -1219,7 +1223,7 @@ namespace Emby.Server.Implementations
 
 
             // Generate self-signed cert
             // Generate self-signed cert
             var certHost = GetHostnameFromExternalDns(ServerConfigurationManager.Configuration.WanDdns);
             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";
             const string Password = "embycert";
 
 
             return new CertificateInfo
             return new CertificateInfo
@@ -1457,15 +1461,10 @@ namespace Emby.Server.Implementations
             };
             };
         }
         }
 
 
-        public WakeOnLanInfo[] GetWakeOnLanInfo()
-        {
-            return NetworkManager.GetMacAddresses()
-                .Select(i => new WakeOnLanInfo
-                {
-                    MacAddress = i
-                })
-                .ToArray();
-        }
+        public IEnumerable<WakeOnLanInfo> GetWakeOnLanInfo()
+            => NetworkManager.GetMacAddresses()
+                .Select(i => new WakeOnLanInfo(i))
+                .ToList();
 
 
         public async Task<PublicSystemInfo> GetPublicSystemInfo(CancellationToken cancellationToken)
         public async Task<PublicSystemInfo> GetPublicSystemInfo(CancellationToken cancellationToken)
         {
         {
@@ -1481,6 +1480,7 @@ namespace Emby.Server.Implementations
             {
             {
                 wanAddress = GetWanApiUrl(ServerConfigurationManager.Configuration.WanDdns);
                 wanAddress = GetWanApiUrl(ServerConfigurationManager.Configuration.WanDdns);
             }
             }
+
             return new PublicSystemInfo
             return new PublicSystemInfo
             {
             {
                 Version = ApplicationVersion,
                 Version = ApplicationVersion,
@@ -1546,14 +1546,32 @@ namespace Emby.Server.Implementations
             return null;
             return null;
         }
         }
 
 
-        public string GetLocalApiUrl(IpAddressInfo ipAddress)
+        /// <summary>
+        /// Removes the scope id from IPv6 addresses.
+        /// </summary>
+        /// <param name="address">The IPv6 address.</param>
+        /// <returns>The IPv6 address without the scope id.</returns>
+        private string RemoveScopeId(string address)
         {
         {
-            if (ipAddress.AddressFamily == IpAddressFamily.InterNetworkV6)
+            var index = address.IndexOf('%');
+            if (index == -1)
             {
             {
-                return GetLocalApiUrl("[" + ipAddress.Address + "]");
+                return address;
             }
             }
 
 
-            return GetLocalApiUrl(ipAddress.Address);
+            return address.Substring(0, index);
+        }
+
+        public string GetLocalApiUrl(IPAddress ipAddress)
+        {
+            if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
+            {
+                var str = RemoveScopeId(ipAddress.ToString());
+
+                return GetLocalApiUrl("[" + str + "]");
+            }
+
+            return GetLocalApiUrl(ipAddress.ToString());
         }
         }
 
 
         public string GetLocalApiUrl(string host)
         public string GetLocalApiUrl(string host)
@@ -1564,19 +1582,22 @@ namespace Emby.Server.Implementations
                     host,
                     host,
                     HttpsPort.ToString(CultureInfo.InvariantCulture));
                     HttpsPort.ToString(CultureInfo.InvariantCulture));
             }
             }
+
             return string.Format("http://{0}:{1}",
             return string.Format("http://{0}:{1}",
                     host,
                     host,
                     HttpPort.ToString(CultureInfo.InvariantCulture));
                     HttpPort.ToString(CultureInfo.InvariantCulture));
         }
         }
 
 
-        public string GetWanApiUrl(IpAddressInfo ipAddress)
+        public string GetWanApiUrl(IPAddress ipAddress)
         {
         {
-            if (ipAddress.AddressFamily == IpAddressFamily.InterNetworkV6)
+            if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
             {
             {
-                return GetWanApiUrl("[" + ipAddress.Address + "]");
+                var str = RemoveScopeId(ipAddress.ToString());
+
+                return GetWanApiUrl("[" + str + "]");
             }
             }
 
 
-            return GetWanApiUrl(ipAddress.Address);
+            return GetWanApiUrl(ipAddress.ToString());
         }
         }
 
 
         public string GetWanApiUrl(string host)
         public string GetWanApiUrl(string host)
@@ -1587,17 +1608,18 @@ namespace Emby.Server.Implementations
                     host,
                     host,
                     ServerConfigurationManager.Configuration.PublicHttpsPort.ToString(CultureInfo.InvariantCulture));
                     ServerConfigurationManager.Configuration.PublicHttpsPort.ToString(CultureInfo.InvariantCulture));
             }
             }
+
             return string.Format("http://{0}:{1}",
             return string.Format("http://{0}:{1}",
                     host,
                     host,
                     ServerConfigurationManager.Configuration.PublicPort.ToString(CultureInfo.InvariantCulture));
                     ServerConfigurationManager.Configuration.PublicPort.ToString(CultureInfo.InvariantCulture));
         }
         }
 
 
-        public Task<List<IpAddressInfo>> GetLocalIpAddresses(CancellationToken cancellationToken)
+        public Task<List<IPAddress>> GetLocalIpAddresses(CancellationToken cancellationToken)
         {
         {
             return GetLocalIpAddressesInternal(true, 0, cancellationToken);
             return GetLocalIpAddressesInternal(true, 0, cancellationToken);
         }
         }
 
 
-        private async Task<List<IpAddressInfo>> GetLocalIpAddressesInternal(bool allowLoopback, int limit, CancellationToken cancellationToken)
+        private async Task<List<IPAddress>> GetLocalIpAddressesInternal(bool allowLoopback, int limit, CancellationToken cancellationToken)
         {
         {
             var addresses = ServerConfigurationManager
             var addresses = ServerConfigurationManager
                 .Configuration
                 .Configuration
@@ -1611,13 +1633,13 @@ namespace Emby.Server.Implementations
                 addresses.AddRange(NetworkManager.GetLocalIpAddresses(ServerConfigurationManager.Configuration.IgnoreVirtualInterfaces));
                 addresses.AddRange(NetworkManager.GetLocalIpAddresses(ServerConfigurationManager.Configuration.IgnoreVirtualInterfaces));
             }
             }
 
 
-            var resultList = new List<IpAddressInfo>();
+            var resultList = new List<IPAddress>();
 
 
             foreach (var address in addresses)
             foreach (var address in addresses)
             {
             {
                 if (!allowLoopback)
                 if (!allowLoopback)
                 {
                 {
-                    if (address.Equals(IpAddressInfo.Loopback) || address.Equals(IpAddressInfo.IPv6Loopback))
+                    if (address.Equals(IPAddress.Loopback) || address.Equals(IPAddress.IPv6Loopback))
                     {
                     {
                         continue;
                         continue;
                     }
                     }
@@ -1638,7 +1660,7 @@ namespace Emby.Server.Implementations
             return resultList;
             return resultList;
         }
         }
 
 
-        private IpAddressInfo NormalizeConfiguredLocalAddress(string address)
+        private IPAddress NormalizeConfiguredLocalAddress(string address)
         {
         {
             var index = address.Trim('/').IndexOf('/');
             var index = address.Trim('/').IndexOf('/');
 
 
@@ -1647,7 +1669,7 @@ namespace Emby.Server.Implementations
                 address = address.Substring(index + 1);
                 address = address.Substring(index + 1);
             }
             }
 
 
-            if (NetworkManager.TryParseIpAddress(address.Trim('/'), out IpAddressInfo result))
+            if (IPAddress.TryParse(address.Trim('/'), out IPAddress result))
             {
             {
                 return result;
                 return result;
             }
             }
@@ -1657,10 +1679,10 @@ namespace Emby.Server.Implementations
 
 
         private readonly ConcurrentDictionary<string, bool> _validAddressResults = new ConcurrentDictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
         private readonly ConcurrentDictionary<string, bool> _validAddressResults = new ConcurrentDictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
 
 
-        private async Task<bool> IsIpAddressValidAsync(IpAddressInfo address, CancellationToken cancellationToken)
+        private async Task<bool> IsIpAddressValidAsync(IPAddress address, CancellationToken cancellationToken)
         {
         {
-            if (address.Equals(IpAddressInfo.Loopback) ||
-                address.Equals(IpAddressInfo.IPv6Loopback))
+            if (address.Equals(IPAddress.Loopback) ||
+                address.Equals(IPAddress.IPv6Loopback))
             {
             {
                 return true;
                 return true;
             }
             }

+ 9 - 8
Emby.Server.Implementations/Channels/ChannelManager.cs

@@ -1,6 +1,7 @@
 using System;
 using System;
 using System.Collections.Concurrent;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Threading;
 using System.Threading;
@@ -206,7 +207,7 @@ namespace Emby.Server.Implementations.Channels
 
 
                     try
                     try
                     {
                     {
-                        return GetChannelProvider(i).IsEnabledFor(user.Id.ToString("N"));
+                        return GetChannelProvider(i).IsEnabledFor(user.Id.ToString("N", CultureInfo.InvariantCulture));
                     }
                     }
                     catch
                     catch
                     {
                     {
@@ -511,7 +512,7 @@ namespace Emby.Server.Implementations.Channels
                 IncludeItemTypes = new[] { typeof(Channel).Name },
                 IncludeItemTypes = new[] { typeof(Channel).Name },
                 OrderBy = new ValueTuple<string, SortOrder>[] { new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending) }
                 OrderBy = new ValueTuple<string, SortOrder>[] { new ValueTuple<string, SortOrder>(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)
         public ChannelFeatures GetChannelFeatures(string id)
@@ -552,7 +553,7 @@ namespace Emby.Server.Implementations.Channels
                 SupportsSortOrderToggle = features.SupportsSortOrderToggle,
                 SupportsSortOrderToggle = features.SupportsSortOrderToggle,
                 SupportsLatestMedia = supportsLatest,
                 SupportsLatestMedia = supportsLatest,
                 Name = channel.Name,
                 Name = channel.Name,
-                Id = channel.Id.ToString("N"),
+                Id = channel.Id.ToString("N", CultureInfo.InvariantCulture),
                 SupportsContentDownloading = features.SupportsContentDownloading,
                 SupportsContentDownloading = features.SupportsContentDownloading,
                 AutoRefreshLevels = features.AutoRefreshLevels
                 AutoRefreshLevels = features.AutoRefreshLevels
             };
             };
@@ -740,7 +741,7 @@ namespace Emby.Server.Implementations.Channels
             bool sortDescending,
             bool sortDescending,
             CancellationToken cancellationToken)
             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 cacheLength = CacheLength;
             var cachePath = GetChannelDataCachePath(channel, userId, externalFolderId, sortField, sortDescending);
             var cachePath = GetChannelDataCachePath(channel, userId, externalFolderId, sortField, sortDescending);
@@ -836,7 +837,7 @@ namespace Emby.Server.Implementations.Channels
             ChannelItemSortField? sortField,
             ChannelItemSortField? sortField,
             bool sortDescending)
             bool sortDescending)
         {
         {
-            var channelId = GetInternalChannelId(channel.Name).ToString("N");
+            var channelId = GetInternalChannelId(channel.Name).ToString("N", CultureInfo.InvariantCulture);
 
 
             var userCacheKey = string.Empty;
             var userCacheKey = string.Empty;
 
 
@@ -846,10 +847,10 @@ namespace Emby.Server.Implementations.Channels
                 userCacheKey = hasCacheKey.GetCacheKey(userId) ?? string.Empty;
                 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;
             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)
             if (sortField.HasValue)
             {
             {
@@ -860,7 +861,7 @@ namespace Emby.Server.Implementations.Channels
                 filename += "-sortDescending";
                 filename += "-sortDescending";
             }
             }
 
 
-            filename = filename.GetMD5().ToString("N");
+            filename = filename.GetMD5().ToString("N", CultureInfo.InvariantCulture);
 
 
             return Path.Combine(_config.ApplicationPaths.CachePath,
             return Path.Combine(_config.ApplicationPaths.CachePath,
                 "channels",
                 "channels",

+ 2 - 1
Emby.Server.Implementations/Collections/CollectionManager.cs

@@ -1,5 +1,6 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Threading;
 using System.Threading;
@@ -182,7 +183,7 @@ namespace Emby.Server.Implementations.Collections
 
 
         public void AddToCollection(Guid collectionId, IEnumerable<Guid> ids)
         public void AddToCollection(Guid collectionId, IEnumerable<Guid> 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<string> ids, bool fireEvent, MetadataRefreshOptions refreshOptions)
         private void AddToCollection(Guid collectionId, IEnumerable<string> ids, bool fireEvent, MetadataRefreshOptions refreshOptions)

+ 2 - 2
Emby.Server.Implementations/ConfigurationOptions.cs

@@ -6,8 +6,8 @@ namespace Emby.Server.Implementations
     {
     {
         public static readonly Dictionary<string, string> Configuration = new Dictionary<string, string>
         public static readonly Dictionary<string, string> Configuration = new Dictionary<string, string>
         {
         {
-            {"HttpListenerHost:DefaultRedirectPath", "web/index.html"},
-            {"MusicBrainz:BaseUrl", "https://www.musicbrainz.org"}
+            { "HttpListenerHost:DefaultRedirectPath", "web/index.html" },
+            { "MusicBrainz:BaseUrl", "https://www.musicbrainz.org" }
         };
         };
     }
     }
 }
 }

+ 49 - 37
Emby.Server.Implementations/Cryptography/CryptographyProvider.cs

@@ -8,7 +8,7 @@ using MediaBrowser.Model.Cryptography;
 
 
 namespace Emby.Server.Implementations.Cryptography
 namespace Emby.Server.Implementations.Cryptography
 {
 {
-    public class CryptographyProvider : ICryptoProvider
+    public class CryptographyProvider : ICryptoProvider, IDisposable
     {
     {
         private static readonly HashSet<string> _supportedHashMethods = new HashSet<string>()
         private static readonly HashSet<string> _supportedHashMethods = new HashSet<string>()
             {
             {
@@ -28,26 +28,28 @@ namespace Emby.Server.Implementations.Cryptography
                 "System.Security.Cryptography.SHA512"
                 "System.Security.Cryptography.SHA512"
             };
             };
 
 
-        public string DefaultHashMethod => "PBKDF2";
-
         private RandomNumberGenerator _randomNumberGenerator;
         private RandomNumberGenerator _randomNumberGenerator;
 
 
         private const int _defaultIterations = 1000;
         private const int _defaultIterations = 1000;
 
 
+        private bool _disposed = false;
+
         public CryptographyProvider()
         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();
             _randomNumberGenerator = RandomNumberGenerator.Create();
         }
         }
 
 
+        public string DefaultHashMethod => "PBKDF2";
+
+        [Obsolete("Use System.Security.Cryptography.MD5 directly")]
         public Guid GetMD5(string str)
         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)
         public byte[] ComputeSHA1(byte[] bytes)
         {
         {
             using (var provider = SHA1.Create())
             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)
         public byte[] ComputeMD5(Stream str)
         {
         {
             using (var provider = MD5.Create())
             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)
         public byte[] ComputeMD5(byte[] bytes)
         {
         {
             using (var provider = MD5.Create())
             using (var provider = MD5.Create())
@@ -73,9 +77,7 @@ namespace Emby.Server.Implementations.Cryptography
         }
         }
 
 
         public IEnumerable<string> GetSupportedHashMethods()
         public IEnumerable<string> GetSupportedHashMethods()
-        {
-            return _supportedHashMethods;
-        }
+            => _supportedHashMethods;
 
 
         private byte[] PBKDF2(string method, byte[] bytes, byte[] salt, int iterations)
         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)
         public byte[] ComputeHash(string hashMethod, byte[] bytes)
-        {
-            return ComputeHash(hashMethod, bytes, Array.Empty<byte>());
-        }
+            => ComputeHash(hashMethod, bytes, Array.Empty<byte>());
 
 
         public byte[] ComputeHashWithDefaultMethod(byte[] bytes)
         public byte[] ComputeHashWithDefaultMethod(byte[] bytes)
-        {
-            return ComputeHash(DefaultHashMethod, bytes);
-        }
+            => ComputeHash(DefaultHashMethod, bytes);
 
 
         public byte[] ComputeHash(string hashMethod, byte[] bytes, byte[] salt)
         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)
         public byte[] ComputeHashWithDefaultMethod(byte[] bytes, byte[] salt)
-        {
-            return PBKDF2(DefaultHashMethod, bytes, salt, _defaultIterations);
-        }
+            => PBKDF2(DefaultHashMethod, bytes, salt, _defaultIterations);
 
 
         public byte[] ComputeHash(PasswordHash hash)
         public byte[] ComputeHash(PasswordHash hash)
         {
         {
             int iterations = _defaultIterations;
             int iterations = _defaultIterations;
             if (!hash.Parameters.ContainsKey("iterations"))
             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()
         public byte[] GenerateSalt()
@@ -164,5 +152,29 @@ namespace Emby.Server.Implementations.Cryptography
             _randomNumberGenerator.GetBytes(salt);
             _randomNumberGenerator.GetBytes(salt);
             return salt;
             return salt;
         }
         }
+
+        /// <inheritdoc />
+        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;
+        }
     }
     }
 }
 }

+ 2 - 1
Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs

@@ -1,5 +1,6 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Threading;
 using System.Threading;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
@@ -182,7 +183,7 @@ namespace Emby.Server.Implementations.Data
 
 
                 return new DisplayPreferences
                 return new DisplayPreferences
                 {
                 {
-                    Id = guidId.ToString("N")
+                    Id = guidId.ToString("N", CultureInfo.InvariantCulture)
                 };
                 };
             }
             }
         }
         }

+ 10 - 9
Emby.Server.Implementations/Data/SqliteItemRepository.cs

@@ -696,7 +696,7 @@ namespace Emby.Server.Implementations.Data
                 saveItemStatement.TryBindNull("@EndDate");
                 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)
             if (item is IHasProgramAttributes hasProgramAttributes)
             {
             {
@@ -851,7 +851,7 @@ namespace Emby.Server.Implementations.Data
             }
             }
             else
             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)
             if (item is Trailer trailer && trailer.TrailerTypes.Length > 0)
@@ -3548,12 +3548,12 @@ namespace Emby.Server.Implementations.Data
                 whereClauses.Add("ChannelId=@ChannelId");
                 whereClauses.Add("ChannelId=@ChannelId");
                 if (statement != null)
                 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)
             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})");
                 whereClauses.Add($"ChannelId in ({inClause})");
             }
             }
 
 
@@ -4537,12 +4537,12 @@ namespace Emby.Server.Implementations.Data
                 }
                 }
                 if (statement != null)
                 if (statement != null)
                 {
                 {
-                    statement.TryBind("@TopParentId", queryTopParentIds[0].ToString("N"));
+                    statement.TryBind("@TopParentId", queryTopParentIds[0].ToString("N", CultureInfo.InvariantCulture));
                 }
                 }
             }
             }
             else if (queryTopParentIds.Length > 1)
             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)
                 if (enableItemsByName && includedItemByNameTypes.Count == 1)
                 {
                 {
@@ -4574,7 +4574,7 @@ namespace Emby.Server.Implementations.Data
             }
             }
             if (query.AncestorIds.Length > 1)
             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));
                 whereClauses.Add(string.Format("Guid in (select itemId from AncestorIds where AncestorIdText in ({0}))", inClause));
             }
             }
             if (!string.IsNullOrWhiteSpace(query.AncestorWithPresentationUniqueKey))
             if (!string.IsNullOrWhiteSpace(query.AncestorWithPresentationUniqueKey))
@@ -4637,7 +4637,7 @@ namespace Emby.Server.Implementations.Data
 
 
                 foreach (var folderId in query.BoxSetLibraryFolders)
                 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) + ")");
                 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];
                     var ancestorId = ancestorIds[i];
 
 
                     statement.TryBind("@AncestorId" + index, ancestorId.ToGuidBlob());
                     statement.TryBind("@AncestorId" + index, ancestorId.ToGuidBlob());
-                    statement.TryBind("@AncestorIdText" + index, ancestorId.ToString("N"));
+                    statement.TryBind("@AncestorIdText" + index, ancestorId.ToString("N", CultureInfo.InvariantCulture));
                 }
                 }
 
 
                 statement.Reset();
                 statement.Reset();
@@ -5579,6 +5579,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
                 {
                 {
                     counts.TrailerCount = value;
                     counts.TrailerCount = value;
                 }
                 }
+
                 counts.ItemCount += value;
                 counts.ItemCount += value;
             }
             }
 
 

+ 2 - 2
Emby.Server.Implementations/Devices/DeviceId.cs

@@ -1,8 +1,8 @@
 using System;
 using System;
+using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Text;
 using System.Text;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
-using MediaBrowser.Model.IO;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
 
 
 namespace Emby.Server.Implementations.Devices
 namespace Emby.Server.Implementations.Devices
@@ -67,7 +67,7 @@ namespace Emby.Server.Implementations.Devices
 
 
         private static string GetNewId()
         private static string GetNewId()
         {
         {
-            return Guid.NewGuid().ToString("N");
+            return Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
         }
         }
 
 
         private string GetDeviceId()
         private string GetDeviceId()

+ 2 - 2
Emby.Server.Implementations/Devices/DeviceManager.cs

@@ -1,11 +1,11 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Devices;
 using MediaBrowser.Controller.Devices;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
@@ -195,7 +195,7 @@ namespace Emby.Server.Implementations.Devices
 
 
         private string GetDevicePath(string id)
         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)
         public ContentUploadHistory GetCameraUploadHistory(string deviceId)

+ 9 - 8
Emby.Server.Implementations/Dto/DtoService.cs

@@ -1,5 +1,6 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
@@ -213,7 +214,7 @@ namespace Emby.Server.Implementations.Dto
 
 
             if (options.ContainsField(ItemFields.DisplayPreferencesId))
             if (options.ContainsField(ItemFields.DisplayPreferencesId))
             {
             {
-                dto.DisplayPreferencesId = item.DisplayPreferencesId.ToString("N");
+                dto.DisplayPreferencesId = item.DisplayPreferencesId.ToString("N", CultureInfo.InvariantCulture);
             }
             }
 
 
             if (user != null)
             if (user != null)
@@ -444,7 +445,7 @@ namespace Emby.Server.Implementations.Dto
         /// <exception cref="ArgumentNullException">item</exception>
         /// <exception cref="ArgumentNullException">item</exception>
         public string GetDtoId(BaseItem 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)
         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))
                 if (dictionary.TryGetValue(person.Name, out Person entity))
                 {
                 {
                     baseItemPerson.PrimaryImageTag = GetImageCacheTag(entity, ImageType.Primary);
                     baseItemPerson.PrimaryImageTag = GetImageCacheTag(entity, ImageType.Primary);
-                    baseItemPerson.Id = entity.Id.ToString("N");
+                    baseItemPerson.Id = entity.Id.ToString("N", CultureInfo.InvariantCulture);
                     list.Add(baseItemPerson);
                     list.Add(baseItemPerson);
                 }
                 }
             }
             }
@@ -893,7 +894,7 @@ namespace Emby.Server.Implementations.Dto
                 //var artistItems = _libraryManager.GetArtists(new InternalItemsQuery
                 //var artistItems = _libraryManager.GetArtists(new InternalItemsQuery
                 //{
                 //{
                 //    EnableTotalRecordCount = false,
                 //    EnableTotalRecordCount = false,
-                //    ItemIds = new[] { item.Id.ToString("N") }
+                //    ItemIds = new[] { item.Id.ToString("N", CultureInfo.InvariantCulture) }
                 //});
                 //});
 
 
                 //dto.ArtistItems = artistItems.Items
                 //dto.ArtistItems = artistItems.Items
@@ -903,7 +904,7 @@ namespace Emby.Server.Implementations.Dto
                 //        return new NameIdPair
                 //        return new NameIdPair
                 //        {
                 //        {
                 //            Name = artist.Name,
                 //            Name = artist.Name,
-                //            Id = artist.Id.ToString("N")
+                //            Id = artist.Id.ToString("N", CultureInfo.InvariantCulture)
                 //        };
                 //        };
                 //    })
                 //    })
                 //    .ToList();
                 //    .ToList();
@@ -946,7 +947,7 @@ namespace Emby.Server.Implementations.Dto
                 //var artistItems = _libraryManager.GetAlbumArtists(new InternalItemsQuery
                 //var artistItems = _libraryManager.GetAlbumArtists(new InternalItemsQuery
                 //{
                 //{
                 //    EnableTotalRecordCount = false,
                 //    EnableTotalRecordCount = false,
-                //    ItemIds = new[] { item.Id.ToString("N") }
+                //    ItemIds = new[] { item.Id.ToString("N", CultureInfo.InvariantCulture) }
                 //});
                 //});
 
 
                 //dto.AlbumArtists = artistItems.Items
                 //dto.AlbumArtists = artistItems.Items
@@ -956,7 +957,7 @@ namespace Emby.Server.Implementations.Dto
                 //        return new NameIdPair
                 //        return new NameIdPair
                 //        {
                 //        {
                 //            Name = artist.Name,
                 //            Name = artist.Name,
-                //            Id = artist.Id.ToString("N")
+                //            Id = artist.Id.ToString("N", CultureInfo.InvariantCulture)
                 //        };
                 //        };
                 //    })
                 //    })
                 //    .ToList();
                 //    .ToList();
@@ -1044,7 +1045,7 @@ namespace Emby.Server.Implementations.Dto
                         }
                         }
                         else
                         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))
                             mediaStreams = dto.MediaSources.Where(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase))
                                 .SelectMany(i => i.MediaStreams)
                                 .SelectMany(i => i.MediaStreams)
                                 .ToArray();
                                 .ToArray();

+ 2 - 0
Emby.Server.Implementations/Emby.Server.Implementations.csproj

@@ -20,6 +20,7 @@
   </ItemGroup>
   </ItemGroup>
 
 
   <ItemGroup>
   <ItemGroup>
+    <PackageReference Include="IPNetwork2" Version="2.4.0.126" />
     <PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.0" />
     <PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.0" />
     <PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.2.0" />
     <PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.2.0" />
     <PackageReference Include="Microsoft.AspNetCore.Hosting.Server.Abstractions" Version="2.2.0" />
     <PackageReference Include="Microsoft.AspNetCore.Hosting.Server.Abstractions" Version="2.2.0" />
@@ -43,6 +44,7 @@
   <PropertyGroup>
   <PropertyGroup>
     <TargetFramework>netstandard2.0</TargetFramework>
     <TargetFramework>netstandard2.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+    <GenerateDocumentationFile>true</GenerateDocumentationFile>
   </PropertyGroup>
   </PropertyGroup>
 
 
   <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
   <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">

+ 8 - 8
Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs

@@ -100,7 +100,7 @@ namespace Emby.Server.Implementations.EntryPoints
             _lastProgressMessageTimes[item.Id] = DateTime.UtcNow;
             _lastProgressMessageTimes[item.Id] = DateTime.UtcNow;
 
 
             var dict = new Dictionary<string, string>();
             var dict = new Dictionary<string, string>();
-            dict["ItemId"] = item.Id.ToString("N");
+            dict["ItemId"] = item.Id.ToString("N", CultureInfo.InvariantCulture);
             dict["Progress"] = progress.ToString(CultureInfo.InvariantCulture);
             dict["Progress"] = progress.ToString(CultureInfo.InvariantCulture);
 
 
             try
             try
@@ -116,7 +116,7 @@ namespace Emby.Server.Implementations.EntryPoints
             foreach (var collectionFolder in collectionFolders)
             foreach (var collectionFolder in collectionFolders)
             {
             {
                 var collectionFolderDict = new Dictionary<string, string>();
                 var collectionFolderDict = new Dictionary<string, string>();
-                collectionFolderDict["ItemId"] = collectionFolder.Id.ToString("N");
+                collectionFolderDict["ItemId"] = collectionFolder.Id.ToString("N", CultureInfo.InvariantCulture);
                 collectionFolderDict["Progress"] = (collectionFolder.GetRefreshProgress() ?? 0).ToString(CultureInfo.InvariantCulture);
                 collectionFolderDict["Progress"] = (collectionFolder.GetRefreshProgress() ?? 0).ToString(CultureInfo.InvariantCulture);
 
 
                 try
                 try
@@ -378,15 +378,15 @@ namespace Emby.Server.Implementations.EntryPoints
 
 
             return new LibraryUpdateInfo
             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()
                 CollectionFolders = GetTopParentIds(newAndRemoved, allUserRootChildren).ToArray()
             };
             };
@@ -422,7 +422,7 @@ namespace Emby.Server.Implementations.EntryPoints
                 var collectionFolders = _libraryManager.GetCollectionFolders(item, allUserRootChildren);
                 var collectionFolders = _libraryManager.GetCollectionFolders(item, allUserRootChildren);
                 foreach (var folder in allUserRootChildren)
                 foreach (var folder in allUserRootChildren)
                 {
                 {
-                    list.Add(folder.Id.ToString("N"));
+                    list.Add(folder.Id.ToString("N", CultureInfo.InvariantCulture));
                 }
                 }
             }
             }
 
 

+ 2 - 1
Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs

@@ -1,5 +1,6 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Globalization;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Common.Plugins;
 using MediaBrowser.Common.Plugins;
@@ -134,7 +135,7 @@ namespace Emby.Server.Implementations.EntryPoints
         /// <param name="e">The e.</param>
         /// <param name="e">The e.</param>
         void userManager_UserDeleted(object sender, GenericEventArgs<User> e)
         void userManager_UserDeleted(object sender, GenericEventArgs<User> 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<User> e)
         void _userManager_UserPolicyUpdated(object sender, GenericEventArgs<User> e)

+ 3 - 3
Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs

@@ -1,5 +1,6 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Globalization;
 using System.Linq;
 using System.Linq;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
@@ -8,7 +9,6 @@ using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Plugins;
 using MediaBrowser.Controller.Plugins;
 using MediaBrowser.Controller.Session;
 using MediaBrowser.Controller.Session;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
 using MediaBrowser.Model.Session;
 using MediaBrowser.Model.Session;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
 
 
@@ -125,12 +125,12 @@ namespace Emby.Server.Implementations.EntryPoints
                 .Select(i =>
                 .Select(i =>
                 {
                 {
                     var dto = _userDataManager.GetUserDataDto(i, user);
                     var dto = _userDataManager.GetUserDataDto(i, user);
-                    dto.ItemId = i.Id.ToString("N");
+                    dto.ItemId = i.Id.ToString("N", CultureInfo.InvariantCulture);
                     return dto;
                     return dto;
                 })
                 })
                 .ToArray();
                 .ToArray();
 
 
-            var userIdString = userId.ToString("N");
+            var userIdString = userId.ToString("N", CultureInfo.InvariantCulture);
 
 
             return new UserDataChangeInfo
             return new UserDataChangeInfo
             {
             {

+ 2 - 1
Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs

@@ -1,5 +1,6 @@
 using System;
 using System;
 using System.Collections.Concurrent;
 using System.Collections.Concurrent;
+using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Net;
 using System.Net;
@@ -195,7 +196,7 @@ namespace Emby.Server.Implementations.HttpClientManager
             }
             }
 
 
             var url = options.Url;
             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);
             var responseCachePath = Path.Combine(_appPaths.CachePath, "httpclient", urlHash);
 
 

+ 107 - 67
Emby.Server.Implementations/HttpServer/FileWriter.cs

@@ -1,50 +1,43 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Globalization;
 using System.Globalization;
+using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Net;
 using System.Net;
+using System.Runtime.InteropServices;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
-using Emby.Server.Implementations.IO;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.Services;
 using MediaBrowser.Model.Services;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
+using Microsoft.AspNetCore.Http;
 using Microsoft.Net.Http.Headers;
 using Microsoft.Net.Http.Headers;
 
 
 namespace Emby.Server.Implementations.HttpServer
 namespace Emby.Server.Implementations.HttpServer
 {
 {
     public class FileWriter : IHttpResult
     public class FileWriter : IHttpResult
     {
     {
-        private readonly IStreamHelper _streamHelper;
-        private ILogger Logger { get; set; }
-        private readonly IFileSystem _fileSystem;
-
-        private string RangeHeader { get; set; }
-        private bool IsHeadRequest { get; set; }
-
-        private long RangeStart { get; set; }
-        private long RangeEnd { get; set; }
-        private long RangeLength { get; set; }
-        public long TotalContentLength { get; set; }
+        private static readonly CultureInfo UsCulture = CultureInfo.ReadOnly(new CultureInfo("en-US"));
 
 
-        public Action OnComplete { get; set; }
-        public Action OnError { get; set; }
-        private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
-        public List<Cookie> Cookies { get; private set; }
+        private static readonly string[] _skipLogExtensions = {
+            ".js",
+            ".html",
+            ".css"
+        };
 
 
-        public FileShareMode FileShare { get; set; }
+        private readonly IStreamHelper _streamHelper;
+        private readonly ILogger _logger;
+        private readonly IFileSystem _fileSystem;
 
 
         /// <summary>
         /// <summary>
         /// The _options
         /// The _options
         /// </summary>
         /// </summary>
         private readonly IDictionary<string, string> _options = new Dictionary<string, string>();
         private readonly IDictionary<string, string> _options = new Dictionary<string, string>();
+
         /// <summary>
         /// <summary>
-        /// Gets the options.
+        /// The _requested ranges
         /// </summary>
         /// </summary>
-        /// <value>The options.</value>
-        public IDictionary<string, string> Headers => _options;
-
-        public string Path { get; set; }
+        private List<KeyValuePair<long, long?>> _requestedRanges;
 
 
         public FileWriter(string path, string contentType, string rangeHeader, ILogger logger, IFileSystem fileSystem, IStreamHelper streamHelper)
         public FileWriter(string path, string contentType, string rangeHeader, ILogger logger, IFileSystem fileSystem, IStreamHelper streamHelper)
         {
         {
@@ -57,7 +50,7 @@ namespace Emby.Server.Implementations.HttpServer
             _fileSystem = fileSystem;
             _fileSystem = fileSystem;
 
 
             Path = path;
             Path = path;
-            Logger = logger;
+            _logger = logger;
             RangeHeader = rangeHeader;
             RangeHeader = rangeHeader;
 
 
             Headers[HeaderNames.ContentType] = contentType;
             Headers[HeaderNames.ContentType] = contentType;
@@ -80,39 +73,34 @@ namespace Emby.Server.Implementations.HttpServer
             Cookies = new List<Cookie>();
             Cookies = new List<Cookie>();
         }
         }
 
 
-        /// <summary>
-        /// Sets the range values.
-        /// </summary>
-        private void SetRangeValues()
-        {
-            var requestedRange = RequestedRanges[0];
+        private string RangeHeader { get; set; }
 
 
-            // If the requested range is "0-", we can optimize by just doing a stream copy
-            if (!requestedRange.Value.HasValue)
-            {
-                RangeEnd = TotalContentLength - 1;
-            }
-            else
-            {
-                RangeEnd = requestedRange.Value.Value;
-            }
+        private bool IsHeadRequest { get; set; }
 
 
-            RangeStart = requestedRange.Key;
-            RangeLength = 1 + RangeEnd - RangeStart;
+        private long RangeStart { get; set; }
 
 
-            // Content-Length is the length of what we're serving, not the original content
-            var lengthString = RangeLength.ToString(CultureInfo.InvariantCulture);
-            Headers[HeaderNames.ContentLength] = lengthString;
-            var rangeString = $"bytes {RangeStart}-{RangeEnd}/{TotalContentLength}";
-            Headers[HeaderNames.ContentRange] = rangeString;
+        private long RangeEnd { get; set; }
 
 
-            Logger.LogInformation("Setting range response values for {0}. RangeRequest: {1} Content-Length: {2}, Content-Range: {3}", Path, RangeHeader, lengthString, rangeString);
-        }
+        private long RangeLength { get; set; }
+
+        public long TotalContentLength { get; set; }
+
+        public Action OnComplete { get; set; }
+
+        public Action OnError { get; set; }
+
+        public List<Cookie> Cookies { get; private set; }
+
+        public FileShareMode FileShare { get; set; }
 
 
         /// <summary>
         /// <summary>
-        /// The _requested ranges
+        /// Gets the options.
         /// </summary>
         /// </summary>
-        private List<KeyValuePair<long, long?>> _requestedRanges;
+        /// <value>The options.</value>
+        public IDictionary<string, string> Headers => _options;
+
+        public string Path { get; set; }
+
         /// <summary>
         /// <summary>
         /// Gets the requested ranges.
         /// Gets the requested ranges.
         /// </summary>
         /// </summary>
@@ -139,6 +127,7 @@ namespace Emby.Server.Implementations.HttpServer
                         {
                         {
                             start = long.Parse(vals[0], UsCulture);
                             start = long.Parse(vals[0], UsCulture);
                         }
                         }
+
                         if (!string.IsNullOrEmpty(vals[1]))
                         if (!string.IsNullOrEmpty(vals[1]))
                         {
                         {
                             end = long.Parse(vals[1], UsCulture);
                             end = long.Parse(vals[1], UsCulture);
@@ -152,13 +141,50 @@ namespace Emby.Server.Implementations.HttpServer
             }
             }
         }
         }
 
 
-        private static readonly string[] SkipLogExtensions = {
-            ".js",
-            ".html",
-            ".css"
-        };
+        public string ContentType { get; set; }
+
+        public IRequest RequestContext { get; set; }
+
+        public object Response { get; set; }
+
+        public int Status { get; set; }
+
+        public HttpStatusCode StatusCode
+        {
+            get => (HttpStatusCode)Status;
+            set => Status = (int)value;
+        }
+
+        /// <summary>
+        /// Sets the range values.
+        /// </summary>
+        private void SetRangeValues()
+        {
+            var requestedRange = RequestedRanges[0];
+
+            // If the requested range is "0-", we can optimize by just doing a stream copy
+            if (!requestedRange.Value.HasValue)
+            {
+                RangeEnd = TotalContentLength - 1;
+            }
+            else
+            {
+                RangeEnd = requestedRange.Value.Value;
+            }
+
+            RangeStart = requestedRange.Key;
+            RangeLength = 1 + RangeEnd - RangeStart;
+
+            // Content-Length is the length of what we're serving, not the original content
+            var lengthString = RangeLength.ToString(CultureInfo.InvariantCulture);
+            Headers[HeaderNames.ContentLength] = lengthString;
+            var rangeString = $"bytes {RangeStart}-{RangeEnd}/{TotalContentLength}";
+            Headers[HeaderNames.ContentRange] = rangeString;
 
 
-        public async Task WriteToAsync(IResponse response, CancellationToken cancellationToken)
+            _logger.LogInformation("Setting range response values for {0}. RangeRequest: {1} Content-Length: {2}, Content-Range: {3}", Path, RangeHeader, lengthString, rangeString);
+        }
+
+        public async Task WriteToAsync(HttpResponse response, CancellationToken cancellationToken)
         {
         {
             try
             try
             {
             {
@@ -176,16 +202,16 @@ namespace Emby.Server.Implementations.HttpServer
                 {
                 {
                     var extension = System.IO.Path.GetExtension(path);
                     var extension = System.IO.Path.GetExtension(path);
 
 
-                    if (extension == null || !SkipLogExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
+                    if (extension == null || !_skipLogExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
                     {
                     {
-                        Logger.LogDebug("Transmit file {0}", path);
+                        _logger.LogDebug("Transmit file {0}", path);
                     }
                     }
 
 
                     offset = 0;
                     offset = 0;
                     count = 0;
                     count = 0;
                 }
                 }
 
 
-                await response.TransmitFile(path, offset, count, FileShare, _fileSystem, _streamHelper, cancellationToken).ConfigureAwait(false);
+                await TransmitFile(response.Body, path, offset, count, FileShare, cancellationToken).ConfigureAwait(false);
             }
             }
             finally
             finally
             {
             {
@@ -193,18 +219,32 @@ namespace Emby.Server.Implementations.HttpServer
             }
             }
         }
         }
 
 
-        public string ContentType { get; set; }
-
-        public IRequest RequestContext { get; set; }
+        public async Task TransmitFile(Stream stream, string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken)
+        {
+            var fileOpenOptions = FileOpenOptions.SequentialScan;
 
 
-        public object Response { get; set; }
+            // use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039
+            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                fileOpenOptions |= FileOpenOptions.Asynchronous;
+            }
 
 
-        public int Status { get; set; }
+            using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, fileShareMode, fileOpenOptions))
+            {
+                if (offset > 0)
+                {
+                    fs.Position = offset;
+                }
 
 
-        public HttpStatusCode StatusCode
-        {
-            get => (HttpStatusCode)Status;
-            set => Status = (int)value;
+                if (count > 0)
+                {
+                    await _streamHelper.CopyToAsync(fs, stream, count, cancellationToken).ConfigureAwait(false);
+                }
+                else
+                {
+                    await fs.CopyToAsync(stream, StreamDefaults.DefaultCopyToBufferSize, cancellationToken).ConfigureAwait(false);
+                }
+            }
         }
         }
     }
     }
 }
 }

+ 63 - 91
Emby.Server.Implementations/HttpServer/HttpListenerHost.cs

@@ -5,7 +5,6 @@ using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Net.Sockets;
 using System.Net.Sockets;
 using System.Reflection;
 using System.Reflection;
-using System.Text;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using Emby.Server.Implementations.Net;
 using Emby.Server.Implementations.Net;
@@ -30,11 +29,7 @@ namespace Emby.Server.Implementations.HttpServer
 {
 {
     public class HttpListenerHost : IHttpServer, IDisposable
     public class HttpListenerHost : IHttpServer, IDisposable
     {
     {
-        private string DefaultRedirectPath { get; set; }
-        public string[] UrlPrefixes { get; private set; }
-
-        public event EventHandler<GenericEventArgs<IWebSocketConnection>> WebSocketConnected;
-
+        private readonly ILogger _logger;
         private readonly IServerConfigurationManager _config;
         private readonly IServerConfigurationManager _config;
         private readonly INetworkManager _networkManager;
         private readonly INetworkManager _networkManager;
         private readonly IServerApplicationHost _appHost;
         private readonly IServerApplicationHost _appHost;
@@ -42,18 +37,15 @@ namespace Emby.Server.Implementations.HttpServer
         private readonly IXmlSerializer _xmlSerializer;
         private readonly IXmlSerializer _xmlSerializer;
         private readonly IHttpListener _socketListener;
         private readonly IHttpListener _socketListener;
         private readonly Func<Type, Func<string, object>> _funcParseFn;
         private readonly Func<Type, Func<string, object>> _funcParseFn;
-
-        public Action<IRequest, IResponse, object>[] ResponseFilters { get; set; }
-
+        private readonly string _defaultRedirectPath;
         private readonly Dictionary<Type, Type> ServiceOperationsMap = new Dictionary<Type, Type>();
         private readonly Dictionary<Type, Type> ServiceOperationsMap = new Dictionary<Type, Type>();
-        public static HttpListenerHost Instance { get; protected set; }
-
         private IWebSocketListener[] _webSocketListeners = Array.Empty<IWebSocketListener>();
         private IWebSocketListener[] _webSocketListeners = Array.Empty<IWebSocketListener>();
         private readonly List<IWebSocketConnection> _webSocketConnections = new List<IWebSocketConnection>();
         private readonly List<IWebSocketConnection> _webSocketConnections = new List<IWebSocketConnection>();
+        private bool _disposed = false;
 
 
         public HttpListenerHost(
         public HttpListenerHost(
             IServerApplicationHost applicationHost,
             IServerApplicationHost applicationHost,
-            ILoggerFactory loggerFactory,
+            ILogger<HttpListenerHost> logger,
             IServerConfigurationManager config,
             IServerConfigurationManager config,
             IConfiguration configuration,
             IConfiguration configuration,
             INetworkManager networkManager,
             INetworkManager networkManager,
@@ -62,9 +54,9 @@ namespace Emby.Server.Implementations.HttpServer
             IHttpListener socketListener)
             IHttpListener socketListener)
         {
         {
             _appHost = applicationHost;
             _appHost = applicationHost;
-            Logger = loggerFactory.CreateLogger("HttpServer");
+            _logger = logger;
             _config = config;
             _config = config;
-            DefaultRedirectPath = configuration["HttpListenerHost:DefaultRedirectPath"];
+            _defaultRedirectPath = configuration["HttpListenerHost:DefaultRedirectPath"];
             _networkManager = networkManager;
             _networkManager = networkManager;
             _jsonSerializer = jsonSerializer;
             _jsonSerializer = jsonSerializer;
             _xmlSerializer = xmlSerializer;
             _xmlSerializer = xmlSerializer;
@@ -74,12 +66,20 @@ namespace Emby.Server.Implementations.HttpServer
             _funcParseFn = t => s => JsvReader.GetParseFn(t)(s);
             _funcParseFn = t => s => JsvReader.GetParseFn(t)(s);
 
 
             Instance = this;
             Instance = this;
-            ResponseFilters = Array.Empty<Action<IRequest, IResponse, object>>();
+            ResponseFilters = Array.Empty<Action<IRequest, HttpResponse, object>>();
         }
         }
 
 
+        public Action<IRequest, HttpResponse, object>[] ResponseFilters { get; set; }
+
+        public static HttpListenerHost Instance { get; protected set; }
+
+        public string[] UrlPrefixes { get; private set; }
+
         public string GlobalResponse { get; set; }
         public string GlobalResponse { get; set; }
 
 
-        protected ILogger Logger { get; }
+        public ServiceController ServiceController { get; private set; }
+
+        public event EventHandler<GenericEventArgs<IWebSocketConnection>> WebSocketConnected;
 
 
         public object CreateInstance(Type type)
         public object CreateInstance(Type type)
         {
         {
@@ -91,7 +91,7 @@ namespace Emby.Server.Implementations.HttpServer
         /// and no more processing should be done.
         /// and no more processing should be done.
         /// </summary>
         /// </summary>
         /// <returns></returns>
         /// <returns></returns>
-        public void ApplyRequestFilters(IRequest req, IResponse res, object requestDto)
+        public void ApplyRequestFilters(IRequest req, HttpResponse res, object requestDto)
         {
         {
             //Exec all RequestFilter attributes with Priority < 0
             //Exec all RequestFilter attributes with Priority < 0
             var attributes = GetRequestFilterAttributes(requestDto.GetType());
             var attributes = GetRequestFilterAttributes(requestDto.GetType());
@@ -145,7 +145,7 @@ namespace Emby.Server.Implementations.HttpServer
                 return;
                 return;
             }
             }
 
 
-            var connection = new WebSocketConnection(e.WebSocket, e.Endpoint, _jsonSerializer, Logger)
+            var connection = new WebSocketConnection(e.WebSocket, e.Endpoint, _jsonSerializer, _logger)
             {
             {
                 OnReceive = ProcessWebSocketMessageReceived,
                 OnReceive = ProcessWebSocketMessageReceived,
                 Url = e.Url,
                 Url = e.Url,
@@ -215,16 +215,16 @@ namespace Emby.Server.Implementations.HttpServer
 
 
                 if (logExceptionStackTrace)
                 if (logExceptionStackTrace)
                 {
                 {
-                    Logger.LogError(ex, "Error processing request");
+                    _logger.LogError(ex, "Error processing request");
                 }
                 }
                 else if (logExceptionMessage)
                 else if (logExceptionMessage)
                 {
                 {
-                    Logger.LogError(ex.Message);
+                    _logger.LogError(ex.Message);
                 }
                 }
 
 
                 var httpRes = httpReq.Response;
                 var httpRes = httpReq.Response;
 
 
-                if (httpRes.OriginalResponse.HasStarted)
+                if (httpRes.HasStarted)
                 {
                 {
                     return;
                     return;
                 }
                 }
@@ -233,11 +233,11 @@ namespace Emby.Server.Implementations.HttpServer
                 httpRes.StatusCode = statusCode;
                 httpRes.StatusCode = statusCode;
 
 
                 httpRes.ContentType = "text/html";
                 httpRes.ContentType = "text/html";
-                await Write(httpRes, NormalizeExceptionMessage(ex.Message)).ConfigureAwait(false);
+                await httpRes.WriteAsync(NormalizeExceptionMessage(ex.Message)).ConfigureAwait(false);
             }
             }
             catch (Exception errorEx)
             catch (Exception errorEx)
             {
             {
-                Logger.LogError(errorEx, "Error this.ProcessRequest(context)(Exception while writing error to the response)");
+                _logger.LogError(errorEx, "Error this.ProcessRequest(context)(Exception while writing error to the response)");
             }
             }
         }
         }
 
 
@@ -431,7 +431,7 @@ namespace Emby.Server.Implementations.HttpServer
                 {
                 {
                     httpRes.StatusCode = 503;
                     httpRes.StatusCode = 503;
                     httpRes.ContentType = "text/plain";
                     httpRes.ContentType = "text/plain";
-                    await Write(httpRes, "Server shutting down").ConfigureAwait(false);
+                    await httpRes.WriteAsync("Server shutting down", cancellationToken).ConfigureAwait(false);
                     return;
                     return;
                 }
                 }
 
 
@@ -439,7 +439,7 @@ namespace Emby.Server.Implementations.HttpServer
                 {
                 {
                     httpRes.StatusCode = 400;
                     httpRes.StatusCode = 400;
                     httpRes.ContentType = "text/plain";
                     httpRes.ContentType = "text/plain";
-                    await Write(httpRes, "Invalid host").ConfigureAwait(false);
+                    await httpRes.WriteAsync("Invalid host", cancellationToken).ConfigureAwait(false);
                     return;
                     return;
                 }
                 }
 
 
@@ -447,7 +447,7 @@ namespace Emby.Server.Implementations.HttpServer
                 {
                 {
                     httpRes.StatusCode = 403;
                     httpRes.StatusCode = 403;
                     httpRes.ContentType = "text/plain";
                     httpRes.ContentType = "text/plain";
-                    await Write(httpRes, "Forbidden").ConfigureAwait(false);
+                    await httpRes.WriteAsync("Forbidden", cancellationToken).ConfigureAwait(false);
                     return;
                     return;
                 }
                 }
 
 
@@ -460,28 +460,27 @@ namespace Emby.Server.Implementations.HttpServer
                 if (string.Equals(httpReq.Verb, "OPTIONS", StringComparison.OrdinalIgnoreCase))
                 if (string.Equals(httpReq.Verb, "OPTIONS", StringComparison.OrdinalIgnoreCase))
                 {
                 {
                     httpRes.StatusCode = 200;
                     httpRes.StatusCode = 200;
-                    httpRes.AddHeader("Access-Control-Allow-Origin", "*");
-                    httpRes.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS");
-                    httpRes.AddHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, Range, X-MediaBrowser-Token, X-Emby-Authorization");
+                    httpRes.Headers.Add("Access-Control-Allow-Origin", "*");
+                    httpRes.Headers.Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS");
+                    httpRes.Headers.Add("Access-Control-Allow-Headers", "Content-Type, Authorization, Range, X-MediaBrowser-Token, X-Emby-Authorization");
                     httpRes.ContentType = "text/plain";
                     httpRes.ContentType = "text/plain";
-                    await Write(httpRes, string.Empty).ConfigureAwait(false);
+                    await httpRes.WriteAsync(string.Empty, cancellationToken).ConfigureAwait(false);
                     return;
                     return;
                 }
                 }
 
 
                 urlToLog = GetUrlToLog(urlString);
                 urlToLog = GetUrlToLog(urlString);
-                Logger.LogDebug("HTTP {HttpMethod} {Url} UserAgent: {UserAgent} \nHeaders: {@Headers}", urlToLog, httpReq.UserAgent ?? string.Empty, httpReq.HttpMethod, httpReq.Headers);
 
 
                 if (string.Equals(localPath, "/emby/", StringComparison.OrdinalIgnoreCase) ||
                 if (string.Equals(localPath, "/emby/", StringComparison.OrdinalIgnoreCase) ||
                     string.Equals(localPath, "/mediabrowser/", StringComparison.OrdinalIgnoreCase))
                     string.Equals(localPath, "/mediabrowser/", StringComparison.OrdinalIgnoreCase))
                 {
                 {
-                    RedirectToUrl(httpRes, DefaultRedirectPath);
+                    httpRes.Redirect(_defaultRedirectPath);
                     return;
                     return;
                 }
                 }
 
 
                 if (string.Equals(localPath, "/emby", StringComparison.OrdinalIgnoreCase) ||
                 if (string.Equals(localPath, "/emby", StringComparison.OrdinalIgnoreCase) ||
                     string.Equals(localPath, "/mediabrowser", StringComparison.OrdinalIgnoreCase))
                     string.Equals(localPath, "/mediabrowser", StringComparison.OrdinalIgnoreCase))
                 {
                 {
-                    RedirectToUrl(httpRes, "emby/" + DefaultRedirectPath);
+                    httpRes.Redirect("emby/" + _defaultRedirectPath);
                     return;
                     return;
                 }
                 }
 
 
@@ -494,9 +493,10 @@ namespace Emby.Server.Implementations.HttpServer
 
 
                     if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase))
                     if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase))
                     {
                     {
-                        await Write(httpRes,
+                        await httpRes.WriteAsync(
                             "<!doctype html><html><head><title>Emby</title></head><body>Please update your Emby bookmark to <a href=\"" +
                             "<!doctype html><html><head><title>Emby</title></head><body>Please update your Emby bookmark to <a href=\"" +
-                            newUrl + "\">" + newUrl + "</a></body></html>").ConfigureAwait(false);
+                            newUrl + "\">" + newUrl + "</a></body></html>",
+                            cancellationToken).ConfigureAwait(false);
                         return;
                         return;
                     }
                     }
                 }
                 }
@@ -511,34 +511,35 @@ namespace Emby.Server.Implementations.HttpServer
 
 
                     if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase))
                     if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase))
                     {
                     {
-                        await Write(httpRes,
+                        await httpRes.WriteAsync(
                             "<!doctype html><html><head><title>Emby</title></head><body>Please update your Emby bookmark to <a href=\"" +
                             "<!doctype html><html><head><title>Emby</title></head><body>Please update your Emby bookmark to <a href=\"" +
-                            newUrl + "\">" + newUrl + "</a></body></html>").ConfigureAwait(false);
+                            newUrl + "\">" + newUrl + "</a></body></html>",
+                            cancellationToken).ConfigureAwait(false);
                         return;
                         return;
                     }
                     }
                 }
                 }
 
 
                 if (string.Equals(localPath, "/web", StringComparison.OrdinalIgnoreCase))
                 if (string.Equals(localPath, "/web", StringComparison.OrdinalIgnoreCase))
                 {
                 {
-                    RedirectToUrl(httpRes, DefaultRedirectPath);
+                    httpRes.Redirect(_defaultRedirectPath);
                     return;
                     return;
                 }
                 }
 
 
                 if (string.Equals(localPath, "/web/", StringComparison.OrdinalIgnoreCase))
                 if (string.Equals(localPath, "/web/", StringComparison.OrdinalIgnoreCase))
                 {
                 {
-                    RedirectToUrl(httpRes, "../" + DefaultRedirectPath);
+                    httpRes.Redirect("../" + _defaultRedirectPath);
                     return;
                     return;
                 }
                 }
 
 
                 if (string.Equals(localPath, "/", StringComparison.OrdinalIgnoreCase))
                 if (string.Equals(localPath, "/", StringComparison.OrdinalIgnoreCase))
                 {
                 {
-                    RedirectToUrl(httpRes, DefaultRedirectPath);
+                    httpRes.Redirect(_defaultRedirectPath);
                     return;
                     return;
                 }
                 }
 
 
                 if (string.IsNullOrEmpty(localPath))
                 if (string.IsNullOrEmpty(localPath))
                 {
                 {
-                    RedirectToUrl(httpRes, "/" + DefaultRedirectPath);
+                    httpRes.Redirect("/" + _defaultRedirectPath);
                     return;
                     return;
                 }
                 }
 
 
@@ -546,12 +547,12 @@ namespace Emby.Server.Implementations.HttpServer
                 {
                 {
                     if (localPath.EndsWith("web/dashboard.html", StringComparison.OrdinalIgnoreCase))
                     if (localPath.EndsWith("web/dashboard.html", StringComparison.OrdinalIgnoreCase))
                     {
                     {
-                        RedirectToUrl(httpRes, "index.html#!/dashboard.html");
+                        httpRes.Redirect("index.html#!/dashboard.html");
                     }
                     }
 
 
                     if (localPath.EndsWith("web/home.html", StringComparison.OrdinalIgnoreCase))
                     if (localPath.EndsWith("web/home.html", StringComparison.OrdinalIgnoreCase))
                     {
                     {
-                        RedirectToUrl(httpRes, "index.html");
+                        httpRes.Redirect("index.html");
                     }
                     }
                 }
                 }
 
 
@@ -562,7 +563,7 @@ namespace Emby.Server.Implementations.HttpServer
                     {
                     {
                         httpRes.StatusCode = 503;
                         httpRes.StatusCode = 503;
                         httpRes.ContentType = "text/html";
                         httpRes.ContentType = "text/html";
-                        await Write(httpRes, GlobalResponse).ConfigureAwait(false);
+                        await httpRes.WriteAsync(GlobalResponse, cancellationToken).ConfigureAwait(false);
                         return;
                         return;
                     }
                     }
                 }
                 }
@@ -571,7 +572,7 @@ namespace Emby.Server.Implementations.HttpServer
 
 
                 if (handler != null)
                 if (handler != null)
                 {
                 {
-                    await handler.ProcessRequestAsync(this, httpReq, httpRes, Logger, cancellationToken).ConfigureAwait(false);
+                    await handler.ProcessRequestAsync(this, httpReq, httpRes, _logger, cancellationToken).ConfigureAwait(false);
                 }
                 }
                 else
                 else
                 {
                 {
@@ -598,11 +599,7 @@ namespace Emby.Server.Implementations.HttpServer
                 var elapsed = stopWatch.Elapsed;
                 var elapsed = stopWatch.Elapsed;
                 if (elapsed.TotalMilliseconds > 500)
                 if (elapsed.TotalMilliseconds > 500)
                 {
                 {
-                    Logger.LogWarning("HTTP Response {StatusCode} to {RemoteIp}. Time (slow): {Elapsed:g}. {Url}", httpRes.StatusCode, remoteIp, elapsed, urlToLog);
-                }
-                else
-                {
-                    Logger.LogDebug("HTTP Response {StatusCode} to {RemoteIp}. Time: {Elapsed:g}. {Url}", httpRes.StatusCode, remoteIp, elapsed, urlToLog);
+                    _logger.LogWarning("HTTP Response {StatusCode} to {RemoteIp}. Time (slow): {Elapsed:g}. {Url}", httpRes.StatusCode, remoteIp, elapsed, urlToLog);
                 }
                 }
             }
             }
         }
         }
@@ -619,18 +616,11 @@ namespace Emby.Server.Implementations.HttpServer
                 return new ServiceHandler(restPath, contentType);
                 return new ServiceHandler(restPath, contentType);
             }
             }
 
 
-            Logger.LogError("Could not find handler for {PathInfo}", pathInfo);
+            _logger.LogError("Could not find handler for {PathInfo}", pathInfo);
             return null;
             return null;
         }
         }
 
 
-        private static Task Write(IResponse response, string text)
-        {
-            var bOutput = Encoding.UTF8.GetBytes(text);
-            response.OriginalResponse.ContentLength = bOutput.Length;
-            return response.OutputStream.WriteAsync(bOutput, 0, bOutput.Length);
-        }
-
-        private void RedirectToSecureUrl(IHttpRequest httpReq, IResponse httpRes, string url)
+        private void RedirectToSecureUrl(IHttpRequest httpReq, HttpResponse httpRes, string url)
         {
         {
             if (Uri.TryCreate(url, UriKind.Absolute, out Uri uri))
             if (Uri.TryCreate(url, UriKind.Absolute, out Uri uri))
             {
             {
@@ -640,23 +630,11 @@ namespace Emby.Server.Implementations.HttpServer
                     Scheme = "https"
                     Scheme = "https"
                 };
                 };
                 url = builder.Uri.ToString();
                 url = builder.Uri.ToString();
-
-                RedirectToUrl(httpRes, url);
-            }
-            else
-            {
-                RedirectToUrl(httpRes, url);
             }
             }
-        }
 
 
-        public static void RedirectToUrl(IResponse httpRes, string url)
-        {
-            httpRes.StatusCode = 302;
-            httpRes.AddHeader("Location", url);
+            httpRes.Redirect(url);
         }
         }
 
 
-        public ServiceController ServiceController { get; private set; }
-
         /// <summary>
         /// <summary>
         /// Adds the rest handlers.
         /// Adds the rest handlers.
         /// </summary>
         /// </summary>
@@ -672,9 +650,9 @@ namespace Emby.Server.Implementations.HttpServer
             var types = services.Select(r => r.GetType());
             var types = services.Select(r => r.GetType());
             ServiceController.Init(this, types);
             ServiceController.Init(this, types);
 
 
-            ResponseFilters = new Action<IRequest, IResponse, object>[]
+            ResponseFilters = new Action<IRequest, HttpResponse, object>[]
             {
             {
-                new ResponseFilter(Logger).FilterResponse
+                new ResponseFilter(_logger).FilterResponse
             };
             };
         }
         }
 
 
@@ -772,24 +750,23 @@ namespace Emby.Server.Implementations.HttpServer
             return "emby/emby/" + path;
             return "emby/emby/" + path;
         }
         }
 
 
-        private bool _disposed;
-        private readonly object _disposeLock = new object();
+        /// <inheritdoc />
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
 
 
         protected virtual void Dispose(bool disposing)
         protected virtual void Dispose(bool disposing)
         {
         {
             if (_disposed) return;
             if (_disposed) return;
 
 
-            lock (_disposeLock)
+            if (disposing)
             {
             {
-                if (_disposed) return;
-
-                _disposed = true;
-
-                if (disposing)
-                {
-                    Stop();
-                }
+                Stop();
             }
             }
+
+            _disposed = true;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -803,7 +780,7 @@ namespace Emby.Server.Implementations.HttpServer
                 return Task.CompletedTask;
                 return Task.CompletedTask;
             }
             }
 
 
-            Logger.LogDebug("Websocket message received: {0}", result.MessageType);
+            _logger.LogDebug("Websocket message received: {0}", result.MessageType);
 
 
             IEnumerable<Task> GetTasks()
             IEnumerable<Task> GetTasks()
             {
             {
@@ -815,10 +792,5 @@ namespace Emby.Server.Implementations.HttpServer
 
 
             return Task.WhenAll(GetTasks());
             return Task.WhenAll(GetTasks());
         }
         }
-
-        public void Dispose()
-        {
-            Dispose(true);
-        }
     }
     }
 }
 }

+ 15 - 11
Emby.Server.Implementations/HttpServer/ResponseFilter.cs

@@ -2,6 +2,7 @@ using System;
 using System.Globalization;
 using System.Globalization;
 using System.Text;
 using System.Text;
 using MediaBrowser.Model.Services;
 using MediaBrowser.Model.Services;
+using Microsoft.AspNetCore.Http;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
 using Microsoft.Net.Http.Headers;
 using Microsoft.Net.Http.Headers;
 
 
@@ -9,7 +10,7 @@ namespace Emby.Server.Implementations.HttpServer
 {
 {
     public class ResponseFilter
     public class ResponseFilter
     {
     {
-        private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
+        private static readonly CultureInfo _usCulture = CultureInfo.ReadOnly(new CultureInfo("en-US"));
         private readonly ILogger _logger;
         private readonly ILogger _logger;
 
 
         public ResponseFilter(ILogger logger)
         public ResponseFilter(ILogger logger)
@@ -23,12 +24,12 @@ namespace Emby.Server.Implementations.HttpServer
         /// <param name="req">The req.</param>
         /// <param name="req">The req.</param>
         /// <param name="res">The res.</param>
         /// <param name="res">The res.</param>
         /// <param name="dto">The dto.</param>
         /// <param name="dto">The dto.</param>
-        public void FilterResponse(IRequest req, IResponse res, object dto)
+        public void FilterResponse(IRequest req, HttpResponse res, object dto)
         {
         {
             // Try to prevent compatibility view
             // Try to prevent compatibility view
-            res.AddHeader("Access-Control-Allow-Headers", "Accept, Accept-Language, Authorization, Cache-Control, Content-Disposition, Content-Encoding, Content-Language, Content-Length, Content-MD5, Content-Range, Content-Type, Date, Host, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, Origin, OriginToken, Pragma, Range, Slug, Transfer-Encoding, Want-Digest, X-MediaBrowser-Token, X-Emby-Authorization");
-            res.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS");
-            res.AddHeader("Access-Control-Allow-Origin", "*");
+            res.Headers.Add("Access-Control-Allow-Headers", "Accept, Accept-Language, Authorization, Cache-Control, Content-Disposition, Content-Encoding, Content-Language, Content-Length, Content-MD5, Content-Range, Content-Type, Date, Host, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, Origin, OriginToken, Pragma, Range, Slug, Transfer-Encoding, Want-Digest, X-MediaBrowser-Token, X-Emby-Authorization");
+            res.Headers.Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS");
+            res.Headers.Add("Access-Control-Allow-Origin", "*");
 
 
             if (dto is Exception exception)
             if (dto is Exception exception)
             {
             {
@@ -39,7 +40,7 @@ namespace Emby.Server.Implementations.HttpServer
                     var error = exception.Message.Replace(Environment.NewLine, " ");
                     var error = exception.Message.Replace(Environment.NewLine, " ");
                     error = RemoveControlCharacters(error);
                     error = RemoveControlCharacters(error);
 
 
-                    res.AddHeader("X-Application-Error-Code", error);
+                    res.Headers.Add("X-Application-Error-Code", error);
                 }
                 }
             }
             }
 
 
@@ -54,12 +55,11 @@ namespace Emby.Server.Implementations.HttpServer
                 if (hasHeaders.Headers.TryGetValue(HeaderNames.ContentLength, out string contentLength)
                 if (hasHeaders.Headers.TryGetValue(HeaderNames.ContentLength, out string contentLength)
                     && !string.IsNullOrEmpty(contentLength))
                     && !string.IsNullOrEmpty(contentLength))
                 {
                 {
-                    var length = long.Parse(contentLength, UsCulture);
+                    var length = long.Parse(contentLength, _usCulture);
 
 
                     if (length > 0)
                     if (length > 0)
                     {
                     {
-                        res.OriginalResponse.ContentLength = length;
-                        res.SendChunked = false;
+                        res.ContentLength = length;
                     }
                     }
                 }
                 }
             }
             }
@@ -72,9 +72,12 @@ namespace Emby.Server.Implementations.HttpServer
         /// <returns>System.String.</returns>
         /// <returns>System.String.</returns>
         public static string RemoveControlCharacters(string inString)
         public static string RemoveControlCharacters(string inString)
         {
         {
-            if (inString == null) return null;
+            if (inString == null)
+            {
+                return null;
+            }
 
 
-            var newString = new StringBuilder();
+            var newString = new StringBuilder(inString.Length);
 
 
             foreach (var ch in inString)
             foreach (var ch in inString)
             {
             {
@@ -83,6 +86,7 @@ namespace Emby.Server.Implementations.HttpServer
                     newString.Append(ch);
                     newString.Append(ch);
                 }
                 }
             }
             }
+
             return newString.ToString();
             return newString.ToString();
         }
         }
     }
     }

+ 23 - 25
Emby.Server.Implementations/HttpServer/Security/AuthService.cs

@@ -3,7 +3,6 @@ using System.Linq;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Security;
 using MediaBrowser.Controller.Security;
 using MediaBrowser.Controller.Session;
 using MediaBrowser.Controller.Session;
@@ -13,28 +12,23 @@ namespace Emby.Server.Implementations.HttpServer.Security
 {
 {
     public class AuthService : IAuthService
     public class AuthService : IAuthService
     {
     {
+        private readonly IAuthorizationContext _authorizationContext;
+        private readonly ISessionManager _sessionManager;
         private readonly IServerConfigurationManager _config;
         private readonly IServerConfigurationManager _config;
+        private readonly INetworkManager _networkManager;
 
 
-        public AuthService(IUserManager userManager, IAuthorizationContext authorizationContext, IServerConfigurationManager config, ISessionManager sessionManager, INetworkManager networkManager)
+        public AuthService(
+            IAuthorizationContext authorizationContext,
+            IServerConfigurationManager config,
+            ISessionManager sessionManager,
+            INetworkManager networkManager)
         {
         {
-            AuthorizationContext = authorizationContext;
+            _authorizationContext = authorizationContext;
             _config = config;
             _config = config;
-            SessionManager = sessionManager;
-            UserManager = userManager;
-            NetworkManager = networkManager;
+            _sessionManager = sessionManager;
+            _networkManager = networkManager;
         }
         }
 
 
-        public IUserManager UserManager { get; private set; }
-        public IAuthorizationContext AuthorizationContext { get; private set; }
-        public ISessionManager SessionManager { get; private set; }
-        public INetworkManager NetworkManager { get; private set; }
-
-        /// <summary>
-        /// Redirect the client to a specific URL if authentication failed.
-        /// If this property is null, simply `401 Unauthorized` is returned.
-        /// </summary>
-        public string HtmlRedirect { get; set; }
-
         public void Authenticate(IRequest request, IAuthenticationAttributes authAttribtues)
         public void Authenticate(IRequest request, IAuthenticationAttributes authAttribtues)
         {
         {
             ValidateUser(request, authAttribtues);
             ValidateUser(request, authAttribtues);
@@ -43,7 +37,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
         private void ValidateUser(IRequest request, IAuthenticationAttributes authAttribtues)
         private void ValidateUser(IRequest request, IAuthenticationAttributes authAttribtues)
         {
         {
             // This code is executed before the service
             // This code is executed before the service
-            var auth = AuthorizationContext.GetAuthorizationInfo(request);
+            var auth = _authorizationContext.GetAuthorizationInfo(request);
 
 
             if (!IsExemptFromAuthenticationToken(authAttribtues, request))
             if (!IsExemptFromAuthenticationToken(authAttribtues, request))
             {
             {
@@ -80,7 +74,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
                 !string.IsNullOrEmpty(auth.Client) &&
                 !string.IsNullOrEmpty(auth.Client) &&
                 !string.IsNullOrEmpty(auth.Device))
                 !string.IsNullOrEmpty(auth.Device))
             {
             {
-                SessionManager.LogSessionActivity(auth.Client,
+                _sessionManager.LogSessionActivity(auth.Client,
                     auth.Version,
                     auth.Version,
                     auth.DeviceId,
                     auth.DeviceId,
                     auth.Device,
                     auth.Device,
@@ -89,7 +83,9 @@ namespace Emby.Server.Implementations.HttpServer.Security
             }
             }
         }
         }
 
 
-        private void ValidateUserAccess(User user, IRequest request,
+        private void ValidateUserAccess(
+            User user,
+            IRequest request,
             IAuthenticationAttributes authAttribtues,
             IAuthenticationAttributes authAttribtues,
             AuthorizationInfo auth)
             AuthorizationInfo auth)
         {
         {
@@ -101,7 +97,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
                 };
                 };
             }
             }
 
 
-            if (!user.Policy.EnableRemoteAccess && !NetworkManager.IsInLocalNetwork(request.RemoteIp))
+            if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(request.RemoteIp))
             {
             {
                 throw new SecurityException("User account has been disabled.")
                 throw new SecurityException("User account has been disabled.")
                 {
                 {
@@ -109,11 +105,11 @@ namespace Emby.Server.Implementations.HttpServer.Security
                 };
                 };
             }
             }
 
 
-            if (!user.Policy.IsAdministrator &&
-                !authAttribtues.EscapeParentalControl &&
-                !user.IsParentalScheduleAllowed())
+            if (!user.Policy.IsAdministrator
+                && !authAttribtues.EscapeParentalControl
+                && !user.IsParentalScheduleAllowed())
             {
             {
-                request.Response.AddHeader("X-Application-Error-Code", "ParentalControl");
+                request.Response.Headers.Add("X-Application-Error-Code", "ParentalControl");
 
 
                 throw new SecurityException("This user account is not allowed access at this time.")
                 throw new SecurityException("This user account is not allowed access at this time.")
                 {
                 {
@@ -183,6 +179,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
                     };
                     };
                 }
                 }
             }
             }
+
             if (roles.Contains("delete", StringComparer.OrdinalIgnoreCase))
             if (roles.Contains("delete", StringComparer.OrdinalIgnoreCase))
             {
             {
                 if (user == null || !user.Policy.EnableContentDeletion)
                 if (user == null || !user.Policy.EnableContentDeletion)
@@ -193,6 +190,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
                     };
                     };
                 }
                 }
             }
             }
+
             if (roles.Contains("download", StringComparer.OrdinalIgnoreCase))
             if (roles.Contains("download", StringComparer.OrdinalIgnoreCase))
             {
             {
                 if (user == null || !user.Policy.EnableContentDownloading)
                 if (user == null || !user.Policy.EnableContentDownloading)

+ 2 - 1
Emby.Server.Implementations/IO/ManagedFileSystem.cs

@@ -1,5 +1,6 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Globalization;
 using System.Diagnostics;
 using System.Diagnostics;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
@@ -555,7 +556,7 @@ namespace Emby.Server.Implementations.IO
                 throw new ArgumentNullException(nameof(file2));
                 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
             // Copying over will fail against hidden files
             SetHidden(file1, false);
             SetHidden(file1, false);

+ 2 - 1
Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs

@@ -1,5 +1,6 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Threading;
 using System.Threading;
@@ -89,7 +90,7 @@ namespace Emby.Server.Implementations.Images
             ImageType imageType,
             ImageType imageType,
             CancellationToken cancellationToken)
             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));
             Directory.CreateDirectory(Path.GetDirectoryName(outputPathWithoutExtension));
             string outputPath = CreateImage(item, itemsWithImages, outputPathWithoutExtension, imageType, 0);
             string outputPath = CreateImage(item, itemsWithImages, outputPathWithoutExtension, imageType, 0);
 
 

+ 14 - 55
Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs

@@ -5,7 +5,6 @@ using System.Text.RegularExpressions;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Resolvers;
 using MediaBrowser.Controller.Resolvers;
-using MediaBrowser.Model.Extensions;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.IO;
 
 
 namespace Emby.Server.Implementations.Library
 namespace Emby.Server.Implementations.Library
@@ -17,12 +16,10 @@ namespace Emby.Server.Implementations.Library
     {
     {
         private readonly ILibraryManager _libraryManager;
         private readonly ILibraryManager _libraryManager;
 
 
-        private bool _ignoreDotPrefix;
-
         /// <summary>
         /// <summary>
-        /// 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
         /// </summary>
         /// </summary>
-        public static readonly string[] IgnoreFolders =
+        private static readonly string[] _ignoreFolders =
         {
         {
                 "metadata",
                 "metadata",
                 "ps3_update",
                 "ps3_update",
@@ -43,25 +40,14 @@ namespace Emby.Server.Implementations.Library
                 "$RECYCLE.BIN",
                 "$RECYCLE.BIN",
                 "System Volume Information",
                 "System Volume Information",
                 ".grab",
                 ".grab",
-
-                // macos
-                ".AppleDouble"
-
         };
         };
 
 
         public CoreResolutionIgnoreRule(ILibraryManager libraryManager)
         public CoreResolutionIgnoreRule(ILibraryManager libraryManager)
         {
         {
             _libraryManager = libraryManager;
             _libraryManager = libraryManager;
-
-            _ignoreDotPrefix = Environment.OSVersion.Platform != PlatformID.Win32NT;
         }
         }
 
 
-        /// <summary>
-        /// Shoulds the ignore.
-        /// </summary>
-        /// <param name="fileInfo">The file information.</param>
-        /// <param name="parent">The parent.</param>
-        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
+        /// <inheritdoc />
         public bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem parent)
         public bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem parent)
         {
         {
             // Don't ignore top level folders
             // Don't ignore top level folders
@@ -73,46 +59,17 @@ namespace Emby.Server.Implementations.Library
             var filename = fileInfo.Name;
             var filename = fileInfo.Name;
             var path = fileInfo.FullName;
             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)
             if (fileInfo.IsDirectory)
             {
             {
                 // Ignore any folders in our list
                 // Ignore any folders in our list
-                if (IgnoreFolders.Contains(filename, StringComparer.OrdinalIgnoreCase))
+                if (_ignoreFolders.Contains(filename, StringComparer.OrdinalIgnoreCase))
                 {
                 {
                     return true;
                     return true;
                 }
                 }
@@ -120,8 +77,9 @@ namespace Emby.Server.Implementations.Library
                 if (parent != null)
                 if (parent != null)
                 {
                 {
                     // Ignore trailer folders but allow it at the collection level
                     // 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;
                         return true;
                     }
                     }
@@ -142,14 +100,15 @@ namespace Emby.Server.Implementations.Library
                 if (parent != null)
                 if (parent != null)
                 {
                 {
                     // Don't resolve these into audio files
                     // 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;
                         return true;
                     }
                     }
                 }
                 }
 
 
                 // Ignore samples
                 // Ignore samples
-                Match m = Regex.Match(filename,@"\bsample\b",RegexOptions.IgnoreCase);
+                Match m = Regex.Match(filename, @"\bsample\b", RegexOptions.IgnoreCase);
 
 
                 return m.Success;
                 return m.Success;
             }
             }

+ 55 - 60
Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs

@@ -11,9 +11,9 @@ namespace Emby.Server.Implementations.Library
     public class DefaultAuthenticationProvider : IAuthenticationProvider, IRequiresResolvedUser
     public class DefaultAuthenticationProvider : IAuthenticationProvider, IRequiresResolvedUser
     {
     {
         private readonly ICryptoProvider _cryptographyProvider;
         private readonly ICryptoProvider _cryptographyProvider;
-        public DefaultAuthenticationProvider(ICryptoProvider crypto)
+        public DefaultAuthenticationProvider(ICryptoProvider cryptographyProvider)
         {
         {
-            _cryptographyProvider = crypto;
+            _cryptographyProvider = cryptographyProvider;
         }
         }
 
 
         public string Name => "Default";
         public string Name => "Default";
@@ -28,17 +28,17 @@ namespace Emby.Server.Implementations.Library
             throw new NotImplementedException();
             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<ProviderAuthenticationResult> Authenticate(string username, string password, User resolvedUser)
         public Task<ProviderAuthenticationResult> Authenticate(string username, string password, User resolvedUser)
         {
         {
             bool success = false;
             bool success = false;
             if (resolvedUser == null)
             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
             // 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
                 return Task.FromResult(new ProviderAuthenticationResult
                 {
                 {
@@ -50,37 +50,24 @@ namespace Emby.Server.Implementations.Library
             byte[] passwordbytes = Encoding.UTF8.GetBytes(password);
             byte[] passwordbytes = Encoding.UTF8.GetBytes(password);
 
 
             PasswordHash readyHash = new PasswordHash(resolvedUser.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;
                     success = true;
-                    // throw new Exception("Invalid username or password");
                 }
                 }
             }
             }
             else
             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)
             if (!success)
             {
             {
-                throw new Exception("Invalid username or password");
+                throw new AuthenticationException("Invalid username or password");
             }
             }
 
 
             return Task.FromResult(new ProviderAuthenticationResult
             return Task.FromResult(new ProviderAuthenticationResult
@@ -98,29 +85,22 @@ namespace Emby.Server.Implementations.Library
                 return;
                 return;
             }
             }
 
 
-            if (!user.Password.Contains("$"))
+            if (user.Password.IndexOf('$') == -1)
             {
             {
                 string hash = user.Password;
                 string hash = user.Password;
                 user.Password = string.Format("$SHA1${0}", hash);
                 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;
                 string hash = user.EasyPassword;
                 user.EasyPassword = string.Format("$SHA1${0}", hash);
                 user.EasyPassword = string.Format("$SHA1${0}", hash);
             }
             }
         }
         }
 
 
-        public Task<bool> 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)
         public Task ChangePassword(User user, string newPassword)
         {
         {
@@ -129,30 +109,24 @@ namespace Emby.Server.Implementations.Library
             if (string.IsNullOrEmpty(user.Password))
             if (string.IsNullOrEmpty(user.Password))
             {
             {
                 PasswordHash newPasswordHash = new PasswordHash(_cryptographyProvider);
                 PasswordHash newPasswordHash = new PasswordHash(_cryptographyProvider);
-                newPasswordHash.SaltBytes = _cryptographyProvider.GenerateSalt();
-                newPasswordHash.Salt = PasswordHash.ConvertToByteString(newPasswordHash.SaltBytes);
+                newPasswordHash.Salt = _cryptographyProvider.GenerateSalt();
                 newPasswordHash.Id = _cryptographyProvider.DefaultHashMethod;
                 newPasswordHash.Id = _cryptographyProvider.DefaultHashMethod;
-                newPasswordHash.Hash = GetHashedStringChangeAuth(newPassword, newPasswordHash);
+                newPasswordHash.Hash = GetHashedChangeAuth(newPassword, newPasswordHash);
                 user.Password = newPasswordHash.ToString();
                 user.Password = newPasswordHash.ToString();
                 return Task.CompletedTask;
                 return Task.CompletedTask;
             }
             }
 
 
             PasswordHash passwordHash = new PasswordHash(user.Password);
             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.Id = _cryptographyProvider.DefaultHashMethod;
-                passwordHash.Hash = GetHashedStringChangeAuth(newPassword, passwordHash);
+                passwordHash.Hash = GetHashedChangeAuth(newPassword, passwordHash);
             }
             }
             else if (newPassword != null)
             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();
             user.Password = passwordHash.ToString();
@@ -160,11 +134,6 @@ namespace Emby.Server.Implementations.Library
             return Task.CompletedTask;
             return Task.CompletedTask;
         }
         }
 
 
-        public string GetPasswordHash(User user)
-        {
-            return user.Password;
-        }
-
         public void ChangeEasyPassword(User user, string newPassword, string newPasswordHash)
         public void ChangeEasyPassword(User user, string newPassword, string newPasswordHash)
         {
         {
             ConvertPasswordFormat(user);
             ConvertPasswordFormat(user);
@@ -190,13 +159,13 @@ namespace Emby.Server.Implementations.Library
 
 
             return string.IsNullOrEmpty(user.EasyPassword)
             return string.IsNullOrEmpty(user.EasyPassword)
                 ? null
                 ? 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);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -215,10 +184,10 @@ namespace Emby.Server.Implementations.Library
                 passwordHash = new PasswordHash(user.Password);
                 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
                 // 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));
                 return PasswordHash.ConvertToByteString(_cryptographyProvider.ComputeHash(passwordHash));
             }
             }
             else
             else
@@ -227,5 +196,31 @@ namespace Emby.Server.Implementations.Library
                 return PasswordHash.ConvertToByteString(_cryptographyProvider.ComputeHash(passwordHash.Id, Encoding.UTF8.GetBytes(str)));
                 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));
+            }
+        }
     }
     }
 }
 }

+ 125 - 132
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<PinRedeemResult> RedeemPasswordResetPin(string pin)
-        {
-            SerializablePasswordReset spr;
-            HashSet<string> usersreset = new HashSet<string>();
-            foreach (var resetfile in Directory.EnumerateFiles(_passwordResetFileBaseDir, $"{_passwordResetFileBaseName}*"))
-            {
-                using (var str = File.OpenRead(resetfile))
-                {
-                    spr = await _jsonSerializer.DeserializeFromStreamAsync<SerializablePasswordReset>(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<ForgotPasswordResult> 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<PinRedeemResult> RedeemPasswordResetPin(string pin)
+        {
+            SerializablePasswordReset spr;
+            HashSet<string> usersreset = new HashSet<string>();
+            foreach (var resetfile in Directory.EnumerateFiles(_passwordResetFileBaseDir, $"{_passwordResetFileBaseName}*"))
+            {
+                using (var str = File.OpenRead(resetfile))
+                {
+                    spr = await _jsonSerializer.DeserializeFromStreamAsync<SerializablePasswordReset>(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<ForgotPasswordResult> 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; }
+        }
+    }
+}

+ 2 - 1
Emby.Server.Implementations/Library/ExclusiveLiveStream.cs

@@ -1,4 +1,5 @@
 using System;
 using System;
+using System.Globalization;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;
@@ -26,7 +27,7 @@ namespace Emby.Server.Implementations.Library
             EnableStreamSharing = false;
             EnableStreamSharing = false;
             _closeFn = closeFn;
             _closeFn = closeFn;
             ConsumerCount = 1;
             ConsumerCount = 1;
-            UniqueId = Guid.NewGuid().ToString("N");
+            UniqueId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
         }
         }
 
 
         public Task Close()
         public Task Close()

+ 4 - 7
Emby.Server.Implementations/Library/InvalidAuthProvider.cs

@@ -1,6 +1,3 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Controller.Authentication;
 using MediaBrowser.Controller.Authentication;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
@@ -16,12 +13,12 @@ namespace Emby.Server.Implementations.Library
 
 
         public Task<ProviderAuthenticationResult> Authenticate(string username, string password)
         public Task<ProviderAuthenticationResult> 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<bool> HasPassword(User user)
+        public bool HasPassword(User user)
         {
         {
-            return Task.FromResult(true);
+            return true;
         }
         }
 
 
         public Task ChangePassword(User user, string newPassword)
         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)
         public void ChangeEasyPassword(User user, string newPassword, string newPasswordHash)
         {
         {
-            // Nothing here   
+            // Nothing here
         }
         }
 
 
         public string GetPasswordHash(User user)
         public string GetPasswordHash(User user)

+ 7 - 7
Emby.Server.Implementations/Library/LibraryManager.cs

@@ -1187,12 +1187,12 @@ namespace Emby.Server.Implementations.Library
 
 
             if (libraryFolder != null && libraryFolder.HasImage(ImageType.Primary))
             if (libraryFolder != null && libraryFolder.HasImage(ImageType.Primary))
             {
             {
-                info.PrimaryImageItemId = libraryFolder.Id.ToString("N");
+                info.PrimaryImageItemId = libraryFolder.Id.ToString("N", CultureInfo.InvariantCulture);
             }
             }
 
 
             if (libraryFolder != null)
             if (libraryFolder != null)
             {
             {
-                info.ItemId = libraryFolder.Id.ToString("N");
+                info.ItemId = libraryFolder.Id.ToString("N", CultureInfo.InvariantCulture);
                 info.LibraryOptions = GetLibraryOptions(libraryFolder);
                 info.LibraryOptions = GetLibraryOptions(libraryFolder);
 
 
                 if (refreshQueue != null)
                 if (refreshQueue != null)
@@ -2135,12 +2135,12 @@ namespace Emby.Server.Implementations.Library
             string viewType,
             string viewType,
             string sortName)
             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 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;
             var item = GetItemById(id) as UserView;
 
 
@@ -2271,7 +2271,7 @@ namespace Emby.Server.Implementations.Library
                 throw new ArgumentNullException(nameof(name));
                 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);
             var idValues = "37_namedview_" + name + (parentIdString ?? string.Empty) + (viewType ?? string.Empty);
             if (!string.IsNullOrEmpty(uniqueId))
             if (!string.IsNullOrEmpty(uniqueId))
             {
             {
@@ -2280,7 +2280,7 @@ namespace Emby.Server.Implementations.Library
 
 
             var id = GetNewItemId(idValues, typeof(UserView));
             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;
             var item = GetItemById(id) as UserView;
 
 

+ 1 - 1
Emby.Server.Implementations/Library/LiveStreamHelper.cs

@@ -40,7 +40,7 @@ namespace Emby.Server.Implementations.Library
             var now = DateTime.UtcNow;
             var now = DateTime.UtcNow;
 
 
             MediaInfo mediaInfo = null;
             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))
             if (!string.IsNullOrEmpty(cacheKey))
             {
             {

+ 3 - 3
Emby.Server.Implementations/Library/MediaSourceManager.cs

@@ -269,7 +269,7 @@ namespace Emby.Server.Implementations.Library
 
 
         private static void SetKeyProperties(IMediaSourceProvider provider, MediaSourceInfo mediaSource)
         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))
             if (!string.IsNullOrEmpty(mediaSource.OpenToken) && !mediaSource.OpenToken.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
             {
             {
@@ -626,7 +626,7 @@ namespace Emby.Server.Implementations.Library
             var now = DateTime.UtcNow;
             var now = DateTime.UtcNow;
 
 
             MediaInfo mediaInfo = null;
             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))
             if (!string.IsNullOrEmpty(cacheKey))
             {
             {
@@ -854,7 +854,7 @@ namespace Emby.Server.Implementations.Library
 
 
             var keys = key.Split(new[] { LiveStreamIdDelimeter }, 2);
             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 splitIndex = key.IndexOf(LiveStreamIdDelimeter);
             var keyId = key.Substring(splitIndex + 1);
             var keyId = key.Substring(splitIndex + 1);

+ 15 - 12
Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs

@@ -12,7 +12,6 @@ using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Controller.Resolvers;
 using MediaBrowser.Controller.Resolvers;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.IO;
 
 
 namespace Emby.Server.Implementations.Library.Resolvers.Movies
 namespace Emby.Server.Implementations.Library.Resolvers.Movies
@@ -28,7 +27,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
         /// <value>The priority.</value>
         /// <value>The priority.</value>
         public override ResolverPriority Priority => ResolverPriority.Third;
         public override ResolverPriority Priority => ResolverPriority.Third;
 
 
-        public MultiItemResolverResult ResolveMultiple(Folder parent,
+        public MultiItemResolverResult ResolveMultiple(
+            Folder parent,
             List<FileSystemMetadata> files,
             List<FileSystemMetadata> files,
             string collectionType,
             string collectionType,
             IDirectoryService directoryService)
             IDirectoryService directoryService)
@@ -46,7 +46,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
             return result;
             return result;
         }
         }
 
 
-        private MultiItemResolverResult ResolveMultipleInternal(Folder parent,
+        private MultiItemResolverResult ResolveMultipleInternal(
+            Folder parent,
             List<FileSystemMetadata> files,
             List<FileSystemMetadata> files,
             string collectionType,
             string collectionType,
             IDirectoryService directoryService)
             IDirectoryService directoryService)
@@ -91,7 +92,13 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
             return null;
             return null;
         }
         }
 
 
-        private MultiItemResolverResult ResolveVideos<T>(Folder parent, IEnumerable<FileSystemMetadata> fileSystemEntries, IDirectoryService directoryService, bool suppportMultiEditions, string collectionType, bool parseName)
+        private MultiItemResolverResult ResolveVideos<T>(
+            Folder parent,
+            IEnumerable<FileSystemMetadata> fileSystemEntries,
+            IDirectoryService directoryService,
+            bool suppportMultiEditions,
+            string collectionType,
+            bool parseName)
             where T : Video, new()
             where T : Video, new()
         {
         {
             var files = new List<FileSystemMetadata>();
             var files = new List<FileSystemMetadata>();
@@ -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
                 // This is a hack but currently no better way to resolve a sometimes ambiguous situation
                 if (string.IsNullOrEmpty(collectionType))
                 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;
                         return null;
                     }
                     }
@@ -115,11 +122,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
                 {
                 {
                     leftOver.Add(child);
                     leftOver.Add(child);
                 }
                 }
-                else if (IsIgnored(child.Name))
-                {
-
-                }
-                else
+                else if (!IsIgnored(child.Name))
                 {
                 {
                     files.Add(child);
                     files.Add(child);
                 }
                 }
@@ -168,7 +171,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
         private static bool IsIgnored(string filename)
         private static bool IsIgnored(string filename)
         {
         {
             // Ignore samples
             // Ignore samples
-            Match m = Regex.Match(filename,@"\bsample\b",RegexOptions.IgnoreCase);
+            Match m = Regex.Match(filename, @"\bsample\b", RegexOptions.IgnoreCase);
 
 
             return m.Success;
             return m.Success;
         }
         }

+ 1 - 1
Emby.Server.Implementations/Library/UserDataManager.cs

@@ -152,7 +152,7 @@ namespace Emby.Server.Implementations.Library
         /// <returns>System.String.</returns>
         /// <returns>System.String.</returns>
         private static string GetCacheKey(long internalUserId, Guid itemId)
         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)
         public UserItemData GetUserData(User user, BaseItem item)

+ 27 - 22
Emby.Server.Implementations/Library/UserManager.cs

@@ -266,6 +266,7 @@ namespace Emby.Server.Implementations.Library
                     builder.Append(c);
                     builder.Append(c);
                 }
                 }
             }
             }
+
             return builder.ToString();
             return builder.ToString();
         }
         }
 
 
@@ -286,17 +287,17 @@ namespace Emby.Server.Implementations.Library
             if (user != null)
             if (user != null)
             {
             {
                 var authResult = await AuthenticateLocalUser(username, password, hashedPassword, user, remoteEndPoint).ConfigureAwait(false);
                 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
             else
             {
             {
                 // user is null
                 // user is null
                 var authResult = await AuthenticateLocalUser(username, password, hashedPassword, null, remoteEndPoint).ConfigureAwait(false);
                 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))
                 if (success && authenticationProvider != null && !(authenticationProvider is DefaultAuthenticationProvider))
                 {
                 {
@@ -331,22 +332,25 @@ namespace Emby.Server.Implementations.Library
 
 
             if (user == null)
             if (user == null)
             {
             {
-                throw new SecurityException("Invalid username or password entered.");
+                throw new AuthenticationException("Invalid username or password entered.");
             }
             }
 
 
             if (user.Policy.IsDisabled)
             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))
             if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(remoteEndPoint))
             {
             {
-                throw new SecurityException("Forbidden.");
+                throw new AuthenticationException("Forbidden.");
             }
             }
 
 
             if (!user.IsParentalScheduleAllowed())
             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
             // Update LastActivityDate and LastLoginDate, then save
@@ -357,6 +361,7 @@ namespace Emby.Server.Implementations.Library
                     user.LastActivityDate = user.LastLoginDate = DateTime.UtcNow;
                     user.LastActivityDate = user.LastLoginDate = DateTime.UtcNow;
                     UpdateUser(user);
                     UpdateUser(user);
                 }
                 }
+
                 UpdateInvalidLoginAttemptCount(user, 0);
                 UpdateInvalidLoginAttemptCount(user, 0);
             }
             }
             else
             else
@@ -429,7 +434,7 @@ namespace Emby.Server.Implementations.Library
             return providers;
             return providers;
         }
         }
 
 
-        private async Task<Tuple<string, bool>> 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
             try
             {
             {
@@ -444,23 +449,23 @@ namespace Emby.Server.Implementations.Library
                     authenticationResult = await provider.Authenticate(username, password).ConfigureAwait(false);
                     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);
                     _logger.LogDebug("Authentication provider provided updated username {1}", authenticationResult.Username);
                     username = authenticationResult.Username;
                     username = authenticationResult.Username;
                 }
                 }
 
 
-                return new Tuple<string, bool>(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<string, bool>(username, false);
+                return (username, false);
             }
             }
         }
         }
 
 
-        private async Task<Tuple<IAuthenticationProvider, string, bool>> 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;
             string updatedUsername = null;
             bool success = false;
             bool success = false;
@@ -475,15 +480,15 @@ namespace Emby.Server.Implementations.Library
             if (password == null)
             if (password == null)
             {
             {
                 // legacy
                 // 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
             else
             {
             {
                 foreach (var provider in GetAuthenticationProviders(user))
                 foreach (var provider in GetAuthenticationProviders(user))
                 {
                 {
                     var providerAuthResult = await AuthenticateWithProvider(provider, username, password, user).ConfigureAwait(false);
                     var providerAuthResult = await AuthenticateWithProvider(provider, username, password, user).ConfigureAwait(false);
-                    updatedUsername = providerAuthResult.Item1;
-                    success = providerAuthResult.Item2;
+                    updatedUsername = providerAuthResult.username;
+                    success = providerAuthResult.success;
 
 
                     if (success)
                     if (success)
                     {
                     {
@@ -510,7 +515,7 @@ namespace Emby.Server.Implementations.Library
                 }
                 }
             }
             }
 
 
-            return new Tuple<IAuthenticationProvider, string, bool>(authenticationProvider, username, success);
+            return (authenticationProvider, username, success);
         }
         }
 
 
         private void UpdateInvalidLoginAttemptCount(User user, int newValue)
         private void UpdateInvalidLoginAttemptCount(User user, int newValue)
@@ -593,7 +598,7 @@ namespace Emby.Server.Implementations.Library
                 throw new ArgumentNullException(nameof(user));
                 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 hasConfiguredEasyPassword = !string.IsNullOrEmpty(GetAuthenticationProvider(user).GetEasyPasswordHash(user));
 
 
             bool hasPassword = user.Configuration.EnableLocalPassword && !string.IsNullOrEmpty(remoteEndPoint) && _networkManager.IsInLocalNetwork(remoteEndPoint) ?
             bool hasPassword = user.Configuration.EnableLocalPassword && !string.IsNullOrEmpty(remoteEndPoint) && _networkManager.IsInLocalNetwork(remoteEndPoint) ?

+ 5 - 4
Emby.Server.Implementations/Library/UserViewManager.cs

@@ -1,5 +1,6 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Globalization;
 using System.Linq;
 using System.Linq;
 using System.Threading;
 using System.Threading;
 using MediaBrowser.Controller.Channels;
 using MediaBrowser.Controller.Channels;
@@ -117,7 +118,7 @@ namespace Emby.Server.Implementations.Library
 
 
             if (!query.IncludeHidden)
             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();
             var sorted = _libraryManager.Sort(list, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending).ToList();
@@ -127,7 +128,7 @@ namespace Emby.Server.Implementations.Library
             return list
             return list
                 .OrderBy(i =>
                 .OrderBy(i =>
                 {
                 {
-                    var index = orders.IndexOf(i.Id.ToString("N"));
+                    var index = orders.IndexOf(i.Id.ToString("N", CultureInfo.InvariantCulture));
 
 
                     if (index == -1)
                     if (index == -1)
                     {
                     {
@@ -136,7 +137,7 @@ namespace Emby.Server.Implementations.Library
                         {
                         {
                             if (!view.DisplayParentId.Equals(Guid.Empty))
                             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)
                 parents = _libraryManager.GetUserRootFolder().GetChildren(user, true)
                     .Where(i => i is Folder)
                     .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();
                     .ToList();
             }
             }
 
 

+ 2 - 1
Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs

@@ -1,4 +1,5 @@
 using System;
 using System;
+using System.Globalization;
 using System.Linq;
 using System.Linq;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
@@ -91,7 +92,7 @@ namespace Emby.Server.Implementations.Library.Validators
                     continue;
                     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
                 _libraryManager.DeleteItem(item, new DeleteOptions
                 {
                 {

+ 2 - 2
Emby.Server.Implementations/Library/Validators/PeopleValidator.cs

@@ -1,7 +1,7 @@
 using System;
 using System;
+using System.Globalization;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
-using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Controller.Providers;
@@ -96,7 +96,7 @@ namespace Emby.Server.Implementations.Library.Validators
 
 
             foreach (var item in deadEntities)
             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
                 _libraryManager.DeleteItem(item, new DeleteOptions
                 {
                 {

+ 2 - 1
Emby.Server.Implementations/Library/Validators/StudiosValidator.cs

@@ -1,4 +1,5 @@
 using System;
 using System;
+using System.Globalization;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
@@ -76,7 +77,7 @@ namespace Emby.Server.Implementations.Library.Validators
 
 
             foreach (var item in deadEntities)
             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
                 _libraryManager.DeleteItem(item, new DeleteOptions
                 {
                 {

+ 4 - 4
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;
             LiveTvProgram programInfo = null;
 
 
@@ -713,7 +713,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
 
 
         public async Task<string> CreateSeriesTimer(SeriesTimerInfo info, CancellationToken cancellationToken)
         public async Task<string> CreateSeriesTimer(SeriesTimerInfo info, CancellationToken cancellationToken)
         {
         {
-            info.Id = Guid.NewGuid().ToString("N");
+            info.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
 
 
             // populate info.seriesID
             // populate info.seriesID
             var program = GetProgramInfoFromCache(info.ProgramId);
             var program = GetProgramInfoFromCache(info.ProgramId);
@@ -1059,7 +1059,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             var json = _jsonSerializer.SerializeToString(mediaSource);
             var json = _jsonSerializer.SerializeToString(mediaSource);
             mediaSource = _jsonSerializer.DeserializeFromString<MediaSourceInfo>(json);
             mediaSource = _jsonSerializer.DeserializeFromString<MediaSourceInfo>(json);
 
 
-            mediaSource.Id = Guid.NewGuid().ToString("N") + "_" + mediaSource.Id;
+            mediaSource.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture) + "_" + mediaSource.Id;
 
 
             //if (mediaSource.DateLiveStreamOpened.HasValue && enableStreamSharing)
             //if (mediaSource.DateLiveStreamOpened.HasValue && enableStreamSharing)
             //{
             //{
@@ -2529,7 +2529,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             var timer = new TimerInfo
             var timer = new TimerInfo
             {
             {
                 ChannelId = channelId,
                 ChannelId = channelId,
-                Id = (seriesTimer.Id + parent.ExternalId).GetMD5().ToString("N"),
+                Id = (seriesTimer.Id + parent.ExternalId).GetMD5().ToString("N", CultureInfo.InvariantCulture),
                 StartDate = parent.StartDate,
                 StartDate = parent.StartDate,
                 EndDate = parent.EndDate.Value,
                 EndDate = parent.EndDate.Value,
                 ProgramId = parent.ExternalId,
                 ProgramId = parent.ExternalId,

+ 2 - 2
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),
                 HasImage = program.Icon != null && !string.IsNullOrEmpty(program.Icon.Source),
                 OfficialRating = program.Rating != null && !string.IsNullOrEmpty(program.Rating.Value) ? program.Rating.Value : null,
                 OfficialRating = program.Rating != null && !string.IsNullOrEmpty(program.Rating.Value) ? program.Rating.Value : null,
                 CommunityRating = program.StarRating,
                 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))
             if (string.IsNullOrWhiteSpace(program.ProgramId))
@@ -227,7 +227,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
                     uniqueString = "-" + programInfo.EpisodeNumber.Value.ToString(CultureInfo.InvariantCulture);
                     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 we don't have valid episode info, assume it's a unique program, otherwise recordings might be skipped
                 if (programInfo.IsSeries
                 if (programInfo.IsSeries

+ 14 - 13
Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs

@@ -1,4 +1,5 @@
 using System;
 using System;
+using System.Globalization;
 using System.Linq;
 using System.Linq;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
@@ -52,7 +53,7 @@ namespace Emby.Server.Implementations.LiveTv
                 ExternalId = info.Id,
                 ExternalId = info.Id,
                 ChannelId = GetInternalChannelId(service.Name, info.ChannelId),
                 ChannelId = GetInternalChannelId(service.Name, info.ChannelId),
                 Status = info.Status,
                 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,
                 PrePaddingSeconds = info.PrePaddingSeconds,
                 PostPaddingSeconds = info.PostPaddingSeconds,
                 PostPaddingSeconds = info.PostPaddingSeconds,
                 IsPostPaddingRequired = info.IsPostPaddingRequired,
                 IsPostPaddingRequired = info.IsPostPaddingRequired,
@@ -69,7 +70,7 @@ namespace Emby.Server.Implementations.LiveTv
 
 
             if (!string.IsNullOrEmpty(info.ProgramId))
             if (!string.IsNullOrEmpty(info.ProgramId))
             {
             {
-                dto.ProgramId = GetInternalProgramId(info.ProgramId).ToString("N");
+                dto.ProgramId = GetInternalProgramId(info.ProgramId).ToString("N", CultureInfo.InvariantCulture);
             }
             }
 
 
             if (program != null)
             if (program != null)
@@ -107,7 +108,7 @@ namespace Emby.Server.Implementations.LiveTv
         {
         {
             var dto = new SeriesTimerInfoDto
             var dto = new SeriesTimerInfoDto
             {
             {
-                Id = GetInternalSeriesTimerId(info.Id).ToString("N"),
+                Id = GetInternalSeriesTimerId(info.Id).ToString("N", CultureInfo.InvariantCulture),
                 Overview = info.Overview,
                 Overview = info.Overview,
                 EndDate = info.EndDate,
                 EndDate = info.EndDate,
                 Name = info.Name,
                 Name = info.Name,
@@ -139,7 +140,7 @@ namespace Emby.Server.Implementations.LiveTv
 
 
             if (!string.IsNullOrEmpty(info.ProgramId))
             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());
             dto.DayPattern = info.Days == null ? null : GetDayPattern(info.Days.ToArray());
@@ -169,7 +170,7 @@ namespace Emby.Server.Implementations.LiveTv
                     try
                     try
                     {
                     {
                         dto.ParentThumbImageTag = _imageProcessor.GetImageCacheTag(librarySeries, image);
                         dto.ParentThumbImageTag = _imageProcessor.GetImageCacheTag(librarySeries, image);
-                        dto.ParentThumbItemId = librarySeries.Id.ToString("N");
+                        dto.ParentThumbItemId = librarySeries.Id.ToString("N", CultureInfo.InvariantCulture);
                     }
                     }
                     catch (Exception ex)
                     catch (Exception ex)
                     {
                     {
@@ -185,7 +186,7 @@ namespace Emby.Server.Implementations.LiveTv
                             {
                             {
                                 _imageProcessor.GetImageCacheTag(librarySeries, image)
                                 _imageProcessor.GetImageCacheTag(librarySeries, image)
                             };
                             };
-                        dto.ParentBackdropItemId = librarySeries.Id.ToString("N");
+                        dto.ParentBackdropItemId = librarySeries.Id.ToString("N", CultureInfo.InvariantCulture);
                     }
                     }
                     catch (Exception ex)
                     catch (Exception ex)
                     {
                     {
@@ -213,7 +214,7 @@ namespace Emby.Server.Implementations.LiveTv
                     try
                     try
                     {
                     {
                         dto.ParentPrimaryImageTag = _imageProcessor.GetImageCacheTag(program, image);
                         dto.ParentPrimaryImageTag = _imageProcessor.GetImageCacheTag(program, image);
-                        dto.ParentPrimaryImageItemId = program.Id.ToString("N");
+                        dto.ParentPrimaryImageItemId = program.Id.ToString("N", CultureInfo.InvariantCulture);
                     }
                     }
                     catch (Exception ex)
                     catch (Exception ex)
                     {
                     {
@@ -232,7 +233,7 @@ namespace Emby.Server.Implementations.LiveTv
                         {
                         {
                                 _imageProcessor.GetImageCacheTag(program, image)
                                 _imageProcessor.GetImageCacheTag(program, image)
                         };
                         };
-                            dto.ParentBackdropItemId = program.Id.ToString("N");
+                            dto.ParentBackdropItemId = program.Id.ToString("N", CultureInfo.InvariantCulture);
                         }
                         }
                         catch (Exception ex)
                         catch (Exception ex)
                         {
                         {
@@ -263,7 +264,7 @@ namespace Emby.Server.Implementations.LiveTv
                     try
                     try
                     {
                     {
                         dto.ParentThumbImageTag = _imageProcessor.GetImageCacheTag(librarySeries, image);
                         dto.ParentThumbImageTag = _imageProcessor.GetImageCacheTag(librarySeries, image);
-                        dto.ParentThumbItemId = librarySeries.Id.ToString("N");
+                        dto.ParentThumbItemId = librarySeries.Id.ToString("N", CultureInfo.InvariantCulture);
                     }
                     }
                     catch (Exception ex)
                     catch (Exception ex)
                     {
                     {
@@ -279,7 +280,7 @@ namespace Emby.Server.Implementations.LiveTv
                             {
                             {
                                 _imageProcessor.GetImageCacheTag(librarySeries, image)
                                 _imageProcessor.GetImageCacheTag(librarySeries, image)
                             };
                             };
-                        dto.ParentBackdropItemId = librarySeries.Id.ToString("N");
+                        dto.ParentBackdropItemId = librarySeries.Id.ToString("N", CultureInfo.InvariantCulture);
                     }
                     }
                     catch (Exception ex)
                     catch (Exception ex)
                     {
                     {
@@ -320,7 +321,7 @@ namespace Emby.Server.Implementations.LiveTv
                     try
                     try
                     {
                     {
                         dto.ParentPrimaryImageTag = _imageProcessor.GetImageCacheTag(program, image);
                         dto.ParentPrimaryImageTag = _imageProcessor.GetImageCacheTag(program, image);
-                        dto.ParentPrimaryImageItemId = program.Id.ToString("N");
+                        dto.ParentPrimaryImageItemId = program.Id.ToString("N", CultureInfo.InvariantCulture);
                     }
                     }
                     catch (Exception ex)
                     catch (Exception ex)
                     {
                     {
@@ -339,7 +340,7 @@ namespace Emby.Server.Implementations.LiveTv
                             {
                             {
                                     _imageProcessor.GetImageCacheTag(program, image)
                                     _imageProcessor.GetImageCacheTag(program, image)
                             };
                             };
-                            dto.ParentBackdropItemId = program.Id.ToString("N");
+                            dto.ParentBackdropItemId = program.Id.ToString("N", CultureInfo.InvariantCulture);
                         }
                         }
                         catch (Exception ex)
                         catch (Exception ex)
                         {
                         {
@@ -407,7 +408,7 @@ namespace Emby.Server.Implementations.LiveTv
         {
         {
             var name = ServiceName + externalId + InternalVersionNumber;
             var name = ServiceName + externalId + InternalVersionNumber;
 
 
-            return name.ToLowerInvariant().GetMD5().ToString("N");
+            return name.ToLowerInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture);
         }
         }
 
 
         public Guid GetInternalSeriesTimerId(string externalId)
         public Guid GetInternalSeriesTimerId(string externalId)

+ 12 - 11
Emby.Server.Implementations/LiveTv/LiveTvManager.cs

@@ -1,5 +1,6 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Globalization;
 using System.Linq;
 using System.Linq;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
@@ -258,7 +259,7 @@ namespace Emby.Server.Implementations.LiveTv
             }
             }
             info.RequiresClosing = true;
             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;
             info.LiveStreamId = idPrefix + info.Id;
 
 
@@ -820,7 +821,7 @@ namespace Emby.Server.Implementations.LiveTv
             if (!string.IsNullOrWhiteSpace(query.SeriesTimerId))
             if (!string.IsNullOrWhiteSpace(query.SeriesTimerId))
             {
             {
                 var seriesTimers = await GetSeriesTimersInternal(new SeriesTimerQuery { }, cancellationToken).ConfigureAwait(false);
                 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)
                 if (seriesTimer != null)
                 {
                 {
                     internalQuery.ExternalSeriesId = seriesTimer.SeriesId;
                     internalQuery.ExternalSeriesId = seriesTimer.SeriesId;
@@ -997,7 +998,7 @@ namespace Emby.Server.Implementations.LiveTv
                     if (!string.IsNullOrEmpty(timer.SeriesTimerId))
                     if (!string.IsNullOrEmpty(timer.SeriesTimerId))
                     {
                     {
                         program.SeriesTimerId = _tvDtoService.GetInternalSeriesTimerId(timer.SeriesTimerId)
                         program.SeriesTimerId = _tvDtoService.GetInternalSeriesTimerId(timer.SeriesTimerId)
-                            .ToString("N");
+                            .ToString("N", CultureInfo.InvariantCulture);
 
 
                         foundSeriesTimer = true;
                         foundSeriesTimer = true;
                     }
                     }
@@ -1018,7 +1019,7 @@ namespace Emby.Server.Implementations.LiveTv
                 if (seriesTimer != null)
                 if (seriesTimer != null)
                 {
                 {
                     program.SeriesTimerId = _tvDtoService.GetInternalSeriesTimerId(seriesTimer.Id)
                     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)
             dto.SeriesTimerId = string.IsNullOrEmpty(info.SeriesTimerId)
                 ? null
                 ? null
-                : _tvDtoService.GetInternalSeriesTimerId(info.SeriesTimerId).ToString("N");
+                : _tvDtoService.GetInternalSeriesTimerId(info.SeriesTimerId).ToString("N", CultureInfo.InvariantCulture);
 
 
             dto.TimerId = string.IsNullOrEmpty(info.Id)
             dto.TimerId = string.IsNullOrEmpty(info.Id)
                 ? null
                 ? null
@@ -2027,7 +2028,7 @@ namespace Emby.Server.Implementations.LiveTv
             info.StartDate = program.StartDate;
             info.StartDate = program.StartDate;
             info.Name = program.Name;
             info.Name = program.Name;
             info.Overview = program.Overview;
             info.Overview = program.Overview;
-            info.ProgramId = programDto.Id.ToString("N");
+            info.ProgramId = programDto.Id.ToString("N", CultureInfo.InvariantCulture);
             info.ExternalProgramId = program.ExternalId;
             info.ExternalProgramId = program.ExternalId;
 
 
             if (program.EndDate.HasValue)
             if (program.EndDate.HasValue)
@@ -2088,7 +2089,7 @@ namespace Emby.Server.Implementations.LiveTv
             if (service is ISupportsNewTimerIds supportsNewTimerIds)
             if (service is ISupportsNewTimerIds supportsNewTimerIds)
             {
             {
                 newTimerId = await supportsNewTimerIds.CreateSeriesTimer(info, cancellationToken).ConfigureAwait(false);
                 newTimerId = await supportsNewTimerIds.CreateSeriesTimer(info, cancellationToken).ConfigureAwait(false);
-                newTimerId = _tvDtoService.GetInternalSeriesTimerId(newTimerId).ToString("N");
+                newTimerId = _tvDtoService.GetInternalSeriesTimerId(newTimerId).ToString("N", CultureInfo.InvariantCulture);
             }
             }
             else
             else
             {
             {
@@ -2192,7 +2193,7 @@ namespace Emby.Server.Implementations.LiveTv
 
 
             info.EnabledUsers = _userManager.Users
             info.EnabledUsers = _userManager.Users
                 .Where(IsLiveTvEnabled)
                 .Where(IsLiveTvEnabled)
-                .Select(i => i.Id.ToString("N"))
+                .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture))
                 .ToArray();
                 .ToArray();
 
 
             return info;
             return info;
@@ -2219,7 +2220,7 @@ namespace Emby.Server.Implementations.LiveTv
         {
         {
             var parts = id.Split(new[] { '_' }, 2);
             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)
             if (service == null)
             {
             {
@@ -2269,7 +2270,7 @@ namespace Emby.Server.Implementations.LiveTv
 
 
             if (index == -1 || string.IsNullOrWhiteSpace(info.Id))
             if (index == -1 || string.IsNullOrWhiteSpace(info.Id))
             {
             {
-                info.Id = Guid.NewGuid().ToString("N");
+                info.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
                 list.Add(info);
                 list.Add(info);
                 config.TunerHosts = list.ToArray();
                 config.TunerHosts = list.ToArray();
             }
             }
@@ -2312,7 +2313,7 @@ namespace Emby.Server.Implementations.LiveTv
 
 
             if (index == -1 || string.IsNullOrWhiteSpace(info.Id))
             if (index == -1 || string.IsNullOrWhiteSpace(info.Id))
             {
             {
-                info.Id = Guid.NewGuid().ToString("N");
+                info.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
                 list.Add(info);
                 list.Add(info);
                 config.ListingProviders = list.ToArray();
                 config.ListingProviders = list.ToArray();
             }
             }

+ 2 - 1
Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs

@@ -1,5 +1,6 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Globalization;
 using System.Linq;
 using System.Linq;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
@@ -101,7 +102,7 @@ namespace Emby.Server.Implementations.LiveTv
                 {
                 {
                     var openKeys = new List<string>();
                     var openKeys = new List<string>();
                     openKeys.Add(item.GetType().Name);
                     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);
                     openKeys.Add(source.Id ?? string.Empty);
                     source.OpenToken = string.Join(StreamIdDelimeterString, openKeys.ToArray());
                     source.OpenToken = string.Join(StreamIdDelimeterString, openKeys.ToArray());
                 }
                 }

+ 6 - 6
Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs

@@ -1,7 +1,9 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
+using System.Net;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
@@ -11,7 +13,6 @@ using MediaBrowser.Controller;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
@@ -20,7 +21,6 @@ using MediaBrowser.Model.LiveTv;
 using MediaBrowser.Model.MediaInfo;
 using MediaBrowser.Model.MediaInfo;
 using MediaBrowser.Model.Net;
 using MediaBrowser.Model.Net;
 using MediaBrowser.Model.Serialization;
 using MediaBrowser.Model.Serialization;
-using MediaBrowser.Model.System;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
 
 
 namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
@@ -259,7 +259,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
             using (var manager = new HdHomerunManager(_socketFactory, Logger))
             using (var manager = new HdHomerunManager(_socketFactory, Logger))
             {
             {
                 // Legacy HdHomeruns are IPv4 only
                 // Legacy HdHomeruns are IPv4 only
-                var ipInfo = _networkManager.ParseIpAddress(uri.Host);
+                var ipInfo = IPAddress.Parse(uri.Host);
 
 
                 for (int i = 0; i < model.TunerCount; ++i)
                 for (int i = 0; i < model.TunerCount; ++i)
                 {
                 {
@@ -461,7 +461,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
             {
             {
                 id = "native";
                 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
             var mediaSource = new MediaSourceInfo
             {
             {
@@ -675,13 +675,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                 // Need a way to set the Receive timeout on the socket otherwise this might never timeout?
                 // Need a way to set the Receive timeout on the socket otherwise this might never timeout?
                 try
                 try
                 {
                 {
-                    await udpClient.SendToAsync(discBytes, 0, discBytes.Length, new IpEndPointInfo(new IpAddressInfo("255.255.255.255", IpAddressFamily.InterNetwork), 65001), cancellationToken);
+                    await udpClient.SendToAsync(discBytes, 0, discBytes.Length, new IPEndPoint(IPAddress.Parse("255.255.255.255"), 65001), cancellationToken);
                     var receiveBuffer = new byte[8192];
                     var receiveBuffer = new byte[8192];
 
 
                     while (!cancellationToken.IsCancellationRequested)
                     while (!cancellationToken.IsCancellationRequested)
                     {
                     {
                         var response = await udpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false);
                         var response = await udpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false);
-                        var deviceIp = response.RemoteEndPoint.IpAddress.Address;
+                        var deviceIp = response.RemoteEndPoint.Address.ToString();
 
 
                         // check to make sure we have enough bytes received to be a valid message and make sure the 2nd byte is the discover reply byte
                         // check to make sure we have enough bytes received to be a valid message and make sure the 2nd byte is the discover reply byte
                         if (response.ReceivedBytes > 13 && response.Buffer[1] == 3)
                         if (response.ReceivedBytes > 13 && response.Buffer[1] == 3)

+ 8 - 8
Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs

@@ -89,7 +89,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
         private uint? _lockkey = null;
         private uint? _lockkey = null;
         private int _activeTuner = -1;
         private int _activeTuner = -1;
         private readonly ISocketFactory _socketFactory;
         private readonly ISocketFactory _socketFactory;
-        private IpAddressInfo _remoteIp;
+        private IPAddress _remoteIp;
 
 
         private ILogger _logger;
         private ILogger _logger;
         private ISocket _currentTcpSocket;
         private ISocket _currentTcpSocket;
@@ -114,7 +114,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
             }
             }
         }
         }
 
 
-        public async Task<bool> CheckTunerAvailability(IpAddressInfo remoteIp, int tuner, CancellationToken cancellationToken)
+        public async Task<bool> CheckTunerAvailability(IPAddress remoteIp, int tuner, CancellationToken cancellationToken)
         {
         {
             using (var socket = _socketFactory.CreateTcpSocket(remoteIp, HdHomeRunPort))
             using (var socket = _socketFactory.CreateTcpSocket(remoteIp, HdHomeRunPort))
             {
             {
@@ -122,9 +122,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
             }
             }
         }
         }
 
 
-        private static async Task<bool> CheckTunerAvailability(ISocket socket, IpAddressInfo remoteIp, int tuner, CancellationToken cancellationToken)
+        private static async Task<bool> CheckTunerAvailability(ISocket socket, IPAddress remoteIp, int tuner, CancellationToken cancellationToken)
         {
         {
-            var ipEndPoint = new IpEndPointInfo(remoteIp, HdHomeRunPort);
+            var ipEndPoint = new IPEndPoint(remoteIp, HdHomeRunPort);
 
 
             var lockkeyMsg = CreateGetMessage(tuner, "lockkey");
             var lockkeyMsg = CreateGetMessage(tuner, "lockkey");
             await socket.SendToAsync(lockkeyMsg, 0, lockkeyMsg.Length, ipEndPoint, cancellationToken);
             await socket.SendToAsync(lockkeyMsg, 0, lockkeyMsg.Length, ipEndPoint, cancellationToken);
@@ -137,7 +137,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
             return string.Equals(returnVal, "none", StringComparison.OrdinalIgnoreCase);
             return string.Equals(returnVal, "none", StringComparison.OrdinalIgnoreCase);
         }
         }
 
 
-        public async Task StartStreaming(IpAddressInfo remoteIp, IPAddress localIp, int localPort, IHdHomerunChannelCommands commands, int numTuners, CancellationToken cancellationToken)
+        public async Task StartStreaming(IPAddress remoteIp, IPAddress localIp, int localPort, IHdHomerunChannelCommands commands, int numTuners, CancellationToken cancellationToken)
         {
         {
             _remoteIp = remoteIp;
             _remoteIp = remoteIp;
 
 
@@ -154,7 +154,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 
 
             var lockKeyValue = _lockkey.Value;
             var lockKeyValue = _lockkey.Value;
 
 
-            var ipEndPoint = new IpEndPointInfo(_remoteIp, HdHomeRunPort);
+            var ipEndPoint = new IPEndPoint(_remoteIp, HdHomeRunPort);
 
 
             for (int i = 0; i < numTuners; ++i)
             for (int i = 0; i < numTuners; ++i)
             {
             {
@@ -217,7 +217,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                 foreach (Tuple<string, string> command in commandList)
                 foreach (Tuple<string, string> command in commandList)
                 {
                 {
                     var channelMsg = CreateSetMessage(_activeTuner, command.Item1, command.Item2, _lockkey);
                     var channelMsg = CreateSetMessage(_activeTuner, command.Item1, command.Item2, _lockkey);
-                    await tcpClient.SendToAsync(channelMsg, 0, channelMsg.Length, new IpEndPointInfo(_remoteIp, HdHomeRunPort), cancellationToken).ConfigureAwait(false);
+                    await tcpClient.SendToAsync(channelMsg, 0, channelMsg.Length, new IPEndPoint(_remoteIp, HdHomeRunPort), cancellationToken).ConfigureAwait(false);
                     var response = await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false);
                     var response = await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false);
                     // parse response to make sure it worked
                     // parse response to make sure it worked
                     if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out string returnVal))
                     if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out string returnVal))
@@ -242,7 +242,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
         {
         {
             _logger.LogInformation("HdHomerunManager.ReleaseLockkey {0}", lockKeyValue);
             _logger.LogInformation("HdHomerunManager.ReleaseLockkey {0}", lockKeyValue);
 
 
-            var ipEndPoint = new IpEndPointInfo(_remoteIp, HdHomeRunPort);
+            var ipEndPoint = new IPEndPoint(_remoteIp, HdHomeRunPort);
 
 
             var releaseTarget = CreateSetMessage(_activeTuner, "target", "none", lockKeyValue);
             var releaseTarget = CreateSetMessage(_activeTuner, "target", "none", lockKeyValue);
             await tcpClient.SendToAsync(releaseTarget, 0, releaseTarget.Length, ipEndPoint, CancellationToken.None).ConfigureAwait(false);
             await tcpClient.SendToAsync(releaseTarget, 0, releaseTarget.Length, ipEndPoint, CancellationToken.None).ConfigureAwait(false);

+ 14 - 3
Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs

@@ -25,7 +25,19 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
         private readonly int _numTuners;
         private readonly int _numTuners;
         private readonly INetworkManager _networkManager;
         private readonly INetworkManager _networkManager;
 
 
-        public HdHomerunUdpStream(MediaSourceInfo mediaSource, TunerHostInfo tunerHostInfo, string originalStreamId, IHdHomerunChannelCommands channelCommands, int numTuners, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost, MediaBrowser.Model.Net.ISocketFactory socketFactory, INetworkManager networkManager)
+        public HdHomerunUdpStream(
+            MediaSourceInfo mediaSource,
+            TunerHostInfo tunerHostInfo,
+            string originalStreamId,
+            IHdHomerunChannelCommands channelCommands,
+            int numTuners,
+            IFileSystem fileSystem,
+            IHttpClient httpClient,
+            ILogger logger,
+            IServerApplicationPaths appPaths,
+            IServerApplicationHost appHost,
+            MediaBrowser.Model.Net.ISocketFactory socketFactory,
+            INetworkManager networkManager)
             : base(mediaSource, tunerHostInfo, fileSystem, logger, appPaths)
             : base(mediaSource, tunerHostInfo, fileSystem, logger, appPaths)
         {
         {
             _appHost = appHost;
             _appHost = appHost;
@@ -58,7 +70,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
             Logger.LogInformation("Opening HDHR UDP Live stream from {host}", uri.Host);
             Logger.LogInformation("Opening HDHR UDP Live stream from {host}", uri.Host);
 
 
             var remoteAddress = IPAddress.Parse(uri.Host);
             var remoteAddress = IPAddress.Parse(uri.Host);
-            var embyRemoteAddress = _networkManager.ParseIpAddress(uri.Host);
             IPAddress localAddress = null;
             IPAddress localAddress = null;
             using (var tcpSocket = CreateSocket(remoteAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp))
             using (var tcpSocket = CreateSocket(remoteAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp))
             {
             {
@@ -81,7 +92,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
             try
             try
             {
             {
                 // send url to start streaming
                 // send url to start streaming
-                await hdHomerunManager.StartStreaming(embyRemoteAddress, localAddress, localPort, _channelCommands, _numTuners, openCancellationToken).ConfigureAwait(false);
+                await hdHomerunManager.StartStreaming(remoteAddress, localAddress, localPort, _channelCommands, _numTuners, openCancellationToken).ConfigureAwait(false);
             }
             }
             catch (Exception ex)
             catch (Exception ex)
             {
             {

+ 2 - 1
Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs

@@ -1,5 +1,6 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Threading;
 using System.Threading;
@@ -42,7 +43,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
             MediaSource = mediaSource;
             MediaSource = mediaSource;
             Logger = logger;
             Logger = logger;
             EnableStreamSharing = true;
             EnableStreamSharing = true;
-            UniqueId = Guid.NewGuid().ToString("N");
+            UniqueId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
 
 
             if (tuner != null)
             if (tuner != null)
             {
             {

+ 4 - 3
Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs

@@ -1,5 +1,6 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Threading;
 using System.Threading;
@@ -43,7 +44,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
 
 
         private string GetFullChannelIdPrefix(TunerHostInfo info)
         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<List<ChannelInfo>> GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken)
         protected override async Task<List<ChannelInfo>> GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken)
@@ -61,7 +62,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
                 Name = Name,
                 Name = Name,
                 SourceType = Type,
                 SourceType = Type,
                 Status = LiveTvTunerStatus.Available,
                 Status = LiveTvTunerStatus.Available,
-                Id = i.Url.GetMD5().ToString("N"),
+                Id = i.Url.GetMD5().ToString("N", CultureInfo.InvariantCulture),
                 Url = i.Url
                 Url = i.Url
             })
             })
             .ToList();
             .ToList();
@@ -173,7 +174,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
 
 
                 ReadAtNativeFramerate = false,
                 ReadAtNativeFramerate = false,
 
 
-                Id = channel.Path.GetMD5().ToString("N"),
+                Id = channel.Path.GetMD5().ToString("N", CultureInfo.InvariantCulture),
                 IsInfiniteStream = true,
                 IsInfiniteStream = true,
                 IsRemote = isRemote,
                 IsRemote = isRemote,
 
 

+ 3 - 2
Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs

@@ -58,6 +58,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
                     UserAgent = _appHost.ApplicationUserAgent
                     UserAgent = _appHost.ApplicationUserAgent
                 });
                 });
             }
             }
+
             return Task.FromResult((Stream)File.OpenRead(url));
             return Task.FromResult((Stream)File.OpenRead(url));
         }
         }
 
 
@@ -92,11 +93,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
                     var channel = GetChannelnfo(extInf, tunerHostId, line);
                     var channel = GetChannelnfo(extInf, tunerHostId, line);
                     if (string.IsNullOrWhiteSpace(channel.Id))
                     if (string.IsNullOrWhiteSpace(channel.Id))
                     {
                     {
-                        channel.Id = channelIdPrefix + line.GetMD5().ToString("N");
+                        channel.Id = channelIdPrefix + line.GetMD5().ToString("N", CultureInfo.InvariantCulture);
                     }
                     }
                     else
                     else
                     {
                     {
-                        channel.Id = channelIdPrefix + channel.Id.GetMD5().ToString("N");
+                        channel.Id = channelIdPrefix + channel.Id.GetMD5().ToString("N", CultureInfo.InvariantCulture);
                     }
                     }
 
 
                     channel.Path = line;
                     channel.Path = line;

+ 4 - 4
Emby.Server.Implementations/Localization/Core/da.json

@@ -18,11 +18,11 @@
     "HeaderAlbumArtists": "Albumkunstnere",
     "HeaderAlbumArtists": "Albumkunstnere",
     "HeaderCameraUploads": "Kamera Uploads",
     "HeaderCameraUploads": "Kamera Uploads",
     "HeaderContinueWatching": "Fortsæt Afspilning",
     "HeaderContinueWatching": "Fortsæt Afspilning",
-    "HeaderFavoriteAlbums": "Favoritalbum",
+    "HeaderFavoriteAlbums": "Favoritalbummer",
     "HeaderFavoriteArtists": "Favoritkunstnere",
     "HeaderFavoriteArtists": "Favoritkunstnere",
-    "HeaderFavoriteEpisodes": "Favorit-afsnit",
-    "HeaderFavoriteShows": "Favorit-serier",
-    "HeaderFavoriteSongs": "Favorit-sange",
+    "HeaderFavoriteEpisodes": "Favoritepisoder",
+    "HeaderFavoriteShows": "Favoritserier",
+    "HeaderFavoriteSongs": "Favoritsange",
     "HeaderLiveTV": "Live TV",
     "HeaderLiveTV": "Live TV",
     "HeaderNextUp": "Næste",
     "HeaderNextUp": "Næste",
     "HeaderRecordingGroups": "Optagelsesgrupper",
     "HeaderRecordingGroups": "Optagelsesgrupper",

+ 2 - 2
Emby.Server.Implementations/Localization/Core/hu.json

@@ -19,9 +19,9 @@
     "HeaderCameraUploads": "Kamera feltöltések",
     "HeaderCameraUploads": "Kamera feltöltések",
     "HeaderContinueWatching": "Folyamatban lévő filmek",
     "HeaderContinueWatching": "Folyamatban lévő filmek",
     "HeaderFavoriteAlbums": "Kedvenc Albumok",
     "HeaderFavoriteAlbums": "Kedvenc Albumok",
-    "HeaderFavoriteArtists": "Kedvenc Művészek",
+    "HeaderFavoriteArtists": "Kedvenc Előadók",
     "HeaderFavoriteEpisodes": "Kedvenc Epizódok",
     "HeaderFavoriteEpisodes": "Kedvenc Epizódok",
-    "HeaderFavoriteShows": "Kedvenc Műsorok",
+    "HeaderFavoriteShows": "Kedvenc Sorozatok",
     "HeaderFavoriteSongs": "Kedvenc Dalok",
     "HeaderFavoriteSongs": "Kedvenc Dalok",
     "HeaderLiveTV": "Élő TV",
     "HeaderLiveTV": "Élő TV",
     "HeaderNextUp": "Következik",
     "HeaderNextUp": "Következik",

+ 14 - 14
Emby.Server.Implementations/Localization/Core/pl.json

@@ -18,11 +18,11 @@
     "HeaderAlbumArtists": "Wykonawcy albumów",
     "HeaderAlbumArtists": "Wykonawcy albumów",
     "HeaderCameraUploads": "Przekazane obrazy",
     "HeaderCameraUploads": "Przekazane obrazy",
     "HeaderContinueWatching": "Kontynuuj odtwarzanie",
     "HeaderContinueWatching": "Kontynuuj odtwarzanie",
-    "HeaderFavoriteAlbums": "Albumy ulubione",
-    "HeaderFavoriteArtists": "Wykonawcy ulubieni",
-    "HeaderFavoriteEpisodes": "Odcinki ulubione",
-    "HeaderFavoriteShows": "Seriale ulubione",
-    "HeaderFavoriteSongs": "Utwory ulubione",
+    "HeaderFavoriteAlbums": "Ulubione albumy",
+    "HeaderFavoriteArtists": "Ulubieni wykonawcy",
+    "HeaderFavoriteEpisodes": "Ulubione odcinki",
+    "HeaderFavoriteShows": "Ulubione seriale",
+    "HeaderFavoriteSongs": "Ulubione utwory",
     "HeaderLiveTV": "Telewizja",
     "HeaderLiveTV": "Telewizja",
     "HeaderNextUp": "Do obejrzenia",
     "HeaderNextUp": "Do obejrzenia",
     "HeaderRecordingGroups": "Grupy nagrań",
     "HeaderRecordingGroups": "Grupy nagrań",
@@ -41,26 +41,26 @@
     "Movies": "Filmy",
     "Movies": "Filmy",
     "Music": "Muzyka",
     "Music": "Muzyka",
     "MusicVideos": "Teledyski",
     "MusicVideos": "Teledyski",
-    "NameInstallFailed": "Instalacja {0} nieudana.",
+    "NameInstallFailed": "Instalacja {0} nieudana",
     "NameSeasonNumber": "Sezon {0}",
     "NameSeasonNumber": "Sezon {0}",
-    "NameSeasonUnknown": "Sezon nieznany",
+    "NameSeasonUnknown": "Nieznany sezon",
     "NewVersionIsAvailable": "Nowa wersja serwera Jellyfin jest dostępna do pobrania.",
     "NewVersionIsAvailable": "Nowa wersja serwera Jellyfin jest dostępna do pobrania.",
     "NotificationOptionApplicationUpdateAvailable": "Dostępna aktualizacja aplikacji",
     "NotificationOptionApplicationUpdateAvailable": "Dostępna aktualizacja aplikacji",
-    "NotificationOptionApplicationUpdateInstalled": "Zainstalowano aktualizację aplikacji",
+    "NotificationOptionApplicationUpdateInstalled": "Zaktualizowano aplikację",
     "NotificationOptionAudioPlayback": "Rozpoczęto odtwarzanie muzyki",
     "NotificationOptionAudioPlayback": "Rozpoczęto odtwarzanie muzyki",
     "NotificationOptionAudioPlaybackStopped": "Odtwarzane dźwięku zatrzymane",
     "NotificationOptionAudioPlaybackStopped": "Odtwarzane dźwięku zatrzymane",
-    "NotificationOptionCameraImageUploaded": "Przekazano obraz z urządzenia mobilnego",
-    "NotificationOptionInstallationFailed": "Niepowodzenie instalacji",
+    "NotificationOptionCameraImageUploaded": "Przekazano obraz z urządzenia przenośnego",
+    "NotificationOptionInstallationFailed": "Nieudana instalacja",
     "NotificationOptionNewLibraryContent": "Dodano nową zawartość",
     "NotificationOptionNewLibraryContent": "Dodano nową zawartość",
     "NotificationOptionPluginError": "Awaria wtyczki",
     "NotificationOptionPluginError": "Awaria wtyczki",
     "NotificationOptionPluginInstalled": "Zainstalowano wtyczkę",
     "NotificationOptionPluginInstalled": "Zainstalowano wtyczkę",
     "NotificationOptionPluginUninstalled": "Odinstalowano wtyczkę",
     "NotificationOptionPluginUninstalled": "Odinstalowano wtyczkę",
-    "NotificationOptionPluginUpdateInstalled": "Zainstalowano aktualizację wtyczki",
+    "NotificationOptionPluginUpdateInstalled": "Zaktualizowano wtyczkę",
     "NotificationOptionServerRestartRequired": "Wymagane ponowne uruchomienie serwera",
     "NotificationOptionServerRestartRequired": "Wymagane ponowne uruchomienie serwera",
     "NotificationOptionTaskFailed": "Awaria zaplanowanego zadania",
     "NotificationOptionTaskFailed": "Awaria zaplanowanego zadania",
     "NotificationOptionUserLockedOut": "Użytkownik zablokowany",
     "NotificationOptionUserLockedOut": "Użytkownik zablokowany",
     "NotificationOptionVideoPlayback": "Rozpoczęto odtwarzanie wideo",
     "NotificationOptionVideoPlayback": "Rozpoczęto odtwarzanie wideo",
-    "NotificationOptionVideoPlaybackStopped": "Odtwarzanie wideo zatrzymane",
+    "NotificationOptionVideoPlaybackStopped": "Zatrzymano odtwarzanie wideo",
     "Photos": "Zdjęcia",
     "Photos": "Zdjęcia",
     "Playlists": "Listy odtwarzania",
     "Playlists": "Listy odtwarzania",
     "Plugin": "Wtyczka",
     "Plugin": "Wtyczka",
@@ -73,7 +73,7 @@
     "ServerNameNeedsToBeRestarted": "{0} wymaga ponownego uruchomienia",
     "ServerNameNeedsToBeRestarted": "{0} wymaga ponownego uruchomienia",
     "Shows": "Seriale",
     "Shows": "Seriale",
     "Songs": "Utwory",
     "Songs": "Utwory",
-    "StartupEmbyServerIsLoading": "Twa wczytywanie serwera Jellyfin. Spróbuj ponownie za chwilę.",
+    "StartupEmbyServerIsLoading": "Trwa wczytywanie serwera Jellyfin. Spróbuj ponownie za chwilę.",
     "SubtitleDownloadFailureForItem": "Pobieranie napisów dla {0} zakończone niepowodzeniem",
     "SubtitleDownloadFailureForItem": "Pobieranie napisów dla {0} zakończone niepowodzeniem",
     "SubtitleDownloadFailureFromForItem": "Nieudane pobieranie napisów z {0} dla {1}",
     "SubtitleDownloadFailureFromForItem": "Nieudane pobieranie napisów z {0} dla {1}",
     "SubtitlesDownloadedForItem": "Pobrano napisy dla {0}",
     "SubtitlesDownloadedForItem": "Pobrano napisy dla {0}",
@@ -91,7 +91,7 @@
     "UserPolicyUpdatedWithName": "Zmieniono zasady użytkowania dla {0}",
     "UserPolicyUpdatedWithName": "Zmieniono zasady użytkowania dla {0}",
     "UserStartedPlayingItemWithValues": "{0} odtwarza {1} na {2}",
     "UserStartedPlayingItemWithValues": "{0} odtwarza {1} na {2}",
     "UserStoppedPlayingItemWithValues": "{0} zakończył odtwarzanie {1} na {2}",
     "UserStoppedPlayingItemWithValues": "{0} zakończył odtwarzanie {1} na {2}",
-    "ValueHasBeenAddedToLibrary": "{0} został dodany to biblioteki mediów",
+    "ValueHasBeenAddedToLibrary": "{0} został dodany do biblioteki mediów",
     "ValueSpecialEpisodeName": "Specjalne - {0}",
     "ValueSpecialEpisodeName": "Specjalne - {0}",
     "VersionNumber": "Wersja {0}"
     "VersionNumber": "Wersja {0}"
 }
 }

+ 7 - 7
Emby.Server.Implementations/Localization/Core/pt-BR.json

@@ -21,8 +21,8 @@
     "HeaderFavoriteAlbums": "Álbuns Favoritos",
     "HeaderFavoriteAlbums": "Álbuns Favoritos",
     "HeaderFavoriteArtists": "Artistas Favoritos",
     "HeaderFavoriteArtists": "Artistas Favoritos",
     "HeaderFavoriteEpisodes": "Episódios Favoritos",
     "HeaderFavoriteEpisodes": "Episódios Favoritos",
-    "HeaderFavoriteShows": "Shows Favoritos",
-    "HeaderFavoriteSongs": "Musicas Favoritas",
+    "HeaderFavoriteShows": "Séries Favoritas",
+    "HeaderFavoriteSongs": "Músicas Favoritas",
     "HeaderLiveTV": "TV ao Vivo",
     "HeaderLiveTV": "TV ao Vivo",
     "HeaderNextUp": "Próximos",
     "HeaderNextUp": "Próximos",
     "HeaderRecordingGroups": "Grupos de Gravação",
     "HeaderRecordingGroups": "Grupos de Gravação",
@@ -32,19 +32,19 @@
     "ItemRemovedWithName": "{0} foi removido da biblioteca",
     "ItemRemovedWithName": "{0} foi removido da biblioteca",
     "LabelIpAddressValue": "Endereço IP: {0}",
     "LabelIpAddressValue": "Endereço IP: {0}",
     "LabelRunningTimeValue": "Tempo de execução: {0}",
     "LabelRunningTimeValue": "Tempo de execução: {0}",
-    "Latest": "Recente",
-    "MessageApplicationUpdated": "O servidor Jellyfin foi atualizado",
-    "MessageApplicationUpdatedTo": "O Servidor Jellyfin foi atualizado para {0}",
+    "Latest": "Recentes",
+    "MessageApplicationUpdated": "Servidor Jellyfin atualizado",
+    "MessageApplicationUpdatedTo": "Servidor Jellyfin atualizado para {0}",
     "MessageNamedServerConfigurationUpdatedWithValue": "A seção {0} da configuração do servidor foi atualizada",
     "MessageNamedServerConfigurationUpdatedWithValue": "A seção {0} da configuração do servidor foi atualizada",
     "MessageServerConfigurationUpdated": "A configuração do servidor foi atualizada",
     "MessageServerConfigurationUpdated": "A configuração do servidor foi atualizada",
     "MixedContent": "Conteúdo misto",
     "MixedContent": "Conteúdo misto",
     "Movies": "Filmes",
     "Movies": "Filmes",
     "Music": "Música",
     "Music": "Música",
-    "MusicVideos": "Vídeos musicais",
+    "MusicVideos": "Clipes",
     "NameInstallFailed": "A instalação de {0} falhou",
     "NameInstallFailed": "A instalação de {0} falhou",
     "NameSeasonNumber": "Temporada {0}",
     "NameSeasonNumber": "Temporada {0}",
     "NameSeasonUnknown": "Temporada Desconhecida",
     "NameSeasonUnknown": "Temporada Desconhecida",
-    "NewVersionIsAvailable": "Uma nova versão do servidor Jellyfin está disponível para download.",
+    "NewVersionIsAvailable": "Uma nova versão do Servidor Jellyfin está disponível para download.",
     "NotificationOptionApplicationUpdateAvailable": "Atualização de aplicativo disponível",
     "NotificationOptionApplicationUpdateAvailable": "Atualização de aplicativo disponível",
     "NotificationOptionApplicationUpdateInstalled": "Atualização de aplicativo instalada",
     "NotificationOptionApplicationUpdateInstalled": "Atualização de aplicativo instalada",
     "NotificationOptionAudioPlayback": "Reprodução de áudio iniciada",
     "NotificationOptionAudioPlayback": "Reprodução de áudio iniciada",

+ 6 - 8
Emby.Server.Implementations/Net/SocketFactory.cs

@@ -16,14 +16,14 @@ namespace Emby.Server.Implementations.Net
         // but that wasn't really the point so kept to YAGNI principal for now, even if the
         // but that wasn't really the point so kept to YAGNI principal for now, even if the
         // interfaces are a bit ugly, specific and make assumptions.
         // interfaces are a bit ugly, specific and make assumptions.
 
 
-        public ISocket CreateTcpSocket(IpAddressInfo remoteAddress, int remotePort)
+        public ISocket CreateTcpSocket(IPAddress remoteAddress, int remotePort)
         {
         {
             if (remotePort < 0)
             if (remotePort < 0)
             {
             {
                 throw new ArgumentException("remotePort cannot be less than zero.", nameof(remotePort));
                 throw new ArgumentException("remotePort cannot be less than zero.", nameof(remotePort));
             }
             }
 
 
-            var addressFamily = remoteAddress.AddressFamily == IpAddressFamily.InterNetwork
+            var addressFamily = remoteAddress.AddressFamily == AddressFamily.InterNetwork
                 ? AddressFamily.InterNetwork
                 ? AddressFamily.InterNetwork
                 : AddressFamily.InterNetworkV6;
                 : AddressFamily.InterNetworkV6;
 
 
@@ -40,7 +40,7 @@ namespace Emby.Server.Implementations.Net
 
 
             try
             try
             {
             {
-                return new UdpSocket(retVal, new IpEndPointInfo(remoteAddress, remotePort));
+                return new UdpSocket(retVal, new IPEndPoint(remoteAddress, remotePort));
             }
             }
             catch
             catch
             {
             {
@@ -102,7 +102,7 @@ namespace Emby.Server.Implementations.Net
         /// Creates a new UDP acceptSocket that is a member of the SSDP multicast local admin group and binds it to the specified local port.
         /// Creates a new UDP acceptSocket that is a member of the SSDP multicast local admin group and binds it to the specified local port.
         /// </summary>
         /// </summary>
         /// <returns>An implementation of the <see cref="ISocket"/> interface used by RSSDP components to perform acceptSocket operations.</returns>
         /// <returns>An implementation of the <see cref="ISocket"/> interface used by RSSDP components to perform acceptSocket operations.</returns>
-        public ISocket CreateSsdpUdpSocket(IpAddressInfo localIpAddress, int localPort)
+        public ISocket CreateSsdpUdpSocket(IPAddress localIpAddress, int localPort)
         {
         {
             if (localPort < 0)
             if (localPort < 0)
             {
             {
@@ -115,10 +115,8 @@ namespace Emby.Server.Implementations.Net
                 retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                 retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                 retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 4);
                 retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 4);
 
 
-                var localIp = NetworkManager.ToIPAddress(localIpAddress);
-
-                retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(IPAddress.Parse("239.255.255.250"), localIp));
-                return new UdpSocket(retVal, localPort, localIp);
+                retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(IPAddress.Parse("239.255.255.250"), localIpAddress));
+                return new UdpSocket(retVal, localPort, localIpAddress);
             }
             }
             catch
             catch
             {
             {

+ 9 - 22
Emby.Server.Implementations/Net/UdpSocket.cs

@@ -3,7 +3,6 @@ using System.Net;
 using System.Net.Sockets;
 using System.Net.Sockets;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
-using Emby.Server.Implementations.Networking;
 using MediaBrowser.Model.Net;
 using MediaBrowser.Model.Net;
 
 
 namespace Emby.Server.Implementations.Net
 namespace Emby.Server.Implementations.Net
@@ -19,7 +18,7 @@ namespace Emby.Server.Implementations.Net
 
 
         public Socket Socket => _socket;
         public Socket Socket => _socket;
 
 
-        public IpAddressInfo LocalIPAddress { get; }
+        public IPAddress LocalIPAddress { get; }
 
 
         private readonly SocketAsyncEventArgs _receiveSocketAsyncEventArgs = new SocketAsyncEventArgs()
         private readonly SocketAsyncEventArgs _receiveSocketAsyncEventArgs = new SocketAsyncEventArgs()
         {
         {
@@ -40,7 +39,7 @@ namespace Emby.Server.Implementations.Net
 
 
             _socket = socket;
             _socket = socket;
             _localPort = localPort;
             _localPort = localPort;
-            LocalIPAddress = NetworkManager.ToIpAddressInfo(ip);
+            LocalIPAddress = ip;
 
 
             _socket.Bind(new IPEndPoint(ip, _localPort));
             _socket.Bind(new IPEndPoint(ip, _localPort));
 
 
@@ -71,7 +70,7 @@ namespace Emby.Server.Implementations.Net
                     {
                     {
                         Buffer = e.Buffer,
                         Buffer = e.Buffer,
                         ReceivedBytes = e.BytesTransferred,
                         ReceivedBytes = e.BytesTransferred,
-                        RemoteEndPoint = ToIpEndPointInfo(e.RemoteEndPoint as IPEndPoint),
+                        RemoteEndPoint = e.RemoteEndPoint as IPEndPoint,
                         LocalIPAddress = LocalIPAddress
                         LocalIPAddress = LocalIPAddress
                     });
                     });
                 }
                 }
@@ -100,12 +99,12 @@ namespace Emby.Server.Implementations.Net
             }
             }
         }
         }
 
 
-        public UdpSocket(Socket socket, IpEndPointInfo endPoint)
+        public UdpSocket(Socket socket, IPEndPoint endPoint)
         {
         {
             if (socket == null) throw new ArgumentNullException(nameof(socket));
             if (socket == null) throw new ArgumentNullException(nameof(socket));
 
 
             _socket = socket;
             _socket = socket;
-            _socket.Connect(NetworkManager.ToIPEndPoint(endPoint));
+            _socket.Connect(endPoint);
 
 
             InitReceiveSocketAsyncEventArgs();
             InitReceiveSocketAsyncEventArgs();
         }
         }
@@ -140,7 +139,7 @@ namespace Emby.Server.Implementations.Net
             return new SocketReceiveResult
             return new SocketReceiveResult
             {
             {
                 ReceivedBytes = receivedBytes,
                 ReceivedBytes = receivedBytes,
-                RemoteEndPoint = ToIpEndPointInfo((IPEndPoint)remoteEndPoint),
+                RemoteEndPoint = (IPEndPoint)remoteEndPoint,
                 Buffer = buffer,
                 Buffer = buffer,
                 LocalIPAddress = LocalIPAddress
                 LocalIPAddress = LocalIPAddress
             };
             };
@@ -191,7 +190,7 @@ namespace Emby.Server.Implementations.Net
             return ReceiveAsync(buffer, 0, buffer.Length, cancellationToken);
             return ReceiveAsync(buffer, 0, buffer.Length, cancellationToken);
         }
         }
 
 
-        public Task SendToAsync(byte[] buffer, int offset, int size, IpEndPointInfo endPoint, CancellationToken cancellationToken)
+        public Task SendToAsync(byte[] buffer, int offset, int size, IPEndPoint endPoint, CancellationToken cancellationToken)
         {
         {
             ThrowIfDisposed();
             ThrowIfDisposed();
 
 
@@ -227,13 +226,11 @@ namespace Emby.Server.Implementations.Net
             return taskCompletion.Task;
             return taskCompletion.Task;
         }
         }
 
 
-        public IAsyncResult BeginSendTo(byte[] buffer, int offset, int size, IpEndPointInfo endPoint, AsyncCallback callback, object state)
+        public IAsyncResult BeginSendTo(byte[] buffer, int offset, int size, IPEndPoint endPoint, AsyncCallback callback, object state)
         {
         {
             ThrowIfDisposed();
             ThrowIfDisposed();
 
 
-            var ipEndPoint = NetworkManager.ToIPEndPoint(endPoint);
-
-            return _socket.BeginSendTo(buffer, offset, size, SocketFlags.None, ipEndPoint, callback, state);
+            return _socket.BeginSendTo(buffer, offset, size, SocketFlags.None, endPoint, callback, state);
         }
         }
 
 
         public int EndSendTo(IAsyncResult result)
         public int EndSendTo(IAsyncResult result)
@@ -268,15 +265,5 @@ namespace Emby.Server.Implementations.Net
 
 
             _disposed = true;
             _disposed = true;
         }
         }
-
-        private static IpEndPointInfo ToIpEndPointInfo(IPEndPoint endpoint)
-        {
-            if (endpoint == null)
-            {
-                return null;
-            }
-
-            return NetworkManager.ToIpEndPointInfo(endpoint);
-        }
     }
     }
 }
 }

+ 0 - 167
Emby.Server.Implementations/Networking/IPNetwork/BigIntegerExt.cs

@@ -1,167 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Numerics;
-using System.Text;
-
-namespace Emby.Server.Implementations.Networking.IPNetwork
-{
-    /// <summary>
-    /// Extension methods to convert <see cref="BigInteger"/>
-    /// instances to hexadecimal, octal, and binary strings.
-    /// </summary>
-    public static class BigIntegerExtensions
-    {
-        /// <summary>
-        /// Converts a <see cref="BigInteger"/> to a binary string.
-        /// </summary>
-        /// <param name="bigint">A <see cref="BigInteger"/>.</param>
-        /// <returns>
-        /// A <see cref="string"/> containing a binary
-        /// representation of the supplied <see cref="BigInteger"/>.
-        /// </returns>
-        public static string ToBinaryString(this BigInteger bigint)
-        {
-            var bytes = bigint.ToByteArray();
-            var idx = bytes.Length - 1;
-
-            // Create a StringBuilder having appropriate capacity.
-            var base2 = new StringBuilder(bytes.Length * 8);
-
-            // Convert first byte to binary.
-            var binary = Convert.ToString(bytes[idx], 2);
-
-            // Ensure leading zero exists if value is positive.
-            if (binary[0] != '0' && bigint.Sign == 1)
-            {
-                base2.Append('0');
-            }
-
-            // Append binary string to StringBuilder.
-            base2.Append(binary);
-
-            // Convert remaining bytes adding leading zeros.
-            for (idx--; idx >= 0; idx--)
-            {
-                base2.Append(Convert.ToString(bytes[idx], 2).PadLeft(8, '0'));
-            }
-
-            return base2.ToString();
-        }
-
-        /// <summary>
-        /// Converts a <see cref="BigInteger"/> to a hexadecimal string.
-        /// </summary>
-        /// <param name="bigint">A <see cref="BigInteger"/>.</param>
-        /// <returns>
-        /// A <see cref="string"/> containing a hexadecimal
-        /// representation of the supplied <see cref="BigInteger"/>.
-        /// </returns>
-        public static string ToHexadecimalString(this BigInteger bigint)
-        {
-            return bigint.ToString("X");
-        }
-
-        /// <summary>
-        /// Converts a <see cref="BigInteger"/> to a octal string.
-        /// </summary>
-        /// <param name="bigint">A <see cref="BigInteger"/>.</param>
-        /// <returns>
-        /// A <see cref="string"/> containing an octal
-        /// representation of the supplied <see cref="BigInteger"/>.
-        /// </returns>
-        public static string ToOctalString(this BigInteger bigint)
-        {
-            var bytes = bigint.ToByteArray();
-            var idx = bytes.Length - 1;
-
-            // Create a StringBuilder having appropriate capacity.
-            var base8 = new StringBuilder(((bytes.Length / 3) + 1) * 8);
-
-            // Calculate how many bytes are extra when byte array is split
-            // into three-byte (24-bit) chunks.
-            var extra = bytes.Length % 3;
-
-            // If no bytes are extra, use three bytes for first chunk.
-            if (extra == 0)
-            {
-                extra = 3;
-            }
-
-            // Convert first chunk (24-bits) to integer value.
-            int int24 = 0;
-            for (; extra != 0; extra--)
-            {
-                int24 <<= 8;
-                int24 += bytes[idx--];
-            }
-
-            // Convert 24-bit integer to octal without adding leading zeros.
-            var octal = Convert.ToString(int24, 8);
-
-            // Ensure leading zero exists if value is positive.
-            if (octal[0] != '0')
-            {
-                if (bigint.Sign == 1)
-                {
-                    base8.Append('0');
-                }
-            }
-
-            // Append first converted chunk to StringBuilder.
-            base8.Append(octal);
-
-            // Convert remaining 24-bit chunks, adding leading zeros.
-            for (; idx >= 0; idx -= 3)
-            {
-                int24 = (bytes[idx] << 16) + (bytes[idx - 1] << 8) + bytes[idx - 2];
-                base8.Append(Convert.ToString(int24, 8).PadLeft(8, '0'));
-            }
-
-            return base8.ToString();
-        }
-
-        /// <summary>
-        ///
-        /// Reverse a Positive BigInteger ONLY
-        /// Bitwise ~ operator
-        ///
-        /// Input  : FF FF FF FF
-        /// Width  : 4
-        /// Result : 00 00 00 00
-        ///
-        ///
-        /// Input  : 00 00 00 00
-        /// Width  : 4
-        /// Result : FF FF FF FF
-        ///
-        /// Input  : FF FF FF FF
-        /// Width  : 8
-        /// Result : FF FF FF FF 00 00 00 00
-        ///
-        ///
-        /// Input  : 00 00 00 00
-        /// Width  : 8
-        /// Result : FF FF FF FF FF FF FF FF
-        ///
-        /// </summary>
-        /// <param name="input"></param>
-        /// <param name="width"></param>
-        /// <returns></returns>
-        public static BigInteger PositiveReverse(this BigInteger input, int width)
-        {
-
-            var result = new List<byte>();
-            var bytes = input.ToByteArray();
-            var work = new byte[width];
-            Array.Copy(bytes, 0, work, 0, bytes.Length - 1); // Length -1 : positive BigInteger
-
-            for (int i = 0; i < work.Length; i++)
-            {
-                result.Add((byte)(~work[i]));
-            }
-            result.Add(0); // positive BigInteger
-            return new BigInteger(result.ToArray());
-
-        }
-    }
-}

+ 0 - 94
Emby.Server.Implementations/Networking/IPNetwork/IPAddressCollection.cs

@@ -1,94 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Net;
-using System.Numerics;
-
-namespace Emby.Server.Implementations.Networking.IPNetwork
-{
-    public class IPAddressCollection : IEnumerable<IPAddress>, IEnumerator<IPAddress>
-    {
-
-        private IPNetwork _ipnetwork;
-        private BigInteger _enumerator;
-
-        internal IPAddressCollection(IPNetwork ipnetwork)
-        {
-            this._ipnetwork = ipnetwork;
-            this._enumerator = -1;
-        }
-
-
-        #region Count, Array, Enumerator
-
-        public BigInteger Count => this._ipnetwork.Total;
-
-        public IPAddress this[BigInteger i]
-        {
-            get
-            {
-                if (i >= this.Count)
-                {
-                    throw new ArgumentOutOfRangeException(nameof(i));
-                }
-                byte width = this._ipnetwork.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork ? (byte)32 : (byte)128;
-                var ipn = this._ipnetwork.Subnet(width);
-                return ipn[i].Network;
-            }
-        }
-
-        #endregion
-
-        #region IEnumerable Members
-
-        IEnumerator<IPAddress> IEnumerable<IPAddress>.GetEnumerator()
-        {
-            return this;
-        }
-
-        IEnumerator IEnumerable.GetEnumerator()
-        {
-            return this;
-        }
-
-        #region IEnumerator<IPNetwork> Members
-
-        public IPAddress Current => this[this._enumerator];
-
-        #endregion
-
-        #region IDisposable Members
-
-        public void Dispose()
-        {
-            // nothing to dispose
-            return;
-        }
-
-        #endregion
-
-        #region IEnumerator Members
-
-        object IEnumerator.Current => this.Current;
-
-        public bool MoveNext()
-        {
-            this._enumerator++;
-            if (this._enumerator >= this.Count)
-            {
-                return false;
-            }
-            return true;
-
-        }
-
-        public void Reset()
-        {
-            this._enumerator = -1;
-        }
-
-        #endregion
-
-        #endregion
-    }
-}

+ 0 - 2008
Emby.Server.Implementations/Networking/IPNetwork/IPNetwork.cs

@@ -1,2008 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Net;
-using System.Net.Sockets;
-using System.Numerics;
-using System.Text.RegularExpressions;
-
-namespace Emby.Server.Implementations.Networking.IPNetwork
-{
-    /// <summary>
-    /// IP Network utility class.
-    /// Use IPNetwork.Parse to create instances.
-    /// </summary>
-    public class IPNetwork : IComparable<IPNetwork>
-    {
-
-        #region properties
-
-        //private uint _network;
-        private BigInteger _ipaddress;
-        private AddressFamily _family;
-        //private uint _netmask;
-        //private uint _broadcast;
-        //private uint _firstUsable;
-        //private uint _lastUsable;
-        //private uint _usable;
-        private byte _cidr;
-
-        #endregion
-
-        #region accessors
-
-        private BigInteger _network
-        {
-            get
-            {
-                var uintNetwork = this._ipaddress & this._netmask;
-                return uintNetwork;
-            }
-        }
-
-        /// <summary>
-        /// Network address
-        /// </summary>
-        public IPAddress Network => IPNetwork.ToIPAddress(this._network, this._family);
-
-        /// <summary>
-        /// Address Family
-        /// </summary>
-        public AddressFamily AddressFamily => this._family;
-
-        private BigInteger _netmask => IPNetwork.ToUint(this._cidr, this._family);
-
-        /// <summary>
-        /// Netmask
-        /// </summary>
-        public IPAddress Netmask => IPNetwork.ToIPAddress(this._netmask, this._family);
-
-        private BigInteger _broadcast
-        {
-            get
-            {
-
-                int width = this._family == System.Net.Sockets.AddressFamily.InterNetwork ? 4 : 16;
-                var uintBroadcast = this._network + this._netmask.PositiveReverse(width);
-                return uintBroadcast;
-            }
-        }
-
-        /// <summary>
-        /// Broadcast address
-        /// </summary>
-        public IPAddress Broadcast
-        {
-            get
-            {
-                if (this._family == System.Net.Sockets.AddressFamily.InterNetworkV6)
-                {
-                    return null;
-                }
-                return IPNetwork.ToIPAddress(this._broadcast, this._family);
-            }
-        }
-
-        /// <summary>
-        /// First usable IP adress in Network
-        /// </summary>
-        public IPAddress FirstUsable
-        {
-            get
-            {
-                var fisrt = this._family == System.Net.Sockets.AddressFamily.InterNetworkV6
-                    ? this._network
-                    : (this.Usable <= 0) ? this._network : this._network + 1;
-                return IPNetwork.ToIPAddress(fisrt, this._family);
-            }
-        }
-
-        /// <summary>
-        /// Last usable IP adress in Network
-        /// </summary>
-        public IPAddress LastUsable
-        {
-            get
-            {
-                var last = this._family == System.Net.Sockets.AddressFamily.InterNetworkV6
-                    ? this._broadcast
-                    : (this.Usable <= 0) ? this._network : this._broadcast - 1;
-                return IPNetwork.ToIPAddress(last, this._family);
-            }
-        }
-
-        /// <summary>
-        /// Number of usable IP adress in Network
-        /// </summary>
-        public BigInteger Usable
-        {
-            get
-            {
-
-                if (this._family == System.Net.Sockets.AddressFamily.InterNetworkV6)
-                {
-                    return this.Total;
-                }
-                byte[] mask = new byte[] { 0xff, 0xff, 0xff, 0xff, 0x00 };
-                var bmask = new BigInteger(mask);
-                var usableIps = (_cidr > 30) ? 0 : ((bmask >> _cidr) - 1);
-                return usableIps;
-            }
-        }
-
-        /// <summary>
-        /// Number of IP adress in Network
-        /// </summary>
-        public BigInteger Total
-        {
-            get
-            {
-
-                int max = this._family == System.Net.Sockets.AddressFamily.InterNetwork ? 32 : 128;
-                var count = BigInteger.Pow(2, (max - _cidr));
-                return count;
-            }
-        }
-
-
-        /// <summary>
-        /// The CIDR netmask notation
-        /// </summary>
-        public byte Cidr => this._cidr;
-
-        #endregion
-
-        #region constructor
-
-#if TRAVISCI
-        public
-#else
-        internal
-#endif
-
-        IPNetwork(BigInteger ipaddress, AddressFamily family, byte cidr)
-        {
-
-            int maxCidr = family == System.Net.Sockets.AddressFamily.InterNetwork ? 32 : 128;
-            if (cidr > maxCidr)
-            {
-                throw new ArgumentOutOfRangeException(nameof(cidr));
-            }
-
-            this._ipaddress = ipaddress;
-            this._family = family;
-            this._cidr = cidr;
-
-        }
-
-        #endregion
-
-        #region parsers
-
-        /// <summary>
-        /// 192.168.168.100 - 255.255.255.0
-        ///
-        /// Network   : 192.168.168.0
-        /// Netmask   : 255.255.255.0
-        /// Cidr      : 24
-        /// Start     : 192.168.168.1
-        /// End       : 192.168.168.254
-        /// Broadcast : 192.168.168.255
-        /// </summary>
-        /// <param name="ipaddress"></param>
-        /// <param name="netmask"></param>
-        /// <returns></returns>
-        public static IPNetwork Parse(string ipaddress, string netmask)
-        {
-
-            IPNetwork ipnetwork = null;
-            IPNetwork.InternalParse(false, ipaddress, netmask, out ipnetwork);
-            return ipnetwork;
-        }
-
-        /// <summary>
-        /// 192.168.168.100/24
-        ///
-        /// Network   : 192.168.168.0
-        /// Netmask   : 255.255.255.0
-        /// Cidr      : 24
-        /// Start     : 192.168.168.1
-        /// End       : 192.168.168.254
-        /// Broadcast : 192.168.168.255
-        /// </summary>
-        /// <param name="ipaddress"></param>
-        /// <param name="cidr"></param>
-        /// <returns></returns>
-        public static IPNetwork Parse(string ipaddress, byte cidr)
-        {
-
-            IPNetwork ipnetwork = null;
-            IPNetwork.InternalParse(false, ipaddress, cidr, out ipnetwork);
-            return ipnetwork;
-
-        }
-
-        /// <summary>
-        /// 192.168.168.100 255.255.255.0
-        ///
-        /// Network   : 192.168.168.0
-        /// Netmask   : 255.255.255.0
-        /// Cidr      : 24
-        /// Start     : 192.168.168.1
-        /// End       : 192.168.168.254
-        /// Broadcast : 192.168.168.255
-        /// </summary>
-        /// <param name="ipaddress"></param>
-        /// <param name="netmask"></param>
-        /// <returns></returns>
-        public static IPNetwork Parse(IPAddress ipaddress, IPAddress netmask)
-        {
-
-            IPNetwork ipnetwork = null;
-            IPNetwork.InternalParse(false, ipaddress, netmask, out ipnetwork);
-            return ipnetwork;
-
-        }
-
-        /// <summary>
-        /// 192.168.0.1/24
-        /// 192.168.0.1 255.255.255.0
-        ///
-        /// Network   : 192.168.0.0
-        /// Netmask   : 255.255.255.0
-        /// Cidr      : 24
-        /// Start     : 192.168.0.1
-        /// End       : 192.168.0.254
-        /// Broadcast : 192.168.0.255
-        /// </summary>
-        /// <param name="network"></param>
-        /// <returns></returns>
-        public static IPNetwork Parse(string network)
-        {
-
-            IPNetwork ipnetwork = null;
-            IPNetwork.InternalParse(false, network, out ipnetwork);
-            return ipnetwork;
-
-        }
-
-        #endregion
-
-        #region TryParse
-
-
-
-        /// <summary>
-        /// 192.168.168.100 - 255.255.255.0
-        ///
-        /// Network   : 192.168.168.0
-        /// Netmask   : 255.255.255.0
-        /// Cidr      : 24
-        /// Start     : 192.168.168.1
-        /// End       : 192.168.168.254
-        /// Broadcast : 192.168.168.255
-        /// </summary>
-        /// <param name="ipaddress"></param>
-        /// <param name="netmask"></param>
-        /// <returns></returns>
-        public static bool TryParse(string ipaddress, string netmask, out IPNetwork ipnetwork)
-        {
-
-            IPNetwork ipnetwork2 = null;
-            IPNetwork.InternalParse(true, ipaddress, netmask, out ipnetwork2);
-            bool parsed = (ipnetwork2 != null);
-            ipnetwork = ipnetwork2;
-            return parsed;
-
-        }
-
-
-
-        /// <summary>
-        /// 192.168.168.100/24
-        ///
-        /// Network   : 192.168.168.0
-        /// Netmask   : 255.255.255.0
-        /// Cidr      : 24
-        /// Start     : 192.168.168.1
-        /// End       : 192.168.168.254
-        /// Broadcast : 192.168.168.255
-        /// </summary>
-        /// <param name="ipaddress"></param>
-        /// <param name="cidr"></param>
-        /// <returns></returns>
-        public static bool TryParse(string ipaddress, byte cidr, out IPNetwork ipnetwork)
-        {
-
-            IPNetwork ipnetwork2 = null;
-            IPNetwork.InternalParse(true, ipaddress, cidr, out ipnetwork2);
-            bool parsed = (ipnetwork2 != null);
-            ipnetwork = ipnetwork2;
-            return parsed;
-
-        }
-
-        /// <summary>
-        /// 192.168.0.1/24
-        /// 192.168.0.1 255.255.255.0
-        ///
-        /// Network   : 192.168.0.0
-        /// Netmask   : 255.255.255.0
-        /// Cidr      : 24
-        /// Start     : 192.168.0.1
-        /// End       : 192.168.0.254
-        /// Broadcast : 192.168.0.255
-        /// </summary>
-        /// <param name="network"></param>
-        /// <param name="ipnetwork"></param>
-        /// <returns></returns>
-        public static bool TryParse(string network, out IPNetwork ipnetwork)
-        {
-
-            IPNetwork ipnetwork2 = null;
-            IPNetwork.InternalParse(true, network, out ipnetwork2);
-            bool parsed = (ipnetwork2 != null);
-            ipnetwork = ipnetwork2;
-            return parsed;
-
-        }
-
-        /// <summary>
-        /// 192.168.0.1/24
-        /// 192.168.0.1 255.255.255.0
-        ///
-        /// Network   : 192.168.0.0
-        /// Netmask   : 255.255.255.0
-        /// Cidr      : 24
-        /// Start     : 192.168.0.1
-        /// End       : 192.168.0.254
-        /// Broadcast : 192.168.0.255
-        /// </summary>
-        /// <param name="ipaddress"></param>
-        /// <param name="netmask"></param>
-        /// <param name="ipnetwork"></param>
-        /// <returns></returns>
-        public static bool TryParse(IPAddress ipaddress, IPAddress netmask, out IPNetwork ipnetwork)
-        {
-
-            IPNetwork ipnetwork2 = null;
-            IPNetwork.InternalParse(true, ipaddress, netmask, out ipnetwork2);
-            bool parsed = (ipnetwork2 != null);
-            ipnetwork = ipnetwork2;
-            return parsed;
-
-        }
-
-
-        #endregion
-
-        #region InternalParse
-
-        /// <summary>
-        /// 192.168.168.100 - 255.255.255.0
-        ///
-        /// Network   : 192.168.168.0
-        /// Netmask   : 255.255.255.0
-        /// Cidr      : 24
-        /// Start     : 192.168.168.1
-        /// End       : 192.168.168.254
-        /// Broadcast : 192.168.168.255
-        /// </summary>
-        /// <param name="ipaddress"></param>
-        /// <param name="netmask"></param>
-        /// <returns></returns>
-        private static void InternalParse(bool tryParse, string ipaddress, string netmask, out IPNetwork ipnetwork)
-        {
-
-            if (string.IsNullOrEmpty(ipaddress))
-            {
-                if (tryParse == false)
-                {
-                    throw new ArgumentNullException(nameof(ipaddress));
-                }
-                ipnetwork = null;
-                return;
-            }
-
-            if (string.IsNullOrEmpty(netmask))
-            {
-                if (tryParse == false)
-                {
-                    throw new ArgumentNullException(nameof(netmask));
-                }
-                ipnetwork = null;
-                return;
-            }
-
-            IPAddress ip = null;
-            bool ipaddressParsed = IPAddress.TryParse(ipaddress, out ip);
-            if (ipaddressParsed == false)
-            {
-                if (tryParse == false)
-                {
-                    throw new ArgumentException("ipaddress");
-                }
-                ipnetwork = null;
-                return;
-            }
-
-            IPAddress mask = null;
-            bool netmaskParsed = IPAddress.TryParse(netmask, out mask);
-            if (netmaskParsed == false)
-            {
-                if (tryParse == false)
-                {
-                    throw new ArgumentException("netmask");
-                }
-                ipnetwork = null;
-                return;
-            }
-
-            IPNetwork.InternalParse(tryParse, ip, mask, out ipnetwork);
-        }
-
-        private static void InternalParse(bool tryParse, string network, out IPNetwork ipnetwork)
-        {
-
-            if (string.IsNullOrEmpty(network))
-            {
-                if (tryParse == false)
-                {
-                    throw new ArgumentNullException(nameof(network));
-                }
-                ipnetwork = null;
-                return;
-            }
-
-            network = Regex.Replace(network, @"[^0-9a-fA-F\.\/\s\:]+", "");
-            network = Regex.Replace(network, @"\s{2,}", " ");
-            network = network.Trim();
-            string[] args = network.Split(new char[] { ' ', '/' });
-            byte cidr = 0;
-            if (args.Length == 1)
-            {
-
-                if (IPNetwork.TryGuessCidr(args[0], out cidr))
-                {
-                    IPNetwork.InternalParse(tryParse, args[0], cidr, out ipnetwork);
-                    return;
-                }
-
-                if (tryParse == false)
-                {
-                    throw new ArgumentException("network");
-                }
-                ipnetwork = null;
-                return;
-            }
-
-            if (byte.TryParse(args[1], out cidr))
-            {
-                IPNetwork.InternalParse(tryParse, args[0], cidr, out ipnetwork);
-                return;
-            }
-
-            IPNetwork.InternalParse(tryParse, args[0], args[1], out ipnetwork);
-            return;
-
-        }
-
-
-
-        /// <summary>
-        /// 192.168.168.100 255.255.255.0
-        ///
-        /// Network   : 192.168.168.0
-        /// Netmask   : 255.255.255.0
-        /// Cidr      : 24
-        /// Start     : 192.168.168.1
-        /// End       : 192.168.168.254
-        /// Broadcast : 192.168.168.255
-        /// </summary>
-        /// <param name="ipaddress"></param>
-        /// <param name="netmask"></param>
-        /// <returns></returns>
-        private static void InternalParse(bool tryParse, IPAddress ipaddress, IPAddress netmask, out IPNetwork ipnetwork)
-        {
-
-            if (ipaddress == null)
-            {
-                if (tryParse == false)
-                {
-                    throw new ArgumentNullException(nameof(ipaddress));
-                }
-                ipnetwork = null;
-                return;
-            }
-
-            if (netmask == null)
-            {
-                if (tryParse == false)
-                {
-                    throw new ArgumentNullException(nameof(netmask));
-                }
-                ipnetwork = null;
-                return;
-            }
-
-            var uintIpAddress = IPNetwork.ToBigInteger(ipaddress);
-            bool parsed = IPNetwork.TryToCidr(netmask, out var cidr2);
-            if (parsed == false)
-            {
-                if (tryParse == false)
-                {
-                    throw new ArgumentException("netmask");
-                }
-                ipnetwork = null;
-                return;
-            }
-            byte cidr = (byte)cidr2;
-
-            var ipnet = new IPNetwork(uintIpAddress, ipaddress.AddressFamily, cidr);
-            ipnetwork = ipnet;
-
-            return;
-        }
-
-
-
-        /// <summary>
-        /// 192.168.168.100/24
-        ///
-        /// Network   : 192.168.168.0
-        /// Netmask   : 255.255.255.0
-        /// Cidr      : 24
-        /// Start     : 192.168.168.1
-        /// End       : 192.168.168.254
-        /// Broadcast : 192.168.168.255
-        /// </summary>
-        /// <param name="ipaddress"></param>
-        /// <param name="cidr"></param>
-        /// <returns></returns>
-        private static void InternalParse(bool tryParse, string ipaddress, byte cidr, out IPNetwork ipnetwork)
-        {
-
-            if (string.IsNullOrEmpty(ipaddress))
-            {
-                if (tryParse == false)
-                {
-                    throw new ArgumentNullException(nameof(ipaddress));
-                }
-                ipnetwork = null;
-                return;
-            }
-
-
-            IPAddress ip = null;
-            bool ipaddressParsed = IPAddress.TryParse(ipaddress, out ip);
-            if (ipaddressParsed == false)
-            {
-                if (tryParse == false)
-                {
-                    throw new ArgumentException("ipaddress");
-                }
-                ipnetwork = null;
-                return;
-            }
-
-            IPAddress mask = null;
-            bool parsedNetmask = IPNetwork.TryToNetmask(cidr, ip.AddressFamily, out mask);
-            if (parsedNetmask == false)
-            {
-                if (tryParse == false)
-                {
-                    throw new ArgumentException("cidr");
-                }
-                ipnetwork = null;
-                return;
-            }
-
-
-            IPNetwork.InternalParse(tryParse, ip, mask, out ipnetwork);
-        }
-
-        #endregion
-
-        #region converters
-
-        #region ToUint
-
-        /// <summary>
-        /// Convert an ipadress to decimal
-        /// 0.0.0.0 -> 0
-        /// 0.0.1.0 -> 256
-        /// </summary>
-        /// <param name="ipaddress"></param>
-        /// <returns></returns>
-        public static BigInteger ToBigInteger(IPAddress ipaddress)
-        {
-            IPNetwork.InternalToBigInteger(false, ipaddress, out var uintIpAddress);
-            return (BigInteger)uintIpAddress;
-
-        }
-
-        /// <summary>
-        /// Convert an ipadress to decimal
-        /// 0.0.0.0 -> 0
-        /// 0.0.1.0 -> 256
-        /// </summary>
-        /// <param name="ipaddress"></param>
-        /// <returns></returns>
-        public static bool TryToBigInteger(IPAddress ipaddress, out BigInteger? uintIpAddress)
-        {
-            IPNetwork.InternalToBigInteger(true, ipaddress, out var uintIpAddress2);
-            bool parsed = (uintIpAddress2 != null);
-            uintIpAddress = uintIpAddress2;
-            return parsed;
-        }
-
-#if TRAVISCI
-        public
-#else
-        internal
-#endif
-            static void InternalToBigInteger(bool tryParse, IPAddress ipaddress, out BigInteger? uintIpAddress)
-        {
-
-            if (ipaddress == null)
-            {
-                if (tryParse == false)
-                {
-                    throw new ArgumentNullException(nameof(ipaddress));
-                }
-                uintIpAddress = null;
-                return;
-            }
-
-            byte[] bytes = ipaddress.GetAddressBytes();
-            /// 20180217 lduchosal
-            /// code impossible to reach, GetAddressBytes returns either 4 or 16 bytes length addresses
-            /// if (bytes.Length != 4 && bytes.Length != 16) {
-            ///     if (tryParse == false) {
-            ///         throw new ArgumentException("bytes");
-            ///     }
-            ///     uintIpAddress = null;
-            ///     return;
-            /// }
-
-            Array.Reverse(bytes);
-            var unsigned = new List<byte>(bytes);
-            unsigned.Add(0);
-            uintIpAddress = new BigInteger(unsigned.ToArray());
-            return;
-        }
-
-
-        /// <summary>
-        /// Convert a cidr to BigInteger netmask
-        /// </summary>
-        /// <param name="cidr"></param>
-        /// <returns></returns>
-        public static BigInteger ToUint(byte cidr, AddressFamily family)
-        {
-            IPNetwork.InternalToBigInteger(false, cidr, family, out var uintNetmask);
-            return (BigInteger)uintNetmask;
-        }
-
-
-        /// <summary>
-        /// Convert a cidr to uint netmask
-        /// </summary>
-        /// <param name="cidr"></param>
-        /// <returns></returns>
-        public static bool TryToUint(byte cidr, AddressFamily family, out BigInteger? uintNetmask)
-        {
-            IPNetwork.InternalToBigInteger(true, cidr, family, out var uintNetmask2);
-            bool parsed = (uintNetmask2 != null);
-            uintNetmask = uintNetmask2;
-            return parsed;
-        }
-
-        /// <summary>
-        /// Convert a cidr to uint netmask
-        /// </summary>
-        /// <param name="cidr"></param>
-        /// <returns></returns>
-#if TRAVISCI
-        public
-#else
-        internal
-#endif
-            static void InternalToBigInteger(bool tryParse, byte cidr, AddressFamily family, out BigInteger? uintNetmask)
-        {
-
-            if (family == AddressFamily.InterNetwork && cidr > 32)
-            {
-                if (tryParse == false)
-                {
-                    throw new ArgumentOutOfRangeException(nameof(cidr));
-                }
-                uintNetmask = null;
-                return;
-            }
-
-            if (family == AddressFamily.InterNetworkV6 && cidr > 128)
-            {
-                if (tryParse == false)
-                {
-                    throw new ArgumentOutOfRangeException(nameof(cidr));
-                }
-                uintNetmask = null;
-                return;
-            }
-
-            if (family != AddressFamily.InterNetwork
-                && family != AddressFamily.InterNetworkV6)
-            {
-                if (tryParse == false)
-                {
-                    throw new NotSupportedException(family.ToString());
-                }
-                uintNetmask = null;
-                return;
-            }
-
-            if (family == AddressFamily.InterNetwork)
-            {
-
-                uintNetmask = cidr == 0 ? 0 : 0xffffffff << (32 - cidr);
-                return;
-            }
-
-            var mask = new BigInteger(new byte[] {
-                0xff, 0xff, 0xff, 0xff,
-                0xff, 0xff, 0xff, 0xff,
-                0xff, 0xff, 0xff, 0xff,
-                0xff, 0xff, 0xff, 0xff,
-                0x00
-            });
-
-            var masked = cidr == 0 ? 0 : mask << (128 - cidr);
-            byte[] m = masked.ToByteArray();
-            byte[] bmask = new byte[17];
-            int copy = m.Length > 16 ? 16 : m.Length;
-            Array.Copy(m, 0, bmask, 0, copy);
-            uintNetmask = new BigInteger(bmask);
-
-
-        }
-
-        #endregion
-
-        #region ToCidr
-
-        /// <summary>
-        /// Convert netmask to CIDR
-        ///  255.255.255.0 -> 24
-        ///  255.255.0.0   -> 16
-        ///  255.0.0.0     -> 8
-        /// </summary>
-        /// <param name="netmask"></param>
-        /// <returns></returns>
-        private static void InternalToCidr(bool tryParse, BigInteger netmask, AddressFamily family, out byte? cidr)
-        {
-
-            if (!IPNetwork.InternalValidNetmask(netmask, family))
-            {
-                if (tryParse == false)
-                {
-                    throw new ArgumentException("netmask");
-                }
-                cidr = null;
-                return;
-            }
-
-            byte cidr2 = IPNetwork.BitsSet(netmask, family);
-            cidr = cidr2;
-            return;
-
-        }
-        /// <summary>
-        /// Convert netmask to CIDR
-        ///  255.255.255.0 -> 24
-        ///  255.255.0.0   -> 16
-        ///  255.0.0.0     -> 8
-        /// </summary>
-        /// <param name="netmask"></param>
-        /// <returns></returns>
-        public static byte ToCidr(IPAddress netmask)
-        {
-            IPNetwork.InternalToCidr(false, netmask, out var cidr);
-            return (byte)cidr;
-        }
-
-        /// <summary>
-        /// Convert netmask to CIDR
-        ///  255.255.255.0 -> 24
-        ///  255.255.0.0   -> 16
-        ///  255.0.0.0     -> 8
-        /// </summary>
-        /// <param name="netmask"></param>
-        /// <returns></returns>
-        public static bool TryToCidr(IPAddress netmask, out byte? cidr)
-        {
-            IPNetwork.InternalToCidr(true, netmask, out var cidr2);
-            bool parsed = (cidr2 != null);
-            cidr = cidr2;
-            return parsed;
-        }
-
-        private static void InternalToCidr(bool tryParse, IPAddress netmask, out byte? cidr)
-        {
-
-            if (netmask == null)
-            {
-                if (tryParse == false)
-                {
-                    throw new ArgumentNullException(nameof(netmask));
-                }
-                cidr = null;
-                return;
-            }
-
-            bool parsed = IPNetwork.TryToBigInteger(netmask, out var uintNetmask2);
-
-            /// 20180217 lduchosal
-            /// impossible to reach code.
-            /// if (parsed == false) {
-            ///     if (tryParse == false) {
-            ///         throw new ArgumentException("netmask");
-            ///     }
-            ///     cidr = null;
-            ///     return;
-            /// }
-            var uintNetmask = (BigInteger)uintNetmask2;
-
-            IPNetwork.InternalToCidr(tryParse, uintNetmask, netmask.AddressFamily, out var cidr2);
-            cidr = cidr2;
-
-            return;
-
-        }
-
-
-        #endregion
-
-        #region ToNetmask
-
-        /// <summary>
-        /// Convert CIDR to netmask
-        ///  24 -> 255.255.255.0
-        ///  16 -> 255.255.0.0
-        ///  8 -> 255.0.0.0
-        /// </summary>
-        /// <see cref="http://snipplr.com/view/15557/cidr-class-for-ipv4/"/>
-        /// <param name="cidr"></param>
-        /// <returns></returns>
-        public static IPAddress ToNetmask(byte cidr, AddressFamily family)
-        {
-
-            IPAddress netmask = null;
-            IPNetwork.InternalToNetmask(false, cidr, family, out netmask);
-            return netmask;
-        }
-
-        /// <summary>
-        /// Convert CIDR to netmask
-        ///  24 -> 255.255.255.0
-        ///  16 -> 255.255.0.0
-        ///  8 -> 255.0.0.0
-        /// </summary>
-        /// <see cref="http://snipplr.com/view/15557/cidr-class-for-ipv4/"/>
-        /// <param name="cidr"></param>
-        /// <returns></returns>
-        public static bool TryToNetmask(byte cidr, AddressFamily family, out IPAddress netmask)
-        {
-
-            IPAddress netmask2 = null;
-            IPNetwork.InternalToNetmask(true, cidr, family, out netmask2);
-            bool parsed = (netmask2 != null);
-            netmask = netmask2;
-            return parsed;
-        }
-
-
-#if TRAVISCI
-        public
-#else
-        internal
-#endif
-            static void InternalToNetmask(bool tryParse, byte cidr, AddressFamily family, out IPAddress netmask)
-        {
-
-            if (family != AddressFamily.InterNetwork
-                && family != AddressFamily.InterNetworkV6)
-            {
-                if (tryParse == false)
-                {
-                    throw new ArgumentException("family");
-                }
-                netmask = null;
-                return;
-            }
-
-            /// 20180217 lduchosal
-            /// impossible to reach code, byte cannot be negative :
-            ///
-            /// if (cidr < 0) {
-            ///     if (tryParse == false) {
-            ///         throw new ArgumentOutOfRangeException("cidr");
-            ///     }
-            ///     netmask = null;
-            ///     return;
-            /// }
-
-            int maxCidr = family == System.Net.Sockets.AddressFamily.InterNetwork ? 32 : 128;
-            if (cidr > maxCidr)
-            {
-                if (tryParse == false)
-                {
-                    throw new ArgumentOutOfRangeException(nameof(cidr));
-                }
-                netmask = null;
-                return;
-            }
-
-            var mask = IPNetwork.ToUint(cidr, family);
-            var netmask2 = IPNetwork.ToIPAddress(mask, family);
-            netmask = netmask2;
-
-            return;
-        }
-
-        #endregion
-
-        #endregion
-
-        #region utils
-
-        #region BitsSet
-
-        /// <summary>
-        /// Count bits set to 1 in netmask
-        /// </summary>
-        /// <see cref="http://stackoverflow.com/questions/109023/best-algorithm-to-count-the-number-of-set-bits-in-a-32-bit-integer"/>
-        /// <param name="netmask"></param>
-        /// <returns></returns>
-        private static byte BitsSet(BigInteger netmask, AddressFamily family)
-        {
-
-            string s = netmask.ToBinaryString();
-            return (byte)s.Replace("0", "")
-                .ToCharArray()
-                .Length;
-
-        }
-
-
-        /// <summary>
-        /// Count bits set to 1 in netmask
-        /// </summary>
-        /// <param name="netmask"></param>
-        /// <returns></returns>
-        public static uint BitsSet(IPAddress netmask)
-        {
-            var uintNetmask = IPNetwork.ToBigInteger(netmask);
-            uint bits = IPNetwork.BitsSet(uintNetmask, netmask.AddressFamily);
-            return bits;
-        }
-
-        #endregion
-
-        #region ValidNetmask
-
-        /// <summary>
-        /// return true if netmask is a valid netmask
-        /// 255.255.255.0, 255.0.0.0, 255.255.240.0, ...
-        /// </summary>
-        /// <see cref="http://www.actionsnip.com/snippets/tomo_atlacatl/calculate-if-a-netmask-is-valid--as2-"/>
-        /// <param name="netmask"></param>
-        /// <returns></returns>
-        public static bool ValidNetmask(IPAddress netmask)
-        {
-
-            if (netmask == null)
-            {
-                throw new ArgumentNullException(nameof(netmask));
-            }
-            var uintNetmask = IPNetwork.ToBigInteger(netmask);
-            bool valid = IPNetwork.InternalValidNetmask(uintNetmask, netmask.AddressFamily);
-            return valid;
-        }
-
-#if TRAVISCI
-        public
-#else
-        internal
-#endif
-            static bool InternalValidNetmask(BigInteger netmask, AddressFamily family)
-        {
-
-            if (family != AddressFamily.InterNetwork
-                && family != AddressFamily.InterNetworkV6)
-            {
-                throw new ArgumentException("family");
-            }
-
-            var mask = family == AddressFamily.InterNetwork
-                ? new BigInteger(0x0ffffffff)
-                : new BigInteger(new byte[]{
-                    0xff, 0xff, 0xff, 0xff,
-                    0xff, 0xff, 0xff, 0xff,
-                    0xff, 0xff, 0xff, 0xff,
-                    0xff, 0xff, 0xff, 0xff,
-                    0x00
-                });
-
-            var neg = ((~netmask) & (mask));
-            bool isNetmask = ((neg + 1) & neg) == 0;
-            return isNetmask;
-
-        }
-
-        #endregion
-
-        #region ToIPAddress
-
-        /// <summary>
-        /// Transform a uint ipaddress into IPAddress object
-        /// </summary>
-        /// <param name="ipaddress"></param>
-        /// <returns></returns>
-        public static IPAddress ToIPAddress(BigInteger ipaddress, AddressFamily family)
-        {
-
-            int width = family == AddressFamily.InterNetwork ? 4 : 16;
-            byte[] bytes = ipaddress.ToByteArray();
-            byte[] bytes2 = new byte[width];
-            int copy = bytes.Length > width ? width : bytes.Length;
-            Array.Copy(bytes, 0, bytes2, 0, copy);
-            Array.Reverse(bytes2);
-
-            byte[] sized = Resize(bytes2, family);
-            var ip = new IPAddress(sized);
-            return ip;
-        }
-
-#if TRAVISCI
-        public
-#else
-        internal
-#endif
-            static byte[] Resize(byte[] bytes, AddressFamily family)
-        {
-
-            if (family != AddressFamily.InterNetwork
-                && family != AddressFamily.InterNetworkV6)
-            {
-                throw new ArgumentException("family");
-            }
-
-            int width = family == AddressFamily.InterNetwork ? 4 : 16;
-
-            if (bytes.Length > width)
-            {
-                throw new ArgumentException("bytes");
-            }
-
-            byte[] result = new byte[width];
-            Array.Copy(bytes, 0, result, 0, bytes.Length);
-            return result;
-        }
-
-        #endregion
-
-        #endregion
-
-        #region contains
-
-        /// <summary>
-        /// return true if ipaddress is contained in network
-        /// </summary>
-        /// <param name="ipaddress"></param>
-        /// <returns></returns>
-        public bool Contains(IPAddress ipaddress)
-        {
-
-            if (ipaddress == null)
-            {
-                throw new ArgumentNullException(nameof(ipaddress));
-            }
-
-            if (AddressFamily != ipaddress.AddressFamily)
-            {
-                return false;
-            }
-
-            var uintNetwork = _network;
-            var uintBroadcast = _broadcast;
-            var uintAddress = IPNetwork.ToBigInteger(ipaddress);
-
-            bool contains = (uintAddress >= uintNetwork
-                && uintAddress <= uintBroadcast);
-
-            return contains;
-
-        }
-
-        /// <summary>
-        /// return true is network2 is fully contained in network
-        /// </summary>
-        /// <param name="network2"></param>
-        /// <returns></returns>
-        public bool Contains(IPNetwork network2)
-        {
-
-            if (network2 == null)
-            {
-                throw new ArgumentNullException(nameof(network2));
-            }
-
-            var uintNetwork = _network;
-            var uintBroadcast = _broadcast;
-
-            var uintFirst = network2._network;
-            var uintLast = network2._broadcast;
-
-            bool contains = (uintFirst >= uintNetwork
-                && uintLast <= uintBroadcast);
-
-            return contains;
-        }
-
-        #endregion
-
-        #region overlap
-
-        /// <summary>
-        /// return true is network2 overlap network
-        /// </summary>
-        /// <param name="network2"></param>
-        /// <returns></returns>
-        public bool Overlap(IPNetwork network2)
-        {
-
-            if (network2 == null)
-            {
-                throw new ArgumentNullException(nameof(network2));
-            }
-
-            var uintNetwork = _network;
-            var uintBroadcast = _broadcast;
-
-            var uintFirst = network2._network;
-            var uintLast = network2._broadcast;
-
-            bool overlap =
-                (uintFirst >= uintNetwork && uintFirst <= uintBroadcast)
-                || (uintLast >= uintNetwork && uintLast <= uintBroadcast)
-                || (uintFirst <= uintNetwork && uintLast >= uintBroadcast)
-                || (uintFirst >= uintNetwork && uintLast <= uintBroadcast);
-
-            return overlap;
-        }
-
-        #endregion
-
-        #region ToString
-
-        public override string ToString()
-        {
-            return string.Format("{0}/{1}", this.Network, this.Cidr);
-        }
-
-        #endregion
-
-        #region IANA block
-
-        private static readonly Lazy<IPNetwork> _iana_ablock_reserved = new Lazy<IPNetwork>(() => IPNetwork.Parse("10.0.0.0/8"));
-        private static readonly Lazy<IPNetwork> _iana_bblock_reserved = new Lazy<IPNetwork>(() => IPNetwork.Parse("172.16.0.0/12"));
-        private static readonly Lazy<IPNetwork> _iana_cblock_reserved = new Lazy<IPNetwork>(() => IPNetwork.Parse("192.168.0.0/16"));
-
-        /// <summary>
-        /// 10.0.0.0/8
-        /// </summary>
-        /// <returns></returns>
-        public static IPNetwork IANA_ABLK_RESERVED1 => _iana_ablock_reserved.Value;
-
-        /// <summary>
-        /// 172.12.0.0/12
-        /// </summary>
-        /// <returns></returns>
-        public static IPNetwork IANA_BBLK_RESERVED1 => _iana_bblock_reserved.Value;
-
-        /// <summary>
-        /// 192.168.0.0/16
-        /// </summary>
-        /// <returns></returns>
-        public static IPNetwork IANA_CBLK_RESERVED1 => _iana_cblock_reserved.Value;
-
-        /// <summary>
-        /// return true if ipaddress is contained in
-        /// IANA_ABLK_RESERVED1, IANA_BBLK_RESERVED1, IANA_CBLK_RESERVED1
-        /// </summary>
-        /// <param name="ipaddress"></param>
-        /// <returns></returns>
-        public static bool IsIANAReserved(IPAddress ipaddress)
-        {
-
-            if (ipaddress == null)
-            {
-                throw new ArgumentNullException(nameof(ipaddress));
-            }
-
-            return IPNetwork.IANA_ABLK_RESERVED1.Contains(ipaddress)
-                || IPNetwork.IANA_BBLK_RESERVED1.Contains(ipaddress)
-                || IPNetwork.IANA_CBLK_RESERVED1.Contains(ipaddress);
-        }
-
-        /// <summary>
-        /// return true if ipnetwork is contained in
-        /// IANA_ABLK_RESERVED1, IANA_BBLK_RESERVED1, IANA_CBLK_RESERVED1
-        /// </summary>
-        /// <returns></returns>
-        public bool IsIANAReserved()
-        {
-            return IPNetwork.IANA_ABLK_RESERVED1.Contains(this)
-                || IPNetwork.IANA_BBLK_RESERVED1.Contains(this)
-                || IPNetwork.IANA_CBLK_RESERVED1.Contains(this);
-        }
-
-        #endregion
-
-        #region Subnet
-
-        /// <summary>
-        /// Subnet a network into multiple nets of cidr mask
-        /// Subnet 192.168.0.0/24 into cidr 25 gives 192.168.0.0/25, 192.168.0.128/25
-        /// Subnet 10.0.0.0/8 into cidr 9 gives 10.0.0.0/9, 10.128.0.0/9
-        /// </summary>
-        /// <param name="cidr"></param>
-        /// <returns></returns>
-        public IPNetworkCollection Subnet(byte cidr)
-        {
-            IPNetworkCollection ipnetworkCollection = null;
-            IPNetwork.InternalSubnet(false, this, cidr, out ipnetworkCollection);
-            return ipnetworkCollection;
-        }
-
-        /// <summary>
-        /// Subnet a network into multiple nets of cidr mask
-        /// Subnet 192.168.0.0/24 into cidr 25 gives 192.168.0.0/25, 192.168.0.128/25
-        /// Subnet 10.0.0.0/8 into cidr 9 gives 10.0.0.0/9, 10.128.0.0/9
-        /// </summary>
-        /// <param name="cidr"></param>
-        /// <returns></returns>
-        public bool TrySubnet(byte cidr, out IPNetworkCollection ipnetworkCollection)
-        {
-            IPNetworkCollection inc = null;
-            IPNetwork.InternalSubnet(true, this, cidr, out inc);
-            if (inc == null)
-            {
-                ipnetworkCollection = null;
-                return false;
-            }
-
-            ipnetworkCollection = inc;
-            return true;
-        }
-
-#if TRAVISCI
-        public
-#else
-        internal
-#endif
-            static void InternalSubnet(bool trySubnet, IPNetwork network, byte cidr, out IPNetworkCollection ipnetworkCollection)
-        {
-
-            if (network == null)
-            {
-                if (trySubnet == false)
-                {
-                    throw new ArgumentNullException(nameof(network));
-                }
-                ipnetworkCollection = null;
-                return;
-            }
-
-            int maxCidr = network._family == System.Net.Sockets.AddressFamily.InterNetwork ? 32 : 128;
-            if (cidr > maxCidr)
-            {
-                if (trySubnet == false)
-                {
-                    throw new ArgumentOutOfRangeException(nameof(cidr));
-                }
-                ipnetworkCollection = null;
-                return;
-            }
-
-            if (cidr < network.Cidr)
-            {
-                if (trySubnet == false)
-                {
-                    throw new ArgumentException("cidr");
-                }
-                ipnetworkCollection = null;
-                return;
-            }
-
-            ipnetworkCollection = new IPNetworkCollection(network, cidr);
-            return;
-        }
-
-
-
-        #endregion
-
-        #region Supernet
-
-        /// <summary>
-        /// Supernet two consecutive cidr equal subnet into a single one
-        /// 192.168.0.0/24 + 192.168.1.0/24 = 192.168.0.0/23
-        /// 10.1.0.0/16 + 10.0.0.0/16 = 10.0.0.0/15
-        /// 192.168.0.0/24 + 192.168.0.0/25 = 192.168.0.0/24
-        /// </summary>
-        /// <param name="network2"></param>
-        /// <returns></returns>
-        public IPNetwork Supernet(IPNetwork network2)
-        {
-            IPNetwork supernet = null;
-            IPNetwork.InternalSupernet(false, this, network2, out supernet);
-            return supernet;
-        }
-
-        /// <summary>
-        /// Try to supernet two consecutive cidr equal subnet into a single one
-        /// 192.168.0.0/24 + 192.168.1.0/24 = 192.168.0.0/23
-        /// 10.1.0.0/16 + 10.0.0.0/16 = 10.0.0.0/15
-        /// 192.168.0.0/24 + 192.168.0.0/25 = 192.168.0.0/24
-        /// </summary>
-        /// <param name="network2"></param>
-        /// <returns></returns>
-        public bool TrySupernet(IPNetwork network2, out IPNetwork supernet)
-        {
-
-            IPNetwork outSupernet = null;
-            IPNetwork.InternalSupernet(true, this, network2, out outSupernet);
-            bool parsed = (outSupernet != null);
-            supernet = outSupernet;
-            return parsed;
-        }
-
-#if TRAVISCI
-        public
-#else
-        internal
-#endif
-            static void InternalSupernet(bool trySupernet, IPNetwork network1, IPNetwork network2, out IPNetwork supernet)
-        {
-
-            if (network1 == null)
-            {
-                if (trySupernet == false)
-                {
-                    throw new ArgumentNullException(nameof(network1));
-                }
-                supernet = null;
-                return;
-            }
-
-            if (network2 == null)
-            {
-                if (trySupernet == false)
-                {
-                    throw new ArgumentNullException(nameof(network2));
-                }
-                supernet = null;
-                return;
-            }
-
-
-            if (network1.Contains(network2))
-            {
-                supernet = new IPNetwork(network1._network, network1._family, network1.Cidr);
-                return;
-            }
-
-            if (network2.Contains(network1))
-            {
-                supernet = new IPNetwork(network2._network, network2._family, network2.Cidr);
-                return;
-            }
-
-            if (network1._cidr != network2._cidr)
-            {
-                if (trySupernet == false)
-                {
-                    throw new ArgumentException("cidr");
-                }
-                supernet = null;
-                return;
-            }
-
-            var first = (network1._network < network2._network) ? network1 : network2;
-            var last = (network1._network > network2._network) ? network1 : network2;
-
-            /// Starting from here :
-            /// network1 and network2 have the same cidr,
-            /// network1 does not contain network2,
-            /// network2 does not contain network1,
-            /// first is the lower subnet
-            /// last is the higher subnet
-
-
-            if ((first._broadcast + 1) != last._network)
-            {
-                if (trySupernet == false)
-                {
-                    throw new ArgumentOutOfRangeException(nameof(trySupernet), "TrySupernet was false while the first and last networks are not adjacent.");
-                }
-                supernet = null;
-                return;
-            }
-
-            var uintSupernet = first._network;
-            byte cidrSupernet = (byte)(first._cidr - 1);
-
-            var networkSupernet = new IPNetwork(uintSupernet, first._family, cidrSupernet);
-            if (networkSupernet._network != first._network)
-            {
-                if (trySupernet == false)
-                {
-                    throw new ArgumentException("network");
-                }
-                supernet = null;
-                return;
-            }
-            supernet = networkSupernet;
-            return;
-        }
-
-        #endregion
-
-        #region GetHashCode
-
-        public override int GetHashCode()
-        {
-            return string.Format("{0}|{1}|{2}",
-                this._ipaddress.GetHashCode(),
-                this._network.GetHashCode(),
-                this._cidr.GetHashCode()).GetHashCode();
-        }
-
-        #endregion
-
-        #region SupernetArray
-
-        /// <summary>
-        /// Supernet a list of subnet
-        /// 192.168.0.0/24 + 192.168.1.0/24 = 192.168.0.0/23
-        /// 192.168.0.0/24 + 192.168.1.0/24 + 192.168.2.0/24 + 192.168.3.0/24 = 192.168.0.0/22
-        /// </summary>
-        /// <param name="ipnetworks">The IP networks</param>
-        /// <returns></returns>
-        public static IPNetwork[] Supernet(IPNetwork[] ipnetworks)
-        {
-            InternalSupernet(false, ipnetworks, out var supernet);
-            return supernet;
-        }
-
-        /// <summary>
-        /// Supernet a list of subnet
-        /// 192.168.0.0/24 + 192.168.1.0/24 = 192.168.0.0/23
-        /// 192.168.0.0/24 + 192.168.1.0/24 + 192.168.2.0/24 + 192.168.3.0/24 = 192.168.0.0/22
-        /// </summary>
-        /// <param name="ipnetworks"></param>
-        /// <param name="supernet"></param>
-        /// <returns></returns>
-        public static bool TrySupernet(IPNetwork[] ipnetworks, out IPNetwork[] supernet)
-        {
-            bool supernetted = InternalSupernet(true, ipnetworks, out supernet);
-            return supernetted;
-
-        }
-
-#if TRAVISCI
-        public
-#else
-        internal
-#endif
-        static bool InternalSupernet(bool trySupernet, IPNetwork[] ipnetworks, out IPNetwork[] supernet)
-        {
-
-            if (ipnetworks == null)
-            {
-                if (trySupernet == false)
-                {
-                    throw new ArgumentNullException(nameof(ipnetworks));
-                }
-                supernet = null;
-                return false;
-            }
-
-            if (ipnetworks.Length <= 0)
-            {
-                supernet = new IPNetwork[0];
-                return true;
-            }
-
-            var supernetted = new List<IPNetwork>();
-            var ipns = IPNetwork.Array2List(ipnetworks);
-            var current = IPNetwork.List2Stack(ipns);
-            int previousCount = 0;
-            int currentCount = current.Count;
-
-            while (previousCount != currentCount)
-            {
-
-                supernetted.Clear();
-                while (current.Count > 1)
-                {
-                    var ipn1 = current.Pop();
-                    var ipn2 = current.Peek();
-
-                    IPNetwork outNetwork = null;
-                    bool success = ipn1.TrySupernet(ipn2, out outNetwork);
-                    if (success)
-                    {
-                        current.Pop();
-                        current.Push(outNetwork);
-                    }
-                    else
-                    {
-                        supernetted.Add(ipn1);
-                    }
-                }
-                if (current.Count == 1)
-                {
-                    supernetted.Add(current.Pop());
-                }
-
-                previousCount = currentCount;
-                currentCount = supernetted.Count;
-                current = IPNetwork.List2Stack(supernetted);
-
-            }
-            supernet = supernetted.ToArray();
-            return true;
-        }
-
-        private static Stack<IPNetwork> List2Stack(List<IPNetwork> list)
-        {
-            var stack = new Stack<IPNetwork>();
-            list.ForEach(new Action<IPNetwork>(
-                delegate (IPNetwork ipn)
-                {
-                    stack.Push(ipn);
-                }
-            ));
-            return stack;
-        }
-
-        private static List<IPNetwork> Array2List(IPNetwork[] array)
-        {
-            var ipns = new List<IPNetwork>();
-            ipns.AddRange(array);
-            IPNetwork.RemoveNull(ipns);
-            ipns.Sort(new Comparison<IPNetwork>(
-                delegate (IPNetwork ipn1, IPNetwork ipn2)
-                {
-                    int networkCompare = ipn1._network.CompareTo(ipn2._network);
-                    if (networkCompare == 0)
-                    {
-                        int cidrCompare = ipn1._cidr.CompareTo(ipn2._cidr);
-                        return cidrCompare;
-                    }
-                    return networkCompare;
-                }
-            ));
-            ipns.Reverse();
-
-            return ipns;
-        }
-
-        private static void RemoveNull(List<IPNetwork> ipns)
-        {
-            ipns.RemoveAll(new Predicate<IPNetwork>(
-                delegate (IPNetwork ipn)
-                {
-                    if (ipn == null)
-                    {
-                        return true;
-                    }
-                    return false;
-                }
-            ));
-
-        }
-
-        #endregion
-
-        #region WideSubnet
-
-        public static IPNetwork WideSubnet(string start, string end)
-        {
-
-            if (string.IsNullOrEmpty(start))
-            {
-                throw new ArgumentNullException(nameof(start));
-            }
-
-            if (string.IsNullOrEmpty(end))
-            {
-                throw new ArgumentNullException(nameof(end));
-            }
-
-            if (!IPAddress.TryParse(start, out var startIP))
-            {
-                throw new ArgumentException("start");
-            }
-
-            if (!IPAddress.TryParse(end, out var endIP))
-            {
-                throw new ArgumentException("end");
-            }
-
-            if (startIP.AddressFamily != endIP.AddressFamily)
-            {
-                throw new NotSupportedException("MixedAddressFamily");
-            }
-
-            var ipnetwork = new IPNetwork(0, startIP.AddressFamily, 0);
-            for (byte cidr = 32; cidr >= 0; cidr--)
-            {
-                var wideSubnet = IPNetwork.Parse(start, cidr);
-                if (wideSubnet.Contains(endIP))
-                {
-                    ipnetwork = wideSubnet;
-                    break;
-                }
-            }
-            return ipnetwork;
-
-        }
-
-        public static bool TryWideSubnet(IPNetwork[] ipnetworks, out IPNetwork ipnetwork)
-        {
-            IPNetwork ipn = null;
-            IPNetwork.InternalWideSubnet(true, ipnetworks, out ipn);
-            if (ipn == null)
-            {
-                ipnetwork = null;
-                return false;
-            }
-            ipnetwork = ipn;
-            return true;
-        }
-
-        public static IPNetwork WideSubnet(IPNetwork[] ipnetworks)
-        {
-            IPNetwork ipn = null;
-            IPNetwork.InternalWideSubnet(false, ipnetworks, out ipn);
-            return ipn;
-        }
-
-        internal static void InternalWideSubnet(bool tryWide, IPNetwork[] ipnetworks, out IPNetwork ipnetwork)
-        {
-
-            if (ipnetworks == null)
-            {
-                if (tryWide == false)
-                {
-                    throw new ArgumentNullException(nameof(ipnetworks));
-                }
-                ipnetwork = null;
-                return;
-            }
-
-
-            IPNetwork[] nnin = Array.FindAll(ipnetworks, new Predicate<IPNetwork>(
-                delegate (IPNetwork ipnet)
-                {
-                    return ipnet != null;
-                }
-            ));
-
-            if (nnin.Length <= 0)
-            {
-                if (tryWide == false)
-                {
-                    throw new ArgumentException("ipnetworks");
-                }
-                ipnetwork = null;
-                return;
-            }
-
-            if (nnin.Length == 1)
-            {
-                var ipn0 = nnin[0];
-                ipnetwork = ipn0;
-                return;
-            }
-
-            Array.Sort(nnin);
-            var nnin0 = nnin[0];
-            var uintNnin0 = nnin0._ipaddress;
-
-            var nninX = nnin[nnin.Length - 1];
-            var ipaddressX = nninX.Broadcast;
-
-            var family = ipnetworks[0]._family;
-            foreach (var ipnx in ipnetworks)
-            {
-                if (ipnx._family != family)
-                {
-                    throw new ArgumentException("MixedAddressFamily");
-                }
-            }
-
-            var ipn = new IPNetwork(0, family, 0);
-            for (byte cidr = nnin0._cidr; cidr >= 0; cidr--)
-            {
-                var wideSubnet = new IPNetwork(uintNnin0, family, cidr);
-                if (wideSubnet.Contains(ipaddressX))
-                {
-                    ipn = wideSubnet;
-                    break;
-                }
-            }
-
-            ipnetwork = ipn;
-            return;
-        }
-
-        #endregion
-
-        #region Print
-
-        /// <summary>
-        /// Print an ipnetwork in a clear representation string
-        /// </summary>
-        /// <returns></returns>
-        public string Print()
-        {
-
-            var sw = new StringWriter();
-
-            sw.WriteLine("IPNetwork   : {0}", ToString());
-            sw.WriteLine("Network     : {0}", Network);
-            sw.WriteLine("Netmask     : {0}", Netmask);
-            sw.WriteLine("Cidr        : {0}", Cidr);
-            sw.WriteLine("Broadcast   : {0}", Broadcast);
-            sw.WriteLine("FirstUsable : {0}", FirstUsable);
-            sw.WriteLine("LastUsable  : {0}", LastUsable);
-            sw.WriteLine("Usable      : {0}", Usable);
-
-            return sw.ToString();
-        }
-
-        #endregion
-
-        #region TryGuessCidr
-
-        /// <summary>
-        ///
-        /// Class            Leading bits Default netmask
-        ///     A (CIDR /8)  00           255.0.0.0
-        ///     A (CIDR /8)  01           255.0.0.0
-        ///     B (CIDR /16) 10           255.255.0.0
-        ///     C (CIDR /24) 11           255.255.255.0
-        ///
-        /// </summary>
-        /// <param name="ip"></param>
-        /// <param name="cidr"></param>
-        /// <returns></returns>
-        public static bool TryGuessCidr(string ip, out byte cidr)
-        {
-
-            IPAddress ipaddress = null;
-            bool parsed = IPAddress.TryParse(string.Format("{0}", ip), out ipaddress);
-            if (parsed == false)
-            {
-                cidr = 0;
-                return false;
-            }
-
-            if (ipaddress.AddressFamily == AddressFamily.InterNetworkV6)
-            {
-                cidr = 64;
-                return true;
-            }
-            var uintIPAddress = IPNetwork.ToBigInteger(ipaddress);
-            uintIPAddress = uintIPAddress >> 29;
-            if (uintIPAddress <= 3)
-            {
-                cidr = 8;
-                return true;
-            }
-            else if (uintIPAddress <= 5)
-            {
-                cidr = 16;
-                return true;
-            }
-            else if (uintIPAddress <= 6)
-            {
-                cidr = 24;
-                return true;
-            }
-
-            cidr = 0;
-            return false;
-
-        }
-
-        /// <summary>
-        /// Try to parse cidr. Have to be >= 0 and <= 32 or 128
-        /// </summary>
-        /// <param name="sidr"></param>
-        /// <param name="cidr"></param>
-        /// <returns></returns>
-        public static bool TryParseCidr(string sidr, AddressFamily family, out byte? cidr)
-        {
-
-            byte b = 0;
-            if (!byte.TryParse(sidr, out b))
-            {
-                cidr = null;
-                return false;
-            }
-
-            IPAddress netmask = null;
-            if (!IPNetwork.TryToNetmask(b, family, out netmask))
-            {
-                cidr = null;
-                return false;
-            }
-
-            cidr = b;
-            return true;
-        }
-
-        #endregion
-
-        #region ListIPAddress
-
-        public IPAddressCollection ListIPAddress()
-        {
-            return new IPAddressCollection(this);
-        }
-
-        #endregion
-
-        /**
-         * Need a better way to do it
-         *
-#region TrySubstractNetwork
-
-        public static bool TrySubstractNetwork(IPNetwork[] ipnetworks, IPNetwork substract, out IEnumerable<IPNetwork> result) {
-
-            if (ipnetworks == null) {
-                result = null;
-                return false;
-            }
-            if (ipnetworks.Length <= 0) {
-                result = null;
-                return false;
-            }
-            if (substract == null) {
-                result = null;
-                return false;
-            }
-            var results = new List<IPNetwork>();
-            foreach (var ipn in ipnetworks) {
-                if (!Overlap(ipn, substract)) {
-                    results.Add(ipn);
-                    continue;
-                }
-
-                var collection = ipn.Subnet(substract.Cidr);
-                var rtemp = new List<IPNetwork>();
-                foreach(var subnet in collection) {
-                    if (subnet != substract) {
-                        rtemp.Add(subnet);
-                    }
-                }
-                var supernets = Supernet(rtemp.ToArray());
-                results.AddRange(supernets);
-            }
-            result = results;
-            return true;
-        }
-#endregion
-         * **/
-
-        #region IComparable<IPNetwork> Members
-
-        public static int Compare(IPNetwork left, IPNetwork right)
-        {
-            //  two null IPNetworks are equal
-            if (ReferenceEquals(left, null) && ReferenceEquals(right, null)) return 0;
-
-            //  two same IPNetworks are equal
-            if (ReferenceEquals(left, right)) return 0;
-
-            //  null is always sorted first
-            if (ReferenceEquals(left, null)) return -1;
-            if (ReferenceEquals(right, null)) return 1;
-
-            //  first test the network
-            var result = left._network.CompareTo(right._network);
-            if (result != 0) return result;
-
-            //  then test the cidr
-            result = left._cidr.CompareTo(right._cidr);
-            return result;
-        }
-
-        public int CompareTo(IPNetwork other)
-        {
-            return Compare(this, other);
-        }
-
-        public int CompareTo(object obj)
-        {
-            //  null is at less
-            if (obj == null) return 1;
-
-            //  convert to a proper Cidr object
-            var other = obj as IPNetwork;
-
-            //  type problem if null
-            if (other == null)
-            {
-                throw new ArgumentException(
-                    "The supplied parameter is an invalid type. Please supply an IPNetwork type.",
-                    nameof(obj));
-            }
-
-            //  perform the comparision
-            return CompareTo(other);
-        }
-
-        #endregion
-
-        #region IEquatable<IPNetwork> Members
-
-        public static bool Equals(IPNetwork left, IPNetwork right)
-        {
-            return Compare(left, right) == 0;
-        }
-
-        public bool Equals(IPNetwork other)
-        {
-            return Equals(this, other);
-        }
-
-        public override bool Equals(object obj)
-        {
-            return Equals(this, obj as IPNetwork);
-        }
-
-        #endregion
-
-        #region Operators
-
-        public static bool operator ==(IPNetwork left, IPNetwork right)
-        {
-            return Equals(left, right);
-        }
-
-        public static bool operator !=(IPNetwork left, IPNetwork right)
-        {
-            return !Equals(left, right);
-        }
-
-        public static bool operator <(IPNetwork left, IPNetwork right)
-        {
-            return Compare(left, right) < 0;
-        }
-
-        public static bool operator >(IPNetwork left, IPNetwork right)
-        {
-            return Compare(left, right) > 0;
-        }
-
-        #endregion
-
-    }
-}

+ 0 - 129
Emby.Server.Implementations/Networking/IPNetwork/IPNetworkCollection.cs

@@ -1,129 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Numerics;
-
-namespace Emby.Server.Implementations.Networking.IPNetwork
-{
-    public class IPNetworkCollection : IEnumerable<IPNetwork>, IEnumerator<IPNetwork>
-    {
-
-        private BigInteger _enumerator;
-        private byte _cidrSubnet;
-        private IPNetwork _ipnetwork;
-
-        private byte _cidr => this._ipnetwork.Cidr;
-
-        private BigInteger _broadcast => IPNetwork.ToBigInteger(this._ipnetwork.Broadcast);
-
-        private BigInteger _lastUsable => IPNetwork.ToBigInteger(this._ipnetwork.LastUsable);
-        private BigInteger _network => IPNetwork.ToBigInteger(this._ipnetwork.Network);
-#if TRAVISCI
-        public
-#else
-        internal
-#endif
-        IPNetworkCollection(IPNetwork ipnetwork, byte cidrSubnet)
-        {
-
-            int maxCidr = ipnetwork.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork ? 32 : 128;
-            if (cidrSubnet > maxCidr)
-            {
-                throw new ArgumentOutOfRangeException(nameof(cidrSubnet));
-            }
-
-            if (cidrSubnet < ipnetwork.Cidr)
-            {
-                throw new ArgumentException("cidr");
-            }
-
-            this._cidrSubnet = cidrSubnet;
-            this._ipnetwork = ipnetwork;
-            this._enumerator = -1;
-        }
-
-        #region Count, Array, Enumerator
-
-        public BigInteger Count
-        {
-            get
-            {
-                var count = BigInteger.Pow(2, this._cidrSubnet - this._cidr);
-                return count;
-            }
-        }
-
-        public IPNetwork this[BigInteger i]
-        {
-            get
-            {
-                if (i >= this.Count)
-                {
-                    throw new ArgumentOutOfRangeException(nameof(i));
-                }
-
-                var last = this._ipnetwork.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6
-                    ? this._lastUsable : this._broadcast;
-                var increment = (last - this._network) / this.Count;
-                var uintNetwork = this._network + ((increment + 1) * i);
-                var ipn = new IPNetwork(uintNetwork, this._ipnetwork.AddressFamily, this._cidrSubnet);
-                return ipn;
-            }
-        }
-
-        #endregion
-
-        #region IEnumerable Members
-
-        IEnumerator<IPNetwork> IEnumerable<IPNetwork>.GetEnumerator()
-        {
-            return this;
-        }
-
-        IEnumerator IEnumerable.GetEnumerator()
-        {
-            return this;
-        }
-
-        #region IEnumerator<IPNetwork> Members
-
-        public IPNetwork Current => this[this._enumerator];
-
-        #endregion
-
-        #region IDisposable Members
-
-        public void Dispose()
-        {
-            // nothing to dispose
-            return;
-        }
-
-        #endregion
-
-        #region IEnumerator Members
-
-        object IEnumerator.Current => this.Current;
-
-        public bool MoveNext()
-        {
-            this._enumerator++;
-            if (this._enumerator >= this.Count)
-            {
-                return false;
-            }
-            return true;
-
-        }
-
-        public void Reset()
-        {
-            this._enumerator = -1;
-        }
-
-        #endregion
-
-        #endregion
-
-    }
-}

+ 0 - 24
Emby.Server.Implementations/Networking/IPNetwork/LICENSE.txt

@@ -1,24 +0,0 @@
-Copyright (c) 2015, lduchosal
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-* Redistributions of source code must retain the above copyright notice, this
-  list of conditions and the following disclaimer.
-
-* Redistributions in binary form must reproduce the above copyright notice,
-  this list of conditions and the following disclaimer in the documentation
-  and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-

+ 65 - 212
Emby.Server.Implementations/Networking/NetworkManager.cs

@@ -9,55 +9,38 @@ using System.Threading.Tasks;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.Net;
 using MediaBrowser.Model.Net;
-using MediaBrowser.Model.System;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
-using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
 
 
 namespace Emby.Server.Implementations.Networking
 namespace Emby.Server.Implementations.Networking
 {
 {
     public class NetworkManager : INetworkManager
     public class NetworkManager : INetworkManager
     {
     {
-        protected ILogger Logger { get; private set; }
+        private readonly ILogger _logger;
 
 
-        public event EventHandler NetworkChanged;
-        public Func<string[]> LocalSubnetsFn { get; set; }
+        private IPAddress[] _localIpAddresses;
+        private readonly object _localIpAddressSyncLock = new object();
 
 
-        public NetworkManager(ILoggerFactory loggerFactory)
+        public NetworkManager(ILogger<NetworkManager> logger)
         {
         {
-            Logger = loggerFactory.CreateLogger(nameof(NetworkManager));
-
-            // In FreeBSD these events cause a crash
-            if (OperatingSystem.Id != OperatingSystemId.BSD)
-            {
-                try
-                {
-                    NetworkChange.NetworkAddressChanged += NetworkChange_NetworkAddressChanged;
-                }
-                catch (Exception ex)
-                {
-                    Logger.LogError(ex, "Error binding to NetworkAddressChanged event");
-                }
+            _logger = logger;
 
 
-                try
-                {
-                    NetworkChange.NetworkAvailabilityChanged += NetworkChange_NetworkAvailabilityChanged;
-                }
-                catch (Exception ex)
-                {
-                    Logger.LogError(ex, "Error binding to NetworkChange_NetworkAvailabilityChanged event");
-                }
-            }
+            NetworkChange.NetworkAddressChanged += OnNetworkAddressChanged;
+            NetworkChange.NetworkAvailabilityChanged += OnNetworkAvailabilityChanged;
         }
         }
 
 
-        private void NetworkChange_NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e)
+        public Func<string[]> LocalSubnetsFn { get; set; }
+
+        public event EventHandler NetworkChanged;
+
+        private void OnNetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e)
         {
         {
-            Logger.LogDebug("NetworkAvailabilityChanged");
+            _logger.LogDebug("NetworkAvailabilityChanged");
             OnNetworkChanged();
             OnNetworkChanged();
         }
         }
 
 
-        private void NetworkChange_NetworkAddressChanged(object sender, EventArgs e)
+        private void OnNetworkAddressChanged(object sender, EventArgs e)
         {
         {
-            Logger.LogDebug("NetworkAddressChanged");
+            _logger.LogDebug("NetworkAddressChanged");
             OnNetworkChanged();
             OnNetworkChanged();
         }
         }
 
 
@@ -68,39 +51,35 @@ namespace Emby.Server.Implementations.Networking
                 _localIpAddresses = null;
                 _localIpAddresses = null;
                 _macAddresses = null;
                 _macAddresses = null;
             }
             }
+
             if (NetworkChanged != null)
             if (NetworkChanged != null)
             {
             {
                 NetworkChanged(this, EventArgs.Empty);
                 NetworkChanged(this, EventArgs.Empty);
             }
             }
         }
         }
 
 
-        private IpAddressInfo[] _localIpAddresses;
-        private readonly object _localIpAddressSyncLock = new object();
-
-        public IpAddressInfo[] GetLocalIpAddresses(bool ignoreVirtualInterface = true)
+        public IPAddress[] GetLocalIpAddresses(bool ignoreVirtualInterface = true)
         {
         {
             lock (_localIpAddressSyncLock)
             lock (_localIpAddressSyncLock)
             {
             {
                 if (_localIpAddresses == null)
                 if (_localIpAddresses == null)
                 {
                 {
-                    var addresses = GetLocalIpAddressesInternal(ignoreVirtualInterface).Result.Select(ToIpAddressInfo).ToArray();
+                    var addresses = GetLocalIpAddressesInternal(ignoreVirtualInterface).ToArray();
 
 
                     _localIpAddresses = addresses;
                     _localIpAddresses = addresses;
-
-                    return addresses;
                 }
                 }
+
                 return _localIpAddresses;
                 return _localIpAddresses;
             }
             }
         }
         }
 
 
-        private async Task<List<IPAddress>> GetLocalIpAddressesInternal(bool ignoreVirtualInterface)
+        private List<IPAddress> GetLocalIpAddressesInternal(bool ignoreVirtualInterface)
         {
         {
-            var list = GetIPsDefault(ignoreVirtualInterface)
-                .ToList();
+            var list = GetIPsDefault(ignoreVirtualInterface).ToList();
 
 
             if (list.Count == 0)
             if (list.Count == 0)
             {
             {
-                list.AddRange(await GetLocalIpAddressesFallback().ConfigureAwait(false));
+                list = GetLocalIpAddressesFallback().GetAwaiter().GetResult().ToList();
             }
             }
 
 
             var listClone = list.ToList();
             var listClone = list.ToList();
@@ -116,9 +95,8 @@ namespace Emby.Server.Implementations.Networking
 
 
         private static bool FilterIpAddress(IPAddress address)
         private static bool FilterIpAddress(IPAddress address)
         {
         {
-            var addressString = address.ToString();
-
-            if (addressString.StartsWith("169.", StringComparison.OrdinalIgnoreCase))
+            if (address.IsIPv6LinkLocal
+                || address.ToString().StartsWith("169.", StringComparison.OrdinalIgnoreCase))
             {
             {
                 return false;
                 return false;
             }
             }
@@ -279,7 +257,7 @@ namespace Emby.Server.Implementations.Networking
 
 
                 if (normalizedSubnet.IndexOf('/') != -1)
                 if (normalizedSubnet.IndexOf('/') != -1)
                 {
                 {
-                    var ipnetwork = IPNetwork.IPNetwork.Parse(normalizedSubnet);
+                    var ipnetwork = IPNetwork.Parse(normalizedSubnet);
                     if (ipnetwork.Contains(address))
                     if (ipnetwork.Contains(address))
                     {
                     {
                         return true;
                         return true;
@@ -351,13 +329,13 @@ namespace Emby.Server.Implementations.Networking
                     try
                     try
                     {
                     {
                         var host = uri.DnsSafeHost;
                         var host = uri.DnsSafeHost;
-                        Logger.LogDebug("Resolving host {0}", host);
+                        _logger.LogDebug("Resolving host {0}", host);
 
 
                         address = GetIpAddresses(host).Result.FirstOrDefault();
                         address = GetIpAddresses(host).Result.FirstOrDefault();
 
 
                         if (address != null)
                         if (address != null)
                         {
                         {
-                            Logger.LogDebug("{0} resolved to {1}", host, address);
+                            _logger.LogDebug("{0} resolved to {1}", host, address);
 
 
                             return IsInLocalNetworkInternal(address.ToString(), false);
                             return IsInLocalNetworkInternal(address.ToString(), false);
                         }
                         }
@@ -368,7 +346,7 @@ namespace Emby.Server.Implementations.Networking
                     }
                     }
                     catch (Exception ex)
                     catch (Exception ex)
                     {
                     {
-                        Logger.LogError(ex, "Error resolving hostname");
+                        _logger.LogError(ex, "Error resolving hostname");
                     }
                     }
                 }
                 }
             }
             }
@@ -381,56 +359,41 @@ namespace Emby.Server.Implementations.Networking
             return Dns.GetHostAddressesAsync(hostName);
             return Dns.GetHostAddressesAsync(hostName);
         }
         }
 
 
-        private List<IPAddress> GetIPsDefault(bool ignoreVirtualInterface)
+        private IEnumerable<IPAddress> GetIPsDefault(bool ignoreVirtualInterface)
         {
         {
-            NetworkInterface[] interfaces;
+            IEnumerable<NetworkInterface> interfaces;
 
 
             try
             try
             {
             {
-                var validStatuses = new[] { OperationalStatus.Up, OperationalStatus.Unknown };
-
                 interfaces = NetworkInterface.GetAllNetworkInterfaces()
                 interfaces = NetworkInterface.GetAllNetworkInterfaces()
-                    .Where(i => validStatuses.Contains(i.OperationalStatus))
-                    .ToArray();
+                    .Where(x => x.OperationalStatus == OperationalStatus.Up
+                        || x.OperationalStatus == OperationalStatus.Unknown);
             }
             }
-            catch (Exception ex)
+            catch (NetworkInformationException ex)
             {
             {
-                Logger.LogError(ex, "Error in GetAllNetworkInterfaces");
-                return new List<IPAddress>();
+                _logger.LogError(ex, "Error in GetAllNetworkInterfaces");
+                return Enumerable.Empty<IPAddress>();
             }
             }
 
 
             return interfaces.SelectMany(network =>
             return interfaces.SelectMany(network =>
             {
             {
+                var ipProperties = network.GetIPProperties();
 
 
-                try
-                {
-                    // suppress logging because it might be causing nas device wake up
-                    //logger.LogDebug("Querying interface: {0}. Type: {1}. Status: {2}", network.Name, network.NetworkInterfaceType, network.OperationalStatus);
-
-                    var ipProperties = network.GetIPProperties();
-
-                    // Try to exclude virtual adapters
-                    // http://stackoverflow.com/questions/8089685/c-sharp-finding-my-machines-local-ip-address-and-not-the-vms
-                    var addr = ipProperties.GatewayAddresses.FirstOrDefault();
-                    if (addr == null || ignoreVirtualInterface && string.Equals(addr.Address.ToString(), "0.0.0.0", StringComparison.OrdinalIgnoreCase))
-                    {
-                        return new List<IPAddress>();
-                    }
-
-                    return ipProperties.UnicastAddresses
-                        .Select(i => i.Address)
-                        .Where(i => i.AddressFamily == AddressFamily.InterNetwork || i.AddressFamily == AddressFamily.InterNetworkV6)
-                        .ToList();
-                }
-                catch (Exception ex)
+                // Try to exclude virtual adapters
+                // http://stackoverflow.com/questions/8089685/c-sharp-finding-my-machines-local-ip-address-and-not-the-vms
+                var addr = ipProperties.GatewayAddresses.FirstOrDefault();
+                if (addr == null
+                    || (ignoreVirtualInterface
+                        && (addr.Address.Equals(IPAddress.Any) || addr.Address.Equals(IPAddress.IPv6Any))))
                 {
                 {
-                    Logger.LogError(ex, "Error querying network interface");
-                    return new List<IPAddress>();
+                    return Enumerable.Empty<IPAddress>();
                 }
                 }
 
 
+                return ipProperties.UnicastAddresses
+                    .Select(i => i.Address)
+                    .Where(i => i.AddressFamily == AddressFamily.InterNetwork || i.AddressFamily == AddressFamily.InterNetworkV6);
             }).GroupBy(i => i.ToString())
             }).GroupBy(i => i.ToString())
-                .Select(x => x.First())
-                .ToList();
+                .Select(x => x.First());
         }
         }
 
 
         private static async Task<IEnumerable<IPAddress>> GetLocalIpAddressesFallback()
         private static async Task<IEnumerable<IPAddress>> GetLocalIpAddressesFallback()
@@ -462,47 +425,27 @@ namespace Emby.Server.Implementations.Networking
             var localEndPoint = new IPEndPoint(IPAddress.Any, 0);
             var localEndPoint = new IPEndPoint(IPAddress.Any, 0);
             using (var udpClient = new UdpClient(localEndPoint))
             using (var udpClient = new UdpClient(localEndPoint))
             {
             {
-                var port = ((IPEndPoint)(udpClient.Client.LocalEndPoint)).Port;
+                var port = ((IPEndPoint)udpClient.Client.LocalEndPoint).Port;
                 return port;
                 return port;
             }
             }
         }
         }
 
 
-        private List<string> _macAddresses;
-        public List<string> GetMacAddresses()
+        private List<PhysicalAddress> _macAddresses;
+        public List<PhysicalAddress> GetMacAddresses()
         {
         {
             if (_macAddresses == null)
             if (_macAddresses == null)
             {
             {
-                _macAddresses = GetMacAddressesInternal();
+                _macAddresses = GetMacAddressesInternal().ToList();
             }
             }
+
             return _macAddresses;
             return _macAddresses;
         }
         }
 
 
-        private static List<string> GetMacAddressesInternal()
-        {
-            return NetworkInterface.GetAllNetworkInterfaces()
+        private static IEnumerable<PhysicalAddress> GetMacAddressesInternal()
+            => NetworkInterface.GetAllNetworkInterfaces()
                 .Where(i => i.NetworkInterfaceType != NetworkInterfaceType.Loopback)
                 .Where(i => i.NetworkInterfaceType != NetworkInterfaceType.Loopback)
-                .Select(i =>
-                {
-                    try
-                    {
-                        var physicalAddress = i.GetPhysicalAddress();
-
-                        if (physicalAddress == null)
-                        {
-                            return null;
-                        }
-
-                        return physicalAddress.ToString();
-                    }
-                    catch (Exception)
-                    {
-                        //TODO Log exception.
-                        return null;
-                    }
-                })
-                .Where(i => i != null)
-                .ToList();
-        }
+                .Select(x => x.GetPhysicalAddress())
+                .Where(x => x != null && x != PhysicalAddress.None);
 
 
         /// <summary>
         /// <summary>
         /// Parses the specified endpointstring.
         /// Parses the specified endpointstring.
@@ -612,32 +555,10 @@ namespace Emby.Server.Implementations.Networking
             return hosts[0];
             return hosts[0];
         }
         }
 
 
-        public IpAddressInfo ParseIpAddress(string ipAddress)
-        {
-            if (TryParseIpAddress(ipAddress, out var info))
-            {
-                return info;
-            }
-
-            throw new ArgumentException("Invalid ip address: " + ipAddress);
-        }
-
-        public bool TryParseIpAddress(string ipAddress, out IpAddressInfo ipAddressInfo)
-        {
-            if (IPAddress.TryParse(ipAddress, out var address))
-            {
-                ipAddressInfo = ToIpAddressInfo(address);
-                return true;
-            }
-
-            ipAddressInfo = null;
-            return false;
-        }
-
-        public bool IsInSameSubnet(IpAddressInfo address1, IpAddressInfo address2, IpAddressInfo subnetMask)
+        public bool IsInSameSubnet(IPAddress address1, IPAddress address2, IPAddress subnetMask)
         {
         {
-             IPAddress network1 = GetNetworkAddress(ToIPAddress(address1), ToIPAddress(subnetMask));
-             IPAddress network2 = GetNetworkAddress(ToIPAddress(address2), ToIPAddress(subnetMask));
+             IPAddress network1 = GetNetworkAddress(address1, subnetMask);
+             IPAddress network2 = GetNetworkAddress(address2, subnetMask);
              return network1.Equals(network2);
              return network1.Equals(network2);
         }
         }
 
 
@@ -656,13 +577,13 @@ namespace Emby.Server.Implementations.Networking
             {
             {
                 broadcastAddress[i] = (byte)(ipAdressBytes[i] & (subnetMaskBytes[i]));
                 broadcastAddress[i] = (byte)(ipAdressBytes[i] & (subnetMaskBytes[i]));
             }
             }
+
             return new IPAddress(broadcastAddress);
             return new IPAddress(broadcastAddress);
         }
         }
 
 
-        public IpAddressInfo GetLocalIpSubnetMask(IpAddressInfo address)
+        public IPAddress GetLocalIpSubnetMask(IPAddress address)
         {
         {
             NetworkInterface[] interfaces;
             NetworkInterface[] interfaces;
-            IPAddress ipaddress = ToIPAddress(address);
 
 
             try
             try
             {
             {
@@ -674,7 +595,7 @@ namespace Emby.Server.Implementations.Networking
             }
             }
             catch (Exception ex)
             catch (Exception ex)
             {
             {
-                Logger.LogError(ex, "Error in GetAllNetworkInterfaces");
+                _logger.LogError(ex, "Error in GetAllNetworkInterfaces");
                 return null;
                 return null;
             }
             }
 
 
@@ -684,83 +605,15 @@ namespace Emby.Server.Implementations.Networking
                 {
                 {
                     foreach (UnicastIPAddressInformation ip in ni.GetIPProperties().UnicastAddresses)
                     foreach (UnicastIPAddressInformation ip in ni.GetIPProperties().UnicastAddresses)
                     {
                     {
-                        if (ip.Address.Equals(ipaddress) && ip.IPv4Mask != null)
+                        if (ip.Address.Equals(address) && ip.IPv4Mask != null)
                         {
                         {
-                           return ToIpAddressInfo(ip.IPv4Mask);
+                           return ip.IPv4Mask;
                         }
                         }
                     }
                     }
                 }
                 }
             }
             }
-            return null;
-        }
-
-        public static IpEndPointInfo ToIpEndPointInfo(IPEndPoint endpoint)
-        {
-            if (endpoint == null)
-            {
-                return null;
-            }
-
-            return new IpEndPointInfo(ToIpAddressInfo(endpoint.Address), endpoint.Port);
-        }
-
-        public static IPEndPoint ToIPEndPoint(IpEndPointInfo endpoint)
-        {
-            if (endpoint == null)
-            {
-                return null;
-            }
 
 
-            return new IPEndPoint(ToIPAddress(endpoint.IpAddress), endpoint.Port);
-        }
-
-        public static IPAddress ToIPAddress(IpAddressInfo address)
-        {
-            if (address.Equals(IpAddressInfo.Any))
-            {
-                return IPAddress.Any;
-            }
-            if (address.Equals(IpAddressInfo.IPv6Any))
-            {
-                return IPAddress.IPv6Any;
-            }
-            if (address.Equals(IpAddressInfo.Loopback))
-            {
-                return IPAddress.Loopback;
-            }
-            if (address.Equals(IpAddressInfo.IPv6Loopback))
-            {
-                return IPAddress.IPv6Loopback;
-            }
-
-            return IPAddress.Parse(address.Address);
-        }
-
-        public static IpAddressInfo ToIpAddressInfo(IPAddress address)
-        {
-            if (address.Equals(IPAddress.Any))
-            {
-                return IpAddressInfo.Any;
-            }
-            if (address.Equals(IPAddress.IPv6Any))
-            {
-                return IpAddressInfo.IPv6Any;
-            }
-            if (address.Equals(IPAddress.Loopback))
-            {
-                return IpAddressInfo.Loopback;
-            }
-            if (address.Equals(IPAddress.IPv6Loopback))
-            {
-                return IpAddressInfo.IPv6Loopback;
-            }
-            return new IpAddressInfo(address.ToString(), address.AddressFamily == AddressFamily.InterNetworkV6 ? IpAddressFamily.InterNetworkV6 : IpAddressFamily.InterNetwork);
-        }
-
-        public async Task<IpAddressInfo[]> GetHostAddressesAsync(string host)
-        {
-            var addresses = await Dns.GetHostAddressesAsync(host).ConfigureAwait(false);
-            return addresses.Select(ToIpAddressInfo).ToArray();
+            return null;
         }
         }
 
 
         /// <summary>
         /// <summary>

+ 4 - 3
Emby.Server.Implementations/Playlists/PlaylistManager.cs

@@ -1,5 +1,6 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Threading;
 using System.Threading;
@@ -129,7 +130,7 @@ namespace Emby.Server.Implementations.Playlists
                     {
                     {
                         new Share
                         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
                             CanEdit = true
                         }
                         }
                     }
                     }
@@ -144,7 +145,7 @@ namespace Emby.Server.Implementations.Playlists
 
 
                 if (options.ItemIdList.Length > 0)
                 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
                         EnableImages = true
                     });
                     });
@@ -152,7 +153,7 @@ namespace Emby.Server.Implementations.Playlists
 
 
                 return new PlaylistCreationResult
                 return new PlaylistCreationResult
                 {
                 {
-                    Id = playlist.Id.ToString("N")
+                    Id = playlist.Id.ToString("N", CultureInfo.InvariantCulture)
                 };
                 };
             }
             }
             finally
             finally

+ 2 - 2
Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs

@@ -1,5 +1,5 @@
 using System;
 using System;
-using System.Collections.Generic;
+using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Threading;
 using System.Threading;
@@ -287,7 +287,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
             {
             {
                 if (_id == null)
                 if (_id == null)
                 {
                 {
-                    _id = ScheduledTask.GetType().FullName.GetMD5().ToString("N");
+                    _id = ScheduledTask.GetType().FullName.GetMD5().ToString("N", CultureInfo.InvariantCulture);
                 }
                 }
 
 
                 return _id;
                 return _id;

+ 3 - 3
Emby.Server.Implementations/Security/AuthenticationRepository.cs

@@ -97,7 +97,7 @@ namespace Emby.Server.Implementations.Security
                         statement.TryBind("@AppName", info.AppName);
                         statement.TryBind("@AppName", info.AppName);
                         statement.TryBind("@AppVersion", info.AppVersion);
                         statement.TryBind("@AppVersion", info.AppVersion);
                         statement.TryBind("@DeviceName", info.DeviceName);
                         statement.TryBind("@DeviceName", info.DeviceName);
-                        statement.TryBind("@UserId", (info.UserId.Equals(Guid.Empty) ? null : info.UserId.ToString("N")));
+                        statement.TryBind("@UserId", (info.UserId.Equals(Guid.Empty) ? null : info.UserId.ToString("N", CultureInfo.InvariantCulture)));
                         statement.TryBind("@UserName", info.UserName);
                         statement.TryBind("@UserName", info.UserName);
                         statement.TryBind("@IsActive", true);
                         statement.TryBind("@IsActive", true);
                         statement.TryBind("@DateCreated", info.DateCreated.ToDateTimeParamValue());
                         statement.TryBind("@DateCreated", info.DateCreated.ToDateTimeParamValue());
@@ -131,7 +131,7 @@ namespace Emby.Server.Implementations.Security
                         statement.TryBind("@AppName", info.AppName);
                         statement.TryBind("@AppName", info.AppName);
                         statement.TryBind("@AppVersion", info.AppVersion);
                         statement.TryBind("@AppVersion", info.AppVersion);
                         statement.TryBind("@DeviceName", info.DeviceName);
                         statement.TryBind("@DeviceName", info.DeviceName);
-                        statement.TryBind("@UserId", (info.UserId.Equals(Guid.Empty) ? null : info.UserId.ToString("N")));
+                        statement.TryBind("@UserId", (info.UserId.Equals(Guid.Empty) ? null : info.UserId.ToString("N", CultureInfo.InvariantCulture)));
                         statement.TryBind("@UserName", info.UserName);
                         statement.TryBind("@UserName", info.UserName);
                         statement.TryBind("@DateCreated", info.DateCreated.ToDateTimeParamValue());
                         statement.TryBind("@DateCreated", info.DateCreated.ToDateTimeParamValue());
                         statement.TryBind("@DateLastActivity", info.DateLastActivity.ToDateTimeParamValue());
                         statement.TryBind("@DateLastActivity", info.DateLastActivity.ToDateTimeParamValue());
@@ -174,7 +174,7 @@ namespace Emby.Server.Implementations.Security
 
 
             if (!query.UserId.Equals(Guid.Empty))
             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))
             if (!string.IsNullOrEmpty(query.DeviceId))

+ 2 - 1
Emby.Server.Implementations/Serialization/JsonSerializer.cs

@@ -1,4 +1,5 @@
 using System;
 using System;
+using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.IO;
@@ -245,7 +246,7 @@ namespace Emby.Server.Implementations.Serialization
                 return null;
                 return null;
             }
             }
 
 
-            return guid.ToString("N");
+            return guid.ToString("N", CultureInfo.InvariantCulture);
         }
         }
 
 
         /// <summary>
         /// <summary>

+ 14 - 13
Emby.Server.Implementations/ServerApplicationPaths.cs

@@ -10,8 +10,12 @@ namespace Emby.Server.Implementations
     /// </summary>
     /// </summary>
     public class ServerApplicationPaths : BaseApplicationPaths, IServerApplicationPaths
     public class ServerApplicationPaths : BaseApplicationPaths, IServerApplicationPaths
     {
     {
+        private string _defaultTranscodingTempPath;
+        private string _transcodingTempPath;
+        private string _internalMetadataPath;
+
         /// <summary>
         /// <summary>
-        /// Initializes a new instance of the <see cref="BaseApplicationPaths" /> class.
+        /// Initializes a new instance of the <see cref="ServerApplicationPaths" /> class.
         /// </summary>
         /// </summary>
         public ServerApplicationPaths(
         public ServerApplicationPaths(
             string programDataPath,
             string programDataPath,
@@ -30,7 +34,7 @@ namespace Emby.Server.Implementations
         public string ApplicationResourcesPath { get; } = AppContext.BaseDirectory;
         public string ApplicationResourcesPath { get; } = AppContext.BaseDirectory;
 
 
         /// <summary>
         /// <summary>
-        /// Gets the path to the base root media directory
+        /// Gets the path to the base root media directory.
         /// </summary>
         /// </summary>
         /// <value>The root folder path.</value>
         /// <value>The root folder path.</value>
         public string RootFolderPath => Path.Combine(ProgramDataPath, "root");
         public string RootFolderPath => Path.Combine(ProgramDataPath, "root");
@@ -48,7 +52,7 @@ namespace Emby.Server.Implementations
         public string LocalizationPath => Path.Combine(ProgramDataPath, "localization");
         public string LocalizationPath => Path.Combine(ProgramDataPath, "localization");
 
 
         /// <summary>
         /// <summary>
-        /// Gets the path to the People directory
+        /// Gets the path to the People directory.
         /// </summary>
         /// </summary>
         /// <value>The people path.</value>
         /// <value>The people path.</value>
         public string PeoplePath => Path.Combine(InternalMetadataPath, "People");
         public string PeoplePath => Path.Combine(InternalMetadataPath, "People");
@@ -56,37 +60,37 @@ namespace Emby.Server.Implementations
         public string ArtistsPath => Path.Combine(InternalMetadataPath, "artists");
         public string ArtistsPath => Path.Combine(InternalMetadataPath, "artists");
 
 
         /// <summary>
         /// <summary>
-        /// Gets the path to the Genre directory
+        /// Gets the path to the Genre directory.
         /// </summary>
         /// </summary>
         /// <value>The genre path.</value>
         /// <value>The genre path.</value>
         public string GenrePath => Path.Combine(InternalMetadataPath, "Genre");
         public string GenrePath => Path.Combine(InternalMetadataPath, "Genre");
 
 
         /// <summary>
         /// <summary>
-        /// Gets the path to the Genre directory
+        /// Gets the path to the Genre directory.
         /// </summary>
         /// </summary>
         /// <value>The genre path.</value>
         /// <value>The genre path.</value>
         public string MusicGenrePath => Path.Combine(InternalMetadataPath, "MusicGenre");
         public string MusicGenrePath => Path.Combine(InternalMetadataPath, "MusicGenre");
 
 
         /// <summary>
         /// <summary>
-        /// Gets the path to the Studio directory
+        /// Gets the path to the Studio directory.
         /// </summary>
         /// </summary>
         /// <value>The studio path.</value>
         /// <value>The studio path.</value>
         public string StudioPath => Path.Combine(InternalMetadataPath, "Studio");
         public string StudioPath => Path.Combine(InternalMetadataPath, "Studio");
 
 
         /// <summary>
         /// <summary>
-        /// Gets the path to the Year directory
+        /// Gets the path to the Year directory.
         /// </summary>
         /// </summary>
         /// <value>The year path.</value>
         /// <value>The year path.</value>
         public string YearPath => Path.Combine(InternalMetadataPath, "Year");
         public string YearPath => Path.Combine(InternalMetadataPath, "Year");
 
 
         /// <summary>
         /// <summary>
-        /// Gets the path to the General IBN directory
+        /// Gets the path to the General IBN directory.
         /// </summary>
         /// </summary>
         /// <value>The general path.</value>
         /// <value>The general path.</value>
         public string GeneralPath => Path.Combine(InternalMetadataPath, "general");
         public string GeneralPath => Path.Combine(InternalMetadataPath, "general");
 
 
         /// <summary>
         /// <summary>
-        /// Gets the path to the Ratings IBN directory
+        /// Gets the path to the Ratings IBN directory.
         /// </summary>
         /// </summary>
         /// <value>The ratings path.</value>
         /// <value>The ratings path.</value>
         public string RatingsPath => Path.Combine(InternalMetadataPath, "ratings");
         public string RatingsPath => Path.Combine(InternalMetadataPath, "ratings");
@@ -98,15 +102,13 @@ namespace Emby.Server.Implementations
         public string MediaInfoImagesPath => Path.Combine(InternalMetadataPath, "mediainfo");
         public string MediaInfoImagesPath => Path.Combine(InternalMetadataPath, "mediainfo");
 
 
         /// <summary>
         /// <summary>
-        /// Gets the path to the user configuration directory
+        /// Gets the path to the user configuration directory.
         /// </summary>
         /// </summary>
         /// <value>The user configuration directory path.</value>
         /// <value>The user configuration directory path.</value>
         public string UserConfigurationDirectoryPath => Path.Combine(ConfigurationDirectoryPath, "users");
         public string UserConfigurationDirectoryPath => Path.Combine(ConfigurationDirectoryPath, "users");
 
 
-        private string _defaultTranscodingTempPath;
         public string DefaultTranscodingTempPath => _defaultTranscodingTempPath ?? (_defaultTranscodingTempPath = Path.Combine(ProgramDataPath, "transcoding-temp"));
         public string DefaultTranscodingTempPath => _defaultTranscodingTempPath ?? (_defaultTranscodingTempPath = Path.Combine(ProgramDataPath, "transcoding-temp"));
 
 
-        private string _transcodingTempPath;
         public string TranscodingTempPath
         public string TranscodingTempPath
         {
         {
             get => _transcodingTempPath ?? (_transcodingTempPath = DefaultTranscodingTempPath);
             get => _transcodingTempPath ?? (_transcodingTempPath = DefaultTranscodingTempPath);
@@ -139,7 +141,6 @@ namespace Emby.Server.Implementations
             return path;
             return path;
         }
         }
 
 
-        private string _internalMetadataPath;
         public string InternalMetadataPath
         public string InternalMetadataPath
         {
         {
             get => _internalMetadataPath ?? (_internalMetadataPath = Path.Combine(DataPath, "metadata"));
             get => _internalMetadataPath ?? (_internalMetadataPath = Path.Combine(DataPath, "metadata"));

+ 5 - 4
Emby.Server.Implementations/Services/HttpResult.cs

@@ -10,8 +10,6 @@ namespace Emby.Server.Implementations.Services
     public class HttpResult
     public class HttpResult
         : IHttpResult, IAsyncStreamWriter
         : IHttpResult, IAsyncStreamWriter
     {
     {
-        public object Response { get; set; }
-
         public HttpResult(object response, string contentType, HttpStatusCode statusCode)
         public HttpResult(object response, string contentType, HttpStatusCode statusCode)
         {
         {
             this.Headers = new Dictionary<string, string>();
             this.Headers = new Dictionary<string, string>();
@@ -21,6 +19,8 @@ namespace Emby.Server.Implementations.Services
             this.StatusCode = statusCode;
             this.StatusCode = statusCode;
         }
         }
 
 
+        public object Response { get; set; }
+
         public string ContentType { get; set; }
         public string ContentType { get; set; }
 
 
         public IDictionary<string, string> Headers { get; private set; }
         public IDictionary<string, string> Headers { get; private set; }
@@ -37,7 +37,7 @@ namespace Emby.Server.Implementations.Services
 
 
         public async Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken)
         public async Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken)
         {
         {
-            var response = RequestContext == null ? null : RequestContext.Response;
+            var response = RequestContext?.Response;
 
 
             if (this.Response is byte[] bytesResponse)
             if (this.Response is byte[] bytesResponse)
             {
             {
@@ -45,13 +45,14 @@ namespace Emby.Server.Implementations.Services
 
 
                 if (response != null)
                 if (response != null)
                 {
                 {
-                    response.OriginalResponse.ContentLength = contentLength;
+                    response.ContentLength = contentLength;
                 }
                 }
 
 
                 if (contentLength > 0)
                 if (contentLength > 0)
                 {
                 {
                     await responseStream.WriteAsync(bytesResponse, 0, contentLength, cancellationToken).ConfigureAwait(false);
                     await responseStream.WriteAsync(bytesResponse, 0, contentLength, cancellationToken).ConfigureAwait(false);
                 }
                 }
+
                 return;
                 return;
             }
             }
 
 

+ 15 - 16
Emby.Server.Implementations/Services/ResponseHelper.cs

@@ -1,5 +1,4 @@
 using System;
 using System;
-using System.Collections.Generic;
 using System.Globalization;
 using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Net;
 using System.Net;
@@ -7,13 +6,14 @@ using System.Text;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using Emby.Server.Implementations.HttpServer;
 using Emby.Server.Implementations.HttpServer;
+using Microsoft.AspNetCore.Http;
 using MediaBrowser.Model.Services;
 using MediaBrowser.Model.Services;
 
 
 namespace Emby.Server.Implementations.Services
 namespace Emby.Server.Implementations.Services
 {
 {
     public static class ResponseHelper
     public static class ResponseHelper
     {
     {
-        public static Task WriteToResponse(IResponse response, IRequest request, object result, CancellationToken cancellationToken)
+        public static Task WriteToResponse(HttpResponse response, IRequest request, object result, CancellationToken cancellationToken)
         {
         {
             if (result == null)
             if (result == null)
             {
             {
@@ -22,7 +22,7 @@ namespace Emby.Server.Implementations.Services
                     response.StatusCode = (int)HttpStatusCode.NoContent;
                     response.StatusCode = (int)HttpStatusCode.NoContent;
                 }
                 }
 
 
-                response.OriginalResponse.ContentLength = 0;
+                response.ContentLength = 0;
                 return Task.CompletedTask;
                 return Task.CompletedTask;
             }
             }
 
 
@@ -41,7 +41,6 @@ namespace Emby.Server.Implementations.Services
                     httpResult.RequestContext = request;
                     httpResult.RequestContext = request;
 
 
                 response.StatusCode = httpResult.Status;
                 response.StatusCode = httpResult.Status;
-                response.StatusDescription = httpResult.StatusCode.ToString();
             }
             }
 
 
             var responseOptions = result as IHasHeaders;
             var responseOptions = result as IHasHeaders;
@@ -51,11 +50,11 @@ namespace Emby.Server.Implementations.Services
                 {
                 {
                     if (string.Equals(responseHeaders.Key, "Content-Length", StringComparison.OrdinalIgnoreCase))
                     if (string.Equals(responseHeaders.Key, "Content-Length", StringComparison.OrdinalIgnoreCase))
                     {
                     {
-                        response.OriginalResponse.ContentLength = long.Parse(responseHeaders.Value, CultureInfo.InvariantCulture);
+                        response.ContentLength = long.Parse(responseHeaders.Value, CultureInfo.InvariantCulture);
                         continue;
                         continue;
                     }
                     }
 
 
-                    response.AddHeader(responseHeaders.Key, responseHeaders.Value);
+                    response.Headers.Add(responseHeaders.Key, responseHeaders.Value);
                 }
                 }
             }
             }
 
 
@@ -74,31 +73,31 @@ namespace Emby.Server.Implementations.Services
             switch (result)
             switch (result)
             {
             {
                 case IAsyncStreamWriter asyncStreamWriter:
                 case IAsyncStreamWriter asyncStreamWriter:
-                    return asyncStreamWriter.WriteToAsync(response.OutputStream, cancellationToken);
+                    return asyncStreamWriter.WriteToAsync(response.Body, cancellationToken);
                 case IStreamWriter streamWriter:
                 case IStreamWriter streamWriter:
-                    streamWriter.WriteTo(response.OutputStream);
+                    streamWriter.WriteTo(response.Body);
                     return Task.CompletedTask;
                     return Task.CompletedTask;
                 case FileWriter fileWriter:
                 case FileWriter fileWriter:
                     return fileWriter.WriteToAsync(response, cancellationToken);
                     return fileWriter.WriteToAsync(response, cancellationToken);
                 case Stream stream:
                 case Stream stream:
-                    return CopyStream(stream, response.OutputStream);
+                    return CopyStream(stream, response.Body);
                 case byte[] bytes:
                 case byte[] bytes:
                     response.ContentType = "application/octet-stream";
                     response.ContentType = "application/octet-stream";
-                    response.OriginalResponse.ContentLength = bytes.Length;
+                    response.ContentLength = bytes.Length;
 
 
                     if (bytes.Length > 0)
                     if (bytes.Length > 0)
                     {
                     {
-                        return response.OutputStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken);
+                        return response.Body.WriteAsync(bytes, 0, bytes.Length, cancellationToken);
                     }
                     }
 
 
                     return Task.CompletedTask;
                     return Task.CompletedTask;
                 case string responseText:
                 case string responseText:
                     var responseTextAsBytes = Encoding.UTF8.GetBytes(responseText);
                     var responseTextAsBytes = Encoding.UTF8.GetBytes(responseText);
-                    response.OriginalResponse.ContentLength = responseTextAsBytes.Length;
+                    response.ContentLength = responseTextAsBytes.Length;
 
 
                     if (responseTextAsBytes.Length > 0)
                     if (responseTextAsBytes.Length > 0)
                     {
                     {
-                        return response.OutputStream.WriteAsync(responseTextAsBytes, 0, responseTextAsBytes.Length, cancellationToken);
+                        return response.Body.WriteAsync(responseTextAsBytes, 0, responseTextAsBytes.Length, cancellationToken);
                     }
                     }
 
 
                     return Task.CompletedTask;
                     return Task.CompletedTask;
@@ -115,7 +114,7 @@ namespace Emby.Server.Implementations.Services
             }
             }
         }
         }
 
 
-        public static async Task WriteObject(IRequest request, object result, IResponse response)
+        public static async Task WriteObject(IRequest request, object result, HttpResponse response)
         {
         {
             var contentType = request.ResponseContentType;
             var contentType = request.ResponseContentType;
             var serializer = RequestHelper.GetResponseWriter(HttpListenerHost.Instance, contentType);
             var serializer = RequestHelper.GetResponseWriter(HttpListenerHost.Instance, contentType);
@@ -127,11 +126,11 @@ namespace Emby.Server.Implementations.Services
                 ms.Position = 0;
                 ms.Position = 0;
 
 
                 var contentLength = ms.Length;
                 var contentLength = ms.Length;
-                response.OriginalResponse.ContentLength = contentLength;
+                response.ContentLength = contentLength;
 
 
                 if (contentLength > 0)
                 if (contentLength > 0)
                 {
                 {
-                    await ms.CopyToAsync(response.OutputStream).ConfigureAwait(false);
+                    await ms.CopyToAsync(response.Body).ConfigureAwait(false);
                 }
                 }
             }
             }
         }
         }

+ 0 - 4
Emby.Server.Implementations/Services/ServiceController.cs

@@ -147,7 +147,6 @@ namespace Emby.Server.Implementations.Services
 
 
         public Task<object> Execute(HttpListenerHost httpHost, object requestDto, IRequest req)
         public Task<object> Execute(HttpListenerHost httpHost, object requestDto, IRequest req)
         {
         {
-            req.Dto = requestDto;
             var requestType = requestDto.GetType();
             var requestType = requestDto.GetType();
             req.OperationName = requestType.Name;
             req.OperationName = requestType.Name;
 
 
@@ -161,9 +160,6 @@ namespace Emby.Server.Implementations.Services
                 serviceRequiresContext.Request = req;
                 serviceRequiresContext.Request = req;
             }
             }
 
 
-            if (req.Dto == null) // Don't override existing batched DTO[]
-                req.Dto = requestDto;
-
             //Executes the service and returns the result
             //Executes the service and returns the result
             return ServiceExecGeneral.Execute(serviceType, req, service, requestDto, requestType.GetMethodName());
             return ServiceExecGeneral.Execute(serviceType, req, service, requestDto, requestType.GetMethodName());
         }
         }

+ 1 - 1
Emby.Server.Implementations/Services/ServiceExec.cs

@@ -78,7 +78,7 @@ namespace Emby.Server.Implementations.Services
                     foreach (var requestFilter in actionContext.RequestFilters)
                     foreach (var requestFilter in actionContext.RequestFilters)
                     {
                     {
                         requestFilter.RequestFilter(request, request.Response, requestDto);
                         requestFilter.RequestFilter(request, request.Response, requestDto);
-                        if (request.Response.OriginalResponse.HasStarted)
+                        if (request.Response.HasStarted)
                         {
                         {
                             Task.FromResult<object>(null);
                             Task.FromResult<object>(null);
                         }
                         }

+ 38 - 69
Emby.Server.Implementations/Services/ServiceHandler.cs

@@ -5,20 +5,21 @@ using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using Emby.Server.Implementations.HttpServer;
 using Emby.Server.Implementations.HttpServer;
 using MediaBrowser.Model.Services;
 using MediaBrowser.Model.Services;
+using Microsoft.AspNetCore.Http;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
 
 
 namespace Emby.Server.Implementations.Services
 namespace Emby.Server.Implementations.Services
 {
 {
     public class ServiceHandler
     public class ServiceHandler
     {
     {
-        public RestPath RestPath { get; }
+        private RestPath _restPath;
 
 
-        public string ResponseContentType { get; }
+        private string _responseContentType;
 
 
         internal ServiceHandler(RestPath restPath, string responseContentType)
         internal ServiceHandler(RestPath restPath, string responseContentType)
         {
         {
-            RestPath = restPath;
-            ResponseContentType = responseContentType;
+            _restPath = restPath;
+            _responseContentType = responseContentType;
         }
         }
 
 
         protected static Task<object> CreateContentTypeRequest(HttpListenerHost host, IRequest httpReq, Type requestType, string contentType)
         protected static Task<object> CreateContentTypeRequest(HttpListenerHost host, IRequest httpReq, Type requestType, string contentType)
@@ -54,7 +55,7 @@ namespace Emby.Server.Implementations.Services
 
 
         private static string GetFormatContentType(string format)
         private static string GetFormatContentType(string format)
         {
         {
-            //built-in formats
+            // built-in formats
             switch (format)
             switch (format)
             {
             {
                 case "json": return "application/json";
                 case "json": return "application/json";
@@ -63,16 +64,16 @@ namespace Emby.Server.Implementations.Services
             }
             }
         }
         }
 
 
-        public async Task ProcessRequestAsync(HttpListenerHost httpHost, IRequest httpReq, IResponse httpRes, ILogger logger, CancellationToken cancellationToken)
+        public async Task ProcessRequestAsync(HttpListenerHost httpHost, IRequest httpReq, HttpResponse httpRes, ILogger logger, CancellationToken cancellationToken)
         {
         {
-            httpReq.Items["__route"] = RestPath;
+            httpReq.Items["__route"] = _restPath;
 
 
-            if (ResponseContentType != null)
+            if (_responseContentType != null)
             {
             {
-                httpReq.ResponseContentType = ResponseContentType;
+                httpReq.ResponseContentType = _responseContentType;
             }
             }
 
 
-            var request = httpReq.Dto = await CreateRequest(httpHost, httpReq, RestPath, logger).ConfigureAwait(false);
+            var request = await CreateRequest(httpHost, httpReq, _restPath, logger).ConfigureAwait(false);
 
 
             httpHost.ApplyRequestFilters(httpReq, httpRes, request);
             httpHost.ApplyRequestFilters(httpReq, httpRes, request);
 
 
@@ -94,7 +95,7 @@ namespace Emby.Server.Implementations.Services
             if (RequireqRequestStream(requestType))
             if (RequireqRequestStream(requestType))
             {
             {
                 // Used by IRequiresRequestStream
                 // Used by IRequiresRequestStream
-                var requestParams = await GetRequestParams(httpReq).ConfigureAwait(false);
+                var requestParams = GetRequestParams(httpReq.Response.HttpContext.Request);
                 var request = ServiceHandler.CreateRequest(httpReq, restPath, requestParams, host.CreateInstance(requestType));
                 var request = ServiceHandler.CreateRequest(httpReq, restPath, requestParams, host.CreateInstance(requestType));
 
 
                 var rawReq = (IRequiresRequestStream)request;
                 var rawReq = (IRequiresRequestStream)request;
@@ -103,7 +104,7 @@ namespace Emby.Server.Implementations.Services
             }
             }
             else
             else
             {
             {
-                var requestParams = await GetFlattenedRequestParams(httpReq).ConfigureAwait(false);
+                var requestParams = GetFlattenedRequestParams(httpReq.Response.HttpContext.Request);
 
 
                 var requestDto = await CreateContentTypeRequest(host, httpReq, restPath.RequestType, httpReq.ContentType).ConfigureAwait(false);
                 var requestDto = await CreateContentTypeRequest(host, httpReq, restPath.RequestType, httpReq.ContentType).ConfigureAwait(false);
 
 
@@ -121,7 +122,7 @@ namespace Emby.Server.Implementations.Services
         public static object CreateRequest(IRequest httpReq, RestPath restPath, Dictionary<string, string> requestParams, object requestDto)
         public static object CreateRequest(IRequest httpReq, RestPath restPath, Dictionary<string, string> requestParams, object requestDto)
         {
         {
             var pathInfo = !restPath.IsWildCardPath
             var pathInfo = !restPath.IsWildCardPath
-                ? GetSanitizedPathInfo(httpReq.PathInfo, out string contentType)
+                ? GetSanitizedPathInfo(httpReq.PathInfo, out _)
                 : httpReq.PathInfo;
                 : httpReq.PathInfo;
 
 
             return restPath.CreateRequest(pathInfo, requestParams, requestDto);
             return restPath.CreateRequest(pathInfo, requestParams, requestDto);
@@ -130,56 +131,41 @@ namespace Emby.Server.Implementations.Services
         /// <summary>
         /// <summary>
         /// Duplicate Params are given a unique key by appending a #1 suffix
         /// Duplicate Params are given a unique key by appending a #1 suffix
         /// </summary>
         /// </summary>
-        private static async Task<Dictionary<string, string>> GetRequestParams(IRequest request)
+        private static Dictionary<string, string> GetRequestParams(HttpRequest request)
         {
         {
             var map = new Dictionary<string, string>();
             var map = new Dictionary<string, string>();
 
 
-            foreach (var name in request.QueryString.Keys)
+            foreach (var pair in request.Query)
             {
             {
-                if (name == null)
-                {
-                    // thank you ASP.NET
-                    continue;
-                }
-
-                var values = request.QueryString[name];
+                var values = pair.Value;
                 if (values.Count == 1)
                 if (values.Count == 1)
                 {
                 {
-                    map[name] = values[0];
+                    map[pair.Key] = values[0];
                 }
                 }
                 else
                 else
                 {
                 {
                     for (var i = 0; i < values.Count; i++)
                     for (var i = 0; i < values.Count; i++)
                     {
                     {
-                        map[name + (i == 0 ? "" : "#" + i)] = values[i];
+                        map[pair.Key + (i == 0 ? string.Empty : "#" + i)] = values[i];
                     }
                     }
                 }
                 }
             }
             }
 
 
-            if ((IsMethod(request.Verb, "POST") || IsMethod(request.Verb, "PUT")))
+            if ((IsMethod(request.Method, "POST") || IsMethod(request.Method, "PUT"))
+                && request.HasFormContentType)
             {
             {
-                var formData = await request.GetFormData().ConfigureAwait(false);
-                if (formData != null)
+                foreach (var pair in request.Form)
                 {
                 {
-                    foreach (var name in formData.Keys)
+                    var values = pair.Value;
+                    if (values.Count == 1)
                     {
                     {
-                        if (name == null)
-                        {
-                            // thank you ASP.NET
-                            continue;
-                        }
-
-                        var values = formData.GetValues(name);
-                        if (values.Count == 1)
-                        {
-                            map[name] = values[0];
-                        }
-                        else
+                        map[pair.Key] = values[0];
+                    }
+                    else
+                    {
+                        for (var i = 0; i < values.Count; i++)
                         {
                         {
-                            for (var i = 0; i < values.Count; i++)
-                            {
-                                map[name + (i == 0 ? "" : "#" + i)] = values[i];
-                            }
+                            map[pair.Key + (i == 0 ? string.Empty : "#" + i)] = values[i];
                         }
                         }
                     }
                     }
                 }
                 }
@@ -189,43 +175,26 @@ namespace Emby.Server.Implementations.Services
         }
         }
 
 
         private static bool IsMethod(string method, string expected)
         private static bool IsMethod(string method, string expected)
-        {
-            return string.Equals(method, expected, StringComparison.OrdinalIgnoreCase);
-        }
+            => string.Equals(method, expected, StringComparison.OrdinalIgnoreCase);
 
 
         /// <summary>
         /// <summary>
         /// Duplicate params have their values joined together in a comma-delimited string
         /// Duplicate params have their values joined together in a comma-delimited string
         /// </summary>
         /// </summary>
-        private static async Task<Dictionary<string, string>> GetFlattenedRequestParams(IRequest request)
+        private static Dictionary<string, string> GetFlattenedRequestParams(HttpRequest request)
         {
         {
             var map = new Dictionary<string, string>();
             var map = new Dictionary<string, string>();
 
 
-            foreach (var name in request.QueryString.Keys)
+            foreach (var pair in request.Query)
             {
             {
-                if (name == null)
-                {
-                    // thank you ASP.NET
-                    continue;
-                }
-
-                map[name] = request.QueryString[name];
+                map[pair.Key] = pair.Value;
             }
             }
 
 
-            if ((IsMethod(request.Verb, "POST") || IsMethod(request.Verb, "PUT")))
+            if ((IsMethod(request.Method, "POST") || IsMethod(request.Method, "PUT"))
+                && request.HasFormContentType)
             {
             {
-                var formData = await request.GetFormData().ConfigureAwait(false);
-                if (formData != null)
+                foreach (var pair in request.Form)
                 {
                 {
-                    foreach (var name in formData.Keys)
-                    {
-                        if (name == null)
-                        {
-                            // thank you ASP.NET
-                            continue;
-                        }
-
-                        map[name] = formData[name];
-                    }
+                    map[pair.Key] = pair.Value;
                 }
                 }
             }
             }
 
 

+ 1 - 1
Emby.Server.Implementations/Session/HttpSessionController.cs

@@ -62,7 +62,7 @@ namespace Emby.Server.Implementations.Session
         {
         {
             var dict = new Dictionary<string, string>();
             var dict = new Dictionary<string, string>();
 
 
-            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)
             if (command.StartPositionTicks.HasValue)
             {
             {

+ 9 - 7
Emby.Server.Implementations/Session/SessionManager.cs

@@ -327,7 +327,7 @@ namespace Emby.Server.Implementations.Session
         {
         {
             if (string.IsNullOrEmpty(info.MediaSourceId))
             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)
             if (!info.ItemId.Equals(Guid.Empty) && info.Item == null && libraryItem != null)
@@ -463,7 +463,7 @@ namespace Emby.Server.Implementations.Session
                 Client = appName,
                 Client = appName,
                 DeviceId = deviceId,
                 DeviceId = deviceId,
                 ApplicationVersion = appVersion,
                 ApplicationVersion = appVersion,
-                Id = key.GetMD5().ToString("N"),
+                Id = key.GetMD5().ToString("N", CultureInfo.InvariantCulture),
                 ServerId = _appHost.SystemId
                 ServerId = _appHost.SystemId
             };
             };
 
 
@@ -845,7 +845,7 @@ namespace Emby.Server.Implementations.Session
             // Normalize
             // Normalize
             if (string.IsNullOrEmpty(info.MediaSourceId))
             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)
             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<T>(SessionInfo session, string name, T data, CancellationToken cancellationToken)
         private static async Task SendMessageToSession<T>(SessionInfo session, string name, T data, CancellationToken cancellationToken)
         {
         {
             var controllers = session.SessionControllers.ToArray();
             var controllers = session.SessionControllers.ToArray();
-            var messageId = Guid.NewGuid().ToString("N");
+            var messageId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
 
 
             foreach (var controller in controllers)
             foreach (var controller in controllers)
             {
             {
@@ -1041,7 +1041,7 @@ namespace Emby.Server.Implementations.Session
         {
         {
             IEnumerable<Task> GetTasks()
             IEnumerable<Task> GetTasks()
             {
             {
-                var messageId = Guid.NewGuid().ToString("N");
+                var messageId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
                 foreach (var session in sessions)
                 foreach (var session in sessions)
                 {
                 {
                     var controllers = session.SessionControllers;
                     var controllers = session.SessionControllers;
@@ -1234,7 +1234,7 @@ namespace Emby.Server.Implementations.Session
                 AssertCanControl(session, controllingSession);
                 AssertCanControl(session, controllingSession);
                 if (!controllingSession.UserId.Equals(Guid.Empty))
                 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,
                 DeviceId = deviceId,
                 DeviceName = deviceName,
                 DeviceName = deviceName,
                 UserId = user.Id,
                 UserId = user.Id,
-                AccessToken = Guid.NewGuid().ToString("N"),
+                AccessToken = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture),
                 UserName = user.Name
                 UserName = user.Name
             };
             };
 
 
@@ -1822,6 +1822,7 @@ namespace Emby.Server.Implementations.Session
             CheckDisposed();
             CheckDisposed();
 
 
             var sessions = Sessions.Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase));
             var sessions = Sessions.Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase));
+
             return SendMessageToSessions(sessions, name, data, cancellationToken);
             return SendMessageToSessions(sessions, name, data, cancellationToken);
         }
         }
 
 
@@ -1831,6 +1832,7 @@ namespace Emby.Server.Implementations.Session
 
 
             var sessions = Sessions
             var sessions = Sessions
                 .Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase) || IsAdminSession(i));
                 .Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase) || IsAdminSession(i));
+
             return SendMessageToSessions(sessions, name, data, cancellationToken);
             return SendMessageToSessions(sessions, name, data, cancellationToken);
         }
         }
 
 

+ 0 - 647
Emby.Server.Implementations/SocketSharp/RequestMono.cs

@@ -1,647 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Net;
-using System.Text;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Services;
-using Microsoft.Extensions.Primitives;
-using Microsoft.Net.Http.Headers;
-
-namespace Emby.Server.Implementations.SocketSharp
-{
-    public partial class WebSocketSharpRequest : IHttpRequest
-    {
-        internal static string GetParameter(ReadOnlySpan<char> header, string attr)
-        {
-            int ap = header.IndexOf(attr.AsSpan(), StringComparison.Ordinal);
-            if (ap == -1)
-            {
-                return null;
-            }
-
-            ap += attr.Length;
-            if (ap >= header.Length)
-            {
-                return null;
-            }
-
-            char ending = header[ap];
-            if (ending != '"')
-            {
-                ending = ' ';
-            }
-
-            var slice = header.Slice(ap + 1);
-            int end = slice.IndexOf(ending);
-            if (end == -1)
-            {
-                return ending == '"' ? null : header.Slice(ap).ToString();
-            }
-
-            return slice.Slice(0, end - ap - 1).ToString();
-        }
-
-        private async Task LoadMultiPart(WebROCollection form)
-        {
-            string boundary = GetParameter(ContentType.AsSpan(), "; boundary=");
-            if (boundary == null)
-            {
-                return;
-            }
-
-            using (var requestStream = InputStream)
-            {
-                // DB: 30/01/11 - Hack to get around non-seekable stream and received HTTP request
-                // Not ending with \r\n?
-                var ms = new MemoryStream(32 * 1024);
-                await requestStream.CopyToAsync(ms).ConfigureAwait(false);
-
-                var input = ms;
-                ms.WriteByte((byte)'\r');
-                ms.WriteByte((byte)'\n');
-
-                input.Position = 0;
-
-                // Uncomment to debug
-                // var content = new StreamReader(ms).ReadToEnd();
-                // Console.WriteLine(boundary + "::" + content);
-                // input.Position = 0;
-
-                var multi_part = new HttpMultipart(input, boundary, ContentEncoding);
-
-                HttpMultipart.Element e;
-                while ((e = multi_part.ReadNextElement()) != null)
-                {
-                    if (e.Filename == null)
-                    {
-                        byte[] copy = new byte[e.Length];
-
-                        input.Position = e.Start;
-                        await input.ReadAsync(copy, 0, (int)e.Length).ConfigureAwait(false);
-
-                        form.Add(e.Name, (e.Encoding ?? ContentEncoding).GetString(copy, 0, copy.Length));
-                    }
-                    else
-                    {
-                        // We use a substream, as in 2.x we will support large uploads streamed to disk,
-                        files[e.Name] = new HttpPostedFile(e.Filename, e.ContentType, input, e.Start, e.Length);
-                    }
-                }
-            }
-        }
-
-        public async Task<QueryParamCollection> GetFormData()
-        {
-            var form = new WebROCollection();
-            files = new Dictionary<string, HttpPostedFile>();
-
-            if (IsContentType("multipart/form-data"))
-            {
-                await LoadMultiPart(form).ConfigureAwait(false);
-            }
-            else if (IsContentType("application/x-www-form-urlencoded"))
-            {
-                await LoadWwwForm(form).ConfigureAwait(false);
-            }
-
-            if (validate_form && !checked_form)
-            {
-                checked_form = true;
-                ValidateNameValueCollection("Form", form);
-            }
-
-            return form;
-        }
-
-        public string Accept => StringValues.IsNullOrEmpty(request.Headers[HeaderNames.Accept]) ? null : request.Headers[HeaderNames.Accept].ToString();
-
-        public string Authorization => StringValues.IsNullOrEmpty(request.Headers[HeaderNames.Authorization]) ? null : request.Headers[HeaderNames.Authorization].ToString();
-
-        protected bool validate_form { get; set; }
-        protected bool checked_form { get; set; }
-
-        private static void ThrowValidationException(string name, string key, string value)
-        {
-            string v = "\"" + value + "\"";
-            if (v.Length > 20)
-            {
-                v = v.Substring(0, 16) + "...\"";
-            }
-
-            string msg = string.Format(
-                CultureInfo.InvariantCulture,
-                "A potentially dangerous Request.{0} value was detected from the client ({1}={2}).",
-                name,
-                key,
-                v);
-
-            throw new Exception(msg);
-        }
-
-        private static void ValidateNameValueCollection(string name, QueryParamCollection coll)
-        {
-            if (coll == null)
-            {
-                return;
-            }
-
-            foreach (var pair in coll)
-            {
-                var key = pair.Name;
-                var val = pair.Value;
-                if (val != null && val.Length > 0 && IsInvalidString(val))
-                {
-                    ThrowValidationException(name, key, val);
-                }
-            }
-        }
-
-        internal static bool IsInvalidString(string val)
-            => IsInvalidString(val, out var validationFailureIndex);
-
-        internal static bool IsInvalidString(string val, out int validationFailureIndex)
-        {
-            validationFailureIndex = 0;
-
-            int len = val.Length;
-            if (len < 2)
-            {
-                return false;
-            }
-
-            char current = val[0];
-            for (int idx = 1; idx < len; idx++)
-            {
-                char next = val[idx];
-
-                // See http://secunia.com/advisories/14325
-                if (current == '<' || current == '\xff1c')
-                {
-                    if (next == '!' || next < ' '
-                        || (next >= 'a' && next <= 'z')
-                        || (next >= 'A' && next <= 'Z'))
-                    {
-                        validationFailureIndex = idx - 1;
-                        return true;
-                    }
-                }
-                else if (current == '&' && next == '#')
-                {
-                    validationFailureIndex = idx - 1;
-                    return true;
-                }
-
-                current = next;
-            }
-
-            return false;
-        }
-
-        private bool IsContentType(string ct)
-        {
-            if (ContentType == null)
-            {
-                return false;
-            }
-
-            return ContentType.StartsWith(ct, StringComparison.OrdinalIgnoreCase);
-        }
-
-        private async Task LoadWwwForm(WebROCollection form)
-        {
-            using (var input = InputStream)
-            {
-                using (var ms = new MemoryStream())
-                {
-                    await input.CopyToAsync(ms).ConfigureAwait(false);
-                    ms.Position = 0;
-
-                    using (var s = new StreamReader(ms, ContentEncoding))
-                    {
-                        var key = new StringBuilder();
-                        var value = new StringBuilder();
-                        int c;
-
-                        while ((c = s.Read()) != -1)
-                        {
-                            if (c == '=')
-                            {
-                                value.Length = 0;
-                                while ((c = s.Read()) != -1)
-                                {
-                                    if (c == '&')
-                                    {
-                                        AddRawKeyValue(form, key, value);
-                                        break;
-                                    }
-                                    else
-                                    {
-                                        value.Append((char)c);
-                                    }
-                                }
-
-                                if (c == -1)
-                                {
-                                    AddRawKeyValue(form, key, value);
-                                    return;
-                                }
-                            }
-                            else if (c == '&')
-                            {
-                                AddRawKeyValue(form, key, value);
-                            }
-                            else
-                            {
-                                key.Append((char)c);
-                            }
-                        }
-
-                        if (c == -1)
-                        {
-                            AddRawKeyValue(form, key, value);
-                        }
-                    }
-                }
-            }
-        }
-
-        private static void AddRawKeyValue(WebROCollection form, StringBuilder key, StringBuilder value)
-        {
-            form.Add(WebUtility.UrlDecode(key.ToString()), WebUtility.UrlDecode(value.ToString()));
-
-            key.Length = 0;
-            value.Length = 0;
-        }
-
-        private Dictionary<string, HttpPostedFile> files;
-
-        private class WebROCollection : QueryParamCollection
-        {
-            public override string ToString()
-            {
-                var result = new StringBuilder();
-                foreach (var pair in this)
-                {
-                    if (result.Length > 0)
-                    {
-                        result.Append('&');
-                    }
-
-                    var key = pair.Name;
-                    if (key != null && key.Length > 0)
-                    {
-                        result.Append(key);
-                        result.Append('=');
-                    }
-
-                    result.Append(pair.Value);
-                }
-
-                return result.ToString();
-            }
-        }
-        private class HttpMultipart
-        {
-
-            public class Element
-            {
-                public string ContentType { get; set; }
-
-                public string Name { get; set; }
-
-                public string Filename { get; set; }
-
-                public Encoding Encoding { get; set; }
-
-                public long Start { get; set; }
-
-                public long Length { get; set; }
-
-                public override string ToString()
-                {
-                    return "ContentType " + ContentType + ", Name " + Name + ", Filename " + Filename + ", Start " +
-                        Start.ToString(CultureInfo.CurrentCulture) + ", Length " + Length.ToString(CultureInfo.CurrentCulture);
-                }
-            }
-
-            private const byte LF = (byte)'\n';
-
-            private const byte CR = (byte)'\r';
-
-            private Stream data;
-
-            private string boundary;
-
-            private byte[] boundaryBytes;
-
-            private byte[] buffer;
-
-            private bool atEof;
-
-            private Encoding encoding;
-
-            private StringBuilder sb;
-
-            // See RFC 2046
-            // In the case of multipart entities, in which one or more different
-            // sets of data are combined in a single body, a "multipart" media type
-            // field must appear in the entity's header.  The body must then contain
-            // one or more body parts, each preceded by a boundary delimiter line,
-            // and the last one followed by a closing boundary delimiter line.
-            // After its boundary delimiter line, each body part then consists of a
-            // header area, a blank line, and a body area.  Thus a body part is
-            // similar to an RFC 822 message in syntax, but different in meaning.
-
-            public HttpMultipart(Stream data, string b, Encoding encoding)
-            {
-                this.data = data;
-                boundary = b;
-                boundaryBytes = encoding.GetBytes(b);
-                buffer = new byte[boundaryBytes.Length + 2]; // CRLF or '--'
-                this.encoding = encoding;
-                sb = new StringBuilder();
-            }
-
-            public Element ReadNextElement()
-            {
-                if (atEof || ReadBoundary())
-                {
-                    return null;
-                }
-
-                var elem = new Element();
-                ReadOnlySpan<char> header;
-                while ((header = ReadLine().AsSpan()).Length != 0)
-                {
-                    if (header.StartsWith("Content-Disposition:".AsSpan(), StringComparison.OrdinalIgnoreCase))
-                    {
-                        elem.Name = GetContentDispositionAttribute(header, "name");
-                        elem.Filename = StripPath(GetContentDispositionAttributeWithEncoding(header, "filename"));
-                    }
-                    else if (header.StartsWith("Content-Type:".AsSpan(), StringComparison.OrdinalIgnoreCase))
-                    {
-                        elem.ContentType = header.Slice("Content-Type:".Length).Trim().ToString();
-                        elem.Encoding = GetEncoding(elem.ContentType);
-                    }
-                }
-
-                long start = data.Position;
-                elem.Start = start;
-                long pos = MoveToNextBoundary();
-                if (pos == -1)
-                {
-                    return null;
-                }
-
-                elem.Length = pos - start;
-                return elem;
-            }
-
-            private string ReadLine()
-            {
-                // CRLF or LF are ok as line endings.
-                bool got_cr = false;
-                int b = 0;
-                sb.Length = 0;
-                while (true)
-                {
-                    b = data.ReadByte();
-                    if (b == -1)
-                    {
-                        return null;
-                    }
-
-                    if (b == LF)
-                    {
-                        break;
-                    }
-
-                    got_cr = b == CR;
-                    sb.Append((char)b);
-                }
-
-                if (got_cr)
-                {
-                    sb.Length--;
-                }
-
-                return sb.ToString();
-            }
-
-            private static string GetContentDispositionAttribute(ReadOnlySpan<char> l, string name)
-            {
-                int idx = l.IndexOf((name + "=\"").AsSpan(), StringComparison.Ordinal);
-                if (idx < 0)
-                {
-                    return null;
-                }
-
-                int begin = idx + name.Length + "=\"".Length;
-                int end = l.Slice(begin).IndexOf('"');
-                if (end < 0)
-                {
-                    return null;
-                }
-
-                if (begin == end)
-                {
-                    return string.Empty;
-                }
-
-                return l.Slice(begin, end - begin).ToString();
-            }
-
-            private string GetContentDispositionAttributeWithEncoding(ReadOnlySpan<char> l, string name)
-            {
-                int idx = l.IndexOf((name + "=\"").AsSpan(), StringComparison.Ordinal);
-                if (idx < 0)
-                {
-                    return null;
-                }
-
-                int begin = idx + name.Length + "=\"".Length;
-                int end = l.Slice(begin).IndexOf('"');
-                if (end < 0)
-                {
-                    return null;
-                }
-
-                if (begin == end)
-                {
-                    return string.Empty;
-                }
-
-                ReadOnlySpan<char> temp = l.Slice(begin, end - begin);
-                byte[] source = new byte[temp.Length];
-                for (int i = temp.Length - 1; i >= 0; i--)
-                {
-                    source[i] = (byte)temp[i];
-                }
-
-                return encoding.GetString(source, 0, source.Length);
-            }
-
-            private bool ReadBoundary()
-            {
-                try
-                {
-                    string line;
-                    do
-                    {
-                        line = ReadLine();
-                    }
-                    while (line.Length == 0);
-
-                    if (line[0] != '-' || line[1] != '-')
-                    {
-                        return false;
-                    }
-
-                    if (!line.EndsWith(boundary, StringComparison.Ordinal))
-                    {
-                        return true;
-                    }
-                }
-                catch
-                {
-
-                }
-
-                return false;
-            }
-
-            private static bool CompareBytes(byte[] orig, byte[] other)
-            {
-                for (int i = orig.Length - 1; i >= 0; i--)
-                {
-                    if (orig[i] != other[i])
-                    {
-                        return false;
-                    }
-                }
-
-                return true;
-            }
-
-            private long MoveToNextBoundary()
-            {
-                long retval = 0;
-                bool got_cr = false;
-
-                int state = 0;
-                int c = data.ReadByte();
-                while (true)
-                {
-                    if (c == -1)
-                    {
-                        return -1;
-                    }
-
-                    if (state == 0 && c == LF)
-                    {
-                        retval = data.Position - 1;
-                        if (got_cr)
-                        {
-                            retval--;
-                        }
-
-                        state = 1;
-                        c = data.ReadByte();
-                    }
-                    else if (state == 0)
-                    {
-                        got_cr = c == CR;
-                        c = data.ReadByte();
-                    }
-                    else if (state == 1 && c == '-')
-                    {
-                        c = data.ReadByte();
-                        if (c == -1)
-                        {
-                            return -1;
-                        }
-
-                        if (c != '-')
-                        {
-                            state = 0;
-                            got_cr = false;
-                            continue; // no ReadByte() here
-                        }
-
-                        int nread = data.Read(buffer, 0, buffer.Length);
-                        int bl = buffer.Length;
-                        if (nread != bl)
-                        {
-                            return -1;
-                        }
-
-                        if (!CompareBytes(boundaryBytes, buffer))
-                        {
-                            state = 0;
-                            data.Position = retval + 2;
-                            if (got_cr)
-                            {
-                                data.Position++;
-                                got_cr = false;
-                            }
-
-                            c = data.ReadByte();
-                            continue;
-                        }
-
-                        if (buffer[bl - 2] == '-' && buffer[bl - 1] == '-')
-                        {
-                            atEof = true;
-                        }
-                        else if (buffer[bl - 2] != CR || buffer[bl - 1] != LF)
-                        {
-                            state = 0;
-                            data.Position = retval + 2;
-                            if (got_cr)
-                            {
-                                data.Position++;
-                                got_cr = false;
-                            }
-
-                            c = data.ReadByte();
-                            continue;
-                        }
-
-                        data.Position = retval + 2;
-                        if (got_cr)
-                        {
-                            data.Position++;
-                        }
-
-                        break;
-                    }
-                    else
-                    {
-                        // state == 1
-                        state = 0; // no ReadByte() here
-                    }
-                }
-
-                return retval;
-            }
-
-            private static string StripPath(string path)
-            {
-                if (path == null || path.Length == 0)
-                {
-                    return path;
-                }
-
-                if (path.IndexOf(":\\", StringComparison.Ordinal) != 1
-                    && !path.StartsWith("\\\\", StringComparison.Ordinal))
-                {
-                    return path;
-                }
-
-                return path.Substring(path.LastIndexOf('\\') + 1);
-            }
-        }
-    }
-}

+ 58 - 140
Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs

@@ -1,57 +1,56 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
-using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Net;
 using System.Net;
 using System.Linq;
 using System.Linq;
-using System.Text;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net;
-using MediaBrowser.Model.Services;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Http.Extensions;
 using Microsoft.AspNetCore.Http.Extensions;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Primitives;
 using Microsoft.Extensions.Primitives;
 using Microsoft.Net.Http.Headers;
 using Microsoft.Net.Http.Headers;
-using IHttpFile = MediaBrowser.Model.Services.IHttpFile;
 using IHttpRequest = MediaBrowser.Model.Services.IHttpRequest;
 using IHttpRequest = MediaBrowser.Model.Services.IHttpRequest;
-using IResponse = MediaBrowser.Model.Services.IResponse;
 
 
 namespace Emby.Server.Implementations.SocketSharp
 namespace Emby.Server.Implementations.SocketSharp
 {
 {
     public partial class WebSocketSharpRequest : IHttpRequest
     public partial class WebSocketSharpRequest : IHttpRequest
     {
     {
-        private readonly HttpRequest request;
+        public const string FormUrlEncoded = "application/x-www-form-urlencoded";
+        public const string MultiPartFormData = "multipart/form-data";
+        public const string Soap11 = "text/xml; charset=utf-8";
 
 
-        public WebSocketSharpRequest(HttpRequest httpContext, HttpResponse response, string operationName, ILogger logger)
+        private string _remoteIp;
+        private Dictionary<string, object> _items;
+        private string _responseContentType;
+
+        public WebSocketSharpRequest(HttpRequest httpRequest, HttpResponse httpResponse, string operationName, ILogger logger)
         {
         {
             this.OperationName = operationName;
             this.OperationName = operationName;
-            this.request = httpContext;
-            this.Response = new WebSocketSharpResponse(logger, response);
+            this.Request = httpRequest;
+            this.Response = httpResponse;
         }
         }
 
 
-        public HttpRequest HttpRequest => request;
+        public string Accept => StringValues.IsNullOrEmpty(Request.Headers[HeaderNames.Accept]) ? null : Request.Headers[HeaderNames.Accept].ToString();
 
 
-        public IResponse Response { get; }
+        public string Authorization => StringValues.IsNullOrEmpty(Request.Headers[HeaderNames.Authorization]) ? null : Request.Headers[HeaderNames.Authorization].ToString();
 
 
-        public string OperationName { get; set; }
+        public HttpRequest Request { get; }
 
 
-        public object Dto { get; set; }
+        public HttpResponse Response { get; }
 
 
-        public string RawUrl => request.GetEncodedPathAndQuery();
+        public string OperationName { get; set; }
 
 
-        public string AbsoluteUri => request.GetDisplayUrl().TrimEnd('/');
-        // Header[name] returns "" when undefined
+        public string RawUrl => Request.GetEncodedPathAndQuery();
 
 
-        private string GetHeader(string name) => request.Headers[name].ToString();
+        public string AbsoluteUri => Request.GetDisplayUrl().TrimEnd('/');
 
 
-        private string remoteIp;
         public string RemoteIp
         public string RemoteIp
         {
         {
             get
             get
             {
             {
-                if (remoteIp != null)
+                if (_remoteIp != null)
                 {
                 {
-                    return remoteIp;
+                    return _remoteIp;
                 }
                 }
 
 
                 IPAddress ip;
                 IPAddress ip;
@@ -62,14 +61,51 @@ namespace Emby.Server.Implementations.SocketSharp
                 {
                 {
                     if (!IPAddress.TryParse(GetHeader(CustomHeaderNames.XRealIP), out ip))
                     if (!IPAddress.TryParse(GetHeader(CustomHeaderNames.XRealIP), out ip))
                     {
                     {
-                        ip = request.HttpContext.Connection.RemoteIpAddress;
+                        ip = Request.HttpContext.Connection.RemoteIpAddress;
                     }
                     }
                 }
                 }
 
 
-                return remoteIp = NormalizeIp(ip).ToString();
+                return _remoteIp = NormalizeIp(ip).ToString();
             }
             }
         }
         }
 
 
+        public string[] AcceptTypes => Request.Headers.GetCommaSeparatedValues(HeaderNames.Accept);
+
+        public Dictionary<string, object> Items => _items ?? (_items = new Dictionary<string, object>());
+
+        public string ResponseContentType
+        {
+            get =>
+                _responseContentType
+                ?? (_responseContentType = GetResponseContentType(Request));
+            set => this._responseContentType = value;
+        }
+
+        public string PathInfo => Request.Path.Value;
+
+        public string UserAgent => Request.Headers[HeaderNames.UserAgent];
+
+        public IHeaderDictionary Headers => Request.Headers;
+
+        public IQueryCollection QueryString => Request.Query;
+
+        public bool IsLocal => Request.HttpContext.Connection.LocalIpAddress.Equals(Request.HttpContext.Connection.RemoteIpAddress);
+
+
+        public string HttpMethod => Request.Method;
+
+        public string Verb => HttpMethod;
+
+        public string ContentType => Request.ContentType;
+
+        public Uri UrlReferrer => Request.GetTypedHeaders().Referer;
+
+        public Stream InputStream => Request.Body;
+
+        public long ContentLength => Request.ContentLength ?? 0;
+
+        private string GetHeader(string name) => Request.Headers[name].ToString();
+
         private static IPAddress NormalizeIp(IPAddress ip)
         private static IPAddress NormalizeIp(IPAddress ip)
         {
         {
             if (ip.IsIPv4MappedToIPv6)
             if (ip.IsIPv4MappedToIPv6)
@@ -80,22 +116,6 @@ namespace Emby.Server.Implementations.SocketSharp
             return ip;
             return ip;
         }
         }
 
 
-        public string[] AcceptTypes => request.Headers.GetCommaSeparatedValues(HeaderNames.Accept);
-
-        private Dictionary<string, object> items;
-        public Dictionary<string, object> Items => items ?? (items = new Dictionary<string, object>());
-
-        private string responseContentType;
-        public string ResponseContentType
-        {
-            get =>
-                responseContentType
-                ?? (responseContentType = GetResponseContentType(HttpRequest));
-            set => this.responseContentType = value;
-        }
-
-        public const string FormUrlEncoded = "application/x-www-form-urlencoded";
-        public const string MultiPartFormData = "multipart/form-data";
         public static string GetResponseContentType(HttpRequest httpReq)
         public static string GetResponseContentType(HttpRequest httpReq)
         {
         {
             var specifiedContentType = GetQueryStringContentType(httpReq);
             var specifiedContentType = GetQueryStringContentType(httpReq);
@@ -152,8 +172,6 @@ namespace Emby.Server.Implementations.SocketSharp
             return serverDefaultContentType;
             return serverDefaultContentType;
         }
         }
 
 
-        public const string Soap11 = "text/xml; charset=utf-8";
-
         public static bool HasAnyOfContentTypes(HttpRequest request, params string[] contentTypes)
         public static bool HasAnyOfContentTypes(HttpRequest request, params string[] contentTypes)
         {
         {
             if (contentTypes == null || request.ContentType == null)
             if (contentTypes == null || request.ContentType == null)
@@ -224,105 +242,5 @@ namespace Emby.Server.Implementations.SocketSharp
             var pos = strVal.IndexOf(needle);
             var pos = strVal.IndexOf(needle);
             return pos == -1 ? strVal : strVal.Slice(0, pos);
             return pos == -1 ? strVal : strVal.Slice(0, pos);
         }
         }
-
-        public string PathInfo => this.request.Path.Value;
-
-        public string UserAgent => request.Headers[HeaderNames.UserAgent];
-
-        public IHeaderDictionary Headers => request.Headers;
-
-        public IQueryCollection QueryString => request.Query;
-
-        public bool IsLocal => string.Equals(request.HttpContext.Connection.LocalIpAddress.ToString(), request.HttpContext.Connection.RemoteIpAddress.ToString());
-
-        private string httpMethod;
-        public string HttpMethod =>
-            httpMethod
-            ?? (httpMethod = request.Method);
-
-        public string Verb => HttpMethod;
-
-        public string ContentType => request.ContentType;
-
-        private Encoding ContentEncoding
-        {
-            get
-            {
-                // TODO is this necessary?
-                if (UserAgent != null && CultureInfo.InvariantCulture.CompareInfo.IsPrefix(UserAgent, "UP"))
-                {
-                    string postDataCharset = Headers["x-up-devcap-post-charset"];
-                    if (!string.IsNullOrEmpty(postDataCharset))
-                    {
-                        try
-                        {
-                            return Encoding.GetEncoding(postDataCharset);
-                        }
-                        catch (ArgumentException)
-                        {
-                        }
-                    }
-                }
-
-                return request.GetTypedHeaders().ContentType.Encoding ?? Encoding.UTF8;
-            }
-        }
-
-        public Uri UrlReferrer => request.GetTypedHeaders().Referer;
-
-        public static Encoding GetEncoding(string contentTypeHeader)
-        {
-            var param = GetParameter(contentTypeHeader.AsSpan(), "charset=");
-            if (param == null)
-            {
-                return null;
-            }
-
-            try
-            {
-                return Encoding.GetEncoding(param);
-            }
-            catch (ArgumentException)
-            {
-                return null;
-            }
-        }
-
-        public Stream InputStream => request.Body;
-
-        public long ContentLength => request.ContentLength ?? 0;
-
-        private IHttpFile[] httpFiles;
-        public IHttpFile[] Files
-        {
-            get
-            {
-                if (httpFiles != null)
-                {
-                    return httpFiles;
-                }
-
-                if (files == null)
-                {
-                    return httpFiles = Array.Empty<IHttpFile>();
-                }
-
-                var values = files.Values;
-                httpFiles = new IHttpFile[values.Count];
-                for (int i = 0; i < values.Count; i++)
-                {
-                    var reqFile = values.ElementAt(i);
-                    httpFiles[i] = new HttpFile
-                    {
-                        ContentType = reqFile.ContentType,
-                        ContentLength = reqFile.ContentLength,
-                        FileName = reqFile.FileName,
-                        InputStream = reqFile.InputStream,
-                    };
-                }
-
-                return httpFiles;
-            }
-        }
     }
     }
 }
 }

+ 0 - 98
Emby.Server.Implementations/SocketSharp/WebSocketSharpResponse.cs

@@ -1,98 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Runtime.InteropServices;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Services;
-using Microsoft.AspNetCore.Http;
-using Microsoft.Extensions.Logging;
-using IRequest = MediaBrowser.Model.Services.IRequest;
-
-namespace Emby.Server.Implementations.SocketSharp
-{
-    public class WebSocketSharpResponse : IResponse
-    {
-        private readonly ILogger _logger;
-
-        public WebSocketSharpResponse(ILogger logger, HttpResponse response)
-        {
-            _logger = logger;
-            OriginalResponse = response;
-        }
-
-        public HttpResponse OriginalResponse { get; }
-
-        public int StatusCode
-        {
-            get => OriginalResponse.StatusCode;
-            set => OriginalResponse.StatusCode = value;
-        }
-
-        public string StatusDescription { get; set; }
-
-        public string ContentType
-        {
-            get => OriginalResponse.ContentType;
-            set => OriginalResponse.ContentType = value;
-        }
-
-        public void AddHeader(string name, string value)
-        {
-            if (string.Equals(name, "Content-Type", StringComparison.OrdinalIgnoreCase))
-            {
-                ContentType = value;
-                return;
-            }
-
-            OriginalResponse.Headers.Add(name, value);
-        }
-
-        public void Redirect(string url)
-        {
-            OriginalResponse.Redirect(url);
-        }
-
-        public Stream OutputStream => OriginalResponse.Body;
-
-        public bool SendChunked { get; set; }
-
-        const int StreamCopyToBufferSize = 81920;
-        public async Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, IFileSystem fileSystem, IStreamHelper streamHelper, CancellationToken cancellationToken)
-        {
-            var allowAsync = !RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
-
-            //if (count <= 0)
-            //{
-            //    allowAsync = true;
-            //}
-
-            var fileOpenOptions = FileOpenOptions.SequentialScan;
-
-            if (allowAsync)
-            {
-                fileOpenOptions |= FileOpenOptions.Asynchronous;
-            }
-
-            // use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039
-
-            using (var fs = fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, fileShareMode, fileOpenOptions))
-            {
-                if (offset > 0)
-                {
-                    fs.Position = offset;
-                }
-
-                if (count > 0)
-                {
-                    await streamHelper.CopyToAsync(fs, OutputStream, count, cancellationToken).ConfigureAwait(false);
-                }
-                else
-                {
-                    await fs.CopyToAsync(OutputStream, StreamCopyToBufferSize, cancellationToken).ConfigureAwait(false);
-                }
-            }
-        }
-    }
-}

+ 2 - 1
Emby.Server.Implementations/TV/TVSeriesManager.cs

@@ -1,5 +1,6 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Globalization;
 using System.Linq;
 using System.Linq;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Dto;
@@ -73,7 +74,7 @@ namespace Emby.Server.Implementations.TV
             {
             {
                 parents = _libraryManager.GetUserRootFolder().GetChildren(user, true)
                 parents = _libraryManager.GetUserRootFolder().GetChildren(user, true)
                    .Where(i => i is Folder)
                    .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();
                    .ToArray();
             }
             }
 
 

+ 8 - 7
Emby.Server.Implementations/Udp/UdpServer.cs

@@ -1,6 +1,7 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
+using System.Net;
 using System.Text;
 using System.Text;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
@@ -25,7 +26,7 @@ namespace Emby.Server.Implementations.Udp
 
 
         private bool _isDisposed;
         private bool _isDisposed;
 
 
-        private readonly List<Tuple<string, bool, Func<string, IpEndPointInfo, Encoding, CancellationToken, Task>>> _responders = new List<Tuple<string, bool, Func<string, IpEndPointInfo, Encoding, CancellationToken, Task>>>();
+        private readonly List<Tuple<string, bool, Func<string, IPEndPoint, Encoding, CancellationToken, Task>>> _responders = new List<Tuple<string, bool, Func<string, IPEndPoint, Encoding, CancellationToken, Task>>>();
 
 
         private readonly IServerApplicationHost _appHost;
         private readonly IServerApplicationHost _appHost;
         private readonly IJsonSerializer _json;
         private readonly IJsonSerializer _json;
@@ -43,9 +44,9 @@ namespace Emby.Server.Implementations.Udp
             AddMessageResponder("who is JellyfinServer?", true, RespondToV2Message);
             AddMessageResponder("who is JellyfinServer?", true, RespondToV2Message);
         }
         }
 
 
-        private void AddMessageResponder(string message, bool isSubstring, Func<string, IpEndPointInfo, Encoding, CancellationToken, Task> responder)
+        private void AddMessageResponder(string message, bool isSubstring, Func<string, IPEndPoint, Encoding, CancellationToken, Task> responder)
         {
         {
-            _responders.Add(new Tuple<string, bool, Func<string, IpEndPointInfo, Encoding, CancellationToken, Task>>(message, isSubstring, responder));
+            _responders.Add(new Tuple<string, bool, Func<string, IPEndPoint, Encoding, CancellationToken, Task>>(message, isSubstring, responder));
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -83,7 +84,7 @@ namespace Emby.Server.Implementations.Udp
             }
             }
         }
         }
 
 
-        private Tuple<string, Tuple<string, bool, Func<string, IpEndPointInfo, Encoding, CancellationToken, Task>>> GetResponder(byte[] buffer, int bytesReceived, Encoding encoding)
+        private Tuple<string, Tuple<string, bool, Func<string, IPEndPoint, Encoding, CancellationToken, Task>>> GetResponder(byte[] buffer, int bytesReceived, Encoding encoding)
         {
         {
             var text = encoding.GetString(buffer, 0, bytesReceived);
             var text = encoding.GetString(buffer, 0, bytesReceived);
             var responder = _responders.FirstOrDefault(i =>
             var responder = _responders.FirstOrDefault(i =>
@@ -99,10 +100,10 @@ namespace Emby.Server.Implementations.Udp
             {
             {
                 return null;
                 return null;
             }
             }
-            return new Tuple<string, Tuple<string, bool, Func<string, IpEndPointInfo, Encoding, CancellationToken, Task>>>(text, responder);
+            return new Tuple<string, Tuple<string, bool, Func<string, IPEndPoint, Encoding, CancellationToken, Task>>>(text, responder);
         }
         }
 
 
-        private async Task RespondToV2Message(string messageText, IpEndPointInfo endpoint, Encoding encoding, CancellationToken cancellationToken)
+        private async Task RespondToV2Message(string messageText, IPEndPoint endpoint, Encoding encoding, CancellationToken cancellationToken)
         {
         {
             var parts = messageText.Split('|');
             var parts = messageText.Split('|');
 
 
@@ -254,7 +255,7 @@ namespace Emby.Server.Implementations.Udp
             }
             }
         }
         }
 
 
-        public async Task SendAsync(byte[] bytes, IpEndPointInfo remoteEndPoint, CancellationToken cancellationToken)
+        public async Task SendAsync(byte[] bytes, IPEndPoint remoteEndPoint, CancellationToken cancellationToken)
         {
         {
             if (_isDisposed)
             if (_isDisposed)
             {
             {

+ 6 - 7
Jellyfin.Server/Program.cs

@@ -18,7 +18,6 @@ using Jellyfin.Drawing.Skia;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Model.Globalization;
 using MediaBrowser.Model.Globalization;
-using MediaBrowser.Model.IO;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
@@ -41,12 +40,12 @@ namespace Jellyfin.Server
             // For backwards compatibility.
             // For backwards compatibility.
             // Modify any input arguments now which start with single-hyphen to POSIX standard
             // Modify any input arguments now which start with single-hyphen to POSIX standard
             // double-hyphen to allow parsing by CommandLineParser package.
             // double-hyphen to allow parsing by CommandLineParser package.
-            const string pattern = @"^(-[^-\s]{2})"; // Match -xx, not -x, not --xx, not xx
-            const string substitution = @"-$1"; // Prepend with additional single-hyphen
-            var regex = new Regex(pattern);
+            const string Pattern = @"^(-[^-\s]{2})"; // Match -xx, not -x, not --xx, not xx
+            const string Substitution = @"-$1"; // Prepend with additional single-hyphen
+            var regex = new Regex(Pattern);
             for (var i = 0; i < args.Length; i++)
             for (var i = 0; i < args.Length; i++)
             {
             {
-                args[i] = regex.Replace(args[i], substitution);
+                args[i] = regex.Replace(args[i], Substitution);
             }
             }
 
 
             // Parse the command line arguments and either start the app or exit indicating error
             // Parse the command line arguments and either start the app or exit indicating error
@@ -134,7 +133,7 @@ namespace Jellyfin.Server
             Batteries_V2.Init();
             Batteries_V2.Init();
             if (raw.sqlite3_enable_shared_cache(1) != raw.SQLITE_OK)
             if (raw.sqlite3_enable_shared_cache(1) != raw.SQLITE_OK)
             {
             {
-                Console.WriteLine("WARN: Failed to enable shared cache for SQLite");
+                _logger.LogWarning("Failed to enable shared cache for SQLite");
             }
             }
 
 
             using (var appHost = new CoreAppHost(
             using (var appHost = new CoreAppHost(
@@ -143,7 +142,7 @@ namespace Jellyfin.Server
                 options,
                 options,
                 new ManagedFileSystem(_loggerFactory.CreateLogger<ManagedFileSystem>(), appPaths),
                 new ManagedFileSystem(_loggerFactory.CreateLogger<ManagedFileSystem>(), appPaths),
                 new NullImageEncoder(),
                 new NullImageEncoder(),
-                new NetworkManager(_loggerFactory),
+                new NetworkManager(_loggerFactory.CreateLogger<NetworkManager>()),
                 appConfig))
                 appConfig))
             {
             {
                 await appHost.InitAsync(new ServiceCollection()).ConfigureAwait(false);
                 await appHost.InitAsync(new ServiceCollection()).ConfigureAwait(false);

+ 8 - 7
MediaBrowser.Api/Devices/DeviceService.cs

@@ -133,12 +133,15 @@ namespace MediaBrowser.Api.Devices
             var album = Request.QueryString["Album"];
             var album = Request.QueryString["Album"];
             var id = Request.QueryString["Id"];
             var id = Request.QueryString["Id"];
             var name = Request.QueryString["Name"];
             var name = Request.QueryString["Name"];
+            var req = Request.Response.HttpContext.Request;
 
 
-            if (Request.ContentType.IndexOf("multi", StringComparison.OrdinalIgnoreCase) == -1)
+            if (req.HasFormContentType)
             {
             {
-                return _deviceManager.AcceptCameraUpload(deviceId, request.RequestStream, new LocalFileInfo
+                var file = req.Form.Files.Count == 0 ? null : req.Form.Files[0];
+
+                return _deviceManager.AcceptCameraUpload(deviceId, file.OpenReadStream(), new LocalFileInfo
                 {
                 {
-                    MimeType = Request.ContentType,
+                    MimeType = file.ContentType,
                     Album = album,
                     Album = album,
                     Name = name,
                     Name = name,
                     Id = id
                     Id = id
@@ -146,11 +149,9 @@ namespace MediaBrowser.Api.Devices
             }
             }
             else
             else
             {
             {
-                var file = Request.Files.Length == 0 ? null : Request.Files[0];
-
-                return _deviceManager.AcceptCameraUpload(deviceId, file.InputStream, new LocalFileInfo
+                return _deviceManager.AcceptCameraUpload(deviceId, request.RequestStream, new LocalFileInfo
                 {
                 {
-                    MimeType = file.ContentType,
+                    MimeType = Request.ContentType,
                     Album = album,
                     Album = album,
                     Name = name,
                     Name = name,
                     Id = id
                     Id = id

+ 2 - 1
MediaBrowser.Api/Images/ImageService.cs

@@ -1,5 +1,6 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Threading;
 using System.Threading;
@@ -537,7 +538,7 @@ namespace MediaBrowser.Api.Images
 
 
                 if (item == null)
                 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)));
                 }
                 }
             }
             }
 
 

Some files were not shown because too many files changed in this diff