From 07791d46a571d3d6eed23e98ec0fe1c46ea0d37f Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 23 Oct 2016 15:14:57 -0400 Subject: rework scheduled tasks in preparation of common project going portable --- MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs | 1 + 1 file changed, 1 insertion(+) (limited to 'MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs') diff --git a/MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs b/MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs index 8a7c14eb6..b2cbf2387 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs @@ -3,6 +3,7 @@ using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Net; +using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; using ServiceStack.Logging; -- cgit v1.2.3 From 4b51233cc8faeea344661a2a3427579e534d8ea4 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 26 Oct 2016 02:01:42 -0400 Subject: update plugin interfaces --- MediaBrowser.Api/BaseApiService.cs | 2 +- .../MediaBrowser.Controller.csproj | 1 - MediaBrowser.Controller/Net/IHttpServer.cs | 3 +- MediaBrowser.Controller/Net/IRestfulService.cs | 12 ----- MediaBrowser.Controller/Net/LoggedAttribute.cs | 40 +++++--------- MediaBrowser.Model/MediaBrowser.Model.csproj | 4 ++ MediaBrowser.Model/Plugins/IHasWebPages.cs | 9 ++++ MediaBrowser.Model/Plugins/PluginPageInfo.cs | 9 ++++ MediaBrowser.Model/Reflection/IAssemblyInfo.cs | 10 ++++ MediaBrowser.Model/Services/IRequestFilter.cs | 8 +++ .../MediaBrowser.Providers.csproj | 3 -- .../HttpServer/HttpListenerHost.cs | 17 ++++-- .../HttpServer/ServerFactory.cs | 11 ++-- .../HttpServer/SwaggerService.cs | 2 +- .../MediaBrowser.Server.Implementations.csproj | 1 + .../Reflection/AssemblyInfo.cs | 14 +++++ .../ApplicationHost.cs | 6 ++- .../Api/ConfigurationPageInfo.cs | 12 ++++- MediaBrowser.WebDashboard/Api/DashboardService.cs | 62 ++++++++++++++++++++-- MediaBrowser.WebDashboard/ServerEntryPoint.cs | 5 +- Nuget/MediaBrowser.Common.Internal.nuspec | 4 +- Nuget/MediaBrowser.Common.nuspec | 2 +- Nuget/MediaBrowser.Server.Core.nuspec | 5 +- 23 files changed, 173 insertions(+), 69 deletions(-) delete mode 100644 MediaBrowser.Controller/Net/IRestfulService.cs create mode 100644 MediaBrowser.Model/Plugins/IHasWebPages.cs create mode 100644 MediaBrowser.Model/Plugins/PluginPageInfo.cs create mode 100644 MediaBrowser.Model/Reflection/IAssemblyInfo.cs create mode 100644 MediaBrowser.Model/Services/IRequestFilter.cs create mode 100644 MediaBrowser.Server.Implementations/Reflection/AssemblyInfo.cs (limited to 'MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs') diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs index 0ef06804d..4810d4e9c 100644 --- a/MediaBrowser.Api/BaseApiService.cs +++ b/MediaBrowser.Api/BaseApiService.cs @@ -17,7 +17,7 @@ namespace MediaBrowser.Api /// /// Class BaseApiService /// - public class BaseApiService : IHasResultFactory, IRestfulService, IHasSession + public class BaseApiService : IHasResultFactory, IService, IHasSession { /// /// Gets or sets the logger. diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 7467fc39e..cf20f7bbb 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -208,7 +208,6 @@ - diff --git a/MediaBrowser.Controller/Net/IHttpServer.cs b/MediaBrowser.Controller/Net/IHttpServer.cs index 97c5dd31b..8c503c199 100644 --- a/MediaBrowser.Controller/Net/IHttpServer.cs +++ b/MediaBrowser.Controller/Net/IHttpServer.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using MediaBrowser.Model.Services; namespace MediaBrowser.Controller.Net { @@ -46,7 +47,7 @@ namespace MediaBrowser.Controller.Net /// /// Inits this instance. /// - void Init(IEnumerable services); + void Init(IEnumerable services); /// /// If set, all requests will respond with this message diff --git a/MediaBrowser.Controller/Net/IRestfulService.cs b/MediaBrowser.Controller/Net/IRestfulService.cs deleted file mode 100644 index 01eefe792..000000000 --- a/MediaBrowser.Controller/Net/IRestfulService.cs +++ /dev/null @@ -1,12 +0,0 @@ -using MediaBrowser.Model.Services; - -namespace MediaBrowser.Controller.Net -{ - /// - /// Interface IRestfulService - /// - [Logged] - public interface IRestfulService : IService - { - } -} diff --git a/MediaBrowser.Controller/Net/LoggedAttribute.cs b/MediaBrowser.Controller/Net/LoggedAttribute.cs index c5b9d1cba..6a2a5e2e3 100644 --- a/MediaBrowser.Controller/Net/LoggedAttribute.cs +++ b/MediaBrowser.Controller/Net/LoggedAttribute.cs @@ -7,12 +7,20 @@ using MediaBrowser.Model.Services; namespace MediaBrowser.Controller.Net { - public class LoggedAttribute : Attribute, IHasRequestFilter + public class LoggedAttribute : IRequestFilter { - public ILogger Logger { get; set; } - public IUserManager UserManager { get; set; } - public ISessionManager SessionManager { get; set; } - public IAuthorizationContext AuthorizationContext { get; set; } + public LoggedAttribute(ILogger logger, IUserManager userManager, ISessionManager sessionManager, IAuthorizationContext authorizationContext) + { + Logger = logger; + UserManager = userManager; + SessionManager = sessionManager; + AuthorizationContext = authorizationContext; + } + + public ILogger Logger { get; private set; } + public IUserManager UserManager { get; private set; } + public ISessionManager SessionManager { get; private set; } + public IAuthorizationContext AuthorizationContext { get; private set; } /// /// The request filter is executed before the service. @@ -20,7 +28,7 @@ namespace MediaBrowser.Controller.Net /// The http request wrapper /// The http response wrapper /// The request DTO - public void RequestFilter(IRequest request, IResponse response, object requestDto) + public void Filter(IRequest request, IResponse response, object requestDto) { var serviceRequest = new ServiceRequest(request); @@ -51,25 +59,5 @@ namespace MediaBrowser.Controller.Net } } } - - /// - /// A new shallow copy of this filter is used on every request. - /// - /// IHasRequestFilter. - public IHasRequestFilter Copy() - { - return this; - } - - /// - /// Order in which Request Filters are executed. - /// <0 Executed before global request filters - /// >0 Executed after global request filters - /// - /// The priority. - public int Priority - { - get { return 0; } - } } } diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 37269473e..dca74298e 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -167,6 +167,9 @@ + + + @@ -174,6 +177,7 @@ + diff --git a/MediaBrowser.Model/Plugins/IHasWebPages.cs b/MediaBrowser.Model/Plugins/IHasWebPages.cs new file mode 100644 index 000000000..0745c3c60 --- /dev/null +++ b/MediaBrowser.Model/Plugins/IHasWebPages.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace MediaBrowser.Model.Plugins +{ + public interface IHasWebPages + { + IEnumerable GetPages(); + } +} diff --git a/MediaBrowser.Model/Plugins/PluginPageInfo.cs b/MediaBrowser.Model/Plugins/PluginPageInfo.cs new file mode 100644 index 000000000..4b91e0791 --- /dev/null +++ b/MediaBrowser.Model/Plugins/PluginPageInfo.cs @@ -0,0 +1,9 @@ +namespace MediaBrowser.Model.Plugins +{ + public class PluginPageInfo + { + public string Name { get; set; } + + public string EmbeddedResourcePath { get; set; } + } +} diff --git a/MediaBrowser.Model/Reflection/IAssemblyInfo.cs b/MediaBrowser.Model/Reflection/IAssemblyInfo.cs new file mode 100644 index 000000000..1c65985cb --- /dev/null +++ b/MediaBrowser.Model/Reflection/IAssemblyInfo.cs @@ -0,0 +1,10 @@ +using System; +using System.IO; + +namespace MediaBrowser.Model.Reflection +{ + public interface IAssemblyInfo + { + Stream GetManifestResourceStream(Type type, string resource); + } +} diff --git a/MediaBrowser.Model/Services/IRequestFilter.cs b/MediaBrowser.Model/Services/IRequestFilter.cs new file mode 100644 index 000000000..7f6db2e4d --- /dev/null +++ b/MediaBrowser.Model/Services/IRequestFilter.cs @@ -0,0 +1,8 @@ + +namespace MediaBrowser.Model.Services +{ + public interface IRequestFilter + { + void Filter(IRequest request, IResponse response, object requestDto); + } +} diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index d1954a527..3fcdbf1c7 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -53,10 +53,7 @@ - - - False diff --git a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs index 999634a92..71704f8e2 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -17,9 +17,9 @@ using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; using MediaBrowser.Common.Security; +using MediaBrowser.Controller; using MediaBrowser.Model.Extensions; using MediaBrowser.Model.IO; using MediaBrowser.Model.Services; @@ -34,7 +34,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer private readonly ILogger _logger; public IEnumerable UrlPrefixes { get; private set; } - private readonly List _restServices = new List(); + private readonly List _restServices = new List(); private IHttpListener _listener; @@ -49,13 +49,16 @@ namespace MediaBrowser.Server.Implementations.HttpServer private readonly INetworkManager _networkManager; private readonly IMemoryStreamProvider _memoryStreamProvider; - public HttpListenerHost(IApplicationHost applicationHost, + private readonly IServerApplicationHost _appHost; + + public HttpListenerHost(IServerApplicationHost applicationHost, ILogManager logManager, IServerConfigurationManager config, string serviceName, string defaultRedirectPath, INetworkManager networkManager, IMemoryStreamProvider memoryStreamProvider, params Assembly[] assembliesWithServices) : base(serviceName, assembliesWithServices) { + _appHost = applicationHost; DefaultRedirectPath = defaultRedirectPath; _networkManager = networkManager; _memoryStreamProvider = memoryStreamProvider; @@ -116,6 +119,12 @@ namespace MediaBrowser.Server.Implementations.HttpServer // } //}); + var requestFilters = _appHost.GetExports().ToList(); + foreach (var filter in requestFilters) + { + HostContext.GlobalRequestFilters.Add(filter.Filter); + } + HostContext.GlobalResponseFilters.Add(new ResponseFilter(_logger).FilterResponse); } @@ -569,7 +578,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer /// Adds the rest handlers. /// /// The services. - public void Init(IEnumerable services) + public void Init(IEnumerable services) { _restServices.AddRange(services); diff --git a/MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs b/MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs index b2cbf2387..4dff2d5a3 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs @@ -1,6 +1,5 @@ -using MediaBrowser.Common; -using MediaBrowser.Common.IO; -using MediaBrowser.Common.Net; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Net; using MediaBrowser.Model.IO; @@ -18,17 +17,17 @@ namespace MediaBrowser.Server.Implementations.HttpServer /// Creates the server. /// /// IHttpServer. - public static IHttpServer CreateServer(IApplicationHost applicationHost, + public static IHttpServer CreateServer(IServerApplicationHost applicationHost, ILogManager logManager, IServerConfigurationManager config, - INetworkManager _networkmanager, + INetworkManager networkmanager, IMemoryStreamProvider streamProvider, string serverName, string defaultRedirectpath) { LogManager.LogFactory = new ServerLogFactory(logManager); - return new HttpListenerHost(applicationHost, logManager, config, serverName, defaultRedirectpath, _networkmanager, streamProvider); + return new HttpListenerHost(applicationHost, logManager, config, serverName, defaultRedirectpath, networkmanager, streamProvider); } } } diff --git a/MediaBrowser.Server.Implementations/HttpServer/SwaggerService.cs b/MediaBrowser.Server.Implementations/HttpServer/SwaggerService.cs index 7d4d0a968..54ee5fbb2 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/SwaggerService.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/SwaggerService.cs @@ -5,7 +5,7 @@ using MediaBrowser.Model.Services; namespace MediaBrowser.Server.Implementations.HttpServer { - public class SwaggerService : IHasResultFactory, IRestfulService + public class SwaggerService : IHasResultFactory, IService { private readonly IServerApplicationPaths _appPaths; diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index fd978f3d6..23d373ac4 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -270,6 +270,7 @@ + diff --git a/MediaBrowser.Server.Implementations/Reflection/AssemblyInfo.cs b/MediaBrowser.Server.Implementations/Reflection/AssemblyInfo.cs new file mode 100644 index 000000000..ec25e7951 --- /dev/null +++ b/MediaBrowser.Server.Implementations/Reflection/AssemblyInfo.cs @@ -0,0 +1,14 @@ +using System; +using System.IO; +using MediaBrowser.Model.Reflection; + +namespace MediaBrowser.Server.Implementations.Reflection +{ + public class AssemblyInfo : IAssemblyInfo + { + public Stream GetManifestResourceStream(Type type, string resource) + { + return type.Assembly.GetManifestResourceStream(resource); + } + } +} diff --git a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs index 4372dc283..f8294a24d 100644 --- a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs +++ b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs @@ -111,9 +111,12 @@ using MediaBrowser.Model.Activity; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Net; using MediaBrowser.Model.News; +using MediaBrowser.Model.Reflection; using MediaBrowser.Model.Serialization; +using MediaBrowser.Model.Services; using MediaBrowser.Model.Social; using MediaBrowser.Model.Xml; +using MediaBrowser.Server.Implementations.Reflection; using MediaBrowser.Server.Implementations.Xml; namespace MediaBrowser.Server.Startup.Common @@ -634,6 +637,7 @@ namespace MediaBrowser.Server.Startup.Common RegisterSingleInstance(() => new BdInfoExaminer()); RegisterSingleInstance(new XmlReaderSettingsFactory()); + RegisterSingleInstance(new AssemblyInfo()); UserDataManager = new UserDataManager(LogManager, ServerConfigurationManager); RegisterSingleInstance(UserDataManager); @@ -985,7 +989,7 @@ namespace MediaBrowser.Server.Startup.Common base.FindParts(); - HttpServer.Init(GetExports(false)); + HttpServer.Init(GetExports(false)); ServerManager.AddWebSocketListeners(GetExports(false)); diff --git a/MediaBrowser.WebDashboard/Api/ConfigurationPageInfo.cs b/MediaBrowser.WebDashboard/Api/ConfigurationPageInfo.cs index 16aa14cb7..33289e76c 100644 --- a/MediaBrowser.WebDashboard/Api/ConfigurationPageInfo.cs +++ b/MediaBrowser.WebDashboard/Api/ConfigurationPageInfo.cs @@ -1,4 +1,6 @@ -using MediaBrowser.Controller.Plugins; +using MediaBrowser.Common.Plugins; +using MediaBrowser.Controller.Plugins; +using MediaBrowser.Model.Plugins; namespace MediaBrowser.WebDashboard.Api { @@ -30,5 +32,13 @@ namespace MediaBrowser.WebDashboard.Api // Don't use "N" because it needs to match Plugin.Id PluginId = page.Plugin.Id.ToString(); } + + public ConfigurationPageInfo(IPlugin plugin, PluginPageInfo page) + { + Name = page.Name; + + // Don't use "N" because it needs to match Plugin.Id + PluginId = plugin.Id.ToString(); + } } } diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index 5af580340..6803ad306 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -12,8 +12,11 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; +using MediaBrowser.Common.Plugins; using MediaBrowser.Model.IO; using MediaBrowser.Model.Globalization; +using MediaBrowser.Model.Plugins; +using MediaBrowser.Model.Reflection; using MediaBrowser.Model.Services; namespace MediaBrowser.WebDashboard.Api @@ -76,7 +79,7 @@ namespace MediaBrowser.WebDashboard.Api /// /// Class DashboardService /// - public class DashboardService : IRestfulService, IHasResultFactory + public class DashboardService : IService, IHasResultFactory { /// /// Gets or sets the logger. @@ -109,6 +112,7 @@ namespace MediaBrowser.WebDashboard.Api private readonly IFileSystem _fileSystem; private readonly ILocalizationManager _localization; private readonly IJsonSerializer _jsonSerializer; + private readonly IAssemblyInfo _assemblyInfo; /// /// Initializes a new instance of the class. @@ -116,13 +120,14 @@ namespace MediaBrowser.WebDashboard.Api /// The app host. /// The server configuration manager. /// The file system. - public DashboardService(IServerApplicationHost appHost, IServerConfigurationManager serverConfigurationManager, IFileSystem fileSystem, ILocalizationManager localization, IJsonSerializer jsonSerializer) + public DashboardService(IServerApplicationHost appHost, IServerConfigurationManager serverConfigurationManager, IFileSystem fileSystem, ILocalizationManager localization, IJsonSerializer jsonSerializer, IAssemblyInfo assemblyInfo) { _appHost = appHost; _serverConfigurationManager = serverConfigurationManager; _fileSystem = fileSystem; _localization = localization; _jsonSerializer = jsonSerializer; + _assemblyInfo = assemblyInfo; } /// @@ -132,9 +137,32 @@ namespace MediaBrowser.WebDashboard.Api /// System.Object. public Task Get(GetDashboardConfigurationPage request) { - var page = ServerEntryPoint.Instance.PluginConfigurationPages.First(p => p.Name.Equals(request.Name, StringComparison.OrdinalIgnoreCase)); + IPlugin plugin = null; + Stream stream = null; - return ResultFactory.GetStaticResult(Request, page.Plugin.Version.ToString().GetMD5(), null, null, MimeTypes.GetMimeType("page.html"), () => GetPackageCreator().ModifyHtml("dummy.html", page.GetHtmlStream(), null, _appHost.ApplicationVersion.ToString(), null, false)); + var page = ServerEntryPoint.Instance.PluginConfigurationPages.FirstOrDefault(p => string.Equals(p.Name, request.Name, StringComparison.OrdinalIgnoreCase)); + if (page != null) + { + plugin = page.Plugin; + stream = page.GetHtmlStream(); + } + + if (plugin == null) + { + var altPage = GetPluginPages().FirstOrDefault(p => string.Equals(p.Item1.Name, request.Name, StringComparison.OrdinalIgnoreCase)); + if (altPage != null) + { + plugin = altPage.Item2; + stream = _assemblyInfo.GetManifestResourceStream(plugin.GetType(), altPage.Item1.EmbeddedResourcePath); + } + } + + if (plugin != null && stream != null) + { + return ResultFactory.GetStaticResult(Request, plugin.Version.ToString().GetMD5(), null, null, MimeTypes.GetMimeType("page.html"), () => GetPackageCreator().ModifyHtml("dummy.html", stream, null, _appHost.ApplicationVersion.ToString(), null, false)); + } + + throw new ResourceNotFoundException(); } /// @@ -162,7 +190,7 @@ namespace MediaBrowser.WebDashboard.Api if (request.PageType.HasValue) { - pages = pages.Where(p => p.ConfigurationPageType == request.PageType.Value); + pages = pages.Where(p => p.ConfigurationPageType == request.PageType.Value).ToList(); } // Don't allow a failing plugin to fail them all @@ -182,9 +210,33 @@ namespace MediaBrowser.WebDashboard.Api .Where(i => i != null) .ToList(); + configPages.AddRange(_appHost.Plugins.SelectMany(GetConfigPages)); + return ResultFactory.GetOptimizedResult(Request, configPages); } + private IEnumerable> GetPluginPages() + { + return _appHost.Plugins.SelectMany(GetPluginPages); + } + + private IEnumerable> GetPluginPages(IPlugin plugin) + { + var hasConfig = plugin as IHasWebPages; + + if (hasConfig == null) + { + return new List>(); + } + + return hasConfig.GetPages().Select(i => new Tuple(i, plugin)); + } + + private IEnumerable GetConfigPages(IPlugin plugin) + { + return GetPluginPages(plugin).Select(i => new ConfigurationPageInfo(plugin, i.Item1)); + } + public object Get(GetRobotsTxt request) { return Get(new GetDashboardResource diff --git a/MediaBrowser.WebDashboard/ServerEntryPoint.cs b/MediaBrowser.WebDashboard/ServerEntryPoint.cs index 690c07d8f..b939e4107 100644 --- a/MediaBrowser.WebDashboard/ServerEntryPoint.cs +++ b/MediaBrowser.WebDashboard/ServerEntryPoint.cs @@ -1,6 +1,7 @@ using MediaBrowser.Common; using MediaBrowser.Controller.Plugins; using System.Collections.Generic; +using System.Linq; namespace MediaBrowser.WebDashboard { @@ -10,7 +11,7 @@ namespace MediaBrowser.WebDashboard /// Gets the list of plugin configuration pages /// /// The configuration pages. - public IEnumerable PluginConfigurationPages { get; private set; } + public List PluginConfigurationPages { get; private set; } private readonly IApplicationHost _appHost; @@ -24,7 +25,7 @@ namespace MediaBrowser.WebDashboard public void Run() { - PluginConfigurationPages = _appHost.GetExports(); + PluginConfigurationPages = _appHost.GetExports().ToList(); } public void Dispose() diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index 57b18c338..292c80a7c 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common.Internal - 3.0.676 + 3.0.680 Emby.Common.Internal Luke ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains common components shared by Emby Theater and Emby Server. Not intended for plugin developer consumption. Copyright © Emby 2013 - + diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index 05c9c4afc..111cb0a2a 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common - 3.0.676 + 3.0.680 Emby.Common Emby Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index 67607af50..fec8b7722 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Server.Core - 3.0.676 + 3.0.680 Emby.Server.Core Emby Team ebr,Luke,scottisafool @@ -12,10 +12,11 @@ Contains core components required to build plugins for Emby Server. Copyright © Emby 2013 - + + \ No newline at end of file -- cgit v1.2.3 From a8b340cbb29dbcf7fd5d101e640d66470c6d32bf Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 8 Nov 2016 13:44:23 -0500 Subject: update portable projects --- BDInfo/BDROM.cs | 4 +- BDInfo/TSPlaylistFile.cs | 10 +- BDInfo/TSStreamClipFile.cs | 14 +- Emby.Common.Implementations/BaseApplicationHost.cs | 10 +- .../Cryptography/CryptographyProvider.cs | 17 +- .../HttpClientManager/HttpClientManager.cs | 4 +- Emby.Common.Implementations/Net/NetSocket.cs | 85 +++++ Emby.Common.Implementations/Net/SocketAcceptor.cs | 111 ++++++ Emby.Common.Implementations/Net/SocketFactory.cs | 37 +- Emby.Common.Implementations/Net/UdpSocket.cs | 12 +- .../Networking/BaseNetworkManager.cs | 91 ++++- .../TextEncoding/TextEncoding.cs | 13 +- Emby.Dlna/Main/DlnaEntryPoint.cs | 6 +- Emby.Dlna/PlayTo/PlayToManager.cs | 11 +- .../Connect/ConnectEntryPoint.cs | 8 +- .../Connect/ConnectManager.cs | 2 +- .../Emby.Server.Implementations.csproj | 10 + .../HttpServer/LoggerUtils.cs | 43 +++ .../HttpServer/RangeRequestWriter.cs | 224 +++++++++++++ .../HttpServer/ResponseFilter.cs | 129 +++++++ .../HttpServer/SocketSharp/Extensions.cs | 12 + .../HttpServer/SocketSharp/SharpWebSocket.cs | 166 +++++++++ .../SocketSharp/WebSocketSharpListener.cs | 203 +++++++++++ .../SocketSharp/WebSocketSharpResponse.cs | 204 +++++++++++ Emby.Server.Implementations/Library/UserManager.cs | 6 +- .../Security/MBLicenseFile.cs | 10 +- .../Security/PluginSecurityManager.cs | 4 +- .../ServerManager/ServerManager.cs | 8 +- .../ServerManager/WebSocketConnection.cs | 10 +- Emby.Server.Implementations/Sync/MediaSync.cs | 6 +- .../Sync/MultiProviderSync.cs | 4 +- .../Sync/ServerSyncScheduledTask.cs | 4 +- Emby.Server.Implementations/Sync/SyncManager.cs | 4 +- .../Sync/TargetDataProvider.cs | 4 +- .../Updates/InstallationManager.cs | 6 +- MediaBrowser.Api/Dlna/DlnaServerService.cs | 4 +- MediaBrowser.Api/Playback/BaseStreamingService.cs | 1 + MediaBrowser.Common/Extensions/BaseExtensions.cs | 2 +- MediaBrowser.Common/Net/INetworkManager.cs | 5 + MediaBrowser.Controller/IServerApplicationHost.cs | 2 +- .../MediaEncoding/MediaStreamSelector.cs | 3 +- .../Net/AuthenticatedAttribute.cs | 14 +- .../BdInfo/BdInfoExaminer.cs | 6 +- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 4 +- .../Probing/ProbeResultNormalizer.cs | 4 +- .../Subtitles/SubtitleEncoder.cs | 8 +- MediaBrowser.Model/Cryptography/ICryptoProvider.cs | 13 + .../Cryptography/ICryptographyProvider.cs | 14 - MediaBrowser.Model/IO/IMemoryStreamFactory.cs | 12 + MediaBrowser.Model/IO/IMemoryStreamProvider.cs | 11 - MediaBrowser.Model/MediaBrowser.Model.csproj | 8 +- MediaBrowser.Model/Net/ISocket.cs | 16 + MediaBrowser.Model/Net/ISocketFactory.cs | 14 +- MediaBrowser.Model/Net/IpAddressInfo.cs | 29 +- MediaBrowser.Model/Net/IpEndPointInfo.cs | 14 +- MediaBrowser.Model/Services/IHasRequestFilter.cs | 13 +- MediaBrowser.Model/Services/IHttpResult.cs | 47 +++ MediaBrowser.Model/Services/IRequest.cs | 10 - .../Services/QueryParamCollection.cs | 31 +- MediaBrowser.Model/Text/ITextEncoding.cs | 10 + MediaBrowser.Model/TextEncoding/IEncoding.cs | 12 - MediaBrowser.Providers/Manager/ImageSaver.cs | 4 +- MediaBrowser.Providers/Manager/ProviderManager.cs | 4 +- .../MediaInfo/SubtitleResolver.cs | 13 +- .../TV/TheTVDB/TvdbSeriesProvider.cs | 4 +- .../HttpServer/ContainerAdapter.cs | 11 +- .../HttpServer/HttpListenerHost.cs | 371 ++++++++++++--------- .../HttpServer/HttpResultFactory.cs | 1 + .../HttpServer/LoggerUtils.cs | 43 --- .../HttpServer/RangeRequestWriter.cs | 230 ------------- .../HttpServer/ResponseFilter.cs | 127 ------- .../HttpServer/ServerFactory.cs | 15 +- .../HttpServer/ServerLogFactory.cs | 46 --- .../HttpServer/ServerLogger.cs | 194 ----------- .../HttpServer/SocketSharp/Extensions.cs | 28 -- .../HttpServer/SocketSharp/RequestMono.cs | 13 +- .../HttpServer/SocketSharp/SharpWebSocket.cs | 172 ---------- .../SocketSharp/WebSocketSharpListener.cs | 216 ------------ .../SocketSharp/WebSocketSharpRequest.cs | 100 ++---- .../SocketSharp/WebSocketSharpResponse.cs | 152 --------- .../IO/MemoryStreamProvider.cs | 16 +- .../MediaBrowser.Server.Implementations.csproj | 18 +- .../Persistence/DataExtensions.cs | 4 +- .../SqliteDisplayPreferencesRepository.cs | 4 +- .../Persistence/SqliteItemRepository.cs | 4 +- .../Persistence/SqliteUserRepository.cs | 4 +- .../packages.config | 1 - .../ApplicationHost.cs | 54 ++- Nuget/MediaBrowser.Common.nuspec | 2 +- Nuget/MediaBrowser.Server.Core.nuspec | 4 +- OpenSubtitlesHandler/Utilities.cs | 10 +- 91 files changed, 1899 insertions(+), 1765 deletions(-) create mode 100644 Emby.Common.Implementations/Net/NetSocket.cs create mode 100644 Emby.Common.Implementations/Net/SocketAcceptor.cs create mode 100644 Emby.Server.Implementations/HttpServer/LoggerUtils.cs create mode 100644 Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs create mode 100644 Emby.Server.Implementations/HttpServer/ResponseFilter.cs create mode 100644 Emby.Server.Implementations/HttpServer/SocketSharp/Extensions.cs create mode 100644 Emby.Server.Implementations/HttpServer/SocketSharp/SharpWebSocket.cs create mode 100644 Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs create mode 100644 Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs create mode 100644 MediaBrowser.Model/Cryptography/ICryptoProvider.cs delete mode 100644 MediaBrowser.Model/Cryptography/ICryptographyProvider.cs create mode 100644 MediaBrowser.Model/IO/IMemoryStreamFactory.cs delete mode 100644 MediaBrowser.Model/IO/IMemoryStreamProvider.cs create mode 100644 MediaBrowser.Model/Net/ISocket.cs create mode 100644 MediaBrowser.Model/Services/IHttpResult.cs create mode 100644 MediaBrowser.Model/Text/ITextEncoding.cs delete mode 100644 MediaBrowser.Model/TextEncoding/IEncoding.cs delete mode 100644 MediaBrowser.Server.Implementations/HttpServer/LoggerUtils.cs delete mode 100644 MediaBrowser.Server.Implementations/HttpServer/RangeRequestWriter.cs delete mode 100644 MediaBrowser.Server.Implementations/HttpServer/ResponseFilter.cs delete mode 100644 MediaBrowser.Server.Implementations/HttpServer/ServerLogFactory.cs delete mode 100644 MediaBrowser.Server.Implementations/HttpServer/ServerLogger.cs delete mode 100644 MediaBrowser.Server.Implementations/HttpServer/SocketSharp/Extensions.cs delete mode 100644 MediaBrowser.Server.Implementations/HttpServer/SocketSharp/SharpWebSocket.cs delete mode 100644 MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs delete mode 100644 MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs (limited to 'MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs') diff --git a/BDInfo/BDROM.cs b/BDInfo/BDROM.cs index 2a23645b1..97dbfbf3b 100644 --- a/BDInfo/BDROM.cs +++ b/BDInfo/BDROM.cs @@ -22,7 +22,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using MediaBrowser.Model.IO; -using MediaBrowser.Model.TextEncoding; +using MediaBrowser.Model.Text; namespace BDInfo { @@ -75,7 +75,7 @@ namespace BDInfo public event OnPlaylistFileScanError PlaylistFileScanError; public BDROM( - string path, IFileSystem fileSystem, IEncoding textEncoding) + string path, IFileSystem fileSystem, ITextEncoding textEncoding) { _fileSystem = fileSystem; // diff --git a/BDInfo/TSPlaylistFile.cs b/BDInfo/TSPlaylistFile.cs index 2826b3c80..46d66f513 100644 --- a/BDInfo/TSPlaylistFile.cs +++ b/BDInfo/TSPlaylistFile.cs @@ -23,14 +23,14 @@ using System.Collections.Generic; using System.IO; using System.Text; using MediaBrowser.Model.IO; -using MediaBrowser.Model.TextEncoding; +using MediaBrowser.Model.Text; namespace BDInfo { public class TSPlaylistFile { private readonly IFileSystem _fileSystem; - private readonly IEncoding _textEncoding; + private readonly ITextEncoding _textEncoding; private FileSystemMetadata FileInfo = null; public string FileType = null; public bool IsInitialized = false; @@ -67,7 +67,7 @@ namespace BDInfo public TSPlaylistFile( BDROM bdrom, - FileSystemMetadata fileInfo, IFileSystem fileSystem, IEncoding textEncoding) + FileSystemMetadata fileInfo, IFileSystem fileSystem, ITextEncoding textEncoding) { BDROM = bdrom; FileInfo = fileInfo; @@ -79,7 +79,7 @@ namespace BDInfo public TSPlaylistFile( BDROM bdrom, string name, - List clips, IFileSystem fileSystem, IEncoding textEncoding) + List clips, IFileSystem fileSystem, ITextEncoding textEncoding) { BDROM = bdrom; Name = name; @@ -1247,7 +1247,7 @@ namespace BDInfo ref int pos) { string val = - _textEncoding.GetASCIIString(data, pos, count); + _textEncoding.GetASCIIEncoding().GetString(data, pos, count); pos += count; diff --git a/BDInfo/TSStreamClipFile.cs b/BDInfo/TSStreamClipFile.cs index 118e3b5fc..f2accb88d 100644 --- a/BDInfo/TSStreamClipFile.cs +++ b/BDInfo/TSStreamClipFile.cs @@ -23,14 +23,14 @@ using System.Collections.Generic; using System.IO; using System.Text; using MediaBrowser.Model.IO; -using MediaBrowser.Model.TextEncoding; +using MediaBrowser.Model.Text; namespace BDInfo { public class TSStreamClipFile { private readonly IFileSystem _fileSystem; - private readonly IEncoding _textEncoding; + private readonly ITextEncoding _textEncoding; public FileSystemMetadata FileInfo = null; public string FileType = null; public bool IsValid = false; @@ -40,7 +40,7 @@ namespace BDInfo new Dictionary(); public TSStreamClipFile( - FileSystemMetadata fileInfo, IFileSystem fileSystem, IEncoding textEncoding) + FileSystemMetadata fileInfo, IFileSystem fileSystem, ITextEncoding textEncoding) { FileInfo = fileInfo; _fileSystem = fileSystem; @@ -70,7 +70,7 @@ namespace BDInfo byte[] fileType = new byte[8]; Array.Copy(data, 0, fileType, 0, fileType.Length); - FileType = _textEncoding.GetASCIIString(fileType, 0, fileType.Length); + FileType = _textEncoding.GetASCIIEncoding().GetString(fileType, 0, fileType.Length); if (FileType != "HDMV0100" && FileType != "HDMV0200") { @@ -167,7 +167,7 @@ namespace BDInfo Array.Copy(clipData, streamOffset + 3, languageBytes, 0, languageBytes.Length); string languageCode = - _textEncoding.GetASCIIString(languageBytes, 0, languageBytes.Length); + _textEncoding.GetASCIIEncoding().GetString(languageBytes, 0, languageBytes.Length); TSChannelLayout channelLayout = (TSChannelLayout) (clipData[streamOffset + 2] >> 4); @@ -198,7 +198,7 @@ namespace BDInfo Array.Copy(clipData, streamOffset + 2, languageBytes, 0, languageBytes.Length); string languageCode = - _textEncoding.GetASCIIString(languageBytes, 0, languageBytes.Length); + _textEncoding.GetASCIIEncoding().GetString(languageBytes, 0, languageBytes.Length); stream = new TSGraphicsStream(); stream.LanguageCode = languageCode; @@ -218,7 +218,7 @@ namespace BDInfo Array.Copy(clipData, streamOffset + 3, languageBytes, 0, languageBytes.Length); string languageCode = - _textEncoding.GetASCIIString(languageBytes, 0, languageBytes.Length); + _textEncoding.GetASCIIEncoding().GetString(languageBytes, 0, languageBytes.Length); #if DEBUG Debug.WriteLine(string.Format( "\t{0} {1} {2}", diff --git a/Emby.Common.Implementations/BaseApplicationHost.cs b/Emby.Common.Implementations/BaseApplicationHost.cs index 9585abb2a..0cf11e825 100644 --- a/Emby.Common.Implementations/BaseApplicationHost.cs +++ b/Emby.Common.Implementations/BaseApplicationHost.cs @@ -170,7 +170,7 @@ namespace Emby.Common.Implementations /// true if this instance is running as service; otherwise, false. public abstract bool IsRunningAsService { get; } - protected ICryptographyProvider CryptographyProvider = new CryptographyProvider(); + protected ICryptoProvider CryptographyProvider = new CryptographyProvider(); protected IEnvironmentInfo EnvironmentInfo = new Emby.Common.Implementations.EnvironmentInfo.EnvironmentInfo(); @@ -183,7 +183,7 @@ namespace Emby.Common.Implementations { _deviceId = new DeviceId(ApplicationPaths, LogManager.GetLogger("SystemId"), FileSystemManager); } - + return _deviceId.Value; } } @@ -193,7 +193,7 @@ namespace Emby.Common.Implementations get { return EnvironmentInfo.OperatingSystemName; } } - public IMemoryStreamProvider MemoryStreamProvider { get; set; } + public IMemoryStreamFactory MemoryStreamProvider { get; set; } /// /// The container @@ -209,7 +209,7 @@ namespace Emby.Common.Implementations { // hack alert, until common can target .net core BaseExtensions.CryptographyProvider = CryptographyProvider; - + XmlSerializer = new MyXmlSerializer(fileSystem, logManager.GetLogger("XmlSerializer")); FailedAssemblies = new List(); @@ -267,7 +267,7 @@ namespace Emby.Common.Implementations progress.Report(100); } - protected abstract IMemoryStreamProvider CreateMemoryStreamProvider(); + protected abstract IMemoryStreamFactory CreateMemoryStreamProvider(); protected abstract ISystemEvents CreateSystemEvents(); protected virtual void OnLoggerLoaded(bool isFirstLoad) diff --git a/Emby.Common.Implementations/Cryptography/CryptographyProvider.cs b/Emby.Common.Implementations/Cryptography/CryptographyProvider.cs index 7b8d95b96..01a31bcc0 100644 --- a/Emby.Common.Implementations/Cryptography/CryptographyProvider.cs +++ b/Emby.Common.Implementations/Cryptography/CryptographyProvider.cs @@ -6,21 +6,14 @@ using MediaBrowser.Model.Cryptography; namespace Emby.Common.Implementations.Cryptography { - public class CryptographyProvider : ICryptographyProvider + public class CryptographyProvider : ICryptoProvider { public Guid GetMD5(string str) { - return new Guid(GetMD5Bytes(str)); - } - public byte[] GetMD5Bytes(string str) - { - using (var provider = MD5.Create()) - { - return provider.ComputeHash(Encoding.Unicode.GetBytes(str)); - } + return new Guid(ComputeMD5(Encoding.Unicode.GetBytes(str))); } - public byte[] GetSHA1Bytes(byte[] bytes) + public byte[] ComputeSHA1(byte[] bytes) { using (var provider = SHA1.Create()) { @@ -28,7 +21,7 @@ namespace Emby.Common.Implementations.Cryptography } } - public byte[] GetMD5Bytes(Stream str) + public byte[] ComputeMD5(Stream str) { using (var provider = MD5.Create()) { @@ -36,7 +29,7 @@ namespace Emby.Common.Implementations.Cryptography } } - public byte[] GetMD5Bytes(byte[] bytes) + public byte[] ComputeMD5(byte[] bytes) { using (var provider = MD5.Create()) { diff --git a/Emby.Common.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Common.Implementations/HttpClientManager/HttpClientManager.cs index 85fcb556f..06af5af53 100644 --- a/Emby.Common.Implementations/HttpClientManager/HttpClientManager.cs +++ b/Emby.Common.Implementations/HttpClientManager/HttpClientManager.cs @@ -42,7 +42,7 @@ namespace Emby.Common.Implementations.HttpClientManager private readonly IApplicationPaths _appPaths; private readonly IFileSystem _fileSystem; - private readonly IMemoryStreamProvider _memoryStreamProvider; + private readonly IMemoryStreamFactory _memoryStreamProvider; /// /// Initializes a new instance of the class. @@ -53,7 +53,7 @@ namespace Emby.Common.Implementations.HttpClientManager /// appPaths /// or /// logger - public HttpClientManager(IApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem, IMemoryStreamProvider memoryStreamProvider) + public HttpClientManager(IApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem, IMemoryStreamFactory memoryStreamProvider) { if (appPaths == null) { diff --git a/Emby.Common.Implementations/Net/NetSocket.cs b/Emby.Common.Implementations/Net/NetSocket.cs new file mode 100644 index 000000000..72faa41a9 --- /dev/null +++ b/Emby.Common.Implementations/Net/NetSocket.cs @@ -0,0 +1,85 @@ +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using Emby.Common.Implementations.Networking; +using MediaBrowser.Model.Net; +using MediaBrowser.Model.Logging; + +namespace Emby.Common.Implementations.Net +{ + public class NetSocket : ISocket + { + public Socket Socket { get; private set; } + private readonly ILogger _logger; + + public NetSocket(Socket socket, ILogger logger) + { + Socket = socket; + _logger = logger; + } + + public IpEndPointInfo LocalEndPoint + { + get + { + return BaseNetworkManager.ToIpEndPointInfo((IPEndPoint)Socket.LocalEndPoint); + } + } + + public IpEndPointInfo RemoteEndPoint + { + get + { + return BaseNetworkManager.ToIpEndPointInfo((IPEndPoint)Socket.RemoteEndPoint); + } + } + + public void Close() + { +#if NET46 + Socket.Close(); +#else + Socket.Dispose(); +#endif + } + + public void Shutdown(bool both) + { + if (both) + { + Socket.Shutdown(SocketShutdown.Both); + } + else + { + // Change interface if ever needed + throw new NotImplementedException(); + } + } + + public void Listen(int backlog) + { + Socket.Listen(backlog); + } + + public void Bind(IpEndPointInfo endpoint) + { + var nativeEndpoint = BaseNetworkManager.ToIPEndPoint(endpoint); + + Socket.Bind(nativeEndpoint); + } + + private SocketAcceptor _acceptor; + public void StartAccept(Action onAccept, Func isClosed) + { + _acceptor = new SocketAcceptor(_logger, Socket, onAccept, isClosed); + + _acceptor.StartAccept(); + } + + public void Dispose() + { + Socket.Dispose(); + } + } +} diff --git a/Emby.Common.Implementations/Net/SocketAcceptor.cs b/Emby.Common.Implementations/Net/SocketAcceptor.cs new file mode 100644 index 000000000..fd65e9fbc --- /dev/null +++ b/Emby.Common.Implementations/Net/SocketAcceptor.cs @@ -0,0 +1,111 @@ +using System; +using System.Net.Sockets; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Net; + +namespace Emby.Common.Implementations.Net +{ + public class SocketAcceptor + { + private readonly ILogger _logger; + private readonly Socket _originalSocket; + private readonly Func _isClosed; + private readonly Action _onAccept; + + public SocketAcceptor(ILogger logger, Socket originalSocket, Action onAccept, Func isClosed) + { + _logger = logger; + _originalSocket = originalSocket; + _isClosed = isClosed; + _onAccept = onAccept; + } + + public void StartAccept() + { + Socket dummy = null; + StartAccept(null, ref dummy); + } + + public void StartAccept(SocketAsyncEventArgs acceptEventArg, ref Socket accepted) + { + if (acceptEventArg == null) + { + acceptEventArg = new SocketAsyncEventArgs(); + acceptEventArg.Completed += new EventHandler(AcceptEventArg_Completed); + } + else + { + // socket must be cleared since the context object is being reused + acceptEventArg.AcceptSocket = null; + } + + try + { + bool willRaiseEvent = _originalSocket.AcceptAsync(acceptEventArg); + + if (!willRaiseEvent) + { + ProcessAccept(acceptEventArg); + } + } + catch (Exception ex) + { + if (accepted != null) + { + try + { +#if NET46 + accepted.Close(); +#else + accepted.Dispose(); +#endif + } + catch + { + } + accepted = null; + } + } + } + + // This method is the callback method associated with Socket.AcceptAsync + // operations and is invoked when an accept operation is complete + // + void AcceptEventArg_Completed(object sender, SocketAsyncEventArgs e) + { + ProcessAccept(e); + } + + private void ProcessAccept(SocketAsyncEventArgs e) + { + if (_isClosed()) + { + return; + } + + // http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.acceptasync%28v=vs.110%29.aspx + // Under certain conditions ConnectionReset can occur + // Need to attept to re-accept + if (e.SocketError == SocketError.ConnectionReset) + { + _logger.Error("SocketError.ConnectionReset reported. Attempting to re-accept."); + Socket dummy = null; + StartAccept(e, ref dummy); + return; + } + + var acceptSocket = e.AcceptSocket; + if (acceptSocket != null) + { + //ProcessAccept(acceptSocket); + _onAccept(new NetSocket(acceptSocket, _logger)); + } + + if (_originalSocket != null) + { + // Accept the next connection request + StartAccept(e, ref acceptSocket); + } + } + } +} diff --git a/Emby.Common.Implementations/Net/SocketFactory.cs b/Emby.Common.Implementations/Net/SocketFactory.cs index bb38c72da..922b0f3cc 100644 --- a/Emby.Common.Implementations/Net/SocketFactory.cs +++ b/Emby.Common.Implementations/Net/SocketFactory.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Net; using System.Net.Sockets; using System.Threading.Tasks; +using MediaBrowser.Model.Logging; using MediaBrowser.Model.Net; namespace Emby.Common.Implementations.Net @@ -22,16 +23,28 @@ namespace Emby.Common.Implementations.Net /// private IPAddress _LocalIP; - /// - /// Default constructor. - /// - /// A string containing the IP address of the local network adapter to bind sockets to. Null or empty string will use . - public SocketFactory(string localIP) + private ILogger _logger; + + public SocketFactory(ILogger logger) + { + _logger = logger; + _LocalIP = IPAddress.Any; + } + + public ISocket CreateSocket(IpAddressFamily family, MediaBrowser.Model.Net.SocketType socketType, MediaBrowser.Model.Net.ProtocolType protocolType, bool dualMode) { - if (String.IsNullOrEmpty(localIP)) - _LocalIP = IPAddress.Any; - else - _LocalIP = IPAddress.Parse(localIP); + var addressFamily = family == IpAddressFamily.InterNetwork + ? AddressFamily.InterNetwork + : AddressFamily.InterNetworkV6; + + var socket = new Socket(addressFamily, System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp); + + if (dualMode) + { + socket.DualMode = true; + } + + return new NetSocket(socket, _logger); } #region ISocketFactory Members @@ -44,7 +57,7 @@ namespace Emby.Common.Implementations.Net { if (localPort < 0) throw new ArgumentException("localPort cannot be less than zero.", "localPort"); - var retVal = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + var retVal = new Socket(AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Dgram, System.Net.Sockets.ProtocolType.Udp); try { retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); @@ -68,7 +81,7 @@ namespace Emby.Common.Implementations.Net { if (localPort < 0) throw new ArgumentException("localPort cannot be less than zero.", "localPort"); - var retVal = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + var retVal = new Socket(AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Dgram, System.Net.Sockets.ProtocolType.Udp); try { retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); @@ -99,7 +112,7 @@ namespace Emby.Common.Implementations.Net if (multicastTimeToLive <= 0) throw new ArgumentException("multicastTimeToLive cannot be zero or less.", "multicastTimeToLive"); if (localPort < 0) throw new ArgumentException("localPort cannot be less than zero.", "localPort"); - var retVal = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + var retVal = new Socket(AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Dgram, System.Net.Sockets.ProtocolType.Udp); try { diff --git a/Emby.Common.Implementations/Net/UdpSocket.cs b/Emby.Common.Implementations/Net/UdpSocket.cs index d999d3fe8..244b37bb4 100644 --- a/Emby.Common.Implementations/Net/UdpSocket.cs +++ b/Emby.Common.Implementations/Net/UdpSocket.cs @@ -5,6 +5,7 @@ using System.Net; using System.Net.Sockets; using System.Security; using System.Threading.Tasks; +using Emby.Common.Implementations.Networking; using MediaBrowser.Model.Net; namespace Emby.Common.Implementations.Net @@ -174,16 +175,7 @@ namespace Emby.Common.Implementations.Net return null; } - return new IpEndPointInfo - { - IpAddress = new IpAddressInfo - { - Address = endpoint.Address.ToString(), - IsIpv6 = endpoint.AddressFamily == AddressFamily.InterNetworkV6 - }, - - Port = endpoint.Port - }; + return BaseNetworkManager.ToIpEndPointInfo(endpoint); } private void ProcessResponse(IAsyncResult asyncResult) diff --git a/Emby.Common.Implementations/Networking/BaseNetworkManager.cs b/Emby.Common.Implementations/Networking/BaseNetworkManager.cs index 10d0db968..f1ac8413b 100644 --- a/Emby.Common.Implementations/Networking/BaseNetworkManager.cs +++ b/Emby.Common.Implementations/Networking/BaseNetworkManager.cs @@ -22,14 +22,10 @@ namespace Emby.Common.Implementations.Networking Logger = logger; } - private List _localIpAddresses; + private List _localIpAddresses; private readonly object _localIpAddressSyncLock = new object(); - /// - /// Gets the machine's local ip address - /// - /// IPAddress. - public IEnumerable GetLocalIpAddresses() + public IEnumerable GetLocalIpAddresses() { const int cacheMinutes = 5; @@ -39,7 +35,7 @@ namespace Emby.Common.Implementations.Networking if (_localIpAddresses == null || forceRefresh) { - var addresses = GetLocalIpAddressesInternal().ToList(); + var addresses = GetLocalIpAddressesInternal().Select(ToIpAddressInfo).ToList(); _localIpAddresses = addresses; _lastRefresh = DateTime.UtcNow; @@ -405,18 +401,85 @@ namespace Emby.Common.Implementations.Networking IPAddress address; if (IPAddress.TryParse(ipAddress, out address)) { - - ipAddressInfo = new IpAddressInfo - { - Address = address.ToString(), - IsIpv6 = address.AddressFamily == AddressFamily.InterNetworkV6 - }; - + ipAddressInfo = ToIpAddressInfo(address); return true; } ipAddressInfo = null; return false; } + + 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 = address.ToString(), + AddressFamily = address.AddressFamily == AddressFamily.InterNetworkV6 ? IpAddressFamily.InterNetworkV6 : IpAddressFamily.InterNetwork + }; + } + + public async Task GetHostAddressesAsync(string host) + { + var addresses = await Dns.GetHostAddressesAsync(host).ConfigureAwait(false); + return addresses.Select(ToIpAddressInfo).ToArray(); + } } } diff --git a/Emby.Common.Implementations/TextEncoding/TextEncoding.cs b/Emby.Common.Implementations/TextEncoding/TextEncoding.cs index 35b869e43..254d35222 100644 --- a/Emby.Common.Implementations/TextEncoding/TextEncoding.cs +++ b/Emby.Common.Implementations/TextEncoding/TextEncoding.cs @@ -1,10 +1,10 @@ using System.Text; using MediaBrowser.Model.IO; -using MediaBrowser.Model.TextEncoding; +using MediaBrowser.Model.Text; namespace Emby.Common.Implementations.TextEncoding { - public class TextEncoding : IEncoding + public class TextEncoding : ITextEncoding { private readonly IFileSystem _fileSystem; @@ -13,14 +13,9 @@ namespace Emby.Common.Implementations.TextEncoding _fileSystem = fileSystem; } - public byte[] GetASCIIBytes(string text) + public Encoding GetASCIIEncoding() { - return Encoding.ASCII.GetBytes(text); - } - - public string GetASCIIString(byte[] bytes, int startIndex, int length) - { - return Encoding.ASCII.GetString(bytes, 0, bytes.Length); + return Encoding.ASCII; } public Encoding GetFileEncoding(string srcFile) diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs index 142b9f96e..ef27c029d 100644 --- a/Emby.Dlna/Main/DlnaEntryPoint.cs +++ b/Emby.Dlna/Main/DlnaEntryPoint.cs @@ -250,14 +250,12 @@ namespace Emby.Dlna.Main // continue; //} - var addressString = address.ToString(); - var fullService = "urn:schemas-upnp-org:device:MediaServer:1"; - _logger.Info("Registering publisher for {0} on {1}", fullService, addressString); + _logger.Info("Registering publisher for {0} on {1}", fullService, address.ToString()); var descriptorUri = "/dlna/" + udn + "/description.xml"; - var uri = new Uri(_appHost.GetLocalApiUrl(addressString, address.IsIpv6) + descriptorUri); + var uri = new Uri(_appHost.GetLocalApiUrl(address) + descriptorUri); var device = new SsdpRootDevice { diff --git a/Emby.Dlna/PlayTo/PlayToManager.cs b/Emby.Dlna/PlayTo/PlayToManager.cs index b714f4ac0..a93f7da14 100644 --- a/Emby.Dlna/PlayTo/PlayToManager.cs +++ b/Emby.Dlna/PlayTo/PlayToManager.cs @@ -16,6 +16,7 @@ using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Events; using MediaBrowser.Model.Globalization; +using MediaBrowser.Model.Net; using MediaBrowser.Model.Threading; namespace Emby.Dlna.PlayTo @@ -131,11 +132,11 @@ namespace Emby.Dlna.PlayTo string serverAddress; if (info.LocalIpAddress == null) { - serverAddress = await GetServerAddress(null, false).ConfigureAwait(false); + serverAddress = await GetServerAddress(null).ConfigureAwait(false); } else { - serverAddress = await GetServerAddress(info.LocalIpAddress.Address, info.LocalIpAddress.IsIpv6).ConfigureAwait(false); + serverAddress = await GetServerAddress(info.LocalIpAddress).ConfigureAwait(false); } string accessToken = null; @@ -189,14 +190,14 @@ namespace Emby.Dlna.PlayTo } } - private Task GetServerAddress(string ipAddress, bool isIpv6) + private Task GetServerAddress(IpAddressInfo address) { - if (string.IsNullOrWhiteSpace(ipAddress)) + if (address == null) { return _appHost.GetLocalApiUrl(); } - return Task.FromResult(_appHost.GetLocalApiUrl(ipAddress, isIpv6)); + return Task.FromResult(_appHost.GetLocalApiUrl(address)); } public void Dispose() diff --git a/Emby.Server.Implementations/Connect/ConnectEntryPoint.cs b/Emby.Server.Implementations/Connect/ConnectEntryPoint.cs index d7574d466..170ef07f3 100644 --- a/Emby.Server.Implementations/Connect/ConnectEntryPoint.cs +++ b/Emby.Server.Implementations/Connect/ConnectEntryPoint.cs @@ -64,7 +64,7 @@ namespace Emby.Server.Implementations.Connect validIpAddress = await GetIpAddress(ipLookupUrl).ConfigureAwait(false); // Try to find the ipv4 address, if present - if (!validIpAddress.IsIpv6) + if (validIpAddress.AddressFamily != IpAddressFamily.InterNetworkV6) { break; } @@ -77,9 +77,9 @@ namespace Emby.Server.Implementations.Connect _logger.ErrorException("Error getting connection info", ex); } } - + // If this produced an ipv6 address, try again - if (validIpAddress != null && validIpAddress.IsIpv6) + if (validIpAddress != null && validIpAddress.AddressFamily == IpAddressFamily.InterNetworkV6) { foreach (var ipLookupUrl in _ipLookups) { @@ -88,7 +88,7 @@ namespace Emby.Server.Implementations.Connect var newAddress = await GetIpAddress(ipLookupUrl, true).ConfigureAwait(false); // Try to find the ipv4 address, if present - if (!newAddress.IsIpv6) + if (newAddress.AddressFamily != IpAddressFamily.InterNetworkV6) { validIpAddress = newAddress; break; diff --git a/Emby.Server.Implementations/Connect/ConnectManager.cs b/Emby.Server.Implementations/Connect/ConnectManager.cs index 6c2ac40c3..079bfe868 100644 --- a/Emby.Server.Implementations/Connect/ConnectManager.cs +++ b/Emby.Server.Implementations/Connect/ConnectManager.cs @@ -74,7 +74,7 @@ namespace Emby.Server.Implementations.Connect if (string.IsNullOrWhiteSpace(address) && DiscoveredWanIpAddress != null) { - if (DiscoveredWanIpAddress.IsIpv6) + if (DiscoveredWanIpAddress.AddressFamily == IpAddressFamily.InterNetworkV6) { address = "[" + DiscoveredWanIpAddress + "]"; } diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 894ba334d..806702dfd 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -68,11 +68,18 @@ + + + + + + + @@ -254,6 +261,9 @@ {442b5058-dcaf-4263-bb6a-f21e31120a1b} MediaBrowser.Providers + + ..\ThirdParty\emby\SocketHttpListener.Portable.dll + ..\packages\UniversalDetector.1.0.1\lib\portable-net45+sl4+wp71+win8+wpa81\UniversalDetector.dll True diff --git a/Emby.Server.Implementations/HttpServer/LoggerUtils.cs b/Emby.Server.Implementations/HttpServer/LoggerUtils.cs new file mode 100644 index 000000000..8fc92a09a --- /dev/null +++ b/Emby.Server.Implementations/HttpServer/LoggerUtils.cs @@ -0,0 +1,43 @@ +using MediaBrowser.Model.Logging; +using System; +using System.Globalization; +using SocketHttpListener.Net; + +namespace Emby.Server.Implementations.HttpServer +{ + public static class LoggerUtils + { + /// + /// Logs the request. + /// + /// The logger. + /// The request. + public static void LogRequest(ILogger logger, HttpListenerRequest request) + { + var url = request.Url.ToString(); + + logger.Info("{0} {1}. UserAgent: {2}", request.IsWebSocketRequest ? "WS" : "HTTP " + request.HttpMethod, url, request.UserAgent ?? string.Empty); + } + + public static void LogRequest(ILogger logger, string url, string method, string userAgent) + { + logger.Info("{0} {1}. UserAgent: {2}", "HTTP " + method, url, userAgent ?? string.Empty); + } + + /// + /// Logs the response. + /// + /// The logger. + /// The status code. + /// The URL. + /// The end point. + /// The duration. + public static void LogResponse(ILogger logger, int statusCode, string url, string endPoint, TimeSpan duration) + { + var durationMs = duration.TotalMilliseconds; + var logSuffix = durationMs >= 1000 && durationMs < 60000 ? "ms (slow)" : "ms"; + + logger.Info("HTTP Response {0} to {1}. Time: {2}{3}. {4}", statusCode, endPoint, Convert.ToInt32(durationMs).ToString(CultureInfo.InvariantCulture), logSuffix, url); + } + } +} diff --git a/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs b/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs new file mode 100644 index 000000000..e88994bec --- /dev/null +++ b/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs @@ -0,0 +1,224 @@ +using MediaBrowser.Model.Logging; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Model.Services; + +namespace Emby.Server.Implementations.HttpServer +{ + public class RangeRequestWriter : IAsyncStreamWriter, IHttpResult + { + /// + /// Gets or sets the source stream. + /// + /// The source stream. + private Stream SourceStream { get; set; } + 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; } + private long TotalContentLength { get; set; } + + public Action OnComplete { get; set; } + private readonly ILogger _logger; + + private const int BufferSize = 81920; + + /// + /// The _options + /// + private readonly Dictionary _options = new Dictionary(); + + /// + /// The us culture + /// + private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); + + public List Cookies { get; private set; } + + /// + /// Additional HTTP Headers + /// + /// The headers. + public IDictionary Headers + { + get { return _options; } + } + + /// + /// Initializes a new instance of the class. + /// + /// The range header. + /// The source. + /// Type of the content. + /// if set to true [is head request]. + public RangeRequestWriter(string rangeHeader, Stream source, string contentType, bool isHeadRequest, ILogger logger) + { + if (string.IsNullOrEmpty(contentType)) + { + throw new ArgumentNullException("contentType"); + } + + RangeHeader = rangeHeader; + SourceStream = source; + IsHeadRequest = isHeadRequest; + this._logger = logger; + + ContentType = contentType; + Headers["Content-Type"] = contentType; + Headers["Accept-Ranges"] = "bytes"; + StatusCode = HttpStatusCode.PartialContent; + + Cookies = new List(); + SetRangeValues(); + } + + /// + /// Sets the range values. + /// + private void SetRangeValues() + { + var requestedRange = RequestedRanges[0]; + + TotalContentLength = SourceStream.Length; + + // 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 + Headers["Content-Length"] = RangeLength.ToString(UsCulture); + Headers["Content-Range"] = string.Format("bytes {0}-{1}/{2}", RangeStart, RangeEnd, TotalContentLength); + + if (RangeStart > 0) + { + SourceStream.Position = RangeStart; + } + } + + /// + /// The _requested ranges + /// + private List> _requestedRanges; + /// + /// Gets the requested ranges. + /// + /// The requested ranges. + protected List> RequestedRanges + { + get + { + if (_requestedRanges == null) + { + _requestedRanges = new List>(); + + // Example: bytes=0-,32-63 + var ranges = RangeHeader.Split('=')[1].Split(','); + + foreach (var range in ranges) + { + var vals = range.Split('-'); + + long start = 0; + long? end = null; + + if (!string.IsNullOrEmpty(vals[0])) + { + start = long.Parse(vals[0], UsCulture); + } + if (!string.IsNullOrEmpty(vals[1])) + { + end = long.Parse(vals[1], UsCulture); + } + + _requestedRanges.Add(new KeyValuePair(start, end)); + } + } + + return _requestedRanges; + } + } + + public async Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken) + { + try + { + // Headers only + if (IsHeadRequest) + { + return; + } + + using (var source = SourceStream) + { + // If the requested range is "0-", we can optimize by just doing a stream copy + if (RangeEnd >= TotalContentLength - 1) + { + await source.CopyToAsync(responseStream, BufferSize).ConfigureAwait(false); + } + else + { + await CopyToInternalAsync(source, responseStream, RangeLength).ConfigureAwait(false); + } + } + } + finally + { + if (OnComplete != null) + { + OnComplete(); + } + } + } + + private async Task CopyToInternalAsync(Stream source, Stream destination, long copyLength) + { + var array = new byte[BufferSize]; + int count; + while ((count = await source.ReadAsync(array, 0, array.Length).ConfigureAwait(false)) != 0) + { + var bytesToCopy = Math.Min(count, copyLength); + + await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToCopy)).ConfigureAwait(false); + + copyLength -= bytesToCopy; + + if (copyLength <= 0) + { + break; + } + } + } + + public string ContentType { get; set; } + + public IRequest RequestContext { get; set; } + + public object Response { get; set; } + + public int Status { get; set; } + + public HttpStatusCode StatusCode + { + get { return (HttpStatusCode)Status; } + set { Status = (int)value; } + } + + public string StatusDescription { get; set; } + } +} \ No newline at end of file diff --git a/Emby.Server.Implementations/HttpServer/ResponseFilter.cs b/Emby.Server.Implementations/HttpServer/ResponseFilter.cs new file mode 100644 index 000000000..6d9d7d921 --- /dev/null +++ b/Emby.Server.Implementations/HttpServer/ResponseFilter.cs @@ -0,0 +1,129 @@ +using MediaBrowser.Model.Logging; +using System; +using System.Globalization; +using System.Text; +using Emby.Server.Implementations.HttpServer.SocketSharp; +using MediaBrowser.Model.Services; + +namespace Emby.Server.Implementations.HttpServer +{ + public class ResponseFilter + { + private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); + private readonly ILogger _logger; + + public ResponseFilter(ILogger logger) + { + _logger = logger; + } + + /// + /// Filters the response. + /// + /// The req. + /// The res. + /// The dto. + public void FilterResponse(IRequest req, IResponse res, object dto) + { + // Try to prevent compatibility view + res.AddHeader("X-UA-Compatible", "IE=Edge"); + res.AddHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, Range, X-MediaBrowser-Token, X-Emby-Authorization"); + res.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS"); + res.AddHeader("Access-Control-Allow-Origin", "*"); + + var exception = dto as Exception; + + if (exception != null) + { + _logger.ErrorException("Error processing request for {0}", exception, req.RawUrl); + + if (!string.IsNullOrEmpty(exception.Message)) + { + var error = exception.Message.Replace(Environment.NewLine, " "); + error = RemoveControlCharacters(error); + + res.AddHeader("X-Application-Error-Code", error); + } + } + + var vary = "Accept-Encoding"; + + var hasHeaders = dto as IHasHeaders; + var sharpResponse = res as WebSocketSharpResponse; + + if (hasHeaders != null) + { + if (!hasHeaders.Headers.ContainsKey("Server")) + { + hasHeaders.Headers["Server"] = "Mono-HTTPAPI/1.1, UPnP/1.0 DLNADOC/1.50"; + //hasHeaders.Headers["Server"] = "Mono-HTTPAPI/1.1"; + } + + // Content length has to be explicitly set on on HttpListenerResponse or it won't be happy + string contentLength; + + if (hasHeaders.Headers.TryGetValue("Content-Length", out contentLength) && !string.IsNullOrEmpty(contentLength)) + { + var length = long.Parse(contentLength, UsCulture); + + if (length > 0) + { + res.SetContentLength(length); + + //var listenerResponse = res.OriginalResponse as HttpListenerResponse; + + //if (listenerResponse != null) + //{ + // // Disable chunked encoding. Technically this is only needed when using Content-Range, but + // // anytime we know the content length there's no need for it + // listenerResponse.SendChunked = false; + // return; + //} + + if (sharpResponse != null) + { + sharpResponse.SendChunked = false; + } + } + } + + string hasHeadersVary; + if (hasHeaders.Headers.TryGetValue("Vary", out hasHeadersVary)) + { + vary = hasHeadersVary; + } + + hasHeaders.Headers["Vary"] = vary; + } + + //res.KeepAlive = false; + + // Per Google PageSpeed + // This instructs the proxies to cache two versions of the resource: one compressed, and one uncompressed. + // The correct version of the resource is delivered based on the client request header. + // This is a good choice for applications that are singly homed and depend on public proxies for user locality. + res.AddHeader("Vary", vary); + } + + /// + /// Removes the control characters. + /// + /// The in string. + /// System.String. + public static string RemoveControlCharacters(string inString) + { + if (inString == null) return null; + + var newString = new StringBuilder(); + + foreach (var ch in inString) + { + if (!char.IsControl(ch)) + { + newString.Append(ch); + } + } + return newString.ToString(); + } + } +} diff --git a/Emby.Server.Implementations/HttpServer/SocketSharp/Extensions.cs b/Emby.Server.Implementations/HttpServer/SocketSharp/Extensions.cs new file mode 100644 index 000000000..07a338f19 --- /dev/null +++ b/Emby.Server.Implementations/HttpServer/SocketSharp/Extensions.cs @@ -0,0 +1,12 @@ +using SocketHttpListener.Net; + +namespace Emby.Server.Implementations.HttpServer.SocketSharp +{ + public static class Extensions + { + public static string GetOperationName(this HttpListenerRequest request) + { + return request.Url.Segments[request.Url.Segments.Length - 1]; + } + } +} diff --git a/Emby.Server.Implementations/HttpServer/SocketSharp/SharpWebSocket.cs b/Emby.Server.Implementations/HttpServer/SocketSharp/SharpWebSocket.cs new file mode 100644 index 000000000..0a312f7b9 --- /dev/null +++ b/Emby.Server.Implementations/HttpServer/SocketSharp/SharpWebSocket.cs @@ -0,0 +1,166 @@ +using MediaBrowser.Common.Events; +using MediaBrowser.Controller.Net; +using MediaBrowser.Model.Logging; +using System; +using System.Threading; +using System.Threading.Tasks; +using WebSocketState = MediaBrowser.Model.Net.WebSocketState; + +namespace Emby.Server.Implementations.HttpServer.SocketSharp +{ + public class SharpWebSocket : IWebSocket + { + /// + /// The logger + /// + private readonly ILogger _logger; + + public event EventHandler Closed; + + /// + /// Gets or sets the web socket. + /// + /// The web socket. + private SocketHttpListener.WebSocket WebSocket { get; set; } + + private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); + + public SharpWebSocket(SocketHttpListener.WebSocket socket, ILogger logger) + { + if (socket == null) + { + throw new ArgumentNullException("socket"); + } + + if (logger == null) + { + throw new ArgumentNullException("logger"); + } + + _logger = logger; + WebSocket = socket; + + socket.OnMessage += socket_OnMessage; + socket.OnClose += socket_OnClose; + socket.OnError += socket_OnError; + + WebSocket.ConnectAsServer(); + } + + void socket_OnError(object sender, SocketHttpListener.ErrorEventArgs e) + { + _logger.Error("Error in SharpWebSocket: {0}", e.Message ?? string.Empty); + //EventHelper.FireEventIfNotNull(Closed, this, EventArgs.Empty, _logger); + } + + void socket_OnClose(object sender, SocketHttpListener.CloseEventArgs e) + { + EventHelper.FireEventIfNotNull(Closed, this, EventArgs.Empty, _logger); + } + + void socket_OnMessage(object sender, SocketHttpListener.MessageEventArgs e) + { + //if (!string.IsNullOrWhiteSpace(e.Data)) + //{ + // if (OnReceive != null) + // { + // OnReceive(e.Data); + // } + // return; + //} + if (OnReceiveBytes != null) + { + OnReceiveBytes(e.RawData); + } + } + + /// + /// Gets or sets the state. + /// + /// The state. + public WebSocketState State + { + get + { + WebSocketState commonState; + + if (!Enum.TryParse(WebSocket.ReadyState.ToString(), true, out commonState)) + { + _logger.Warn("Unrecognized WebSocketState: {0}", WebSocket.ReadyState.ToString()); + } + + return commonState; + } + } + + /// + /// Sends the async. + /// + /// The bytes. + /// if set to true [end of message]. + /// The cancellation token. + /// Task. + public Task SendAsync(byte[] bytes, bool endOfMessage, CancellationToken cancellationToken) + { + var completionSource = new TaskCompletionSource(); + + WebSocket.SendAsync(bytes, res => completionSource.TrySetResult(true)); + + return completionSource.Task; + } + + /// + /// Sends the asynchronous. + /// + /// The text. + /// if set to true [end of message]. + /// The cancellation token. + /// Task. + public Task SendAsync(string text, bool endOfMessage, CancellationToken cancellationToken) + { + var completionSource = new TaskCompletionSource(); + + WebSocket.SendAsync(text, res => completionSource.TrySetResult(true)); + + return completionSource.Task; + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + } + + /// + /// Releases unmanaged and - optionally - managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool dispose) + { + if (dispose) + { + WebSocket.OnMessage -= socket_OnMessage; + WebSocket.OnClose -= socket_OnClose; + WebSocket.OnError -= socket_OnError; + + _cancellationTokenSource.Cancel(); + + WebSocket.Close(); + } + } + + /// + /// Gets or sets the receive action. + /// + /// The receive action. + public Action OnReceiveBytes { get; set; } + + /// + /// Gets or sets the on receive. + /// + /// The on receive. + public Action OnReceive { get; set; } + } +} diff --git a/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs b/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs new file mode 100644 index 000000000..0cb4d428b --- /dev/null +++ b/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs @@ -0,0 +1,203 @@ +using MediaBrowser.Controller.Net; +using MediaBrowser.Model.Logging; +using SocketHttpListener.Net; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Emby.Server.Implementations.Logging; +using MediaBrowser.Common.Net; +using MediaBrowser.Model.Cryptography; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Net; +using MediaBrowser.Model.Services; +using MediaBrowser.Model.Text; +using SocketHttpListener.Primitives; + +namespace Emby.Server.Implementations.HttpServer.SocketSharp +{ + public class WebSocketSharpListener : IHttpListener + { + private HttpListener _listener; + + private readonly ILogger _logger; + private readonly ICertificate _certificate; + private readonly IMemoryStreamFactory _memoryStreamProvider; + private readonly ITextEncoding _textEncoding; + private readonly INetworkManager _networkManager; + private readonly ISocketFactory _socketFactory; + private readonly ICryptoProvider _cryptoProvider; + private readonly IStreamFactory _streamFactory; + private readonly Func _httpRequestFactory; + + public WebSocketSharpListener(ILogger logger, ICertificate certificate, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding, INetworkManager networkManager, ISocketFactory socketFactory, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, Func httpRequestFactory) + { + _logger = logger; + _certificate = certificate; + _memoryStreamProvider = memoryStreamProvider; + _textEncoding = textEncoding; + _networkManager = networkManager; + _socketFactory = socketFactory; + _cryptoProvider = cryptoProvider; + _streamFactory = streamFactory; + _httpRequestFactory = httpRequestFactory; + } + + public Action ErrorHandler { get; set; } + public Func RequestHandler { get; set; } + + public Action WebSocketConnecting { get; set; } + + public Action WebSocketConnected { get; set; } + + public void Start(IEnumerable urlPrefixes) + { + if (_listener == null) + _listener = new HttpListener(new PatternsLogger(_logger), _cryptoProvider, _streamFactory, _socketFactory, _networkManager, _textEncoding, _memoryStreamProvider); + + if (_certificate != null) + { + _listener.LoadCert(_certificate); + } + + foreach (var prefix in urlPrefixes) + { + _logger.Info("Adding HttpListener prefix " + prefix); + _listener.Prefixes.Add(prefix); + } + + _listener.OnContext = ProcessContext; + + _listener.Start(); + } + + private void ProcessContext(HttpListenerContext context) + { + Task.Factory.StartNew(() => InitTask(context)); + } + + private Task InitTask(HttpListenerContext context) + { + IHttpRequest httpReq = null; + var request = context.Request; + + try + { + if (request.IsWebSocketRequest) + { + LoggerUtils.LogRequest(_logger, request); + + ProcessWebSocketRequest(context); + return Task.FromResult(true); + } + + httpReq = GetRequest(context); + } + catch (Exception ex) + { + _logger.ErrorException("Error processing request", ex); + + httpReq = httpReq ?? GetRequest(context); + ErrorHandler(ex, httpReq); + return Task.FromResult(true); + } + + return RequestHandler(httpReq, request.Url); + } + + private void ProcessWebSocketRequest(HttpListenerContext ctx) + { + try + { + var endpoint = ctx.Request.RemoteEndPoint.ToString(); + var url = ctx.Request.RawUrl; + + var connectingArgs = new WebSocketConnectingEventArgs + { + Url = url, + QueryString = ctx.Request.QueryString, + Endpoint = endpoint + }; + + if (WebSocketConnecting != null) + { + WebSocketConnecting(connectingArgs); + } + + if (connectingArgs.AllowConnection) + { + _logger.Debug("Web socket connection allowed"); + + var webSocketContext = ctx.AcceptWebSocket(null); + + if (WebSocketConnected != null) + { + WebSocketConnected(new WebSocketConnectEventArgs + { + Url = url, + QueryString = ctx.Request.QueryString, + WebSocket = new SharpWebSocket(webSocketContext.WebSocket, _logger), + Endpoint = endpoint + }); + } + } + else + { + _logger.Warn("Web socket connection not allowed"); + ctx.Response.StatusCode = 401; + ctx.Response.Close(); + } + } + catch (Exception ex) + { + _logger.ErrorException("AcceptWebSocketAsync error", ex); + ctx.Response.StatusCode = 500; + ctx.Response.Close(); + } + } + + private IHttpRequest GetRequest(HttpListenerContext httpContext) + { + return _httpRequestFactory(httpContext); + } + + public void Stop() + { + if (_listener != null) + { + foreach (var prefix in _listener.Prefixes.ToList()) + { + _listener.Prefixes.Remove(prefix); + } + + _listener.Close(); + } + } + + public void Dispose() + { + Dispose(true); + } + + private bool _disposed; + private readonly object _disposeLock = new object(); + protected virtual void Dispose(bool disposing) + { + if (_disposed) return; + + lock (_disposeLock) + { + if (_disposed) return; + + if (disposing) + { + Stop(); + } + + //release unmanaged resources here... + _disposed = true; + } + } + } + +} \ No newline at end of file diff --git a/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs b/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs new file mode 100644 index 000000000..de0b33fe3 --- /dev/null +++ b/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs @@ -0,0 +1,204 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Text; +using MediaBrowser.Model.Logging; +using HttpListenerResponse = SocketHttpListener.Net.HttpListenerResponse; +using IHttpResponse = MediaBrowser.Model.Services.IHttpResponse; +using IRequest = MediaBrowser.Model.Services.IRequest; + +namespace Emby.Server.Implementations.HttpServer.SocketSharp +{ + public class WebSocketSharpResponse : IHttpResponse + { + private readonly ILogger _logger; + private readonly HttpListenerResponse _response; + + public WebSocketSharpResponse(ILogger logger, HttpListenerResponse response, IRequest request) + { + _logger = logger; + this._response = response; + Items = new Dictionary(); + Request = request; + } + + public IRequest Request { get; private set; } + public bool UseBufferedStream { get; set; } + public Dictionary Items { get; private set; } + public object OriginalResponse + { + get { return _response; } + } + + public int StatusCode + { + get { return this._response.StatusCode; } + set { this._response.StatusCode = value; } + } + + public string StatusDescription + { + get { return this._response.StatusDescription; } + set { this._response.StatusDescription = value; } + } + + public string ContentType + { + get { return _response.ContentType; } + set { _response.ContentType = value; } + } + + //public ICookies Cookies { get; set; } + + public void AddHeader(string name, string value) + { + if (string.Equals(name, "Content-Type", StringComparison.OrdinalIgnoreCase)) + { + ContentType = value; + return; + } + + _response.AddHeader(name, value); + } + + public string GetHeader(string name) + { + return _response.Headers[name]; + } + + public void Redirect(string url) + { + _response.Redirect(url); + } + + public Stream OutputStream + { + get { return _response.OutputStream; } + } + + public object Dto { get; set; } + + public void Write(string text) + { + var bOutput = System.Text.Encoding.UTF8.GetBytes(text); + _response.ContentLength64 = bOutput.Length; + + var outputStream = _response.OutputStream; + outputStream.Write(bOutput, 0, bOutput.Length); + Close(); + } + + public void Close() + { + if (!this.IsClosed) + { + this.IsClosed = true; + + try + { + CloseOutputStream(this._response); + } + catch (Exception ex) + { + _logger.ErrorException("Error closing HttpListener output stream", ex); + } + } + } + + public void CloseOutputStream(HttpListenerResponse response) + { + try + { + response.OutputStream.Flush(); + response.OutputStream.Dispose(); + response.Close(); + } + catch (Exception ex) + { + _logger.ErrorException("Error in HttpListenerResponseWrapper: " + ex.Message, ex); + } + } + + public void End() + { + Close(); + } + + public void Flush() + { + _response.OutputStream.Flush(); + } + + public bool IsClosed + { + get; + private set; + } + + public void SetContentLength(long contentLength) + { + //you can happily set the Content-Length header in Asp.Net + //but HttpListener will complain if you do - you have to set ContentLength64 on the response. + //workaround: HttpListener throws "The parameter is incorrect" exceptions when we try to set the Content-Length header + _response.ContentLength64 = contentLength; + } + + public void SetCookie(Cookie cookie) + { + var cookieStr = AsHeaderValue(cookie); + _response.Headers.Add("Set-Cookie", cookieStr); + } + + public static string AsHeaderValue(Cookie cookie) + { + var defaultExpires = DateTime.MinValue; + + var path = cookie.Expires == defaultExpires + ? "/" + : cookie.Path ?? "/"; + + var sb = new StringBuilder(); + + sb.Append($"{cookie.Name}={cookie.Value};path={path}"); + + if (cookie.Expires != defaultExpires) + { + sb.Append($";expires={cookie.Expires:R}"); + } + + if (!string.IsNullOrEmpty(cookie.Domain)) + { + sb.Append($";domain={cookie.Domain}"); + } + //else if (restrictAllCookiesToDomain != null) + //{ + // sb.Append($";domain={restrictAllCookiesToDomain}"); + //} + + if (cookie.Secure) + { + sb.Append(";Secure"); + } + if (cookie.HttpOnly) + { + sb.Append(";HttpOnly"); + } + + return sb.ToString(); + } + + + public bool SendChunked + { + get { return _response.SendChunked; } + set { _response.SendChunked = value; } + } + + public bool KeepAlive { get; set; } + + public void ClearCookies() + { + } + } +} diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index 9c1d7fdf1..2a5706b3b 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -70,10 +70,10 @@ namespace Emby.Server.Implementations.Library private readonly Func _connectFactory; private readonly IServerApplicationHost _appHost; private readonly IFileSystem _fileSystem; - private readonly ICryptographyProvider _cryptographyProvider; + private readonly ICryptoProvider _cryptographyProvider; private readonly string _defaultUserName; - public UserManager(ILogger logger, IServerConfigurationManager configurationManager, IUserRepository userRepository, IXmlSerializer xmlSerializer, INetworkManager networkManager, Func imageProcessorFactory, Func dtoServiceFactory, Func connectFactory, IServerApplicationHost appHost, IJsonSerializer jsonSerializer, IFileSystem fileSystem, ICryptographyProvider cryptographyProvider, string defaultUserName) + public UserManager(ILogger logger, IServerConfigurationManager configurationManager, IUserRepository userRepository, IXmlSerializer xmlSerializer, INetworkManager networkManager, Func imageProcessorFactory, Func dtoServiceFactory, Func connectFactory, IServerApplicationHost appHost, IJsonSerializer jsonSerializer, IFileSystem fileSystem, ICryptoProvider cryptographyProvider, string defaultUserName) { _logger = logger; UserRepository = userRepository; @@ -334,7 +334,7 @@ namespace Emby.Server.Implementations.Library /// System.String. private string GetSha1String(string str) { - return BitConverter.ToString(_cryptographyProvider.GetSHA1Bytes(Encoding.UTF8.GetBytes(str))).Replace("-", string.Empty); + return BitConverter.ToString(_cryptographyProvider.ComputeSHA1(Encoding.UTF8.GetBytes(str))).Replace("-", string.Empty); } /// diff --git a/Emby.Server.Implementations/Security/MBLicenseFile.cs b/Emby.Server.Implementations/Security/MBLicenseFile.cs index 7cb6165a5..4b6a82b6c 100644 --- a/Emby.Server.Implementations/Security/MBLicenseFile.cs +++ b/Emby.Server.Implementations/Security/MBLicenseFile.cs @@ -15,7 +15,7 @@ namespace Emby.Server.Implementations.Security { private readonly IApplicationPaths _appPaths; private readonly IFileSystem _fileSystem; - private readonly ICryptographyProvider _cryptographyProvider; + private readonly ICryptoProvider _cryptographyProvider; public string RegKey { @@ -43,7 +43,7 @@ namespace Emby.Server.Implementations.Security private readonly object _fileLock = new object(); private string _regKey; - public MBLicenseFile(IApplicationPaths appPaths, IFileSystem fileSystem, ICryptographyProvider cryptographyProvider) + public MBLicenseFile(IApplicationPaths appPaths, IFileSystem fileSystem, ICryptoProvider cryptographyProvider) { _appPaths = appPaths; _fileSystem = fileSystem; @@ -59,7 +59,7 @@ namespace Emby.Server.Implementations.Security public void AddRegCheck(string featureId) { - var key = new Guid(_cryptographyProvider.GetMD5Bytes(Encoding.Unicode.GetBytes(featureId))); + var key = new Guid(_cryptographyProvider.ComputeMD5(Encoding.Unicode.GetBytes(featureId))); var value = DateTime.UtcNow; SetUpdateRecord(key, value); @@ -68,7 +68,7 @@ namespace Emby.Server.Implementations.Security public void RemoveRegCheck(string featureId) { - var key = new Guid(_cryptographyProvider.GetMD5Bytes(Encoding.Unicode.GetBytes(featureId))); + var key = new Guid(_cryptographyProvider.ComputeMD5(Encoding.Unicode.GetBytes(featureId))); DateTime val; _updateRecords.TryRemove(key, out val); @@ -79,7 +79,7 @@ namespace Emby.Server.Implementations.Security public DateTime LastChecked(string featureId) { DateTime last; - _updateRecords.TryGetValue(new Guid(_cryptographyProvider.GetMD5Bytes(Encoding.Unicode.GetBytes(featureId))), out last); + _updateRecords.TryGetValue(new Guid(_cryptographyProvider.ComputeMD5(Encoding.Unicode.GetBytes(featureId))), out last); // guard agains people just putting a large number in the file return last < DateTime.UtcNow ? last : DateTime.MinValue; diff --git a/Emby.Server.Implementations/Security/PluginSecurityManager.cs b/Emby.Server.Implementations/Security/PluginSecurityManager.cs index c3a7e9450..61d4f5252 100644 --- a/Emby.Server.Implementations/Security/PluginSecurityManager.cs +++ b/Emby.Server.Implementations/Security/PluginSecurityManager.cs @@ -64,7 +64,7 @@ namespace Emby.Server.Implementations.Security private readonly ILogger _logger; private readonly IApplicationPaths _appPaths; private readonly IFileSystem _fileSystem; - private readonly ICryptographyProvider _cryptographyProvider; + private readonly ICryptoProvider _cryptographyProvider; private IEnumerable _registeredEntities; protected IEnumerable RegisteredEntities @@ -79,7 +79,7 @@ namespace Emby.Server.Implementations.Security /// Initializes a new instance of the class. /// public PluginSecurityManager(IServerApplicationHost appHost, IHttpClient httpClient, IJsonSerializer jsonSerializer, - IApplicationPaths appPaths, ILogManager logManager, IFileSystem fileSystem, ICryptographyProvider cryptographyProvider) + IApplicationPaths appPaths, ILogManager logManager, IFileSystem fileSystem, ICryptoProvider cryptographyProvider) { if (httpClient == null) { diff --git a/Emby.Server.Implementations/ServerManager/ServerManager.cs b/Emby.Server.Implementations/ServerManager/ServerManager.cs index 83ce0b6a3..f660d01ec 100644 --- a/Emby.Server.Implementations/ServerManager/ServerManager.cs +++ b/Emby.Server.Implementations/ServerManager/ServerManager.cs @@ -15,7 +15,7 @@ using System.Threading.Tasks; using MediaBrowser.Common.IO; using MediaBrowser.Model.IO; using MediaBrowser.Model.Services; -using MediaBrowser.Model.TextEncoding; +using MediaBrowser.Model.Text; namespace Emby.Server.Implementations.ServerManager { @@ -75,8 +75,8 @@ namespace Emby.Server.Implementations.ServerManager private readonly List _webSocketListeners = new List(); private bool _disposed; - private readonly IMemoryStreamProvider _memoryStreamProvider; - private readonly IEncoding _textEncoding; + private readonly IMemoryStreamFactory _memoryStreamProvider; + private readonly ITextEncoding _textEncoding; /// /// Initializes a new instance of the class. @@ -86,7 +86,7 @@ namespace Emby.Server.Implementations.ServerManager /// The logger. /// The configuration manager. /// applicationHost - public ServerManager(IServerApplicationHost applicationHost, IJsonSerializer jsonSerializer, ILogger logger, IServerConfigurationManager configurationManager, IMemoryStreamProvider memoryStreamProvider, IEncoding textEncoding) + public ServerManager(IServerApplicationHost applicationHost, IJsonSerializer jsonSerializer, ILogger logger, IServerConfigurationManager configurationManager, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding) { if (applicationHost == null) { diff --git a/Emby.Server.Implementations/ServerManager/WebSocketConnection.cs b/Emby.Server.Implementations/ServerManager/WebSocketConnection.cs index dd17edea5..4608a13e6 100644 --- a/Emby.Server.Implementations/ServerManager/WebSocketConnection.cs +++ b/Emby.Server.Implementations/ServerManager/WebSocketConnection.cs @@ -12,7 +12,7 @@ using System.Threading.Tasks; using MediaBrowser.Common.IO; using MediaBrowser.Model.IO; using MediaBrowser.Model.Services; -using MediaBrowser.Model.TextEncoding; +using MediaBrowser.Model.Text; using UniversalDetector; namespace Emby.Server.Implementations.ServerManager @@ -77,8 +77,8 @@ namespace Emby.Server.Implementations.ServerManager /// /// The query string. public QueryParamCollection QueryString { get; set; } - private readonly IMemoryStreamProvider _memoryStreamProvider; - private readonly IEncoding _textEncoding; + private readonly IMemoryStreamFactory _memoryStreamProvider; + private readonly ITextEncoding _textEncoding; /// /// Initializes a new instance of the class. @@ -88,7 +88,7 @@ namespace Emby.Server.Implementations.ServerManager /// The json serializer. /// The logger. /// socket - public WebSocketConnection(IWebSocket socket, string remoteEndPoint, IJsonSerializer jsonSerializer, ILogger logger, IMemoryStreamProvider memoryStreamProvider, IEncoding textEncoding) + public WebSocketConnection(IWebSocket socket, string remoteEndPoint, IJsonSerializer jsonSerializer, ILogger logger, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding) { if (socket == null) { @@ -145,7 +145,7 @@ namespace Emby.Server.Implementations.ServerManager } else { - OnReceiveInternal(_textEncoding.GetASCIIString(bytes, 0, bytes.Length)); + OnReceiveInternal(_textEncoding.GetASCIIEncoding().GetString(bytes, 0, bytes.Length)); } } private string DetectCharset(byte[] bytes) diff --git a/Emby.Server.Implementations/Sync/MediaSync.cs b/Emby.Server.Implementations/Sync/MediaSync.cs index b420a3df4..fa8388b6c 100644 --- a/Emby.Server.Implementations/Sync/MediaSync.cs +++ b/Emby.Server.Implementations/Sync/MediaSync.cs @@ -29,12 +29,12 @@ namespace Emby.Server.Implementations.Sync private readonly ILogger _logger; private readonly IFileSystem _fileSystem; private readonly IConfigurationManager _config; - private readonly ICryptographyProvider _cryptographyProvider; + private readonly ICryptoProvider _cryptographyProvider; public const string PathSeparatorString = "/"; public const char PathSeparatorChar = '/'; - public MediaSync(ILogger logger, ISyncManager syncManager, IServerApplicationHost appHost, IFileSystem fileSystem, IConfigurationManager config, ICryptographyProvider cryptographyProvider) + public MediaSync(ILogger logger, ISyncManager syncManager, IServerApplicationHost appHost, IFileSystem fileSystem, IConfigurationManager config, ICryptoProvider cryptographyProvider) { _logger = logger; _syncManager = syncManager; @@ -370,7 +370,7 @@ namespace Emby.Server.Implementations.Sync private byte[] CreateMd5(byte[] value) { - return _cryptographyProvider.GetMD5Bytes(value); + return _cryptographyProvider.ComputeMD5(value); } public LocalItem CreateLocalItem(IServerSyncProvider provider, SyncedItem syncedItem, SyncJob job, SyncTarget target, BaseItemDto libraryItem, string serverId, string serverName, string originalFileName) diff --git a/Emby.Server.Implementations/Sync/MultiProviderSync.cs b/Emby.Server.Implementations/Sync/MultiProviderSync.cs index db6cfcbd6..8189b8550 100644 --- a/Emby.Server.Implementations/Sync/MultiProviderSync.cs +++ b/Emby.Server.Implementations/Sync/MultiProviderSync.cs @@ -23,9 +23,9 @@ namespace Emby.Server.Implementations.Sync private readonly ILogger _logger; private readonly IFileSystem _fileSystem; private readonly IConfigurationManager _config; - private readonly ICryptographyProvider _cryptographyProvider; + private readonly ICryptoProvider _cryptographyProvider; - public MultiProviderSync(SyncManager syncManager, IServerApplicationHost appHost, ILogger logger, IFileSystem fileSystem, IConfigurationManager config, ICryptographyProvider cryptographyProvider) + public MultiProviderSync(SyncManager syncManager, IServerApplicationHost appHost, ILogger logger, IFileSystem fileSystem, IConfigurationManager config, ICryptoProvider cryptographyProvider) { _syncManager = syncManager; _appHost = appHost; diff --git a/Emby.Server.Implementations/Sync/ServerSyncScheduledTask.cs b/Emby.Server.Implementations/Sync/ServerSyncScheduledTask.cs index 17171633e..09a0bfde4 100644 --- a/Emby.Server.Implementations/Sync/ServerSyncScheduledTask.cs +++ b/Emby.Server.Implementations/Sync/ServerSyncScheduledTask.cs @@ -20,9 +20,9 @@ namespace Emby.Server.Implementations.Sync private readonly IFileSystem _fileSystem; private readonly IServerApplicationHost _appHost; private readonly IConfigurationManager _config; - private readonly ICryptographyProvider _cryptographyProvider; + private readonly ICryptoProvider _cryptographyProvider; - public ServerSyncScheduledTask(ISyncManager syncManager, ILogger logger, IFileSystem fileSystem, IServerApplicationHost appHost, IConfigurationManager config, ICryptographyProvider cryptographyProvider) + public ServerSyncScheduledTask(ISyncManager syncManager, ILogger logger, IFileSystem fileSystem, IServerApplicationHost appHost, IConfigurationManager config, ICryptoProvider cryptographyProvider) { _syncManager = syncManager; _logger = logger; diff --git a/Emby.Server.Implementations/Sync/SyncManager.cs b/Emby.Server.Implementations/Sync/SyncManager.cs index d06ed49fd..13f60f5ee 100644 --- a/Emby.Server.Implementations/Sync/SyncManager.cs +++ b/Emby.Server.Implementations/Sync/SyncManager.cs @@ -51,7 +51,7 @@ namespace Emby.Server.Implementations.Sync private readonly Func _mediaSourceManager; private readonly IJsonSerializer _json; private readonly ITaskManager _taskManager; - private readonly IMemoryStreamProvider _memoryStreamProvider; + private readonly IMemoryStreamFactory _memoryStreamProvider; private ISyncProvider[] _providers = { }; @@ -61,7 +61,7 @@ namespace Emby.Server.Implementations.Sync public event EventHandler> SyncJobItemUpdated; public event EventHandler> SyncJobItemCreated; - public SyncManager(ILibraryManager libraryManager, ISyncRepository repo, IImageProcessor imageProcessor, ILogger logger, IUserManager userManager, Func dtoService, IServerApplicationHost appHost, ITVSeriesManager tvSeriesManager, Func mediaEncoder, IFileSystem fileSystem, Func subtitleEncoder, IConfigurationManager config, IUserDataManager userDataManager, Func mediaSourceManager, IJsonSerializer json, ITaskManager taskManager, IMemoryStreamProvider memoryStreamProvider) + public SyncManager(ILibraryManager libraryManager, ISyncRepository repo, IImageProcessor imageProcessor, ILogger logger, IUserManager userManager, Func dtoService, IServerApplicationHost appHost, ITVSeriesManager tvSeriesManager, Func mediaEncoder, IFileSystem fileSystem, Func subtitleEncoder, IConfigurationManager config, IUserDataManager userDataManager, Func mediaSourceManager, IJsonSerializer json, ITaskManager taskManager, IMemoryStreamFactory memoryStreamProvider) { _libraryManager = libraryManager; _repo = repo; diff --git a/Emby.Server.Implementations/Sync/TargetDataProvider.cs b/Emby.Server.Implementations/Sync/TargetDataProvider.cs index a0e0f4313..fbd82aa7a 100644 --- a/Emby.Server.Implementations/Sync/TargetDataProvider.cs +++ b/Emby.Server.Implementations/Sync/TargetDataProvider.cs @@ -26,9 +26,9 @@ namespace Emby.Server.Implementations.Sync private readonly IFileSystem _fileSystem; private readonly IApplicationPaths _appPaths; private readonly IServerApplicationHost _appHost; - private readonly IMemoryStreamProvider _memoryStreamProvider; + private readonly IMemoryStreamFactory _memoryStreamProvider; - public TargetDataProvider(IServerSyncProvider provider, SyncTarget target, IServerApplicationHost appHost, ILogger logger, IJsonSerializer json, IFileSystem fileSystem, IApplicationPaths appPaths, IMemoryStreamProvider memoryStreamProvider) + public TargetDataProvider(IServerSyncProvider provider, SyncTarget target, IServerApplicationHost appHost, ILogger logger, IJsonSerializer json, IFileSystem fileSystem, IApplicationPaths appPaths, IMemoryStreamFactory memoryStreamProvider) { _logger = logger; _json = json; diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index 3c7a8242c..52bf09284 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -119,9 +119,9 @@ namespace Emby.Server.Implementations.Updates /// The application host. private readonly IApplicationHost _applicationHost; - private readonly ICryptographyProvider _cryptographyProvider; + private readonly ICryptoProvider _cryptographyProvider; - public InstallationManager(ILogger logger, IApplicationHost appHost, IApplicationPaths appPaths, IHttpClient httpClient, IJsonSerializer jsonSerializer, ISecurityManager securityManager, IConfigurationManager config, IFileSystem fileSystem, ICryptographyProvider cryptographyProvider) + public InstallationManager(ILogger logger, IApplicationHost appHost, IApplicationPaths appPaths, IHttpClient httpClient, IJsonSerializer jsonSerializer, ISecurityManager securityManager, IConfigurationManager config, IFileSystem fileSystem, ICryptoProvider cryptographyProvider) { if (logger == null) { @@ -606,7 +606,7 @@ namespace Emby.Server.Implementations.Updates { using (var stream = _fileSystem.OpenRead(tempFile)) { - var check = Guid.Parse(BitConverter.ToString(_cryptographyProvider.GetMD5Bytes(stream)).Replace("-", String.Empty)); + var check = Guid.Parse(BitConverter.ToString(_cryptographyProvider.ComputeMD5(stream)).Replace("-", String.Empty)); if (check != packageChecksum) { throw new Exception(string.Format("Download validation failed for {0}. Probably corrupted during transfer.", package.name)); diff --git a/MediaBrowser.Api/Dlna/DlnaServerService.cs b/MediaBrowser.Api/Dlna/DlnaServerService.cs index 93d0b3d55..8125951b5 100644 --- a/MediaBrowser.Api/Dlna/DlnaServerService.cs +++ b/MediaBrowser.Api/Dlna/DlnaServerService.cs @@ -111,9 +111,9 @@ namespace MediaBrowser.Api.Dlna private readonly IMediaReceiverRegistrar _mediaReceiverRegistrar; private const string XMLContentType = "text/xml; charset=UTF-8"; - private readonly IMemoryStreamProvider _memoryStreamProvider; + private readonly IMemoryStreamFactory _memoryStreamProvider; - public DlnaServerService(IDlnaManager dlnaManager, IContentDirectory contentDirectory, IConnectionManager connectionManager, IMediaReceiverRegistrar mediaReceiverRegistrar, IMemoryStreamProvider memoryStreamProvider) + public DlnaServerService(IDlnaManager dlnaManager, IContentDirectory contentDirectory, IConnectionManager connectionManager, IMediaReceiverRegistrar mediaReceiverRegistrar, IMemoryStreamFactory memoryStreamProvider) { _dlnaManager = dlnaManager; _contentDirectory = contentDirectory; diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index c62889214..4fa663b19 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1263,6 +1263,7 @@ namespace MediaBrowser.Api.Playback private bool EnableThrottling(StreamState state) { + return false; // do not use throttling with hardware encoders return state.InputProtocol == MediaProtocol.File && state.RunTimeTicks.HasValue && diff --git a/MediaBrowser.Common/Extensions/BaseExtensions.cs b/MediaBrowser.Common/Extensions/BaseExtensions.cs index 09f6e8706..d7f4424fa 100644 --- a/MediaBrowser.Common/Extensions/BaseExtensions.cs +++ b/MediaBrowser.Common/Extensions/BaseExtensions.cs @@ -10,7 +10,7 @@ namespace MediaBrowser.Common.Extensions /// public static class BaseExtensions { - public static ICryptographyProvider CryptographyProvider { get; set; } + public static ICryptoProvider CryptographyProvider { get; set; } /// /// Strips the HTML. diff --git a/MediaBrowser.Common/Net/INetworkManager.cs b/MediaBrowser.Common/Net/INetworkManager.cs index fe60c7ebf..779db0a82 100644 --- a/MediaBrowser.Common/Net/INetworkManager.cs +++ b/MediaBrowser.Common/Net/INetworkManager.cs @@ -2,6 +2,7 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.Net; using System.Collections.Generic; using System.Net; +using System.Threading.Tasks; namespace MediaBrowser.Common.Net { @@ -46,10 +47,14 @@ namespace MediaBrowser.Common.Net /// true if [is in local network] [the specified endpoint]; otherwise, false. bool IsInLocalNetwork(string endpoint); + IEnumerable GetLocalIpAddresses(); + IpAddressInfo ParseIpAddress(string ipAddress); bool TryParseIpAddress(string ipAddress, out IpAddressInfo ipAddressInfo); + Task GetHostAddressesAsync(string host); + /// /// Generates a self signed certificate at the locatation specified by . /// diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index 2fd96577a..d2cf23f43 100644 --- a/MediaBrowser.Controller/IServerApplicationHost.cs +++ b/MediaBrowser.Controller/IServerApplicationHost.cs @@ -85,7 +85,7 @@ namespace MediaBrowser.Controller /// /// Gets the local API URL. /// - string GetLocalApiUrl(string ipAddress, bool isIpv6); + string GetLocalApiUrl(IpAddressInfo address); void LaunchUrl(string url); diff --git a/MediaBrowser.Controller/MediaEncoding/MediaStreamSelector.cs b/MediaBrowser.Controller/MediaEncoding/MediaStreamSelector.cs index 52199c5a2..8169cc7d9 100644 --- a/MediaBrowser.Controller/MediaEncoding/MediaStreamSelector.cs +++ b/MediaBrowser.Controller/MediaEncoding/MediaStreamSelector.cs @@ -69,7 +69,8 @@ namespace MediaBrowser.Controller.MediaEncoding // if the audio language is not understood by the user, load their preferred subs, if there are any if (!ContainsOrdinal(preferredLanguages, audioTrackLanguage)) { - stream = streams.Where(s => !s.IsForced).FirstOrDefault(s => ContainsOrdinal(preferredLanguages, s.Language)); + stream = streams.Where(s => !s.IsForced).FirstOrDefault(s => ContainsOrdinal(preferredLanguages, s.Language)) ?? + streams.FirstOrDefault(s => ContainsOrdinal(preferredLanguages, s.Language)); } } else if (mode == SubtitlePlaybackMode.Always) diff --git a/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs b/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs index d45416f53..b025011d7 100644 --- a/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs +++ b/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs @@ -7,7 +7,7 @@ namespace MediaBrowser.Controller.Net { public class AuthenticatedAttribute : Attribute, IHasRequestFilter, IAuthenticationAttributes { - public IAuthService AuthService { get; set; } + public static IAuthService AuthService { get; set; } /// /// Gets or sets the roles. @@ -26,7 +26,7 @@ namespace MediaBrowser.Controller.Net /// /// true if [allow before startup wizard]; otherwise, false. public bool AllowBeforeStartupWizard { get; set; } - + /// /// The request filter is executed before the service. /// @@ -40,15 +40,6 @@ namespace MediaBrowser.Controller.Net AuthService.Authenticate(serviceRequest, this); } - /// - /// A new shallow copy of this filter is used on every request. - /// - /// IHasRequestFilter. - public IHasRequestFilter Copy() - { - return this; - } - /// /// Order in which Request Filters are executed. /// <0 Executed before global request filters @@ -60,7 +51,6 @@ namespace MediaBrowser.Controller.Net get { return 0; } } - public IEnumerable GetRoles() { return (Roles ?? string.Empty).Split(',') diff --git a/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs b/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs index ee7cb9dda..bf7343f3d 100644 --- a/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs +++ b/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using MediaBrowser.Model.IO; -using MediaBrowser.Model.TextEncoding; +using MediaBrowser.Model.Text; namespace MediaBrowser.MediaEncoding.BdInfo { @@ -15,9 +15,9 @@ namespace MediaBrowser.MediaEncoding.BdInfo public class BdInfoExaminer : IBlurayExaminer { private readonly IFileSystem _fileSystem; - private readonly IEncoding _textEncoding; + private readonly ITextEncoding _textEncoding; - public BdInfoExaminer(IFileSystem fileSystem, IEncoding textEncoding) + public BdInfoExaminer(IFileSystem fileSystem, ITextEncoding textEncoding) { _fileSystem = fileSystem; _textEncoding = textEncoding; diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 098cd14db..0d7a7912a 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -79,7 +79,7 @@ namespace MediaBrowser.MediaEncoding.Encoder private readonly IHttpClient _httpClient; private readonly IZipClient _zipClient; private readonly IProcessFactory _processFactory; - private readonly IMemoryStreamProvider _memoryStreamProvider; + private readonly IMemoryStreamFactory _memoryStreamProvider; private readonly List _runningProcesses = new List(); private readonly bool _hasExternalEncoder; @@ -88,7 +88,7 @@ namespace MediaBrowser.MediaEncoding.Encoder private readonly int DefaultImageExtractionTimeoutMs; private readonly bool EnableEncoderFontFile; - public MediaEncoder(ILogger logger, IJsonSerializer jsonSerializer, string ffMpegPath, string ffProbePath, bool hasExternalEncoder, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, IChannelManager channelManager, ISessionManager sessionManager, Func subtitleEncoder, Func mediaSourceManager, IHttpClient httpClient, IZipClient zipClient, IMemoryStreamProvider memoryStreamProvider, IProcessFactory processFactory, + public MediaEncoder(ILogger logger, IJsonSerializer jsonSerializer, string ffMpegPath, string ffProbePath, bool hasExternalEncoder, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, IChannelManager channelManager, ISessionManager sessionManager, Func subtitleEncoder, Func mediaSourceManager, IHttpClient httpClient, IZipClient zipClient, IMemoryStreamFactory memoryStreamProvider, IProcessFactory processFactory, int defaultImageExtractionTimeoutMs, bool enableEncoderFontFile) { diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index f0c4c465d..6a1583094 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -23,9 +23,9 @@ namespace MediaBrowser.MediaEncoding.Probing private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly ILogger _logger; private readonly IFileSystem _fileSystem; - private readonly IMemoryStreamProvider _memoryStreamProvider; + private readonly IMemoryStreamFactory _memoryStreamProvider; - public ProbeResultNormalizer(ILogger logger, IFileSystem fileSystem, IMemoryStreamProvider memoryStreamProvider) + public ProbeResultNormalizer(ILogger logger, IFileSystem fileSystem, IMemoryStreamFactory memoryStreamProvider) { _logger = logger; _fileSystem = fileSystem; diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index 0baee67ea..5d065f528 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -19,7 +19,7 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.IO; using MediaBrowser.Model.Diagnostics; -using MediaBrowser.Model.TextEncoding; +using MediaBrowser.Model.Text; using UniversalDetector; namespace MediaBrowser.MediaEncoding.Subtitles @@ -34,11 +34,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles private readonly IJsonSerializer _json; private readonly IHttpClient _httpClient; private readonly IMediaSourceManager _mediaSourceManager; - private readonly IMemoryStreamProvider _memoryStreamProvider; + private readonly IMemoryStreamFactory _memoryStreamProvider; private readonly IProcessFactory _processFactory; - private readonly IEncoding _textEncoding; + private readonly ITextEncoding _textEncoding; - public SubtitleEncoder(ILibraryManager libraryManager, ILogger logger, IApplicationPaths appPaths, IFileSystem fileSystem, IMediaEncoder mediaEncoder, IJsonSerializer json, IHttpClient httpClient, IMediaSourceManager mediaSourceManager, IMemoryStreamProvider memoryStreamProvider, IProcessFactory processFactory, IEncoding textEncoding) + public SubtitleEncoder(ILibraryManager libraryManager, ILogger logger, IApplicationPaths appPaths, IFileSystem fileSystem, IMediaEncoder mediaEncoder, IJsonSerializer json, IHttpClient httpClient, IMediaSourceManager mediaSourceManager, IMemoryStreamFactory memoryStreamProvider, IProcessFactory processFactory, ITextEncoding textEncoding) { _libraryManager = libraryManager; _logger = logger; diff --git a/MediaBrowser.Model/Cryptography/ICryptoProvider.cs b/MediaBrowser.Model/Cryptography/ICryptoProvider.cs new file mode 100644 index 000000000..7a82dee52 --- /dev/null +++ b/MediaBrowser.Model/Cryptography/ICryptoProvider.cs @@ -0,0 +1,13 @@ +using System; +using System.IO; + +namespace MediaBrowser.Model.Cryptography +{ + public interface ICryptoProvider + { + Guid GetMD5(string str); + byte[] ComputeMD5(Stream str); + byte[] ComputeMD5(byte[] bytes); + byte[] ComputeSHA1(byte[] bytes); + } +} \ No newline at end of file diff --git a/MediaBrowser.Model/Cryptography/ICryptographyProvider.cs b/MediaBrowser.Model/Cryptography/ICryptographyProvider.cs deleted file mode 100644 index a3f86f9e2..000000000 --- a/MediaBrowser.Model/Cryptography/ICryptographyProvider.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.IO; - -namespace MediaBrowser.Model.Cryptography -{ - public interface ICryptographyProvider - { - Guid GetMD5(string str); - byte[] GetMD5Bytes(string str); - byte[] GetSHA1Bytes(byte[] bytes); - byte[] GetMD5Bytes(Stream str); - byte[] GetMD5Bytes(byte[] bytes); - } -} \ No newline at end of file diff --git a/MediaBrowser.Model/IO/IMemoryStreamFactory.cs b/MediaBrowser.Model/IO/IMemoryStreamFactory.cs new file mode 100644 index 000000000..f4f174643 --- /dev/null +++ b/MediaBrowser.Model/IO/IMemoryStreamFactory.cs @@ -0,0 +1,12 @@ +using System.IO; + +namespace MediaBrowser.Model.IO +{ + public interface IMemoryStreamFactory + { + MemoryStream CreateNew(); + MemoryStream CreateNew(int capacity); + MemoryStream CreateNew(byte[] buffer); + bool TryGetBuffer(MemoryStream stream, out byte[] buffer); + } +} diff --git a/MediaBrowser.Model/IO/IMemoryStreamProvider.cs b/MediaBrowser.Model/IO/IMemoryStreamProvider.cs deleted file mode 100644 index 82a758d9a..000000000 --- a/MediaBrowser.Model/IO/IMemoryStreamProvider.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.IO; - -namespace MediaBrowser.Model.IO -{ - public interface IMemoryStreamProvider - { - MemoryStream CreateNew(); - MemoryStream CreateNew(int capacity); - MemoryStream CreateNew(byte[] buffer); - } -} diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 52e477b1a..ee7582619 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -100,7 +100,7 @@ - + @@ -138,17 +138,19 @@ + + - + - + diff --git a/MediaBrowser.Model/Net/ISocket.cs b/MediaBrowser.Model/Net/ISocket.cs new file mode 100644 index 000000000..371fbc567 --- /dev/null +++ b/MediaBrowser.Model/Net/ISocket.cs @@ -0,0 +1,16 @@ +using System; + +namespace MediaBrowser.Model.Net +{ + public interface ISocket : IDisposable + { + IpEndPointInfo LocalEndPoint { get; } + IpEndPointInfo RemoteEndPoint { get; } + void Close(); + void Shutdown(bool both); + void Listen(int backlog); + void Bind(IpEndPointInfo endpoint); + + void StartAccept(Action onAccept, Func isClosed); + } +} diff --git a/MediaBrowser.Model/Net/ISocketFactory.cs b/MediaBrowser.Model/Net/ISocketFactory.cs index 3f1ddf84f..599292ddf 100644 --- a/MediaBrowser.Model/Net/ISocketFactory.cs +++ b/MediaBrowser.Model/Net/ISocketFactory.cs @@ -27,5 +27,17 @@ namespace MediaBrowser.Model.Net /// The local port to bind to. /// A implementation. IUdpSocket CreateUdpMulticastSocket(string ipAddress, int multicastTimeToLive, int localPort); - } + + ISocket CreateSocket(IpAddressFamily family, SocketType socketType, ProtocolType protocolType, bool dualMode); + } + + public enum SocketType + { + Stream + } + + public enum ProtocolType + { + Tcp + } } diff --git a/MediaBrowser.Model/Net/IpAddressInfo.cs b/MediaBrowser.Model/Net/IpAddressInfo.cs index b48347d1d..47ffe5118 100644 --- a/MediaBrowser.Model/Net/IpAddressInfo.cs +++ b/MediaBrowser.Model/Net/IpAddressInfo.cs @@ -4,12 +4,39 @@ namespace MediaBrowser.Model.Net { public class IpAddressInfo { + public static IpAddressInfo Any = new IpAddressInfo("0.0.0.0", IpAddressFamily.InterNetwork); + public static IpAddressInfo IPv6Any = new IpAddressInfo("00000000000000000000", IpAddressFamily.InterNetworkV6); + public static IpAddressInfo Loopback = new IpAddressInfo("127.0.0.1", IpAddressFamily.InterNetwork); + public static IpAddressInfo IPv6Loopback = new IpAddressInfo("IPv6Loopback", IpAddressFamily.InterNetworkV6); + public string Address { get; set; } - public bool IsIpv6 { get; set; } + public IpAddressFamily AddressFamily { get; set; } + + public IpAddressInfo() + { + + } + + public IpAddressInfo(string address, IpAddressFamily addressFamily) + { + Address = address; + AddressFamily = addressFamily; + } + + public bool Equals(IpAddressInfo address) + { + return string.Equals(address.Address, Address, StringComparison.OrdinalIgnoreCase); + } public override String ToString() { return Address; } } + + public enum IpAddressFamily + { + InterNetwork, + InterNetworkV6 + } } diff --git a/MediaBrowser.Model/Net/IpEndPointInfo.cs b/MediaBrowser.Model/Net/IpEndPointInfo.cs index 5fd331a16..b5cadc429 100644 --- a/MediaBrowser.Model/Net/IpEndPointInfo.cs +++ b/MediaBrowser.Model/Net/IpEndPointInfo.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; namespace MediaBrowser.Model.Net { @@ -8,11 +9,22 @@ namespace MediaBrowser.Model.Net public int Port { get; set; } + public IpEndPointInfo() + { + + } + + public IpEndPointInfo(IpAddressInfo address, int port) + { + IpAddress = address; + Port = port; + } + public override string ToString() { var ipAddresString = IpAddress == null ? string.Empty : IpAddress.ToString(); - return ipAddresString + ":" + this.Port.ToString(); + return ipAddresString + ":" + Port.ToString(CultureInfo.InvariantCulture); } } } diff --git a/MediaBrowser.Model/Services/IHasRequestFilter.cs b/MediaBrowser.Model/Services/IHasRequestFilter.cs index c5c6ccf59..2164179d5 100644 --- a/MediaBrowser.Model/Services/IHasRequestFilter.cs +++ b/MediaBrowser.Model/Services/IHasRequestFilter.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - + namespace MediaBrowser.Model.Services { public interface IHasRequestFilter @@ -22,11 +17,5 @@ namespace MediaBrowser.Model.Services /// The http response wrapper /// The request DTO void RequestFilter(IRequest req, IResponse res, object requestDto); - - /// - /// A new shallow copy of this filter is used on every request. - /// - /// - IHasRequestFilter Copy(); } } diff --git a/MediaBrowser.Model/Services/IHttpResult.cs b/MediaBrowser.Model/Services/IHttpResult.cs new file mode 100644 index 000000000..36ffeb284 --- /dev/null +++ b/MediaBrowser.Model/Services/IHttpResult.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; + +namespace MediaBrowser.Model.Services +{ + public interface IHttpResult : IHasHeaders + { + /// + /// The HTTP Response Status + /// + int Status { get; set; } + + /// + /// The HTTP Response Status Code + /// + HttpStatusCode StatusCode { get; set; } + + /// + /// The HTTP Status Description + /// + string StatusDescription { get; set; } + + /// + /// The HTTP Response ContentType + /// + string ContentType { get; set; } + + /// + /// Additional HTTP Cookies + /// + List Cookies { get; } + + /// + /// Response DTO + /// + object Response { get; set; } + + /// + /// Holds the request call context + /// + IRequest RequestContext { get; set; } + } +} diff --git a/MediaBrowser.Model/Services/IRequest.cs b/MediaBrowser.Model/Services/IRequest.cs index 45dc97b76..5dc995b06 100644 --- a/MediaBrowser.Model/Services/IRequest.cs +++ b/MediaBrowser.Model/Services/IRequest.cs @@ -60,16 +60,6 @@ namespace MediaBrowser.Model.Services QueryParamCollection QueryString { get; } QueryParamCollection FormData { get; } - /// - /// Buffer the Request InputStream so it can be re-read - /// - bool UseBufferedStream { get; set; } - - /// - /// The entire string contents of Request.InputStream - /// - /// - string GetRawBody(); string RawUrl { get; } diff --git a/MediaBrowser.Model/Services/QueryParamCollection.cs b/MediaBrowser.Model/Services/QueryParamCollection.cs index 1ab3f0bfb..dfea62821 100644 --- a/MediaBrowser.Model/Services/QueryParamCollection.cs +++ b/MediaBrowser.Model/Services/QueryParamCollection.cs @@ -9,7 +9,7 @@ namespace MediaBrowser.Model.Services { public QueryParamCollection() { - + } public QueryParamCollection(IDictionary headers) @@ -30,15 +30,30 @@ namespace MediaBrowser.Model.Services return StringComparer.OrdinalIgnoreCase; } + public string GetKey(int index) + { + return this[index].Name; + } + + public string Get(int index) + { + return this[index].Value; + } + + public virtual string[] GetValues(int index) + { + return new[] { Get(index) }; + } + /// /// Adds a new query parameter. /// - public void Add(string key, string value) + public virtual void Add(string key, string value) { Add(new NameValuePair(key, value)); } - public void Set(string key, string value) + public virtual void Set(string key, string value) { if (string.IsNullOrWhiteSpace(value)) { @@ -81,17 +96,21 @@ namespace MediaBrowser.Model.Services /// /// The number of parameters that were removed /// is null. - public int Remove(string name) + public virtual int Remove(string name) { return RemoveAll(p => p.Name == name); } public string Get(string name) { - return GetValues(name).FirstOrDefault(); + var stringComparison = GetStringComparison(); + + return this.Where(p => string.Equals(p.Name, name, stringComparison)) + .Select(p => p.Value) + .FirstOrDefault(); } - public string[] GetValues(string name) + public virtual string[] GetValues(string name) { var stringComparison = GetStringComparison(); diff --git a/MediaBrowser.Model/Text/ITextEncoding.cs b/MediaBrowser.Model/Text/ITextEncoding.cs new file mode 100644 index 000000000..6901f1f94 --- /dev/null +++ b/MediaBrowser.Model/Text/ITextEncoding.cs @@ -0,0 +1,10 @@ +using System.Text; + +namespace MediaBrowser.Model.Text +{ + public interface ITextEncoding + { + Encoding GetASCIIEncoding(); + Encoding GetFileEncoding(string path); + } +} diff --git a/MediaBrowser.Model/TextEncoding/IEncoding.cs b/MediaBrowser.Model/TextEncoding/IEncoding.cs deleted file mode 100644 index 3d884c9d2..000000000 --- a/MediaBrowser.Model/TextEncoding/IEncoding.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text; - -namespace MediaBrowser.Model.TextEncoding -{ - public interface IEncoding - { - byte[] GetASCIIBytes(string text); - string GetASCIIString(byte[] bytes, int startIndex, int length); - - Encoding GetFileEncoding(string path); - } -} diff --git a/MediaBrowser.Providers/Manager/ImageSaver.cs b/MediaBrowser.Providers/Manager/ImageSaver.cs index d4bfad174..59d67740d 100644 --- a/MediaBrowser.Providers/Manager/ImageSaver.cs +++ b/MediaBrowser.Providers/Manager/ImageSaver.cs @@ -40,7 +40,7 @@ namespace MediaBrowser.Providers.Manager private readonly ILibraryMonitor _libraryMonitor; private readonly IFileSystem _fileSystem; private readonly ILogger _logger; - private readonly IMemoryStreamProvider _memoryStreamProvider; + private readonly IMemoryStreamFactory _memoryStreamProvider; /// /// Initializes a new instance of the class. @@ -49,7 +49,7 @@ namespace MediaBrowser.Providers.Manager /// The directory watchers. /// The file system. /// The logger. - public ImageSaver(IServerConfigurationManager config, ILibraryMonitor libraryMonitor, IFileSystem fileSystem, ILogger logger, IMemoryStreamProvider memoryStreamProvider) + public ImageSaver(IServerConfigurationManager config, ILibraryMonitor libraryMonitor, IFileSystem fileSystem, ILogger logger, IMemoryStreamFactory memoryStreamProvider) { _config = config; _libraryMonitor = libraryMonitor; diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index 67ad21dbb..5e00b356a 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -66,7 +66,7 @@ namespace MediaBrowser.Providers.Manager private IExternalId[] _externalIds; private readonly Func _libraryManagerFactory; - private readonly IMemoryStreamProvider _memoryStreamProvider; + private readonly IMemoryStreamFactory _memoryStreamProvider; /// /// Initializes a new instance of the class. @@ -76,7 +76,7 @@ namespace MediaBrowser.Providers.Manager /// The directory watchers. /// The log manager. /// The file system. - public ProviderManager(IHttpClient httpClient, IServerConfigurationManager configurationManager, ILibraryMonitor libraryMonitor, ILogManager logManager, IFileSystem fileSystem, IServerApplicationPaths appPaths, Func libraryManagerFactory, IJsonSerializer json, IMemoryStreamProvider memoryStreamProvider) + public ProviderManager(IHttpClient httpClient, IServerConfigurationManager configurationManager, ILibraryMonitor libraryMonitor, ILogManager logManager, IFileSystem fileSystem, IServerApplicationPaths appPaths, Func libraryManagerFactory, IJsonSerializer json, IMemoryStreamFactory memoryStreamProvider) { _logger = logManager.GetLogger("ProviderManager"); _httpClient = httpClient; diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs index e4becec56..313feda52 100644 --- a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs @@ -45,6 +45,11 @@ namespace MediaBrowser.Providers.MediaInfo var codec = Path.GetExtension(fullName).ToLower().TrimStart('.'); + if (string.Equals(codec, "txt", StringComparison.OrdinalIgnoreCase)) + { + codec = "srt"; + } + // If the subtitle file matches the video file name if (string.Equals(videoFileNameWithoutExtension, fileNameWithoutExtension, StringComparison.OrdinalIgnoreCase)) { @@ -74,9 +79,9 @@ namespace MediaBrowser.Providers.MediaInfo // Try to translate to three character code // Be flexible and check against both the full and three character versions var culture = _localization.GetCultures() - .FirstOrDefault(i => string.Equals(i.DisplayName, language, StringComparison.OrdinalIgnoreCase) || - string.Equals(i.Name, language, StringComparison.OrdinalIgnoreCase) || - string.Equals(i.ThreeLetterISOLanguageName, language, StringComparison.OrdinalIgnoreCase) || + .FirstOrDefault(i => string.Equals(i.DisplayName, language, StringComparison.OrdinalIgnoreCase) || + string.Equals(i.Name, language, StringComparison.OrdinalIgnoreCase) || + string.Equals(i.ThreeLetterISOLanguageName, language, StringComparison.OrdinalIgnoreCase) || string.Equals(i.TwoLetterISOLanguageName, language, StringComparison.OrdinalIgnoreCase)); if (culture != null) @@ -119,7 +124,7 @@ namespace MediaBrowser.Providers.MediaInfo { get { - return new[] { ".srt", ".ssa", ".ass", ".sub", ".smi", ".sami" }; + return new[] { ".srt", ".ssa", ".ass", ".sub", ".smi", ".sami", ".txt" }; } } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs index 66adf6c8a..5147e9d10 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs @@ -40,10 +40,10 @@ namespace MediaBrowser.Providers.TV private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly ILogger _logger; private readonly ILibraryManager _libraryManager; - private readonly IMemoryStreamProvider _memoryStreamProvider; + private readonly IMemoryStreamFactory _memoryStreamProvider; private readonly ILocalizationManager _localizationManager; - public TvdbSeriesProvider(IZipClient zipClient, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager config, ILogger logger, ILibraryManager libraryManager, IMemoryStreamProvider memoryStreamProvider, IXmlReaderSettingsFactory xmlSettings, ILocalizationManager localizationManager) + public TvdbSeriesProvider(IZipClient zipClient, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager config, ILogger logger, ILibraryManager libraryManager, IMemoryStreamFactory memoryStreamProvider, IXmlReaderSettingsFactory xmlSettings, ILocalizationManager localizationManager) { _zipClient = zipClient; _httpClient = httpClient; diff --git a/MediaBrowser.Server.Implementations/HttpServer/ContainerAdapter.cs b/MediaBrowser.Server.Implementations/HttpServer/ContainerAdapter.cs index 93d224b8d..235b62f69 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/ContainerAdapter.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/ContainerAdapter.cs @@ -6,7 +6,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer /// /// Class ContainerAdapter /// - class ContainerAdapter : IContainerAdapter, IRelease + class ContainerAdapter : IContainerAdapter { /// /// The _app host @@ -40,14 +40,5 @@ namespace MediaBrowser.Server.Implementations.HttpServer { return _appHost.TryResolve(); } - - /// - /// Releases the specified instance. - /// - /// The instance. - public void Release(object instance) - { - // Leave this empty so SS doesn't try to dispose our objects - } } } diff --git a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs index 805cb0353..ebb282503 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -8,24 +8,31 @@ using MediaBrowser.Server.Implementations.HttpServer.SocketSharp; using ServiceStack; using ServiceStack.Host; using ServiceStack.Host.Handlers; -using ServiceStack.Logging; using ServiceStack.Web; using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net.Security; +using System.Net.Sockets; using System.Reflection; +using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Tasks; +using Emby.Common.Implementations.Net; using Emby.Server.Implementations.HttpServer; using Emby.Server.Implementations.HttpServer.SocketSharp; using MediaBrowser.Common.Net; using MediaBrowser.Common.Security; using MediaBrowser.Controller; +using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Extensions; using MediaBrowser.Model.IO; +using MediaBrowser.Model.Net; using MediaBrowser.Model.Services; -using ServiceStack.Api.Swagger; +using MediaBrowser.Model.Text; +using SocketHttpListener.Net; +using SocketHttpListener.Primitives; namespace MediaBrowser.Server.Implementations.HttpServer { @@ -49,21 +56,28 @@ namespace MediaBrowser.Server.Implementations.HttpServer private readonly IServerConfigurationManager _config; private readonly INetworkManager _networkManager; - private readonly IMemoryStreamProvider _memoryStreamProvider; + private readonly IMemoryStreamFactory _memoryStreamProvider; private readonly IServerApplicationHost _appHost; + private readonly ITextEncoding _textEncoding; + private readonly ISocketFactory _socketFactory; + private readonly ICryptoProvider _cryptoProvider; + public HttpListenerHost(IServerApplicationHost applicationHost, ILogManager logManager, IServerConfigurationManager config, string serviceName, - string defaultRedirectPath, INetworkManager networkManager, IMemoryStreamProvider memoryStreamProvider, params Assembly[] assembliesWithServices) - : base(serviceName, assembliesWithServices) + string defaultRedirectPath, INetworkManager networkManager, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding, ISocketFactory socketFactory, ICryptoProvider cryptoProvider) + : base(serviceName, new Assembly[] { }) { _appHost = applicationHost; DefaultRedirectPath = defaultRedirectPath; _networkManager = networkManager; _memoryStreamProvider = memoryStreamProvider; + _textEncoding = textEncoding; + _socketFactory = socketFactory; + _cryptoProvider = cryptoProvider; _config = config; _logger = logManager.GetLogger("HttpServer"); @@ -73,10 +87,9 @@ namespace MediaBrowser.Server.Implementations.HttpServer public string GlobalResponse { get; set; } - public override void Configure(Container container) + public override void Configure() { HostConfig.Instance.DefaultRedirectPath = DefaultRedirectPath; - HostConfig.Instance.LogUnobservedTaskExceptions = false; HostConfig.Instance.MapExceptionToStatusCode = new Dictionary { @@ -94,19 +107,12 @@ namespace MediaBrowser.Server.Implementations.HttpServer }; HostConfig.Instance.GlobalResponseHeaders = new Dictionary(); - HostConfig.Instance.DebugMode = false; - - HostConfig.Instance.LogFactory = LogManager.LogFactory; - HostConfig.Instance.AllowJsonpRequests = false; // The Markdown feature causes slow startup times (5 mins+) on cold boots for some users // Custom format allows images HostConfig.Instance.EnableFeatures = Feature.Html | Feature.Json | Feature.Xml | Feature.CustomFormat; - container.Adapter = _containerAdapter; - - Plugins.Add(new SwaggerFeature()); - Plugins.Add(new CorsFeature(allowedHeaders: "Content-Type, Authorization, Range, X-MediaBrowser-Token, X-Emby-Authorization")); + Container.Adapter = _containerAdapter; //Plugins.Add(new AuthFeature(() => new AuthUserSession(), new IAuthProvider[] { // new SessionAuthProvider(_containerAdapter.Resolve()), @@ -130,6 +136,14 @@ namespace MediaBrowser.Server.Implementations.HttpServer HostContext.GlobalResponseFilters.Add(new ResponseFilter(_logger).FilterResponse); } + protected override ILogger Logger + { + get + { + return _logger; + } + } + public override void OnAfterInit() { SetAppDomainData(); @@ -207,7 +221,33 @@ namespace MediaBrowser.Server.Implementations.HttpServer private IHttpListener GetListener() { - return new WebSocketSharpListener(_logger, CertificatePath, _memoryStreamProvider); + var cert = !string.IsNullOrWhiteSpace(CertificatePath) && File.Exists(CertificatePath) + ? GetCert(CertificatePath) : + null; + + return new WebSocketSharpListener(_logger, cert, _memoryStreamProvider, _textEncoding, _networkManager, _socketFactory, _cryptoProvider, new StreamFactory(), GetRequest); + } + + public static ICertificate GetCert(string certificateLocation) + { + X509Certificate2 localCert = new X509Certificate2(certificateLocation); + //localCert.PrivateKey = PrivateKey.CreateFromFile(pvk_file).RSA; + if (localCert.PrivateKey == null) + { + //throw new FileNotFoundException("Secure requested, no private key included", certificateLocation); + return null; + } + + return new Certificate(localCert); + } + + private IHttpRequest GetRequest(HttpListenerContext httpContext) + { + var operationName = httpContext.Request.GetOperationName(); + + var req = new WebSocketSharpRequest(httpContext, operationName, _logger, _memoryStreamProvider); + + return req; } private void OnWebSocketConnecting(WebSocketConnectingEventArgs args) @@ -259,11 +299,11 @@ namespace MediaBrowser.Server.Implementations.HttpServer var contentType = httpReq.ResponseContentType; - var serializer = HostContext.ContentTypes.GetResponseSerializer(contentType); + var serializer = ContentTypes.Instance.GetResponseSerializer(contentType); if (serializer == null) { contentType = HostContext.Config.DefaultContentType; - serializer = HostContext.ContentTypes.GetResponseSerializer(contentType); + serializer = ContentTypes.Instance.GetResponseSerializer(contentType); } var httpError = ex as IHttpError; @@ -411,171 +451,170 @@ namespace MediaBrowser.Server.Implementations.HttpServer protected async Task RequestHandler(IHttpRequest httpReq, Uri url) { var date = DateTime.Now; - var httpRes = httpReq.Response; + bool enableLog = false; + string urlToLog = null; + string remoteIp = null; - if (_disposed) + try { - httpRes.StatusCode = 503; - httpRes.Close(); - return ; - } + if (_disposed) + { + httpRes.StatusCode = 503; + return; + } - if (!ValidateHost(url)) - { - httpRes.StatusCode = 400; - httpRes.ContentType = "text/plain"; - httpRes.Write("Invalid host"); + if (!ValidateHost(url)) + { + httpRes.StatusCode = 400; + httpRes.ContentType = "text/plain"; + httpRes.Write("Invalid host"); + return; + } - httpRes.Close(); - return; - } + if (string.Equals(httpReq.Verb, "OPTIONS", StringComparison.OrdinalIgnoreCase)) + { + 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.ContentType = "text/html"; + return; + } - if (string.Equals(httpReq.Verb, "OPTIONS", StringComparison.OrdinalIgnoreCase)) - { - 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.ContentType = "text/html"; + var operationName = httpReq.OperationName; + var localPath = url.LocalPath; - httpRes.Close(); - } + var urlString = url.OriginalString; + enableLog = EnableLogging(urlString, localPath); + urlToLog = urlString; - var operationName = httpReq.OperationName; - var localPath = url.LocalPath; + if (enableLog) + { + urlToLog = GetUrlToLog(urlString); + remoteIp = httpReq.RemoteIp; - var urlString = url.OriginalString; - var enableLog = EnableLogging(urlString, localPath); - var urlToLog = urlString; + LoggerUtils.LogRequest(_logger, urlToLog, httpReq.HttpMethod, httpReq.UserAgent); + } - if (enableLog) - { - urlToLog = GetUrlToLog(urlString); - LoggerUtils.LogRequest(_logger, urlToLog, httpReq.HttpMethod, httpReq.UserAgent); - } + if (string.Equals(localPath, "/emby/", StringComparison.OrdinalIgnoreCase) || + string.Equals(localPath, "/mediabrowser/", StringComparison.OrdinalIgnoreCase)) + { + RedirectToUrl(httpRes, DefaultRedirectPath); + return; + } + if (string.Equals(localPath, "/emby", StringComparison.OrdinalIgnoreCase) || + string.Equals(localPath, "/mediabrowser", StringComparison.OrdinalIgnoreCase)) + { + RedirectToUrl(httpRes, "emby/" + DefaultRedirectPath); + return; + } - if (string.Equals(localPath, "/emby/", StringComparison.OrdinalIgnoreCase) || - string.Equals(localPath, "/mediabrowser/", StringComparison.OrdinalIgnoreCase)) - { - httpRes.RedirectToUrl(DefaultRedirectPath); - return; - } - if (string.Equals(localPath, "/emby", StringComparison.OrdinalIgnoreCase) || - string.Equals(localPath, "/mediabrowser", StringComparison.OrdinalIgnoreCase)) - { - httpRes.RedirectToUrl("emby/" + DefaultRedirectPath); - return; - } + if (string.Equals(localPath, "/mediabrowser/", StringComparison.OrdinalIgnoreCase) || + string.Equals(localPath, "/mediabrowser", StringComparison.OrdinalIgnoreCase) || + localPath.IndexOf("mediabrowser/web", StringComparison.OrdinalIgnoreCase) != -1) + { + httpRes.StatusCode = 200; + httpRes.ContentType = "text/html"; + var newUrl = urlString.Replace("mediabrowser", "emby", StringComparison.OrdinalIgnoreCase) + .Replace("/dashboard/", "/web/", StringComparison.OrdinalIgnoreCase); - if (string.Equals(localPath, "/mediabrowser/", StringComparison.OrdinalIgnoreCase) || - string.Equals(localPath, "/mediabrowser", StringComparison.OrdinalIgnoreCase) || - localPath.IndexOf("mediabrowser/web", StringComparison.OrdinalIgnoreCase) != -1) - { - httpRes.StatusCode = 200; - httpRes.ContentType = "text/html"; - var newUrl = urlString.Replace("mediabrowser", "emby", StringComparison.OrdinalIgnoreCase) - .Replace("/dashboard/", "/web/", StringComparison.OrdinalIgnoreCase); + if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase)) + { + httpRes.Write( + "EmbyPlease update your Emby bookmark to " + newUrl + ""); + return; + } + } - if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase)) + if (localPath.IndexOf("dashboard/", StringComparison.OrdinalIgnoreCase) != -1 && + localPath.IndexOf("web/dashboard", StringComparison.OrdinalIgnoreCase) == -1) { - httpRes.Write("EmbyPlease update your Emby bookmark to " + newUrl + ""); + httpRes.StatusCode = 200; + httpRes.ContentType = "text/html"; + var newUrl = urlString.Replace("mediabrowser", "emby", StringComparison.OrdinalIgnoreCase) + .Replace("/dashboard/", "/web/", StringComparison.OrdinalIgnoreCase); - httpRes.Close(); - return; + if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase)) + { + httpRes.Write( + "EmbyPlease update your Emby bookmark to " + newUrl + ""); + return; + } } - } - if (localPath.IndexOf("dashboard/", StringComparison.OrdinalIgnoreCase) != -1 && - localPath.IndexOf("web/dashboard", StringComparison.OrdinalIgnoreCase) == -1) - { - httpRes.StatusCode = 200; - httpRes.ContentType = "text/html"; - var newUrl = urlString.Replace("mediabrowser", "emby", StringComparison.OrdinalIgnoreCase) - .Replace("/dashboard/", "/web/", StringComparison.OrdinalIgnoreCase); + if (string.Equals(localPath, "/web", StringComparison.OrdinalIgnoreCase)) + { + RedirectToUrl(httpRes, DefaultRedirectPath); + return; + } + if (string.Equals(localPath, "/web/", StringComparison.OrdinalIgnoreCase)) + { + RedirectToUrl(httpRes, "../" + DefaultRedirectPath); + return; + } + if (string.Equals(localPath, "/", StringComparison.OrdinalIgnoreCase)) + { + RedirectToUrl(httpRes, DefaultRedirectPath); + return; + } + if (string.IsNullOrEmpty(localPath)) + { + RedirectToUrl(httpRes, "/" + DefaultRedirectPath); + return; + } - if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase)) + if (string.Equals(localPath, "/emby/pin", StringComparison.OrdinalIgnoreCase)) { - httpRes.Write("EmbyPlease update your Emby bookmark to " + newUrl + ""); + RedirectToUrl(httpRes, "web/pin.html"); + return; + } - httpRes.Close(); + if (!string.IsNullOrWhiteSpace(GlobalResponse)) + { + httpRes.StatusCode = 503; + httpRes.ContentType = "text/html"; + httpRes.Write(GlobalResponse); return; } - } - if (string.Equals(localPath, "/web", StringComparison.OrdinalIgnoreCase)) - { - httpRes.RedirectToUrl(DefaultRedirectPath); - return; - } - if (string.Equals(localPath, "/web/", StringComparison.OrdinalIgnoreCase)) - { - httpRes.RedirectToUrl("../" + DefaultRedirectPath); - return; - } - if (string.Equals(localPath, "/", StringComparison.OrdinalIgnoreCase)) - { - httpRes.RedirectToUrl(DefaultRedirectPath); - return; - } - if (string.IsNullOrEmpty(localPath)) - { - httpRes.RedirectToUrl("/" + DefaultRedirectPath); - return; - } + var handler = HttpHandlerFactory.GetHandler(httpReq); - if (string.Equals(localPath, "/emby/pin", StringComparison.OrdinalIgnoreCase)) - { - httpRes.RedirectToUrl("web/pin.html"); - return; + if (handler != null) + { + await handler.ProcessRequestAsync(httpReq, httpRes, operationName).ConfigureAwait(false); + } } - - if (!string.IsNullOrWhiteSpace(GlobalResponse)) + catch (Exception ex) { - httpRes.StatusCode = 503; - httpRes.ContentType = "text/html"; - httpRes.Write(GlobalResponse); - - httpRes.Close(); - return; + ErrorHandler(ex, httpReq); } - - var handler = HttpHandlerFactory.GetHandler(httpReq); - - var remoteIp = httpReq.RemoteIp; - - var serviceStackHandler = handler as IServiceStackHandler; - if (serviceStackHandler != null) + finally { - var restHandler = serviceStackHandler as RestHandler; - if (restHandler != null) - { - httpReq.OperationName = operationName = restHandler.RestPath.RequestType.GetOperationName(); - } + httpRes.Close(); - try - { - await serviceStackHandler.ProcessRequestAsync(httpReq, httpRes, operationName).ConfigureAwait(false); - } - finally + if (enableLog) { - httpRes.Close(); var statusCode = httpRes.StatusCode; var duration = DateTime.Now - date; - if (enableLog) - { - LoggerUtils.LogResponse(_logger, statusCode, urlToLog, remoteIp, duration); - } + LoggerUtils.LogResponse(_logger, statusCode, urlToLog, remoteIp, duration); } } - else - { - httpRes.Close(); - } } + public static void RedirectToUrl(IResponse httpRes, string url) + { + httpRes.StatusCode = 302; + httpRes.AddHeader(HttpHeaders.Location, url); + httpRes.EndRequest(); + } + + /// /// Adds the rest handlers. /// @@ -653,15 +692,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer return "mediabrowser/" + path; } - /// - /// Releases the specified instance. - /// - /// The instance. - public override void Release(object instance) - { - // Leave this empty so SS doesn't try to dispose our objects - } - private bool _disposed; private readonly object _disposeLock = new object(); protected virtual void Dispose(bool disposing) @@ -696,4 +726,37 @@ namespace MediaBrowser.Server.Implementations.HttpServer Start(UrlPrefixes.First()); } } + + public class StreamFactory : IStreamFactory + { + public Stream CreateNetworkStream(ISocket socket, bool ownsSocket) + { + var netSocket = (NetSocket)socket; + + return new NetworkStream(netSocket.Socket, ownsSocket); + } + + public Task AuthenticateSslStreamAsServer(Stream stream, ICertificate certificate) + { + var sslStream = (SslStream)stream; + var cert = (Certificate)certificate; + + return sslStream.AuthenticateAsServerAsync(cert.X509Certificate); + } + + public Stream CreateSslStream(Stream innerStream, bool leaveInnerStreamOpen) + { + return new SslStream(innerStream, leaveInnerStreamOpen); + } + } + + public class Certificate : ICertificate + { + public Certificate(X509Certificate x509Certificate) + { + X509Certificate = x509Certificate; + } + + public X509Certificate X509Certificate { get; private set; } + } } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs b/MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs index 95e1a35e6..4c251ba24 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs @@ -9,6 +9,7 @@ using System.IO; using System.Net; using System.Text; using System.Threading.Tasks; +using Emby.Server.Implementations.HttpServer; using MediaBrowser.Model.IO; using MediaBrowser.Model.Services; using ServiceStack; diff --git a/MediaBrowser.Server.Implementations/HttpServer/LoggerUtils.cs b/MediaBrowser.Server.Implementations/HttpServer/LoggerUtils.cs deleted file mode 100644 index bfbb228ed..000000000 --- a/MediaBrowser.Server.Implementations/HttpServer/LoggerUtils.cs +++ /dev/null @@ -1,43 +0,0 @@ -using MediaBrowser.Model.Logging; -using System; -using System.Globalization; -using SocketHttpListener.Net; - -namespace MediaBrowser.Server.Implementations.HttpServer -{ - public static class LoggerUtils - { - /// - /// Logs the request. - /// - /// The logger. - /// The request. - public static void LogRequest(ILogger logger, HttpListenerRequest request) - { - var url = request.Url.ToString(); - - logger.Info("{0} {1}. UserAgent: {2}", request.IsWebSocketRequest ? "WS" : "HTTP " + request.HttpMethod, url, request.UserAgent ?? string.Empty); - } - - public static void LogRequest(ILogger logger, string url, string method, string userAgent) - { - logger.Info("{0} {1}. UserAgent: {2}", "HTTP " + method, url, userAgent ?? string.Empty); - } - - /// - /// Logs the response. - /// - /// The logger. - /// The status code. - /// The URL. - /// The end point. - /// The duration. - public static void LogResponse(ILogger logger, int statusCode, string url, string endPoint, TimeSpan duration) - { - var durationMs = duration.TotalMilliseconds; - var logSuffix = durationMs >= 1000 && durationMs < 60000 ? "ms (slow)" : "ms"; - - logger.Info("HTTP Response {0} to {1}. Time: {2}{3}. {4}", statusCode, endPoint, Convert.ToInt32(durationMs).ToString(CultureInfo.InvariantCulture), logSuffix, url); - } - } -} diff --git a/MediaBrowser.Server.Implementations/HttpServer/RangeRequestWriter.cs b/MediaBrowser.Server.Implementations/HttpServer/RangeRequestWriter.cs deleted file mode 100644 index 7d4cd3b4d..000000000 --- a/MediaBrowser.Server.Implementations/HttpServer/RangeRequestWriter.cs +++ /dev/null @@ -1,230 +0,0 @@ -using MediaBrowser.Model.Logging; -using ServiceStack.Web; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Model.Services; - -namespace MediaBrowser.Server.Implementations.HttpServer -{ - public class RangeRequestWriter : IAsyncStreamWriter, IHttpResult - { - /// - /// Gets or sets the source stream. - /// - /// The source stream. - private Stream SourceStream { get; set; } - 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; } - private long TotalContentLength { get; set; } - - public Action OnComplete { get; set; } - private readonly ILogger _logger; - - private const int BufferSize = 81920; - - /// - /// The _options - /// - private readonly Dictionary _options = new Dictionary(); - - /// - /// The us culture - /// - private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - - public Func ResultScope { get; set; } - public List Cookies { get; private set; } - - /// - /// Additional HTTP Headers - /// - /// The headers. - public IDictionary Headers - { - get { return _options; } - } - - /// - /// Initializes a new instance of the class. - /// - /// The range header. - /// The source. - /// Type of the content. - /// if set to true [is head request]. - public RangeRequestWriter(string rangeHeader, Stream source, string contentType, bool isHeadRequest, ILogger logger) - { - if (string.IsNullOrEmpty(contentType)) - { - throw new ArgumentNullException("contentType"); - } - - RangeHeader = rangeHeader; - SourceStream = source; - IsHeadRequest = isHeadRequest; - this._logger = logger; - - ContentType = contentType; - Headers["Content-Type"] = contentType; - Headers["Accept-Ranges"] = "bytes"; - StatusCode = HttpStatusCode.PartialContent; - - Cookies = new List(); - SetRangeValues(); - } - - /// - /// Sets the range values. - /// - private void SetRangeValues() - { - var requestedRange = RequestedRanges[0]; - - TotalContentLength = SourceStream.Length; - - // 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 - Headers["Content-Length"] = RangeLength.ToString(UsCulture); - Headers["Content-Range"] = string.Format("bytes {0}-{1}/{2}", RangeStart, RangeEnd, TotalContentLength); - - if (RangeStart > 0) - { - SourceStream.Position = RangeStart; - } - } - - /// - /// The _requested ranges - /// - private List> _requestedRanges; - /// - /// Gets the requested ranges. - /// - /// The requested ranges. - protected List> RequestedRanges - { - get - { - if (_requestedRanges == null) - { - _requestedRanges = new List>(); - - // Example: bytes=0-,32-63 - var ranges = RangeHeader.Split('=')[1].Split(','); - - foreach (var range in ranges) - { - var vals = range.Split('-'); - - long start = 0; - long? end = null; - - if (!string.IsNullOrEmpty(vals[0])) - { - start = long.Parse(vals[0], UsCulture); - } - if (!string.IsNullOrEmpty(vals[1])) - { - end = long.Parse(vals[1], UsCulture); - } - - _requestedRanges.Add(new KeyValuePair(start, end)); - } - } - - return _requestedRanges; - } - } - - public async Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken) - { - try - { - // Headers only - if (IsHeadRequest) - { - return; - } - - using (var source = SourceStream) - { - // If the requested range is "0-", we can optimize by just doing a stream copy - if (RangeEnd >= TotalContentLength - 1) - { - await source.CopyToAsync(responseStream, BufferSize).ConfigureAwait(false); - } - else - { - await CopyToInternalAsync(source, responseStream, RangeLength).ConfigureAwait(false); - } - } - } - finally - { - if (OnComplete != null) - { - OnComplete(); - } - } - } - - private async Task CopyToInternalAsync(Stream source, Stream destination, long copyLength) - { - var array = new byte[BufferSize]; - int count; - while ((count = await source.ReadAsync(array, 0, array.Length).ConfigureAwait(false)) != 0) - { - var bytesToCopy = Math.Min(count, copyLength); - - await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToCopy)).ConfigureAwait(false); - - copyLength -= bytesToCopy; - - if (copyLength <= 0) - { - break; - } - } - } - - public string ContentType { get; set; } - - public IRequest RequestContext { get; set; } - - public object Response { get; set; } - - public IContentTypeWriter ResponseFilter { get; set; } - - public int Status { get; set; } - - public HttpStatusCode StatusCode - { - get { return (HttpStatusCode)Status; } - set { Status = (int)value; } - } - - public string StatusDescription { get; set; } - - public int PaddingLength { get; set; } - } -} \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/HttpServer/ResponseFilter.cs b/MediaBrowser.Server.Implementations/HttpServer/ResponseFilter.cs deleted file mode 100644 index 6247e4c17..000000000 --- a/MediaBrowser.Server.Implementations/HttpServer/ResponseFilter.cs +++ /dev/null @@ -1,127 +0,0 @@ -using MediaBrowser.Model.Logging; -using MediaBrowser.Server.Implementations.HttpServer.SocketSharp; -using System; -using System.Globalization; -using System.Net; -using System.Text; -using MediaBrowser.Model.Services; - -namespace MediaBrowser.Server.Implementations.HttpServer -{ - public class ResponseFilter - { - private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - private readonly ILogger _logger; - - public ResponseFilter(ILogger logger) - { - _logger = logger; - } - - /// - /// Filters the response. - /// - /// The req. - /// The res. - /// The dto. - public void FilterResponse(IRequest req, IResponse res, object dto) - { - // Try to prevent compatibility view - res.AddHeader("X-UA-Compatible", "IE=Edge"); - - var exception = dto as Exception; - - if (exception != null) - { - _logger.ErrorException("Error processing request for {0}", exception, req.RawUrl); - - if (!string.IsNullOrEmpty(exception.Message)) - { - var error = exception.Message.Replace(Environment.NewLine, " "); - error = RemoveControlCharacters(error); - - res.AddHeader("X-Application-Error-Code", error); - } - } - - var vary = "Accept-Encoding"; - - var hasHeaders = dto as IHasHeaders; - var sharpResponse = res as WebSocketSharpResponse; - - if (hasHeaders != null) - { - if (!hasHeaders.Headers.ContainsKey("Server")) - { - hasHeaders.Headers["Server"] = "Mono-HTTPAPI/1.1, UPnP/1.0 DLNADOC/1.50"; - //hasHeaders.Headers["Server"] = "Mono-HTTPAPI/1.1"; - } - - // Content length has to be explicitly set on on HttpListenerResponse or it won't be happy - string contentLength; - - if (hasHeaders.Headers.TryGetValue("Content-Length", out contentLength) && !string.IsNullOrEmpty(contentLength)) - { - var length = long.Parse(contentLength, UsCulture); - - if (length > 0) - { - res.SetContentLength(length); - - var listenerResponse = res.OriginalResponse as HttpListenerResponse; - - if (listenerResponse != null) - { - // Disable chunked encoding. Technically this is only needed when using Content-Range, but - // anytime we know the content length there's no need for it - listenerResponse.SendChunked = false; - return; - } - - if (sharpResponse != null) - { - sharpResponse.SendChunked = false; - } - } - } - - string hasHeadersVary; - if (hasHeaders.Headers.TryGetValue("Vary", out hasHeadersVary)) - { - vary = hasHeadersVary; - } - - hasHeaders.Headers["Vary"] = vary; - } - - //res.KeepAlive = false; - - // Per Google PageSpeed - // This instructs the proxies to cache two versions of the resource: one compressed, and one uncompressed. - // The correct version of the resource is delivered based on the client request header. - // This is a good choice for applications that are singly homed and depend on public proxies for user locality. - res.AddHeader("Vary", vary); - } - - /// - /// Removes the control characters. - /// - /// The in string. - /// System.String. - public static string RemoveControlCharacters(string inString) - { - if (inString == null) return null; - - var newString = new StringBuilder(); - - foreach (var ch in inString) - { - if (!char.IsControl(ch)) - { - newString.Append(ch); - } - } - return newString.ToString(); - } - } -} diff --git a/MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs b/MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs index 4dff2d5a3..5da515900 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs @@ -2,9 +2,11 @@ using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Net; +using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; -using ServiceStack.Logging; +using MediaBrowser.Model.Net; +using MediaBrowser.Model.Text; namespace MediaBrowser.Server.Implementations.HttpServer { @@ -21,13 +23,14 @@ namespace MediaBrowser.Server.Implementations.HttpServer ILogManager logManager, IServerConfigurationManager config, INetworkManager networkmanager, - IMemoryStreamProvider streamProvider, + IMemoryStreamFactory streamProvider, string serverName, - string defaultRedirectpath) + string defaultRedirectpath, + ITextEncoding textEncoding, + ISocketFactory socketFactory, + ICryptoProvider cryptoProvider) { - LogManager.LogFactory = new ServerLogFactory(logManager); - - return new HttpListenerHost(applicationHost, logManager, config, serverName, defaultRedirectpath, networkmanager, streamProvider); + return new HttpListenerHost(applicationHost, logManager, config, serverName, defaultRedirectpath, networkmanager, streamProvider, textEncoding, socketFactory, cryptoProvider); } } } diff --git a/MediaBrowser.Server.Implementations/HttpServer/ServerLogFactory.cs b/MediaBrowser.Server.Implementations/HttpServer/ServerLogFactory.cs deleted file mode 100644 index 40af3f3b0..000000000 --- a/MediaBrowser.Server.Implementations/HttpServer/ServerLogFactory.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using MediaBrowser.Model.Logging; -using ServiceStack.Logging; - -namespace MediaBrowser.Server.Implementations.HttpServer -{ - /// - /// Class ServerLogFactory - /// - public class ServerLogFactory : ILogFactory - { - /// - /// The _log manager - /// - private readonly ILogManager _logManager; - - /// - /// Initializes a new instance of the class. - /// - /// The log manager. - public ServerLogFactory(ILogManager logManager) - { - _logManager = logManager; - } - - /// - /// Gets the logger. - /// - /// Name of the type. - /// ILog. - public ILog GetLogger(string typeName) - { - return new ServerLogger(_logManager.GetLogger(typeName)); - } - - /// - /// Gets the logger. - /// - /// The type. - /// ILog. - public ILog GetLogger(Type type) - { - return GetLogger(type.Name); - } - } -} \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/HttpServer/ServerLogger.cs b/MediaBrowser.Server.Implementations/HttpServer/ServerLogger.cs deleted file mode 100644 index bf7924784..000000000 --- a/MediaBrowser.Server.Implementations/HttpServer/ServerLogger.cs +++ /dev/null @@ -1,194 +0,0 @@ -using MediaBrowser.Model.Logging; -using ServiceStack.Logging; -using System; - -namespace MediaBrowser.Server.Implementations.HttpServer -{ - /// - /// Class ServerLogger - /// - public class ServerLogger : ILog - { - /// - /// The _logger - /// - private readonly ILogger _logger; - - /// - /// Initializes a new instance of the class. - /// - /// The logger. - public ServerLogger(ILogger logger) - { - _logger = logger; - } - - /// - /// Logs a Debug message and exception. - /// - /// The message. - /// The exception. - public void Debug(object message, Exception exception) - { - _logger.ErrorException(GetMesssage(message), exception); - } - - /// - /// Logs a Debug message. - /// - /// The message. - public void Debug(object message) - { - // Way too verbose. Can always make this configurable if needed again. - //_logger.Debug(GetMesssage(message)); - } - - /// - /// Logs a Debug format message. - /// - /// The format. - /// The args. - public void DebugFormat(string format, params object[] args) - { - // Way too verbose. Can always make this configurable if needed again. - //_logger.Debug(format, args); - } - - /// - /// Logs a Error message and exception. - /// - /// The message. - /// The exception. - public void Error(object message, Exception exception) - { - _logger.ErrorException(GetMesssage(message), exception); - } - - /// - /// Logs a Error message. - /// - /// The message. - public void Error(object message) - { - _logger.Error(GetMesssage(message)); - } - - /// - /// Logs a Error format message. - /// - /// The format. - /// The args. - public void ErrorFormat(string format, params object[] args) - { - _logger.Error(format, args); - } - - /// - /// Logs a Fatal message and exception. - /// - /// The message. - /// The exception. - public void Fatal(object message, Exception exception) - { - _logger.FatalException(GetMesssage(message), exception); - } - - /// - /// Logs a Fatal message. - /// - /// The message. - public void Fatal(object message) - { - _logger.Fatal(GetMesssage(message)); - } - - /// - /// Logs a Error format message. - /// - /// The format. - /// The args. - public void FatalFormat(string format, params object[] args) - { - _logger.Fatal(format, args); - } - - /// - /// Logs an Info message and exception. - /// - /// The message. - /// The exception. - public void Info(object message, Exception exception) - { - _logger.ErrorException(GetMesssage(message), exception); - } - - /// - /// Logs an Info message and exception. - /// - /// The message. - public void Info(object message) - { - _logger.Info(GetMesssage(message)); - } - - /// - /// Logs an Info format message. - /// - /// The format. - /// The args. - public void InfoFormat(string format, params object[] args) - { - _logger.Info(format, args); - } - - /// - /// Gets or sets a value indicating whether this instance is debug enabled. - /// - /// true if this instance is debug enabled; otherwise, false. - public bool IsDebugEnabled - { - get { return true; } - } - - /// - /// Logs a Warning message and exception. - /// - /// The message. - /// The exception. - public void Warn(object message, Exception exception) - { - _logger.ErrorException(GetMesssage(message), exception); - } - - /// - /// Logs a Warning message. - /// - /// The message. - public void Warn(object message) - { - // Hide StringMapTypeDeserializer messages - // _logger.Warn(GetMesssage(message)); - } - - /// - /// Logs a Warning format message. - /// - /// The format. - /// The args. - public void WarnFormat(string format, params object[] args) - { - // Hide StringMapTypeDeserializer messages - // _logger.Warn(format, args); - } - - /// - /// Gets the messsage. - /// - /// The o. - /// System.String. - private string GetMesssage(object o) - { - return o == null ? string.Empty : o.ToString(); - } - } -} diff --git a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/Extensions.cs b/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/Extensions.cs deleted file mode 100644 index 154313fb9..000000000 --- a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/Extensions.cs +++ /dev/null @@ -1,28 +0,0 @@ -using MediaBrowser.Model.Logging; -using SocketHttpListener.Net; -using System; - -namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp -{ - public static class Extensions - { - public static string GetOperationName(this HttpListenerRequest request) - { - return request.Url.Segments[request.Url.Segments.Length - 1]; - } - - public static void CloseOutputStream(this HttpListenerResponse response, ILogger logger) - { - try - { - response.OutputStream.Flush(); - response.OutputStream.Close(); - response.Close(); - } - catch (Exception ex) - { - logger.ErrorException("Error in HttpListenerResponseWrapper: " + ex.Message, ex); - } - } - } -} diff --git a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/RequestMono.cs b/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/RequestMono.cs index 13ae48cff..543eb4afe 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/RequestMono.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/RequestMono.cs @@ -2,11 +2,10 @@ using System.Collections.Specialized; using System.Globalization; using System.IO; +using System.Net; using System.Text; using System.Threading.Tasks; -using System.Web; using MediaBrowser.Model.Services; -using ServiceStack; namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp { @@ -128,7 +127,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp { get { - return string.IsNullOrEmpty(request.Headers[HttpHeaders.Accept]) ? null : request.Headers[HttpHeaders.Accept]; + return string.IsNullOrEmpty(request.Headers["Accept"]) ? null : request.Headers["Accept"]; } } @@ -136,7 +135,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp { get { - return string.IsNullOrEmpty(request.Headers[HttpHeaders.Authorization]) ? null : request.Headers[HttpHeaders.Authorization]; + return string.IsNullOrEmpty(request.Headers["Authorization"]) ? null : request.Headers["Authorization"]; } } @@ -152,7 +151,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp string msg = String.Format("A potentially dangerous Request.{0} value was " + "detected from the client ({1}={2}).", name, key, v); - throw new HttpRequestValidationException(msg); + throw new Exception(msg); } static void ValidateNameValueCollection(string name, QueryParamCollection coll) @@ -278,9 +277,9 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp void AddRawKeyValue(StringBuilder key, StringBuilder value) { - string decodedKey = HttpUtility.UrlDecode(key.ToString(), ContentEncoding); + string decodedKey = WebUtility.UrlDecode(key.ToString()); form.Add(decodedKey, - HttpUtility.UrlDecode(value.ToString(), ContentEncoding)); + WebUtility.UrlDecode(value.ToString())); key.Length = 0; value.Length = 0; diff --git a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/SharpWebSocket.cs b/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/SharpWebSocket.cs deleted file mode 100644 index d363c4de6..000000000 --- a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/SharpWebSocket.cs +++ /dev/null @@ -1,172 +0,0 @@ -using MediaBrowser.Common.Events; -using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Logging; -using System; -using System.Threading; -using System.Threading.Tasks; -using WebSocketState = MediaBrowser.Model.Net.WebSocketState; - -namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp -{ - public class SharpWebSocket : IWebSocket - { - /// - /// The logger - /// - private readonly ILogger _logger; - - public event EventHandler Closed; - - /// - /// Gets or sets the web socket. - /// - /// The web socket. - private SocketHttpListener.WebSocket WebSocket { get; set; } - - private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); - - /// - /// Initializes a new instance of the class. - /// - /// The socket. - /// The logger. - /// socket - public SharpWebSocket(SocketHttpListener.WebSocket socket, ILogger logger) - { - if (socket == null) - { - throw new ArgumentNullException("socket"); - } - - if (logger == null) - { - throw new ArgumentNullException("logger"); - } - - _logger = logger; - WebSocket = socket; - - socket.OnMessage += socket_OnMessage; - socket.OnClose += socket_OnClose; - socket.OnError += socket_OnError; - - WebSocket.ConnectAsServer(); - } - - void socket_OnError(object sender, SocketHttpListener.ErrorEventArgs e) - { - _logger.Error("Error in SharpWebSocket: {0}", e.Message ?? string.Empty); - //EventHelper.FireEventIfNotNull(Closed, this, EventArgs.Empty, _logger); - } - - void socket_OnClose(object sender, SocketHttpListener.CloseEventArgs e) - { - EventHelper.FireEventIfNotNull(Closed, this, EventArgs.Empty, _logger); - } - - void socket_OnMessage(object sender, SocketHttpListener.MessageEventArgs e) - { - //if (!string.IsNullOrWhiteSpace(e.Data)) - //{ - // if (OnReceive != null) - // { - // OnReceive(e.Data); - // } - // return; - //} - if (OnReceiveBytes != null) - { - OnReceiveBytes(e.RawData); - } - } - - /// - /// Gets or sets the state. - /// - /// The state. - public WebSocketState State - { - get - { - WebSocketState commonState; - - if (!Enum.TryParse(WebSocket.ReadyState.ToString(), true, out commonState)) - { - _logger.Warn("Unrecognized WebSocketState: {0}", WebSocket.ReadyState.ToString()); - } - - return commonState; - } - } - - /// - /// Sends the async. - /// - /// The bytes. - /// if set to true [end of message]. - /// The cancellation token. - /// Task. - public Task SendAsync(byte[] bytes, bool endOfMessage, CancellationToken cancellationToken) - { - var completionSource = new TaskCompletionSource(); - - WebSocket.SendAsync(bytes, res => completionSource.TrySetResult(true)); - - return completionSource.Task; - } - - /// - /// Sends the asynchronous. - /// - /// The text. - /// if set to true [end of message]. - /// The cancellation token. - /// Task. - public Task SendAsync(string text, bool endOfMessage, CancellationToken cancellationToken) - { - var completionSource = new TaskCompletionSource(); - - WebSocket.SendAsync(text, res => completionSource.TrySetResult(true)); - - return completionSource.Task; - } - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - Dispose(true); - } - - /// - /// Releases unmanaged and - optionally - managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool dispose) - { - if (dispose) - { - WebSocket.OnMessage -= socket_OnMessage; - WebSocket.OnClose -= socket_OnClose; - WebSocket.OnError -= socket_OnError; - - _cancellationTokenSource.Cancel(); - - WebSocket.Close(); - } - } - - /// - /// Gets or sets the receive action. - /// - /// The receive action. - public Action OnReceiveBytes { get; set; } - - /// - /// Gets or sets the on receive. - /// - /// The on receive. - public Action OnReceive { get; set; } - } -} diff --git a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs b/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs deleted file mode 100644 index 56f8ab429..000000000 --- a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs +++ /dev/null @@ -1,216 +0,0 @@ -using System.Collections.Specialized; -using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Logging; -using SocketHttpListener.Net; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Emby.Server.Implementations.HttpServer; -using Emby.Server.Implementations.Logging; -using MediaBrowser.Common.IO; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Services; -using ServiceStack; - -namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp -{ - public class WebSocketSharpListener : IHttpListener - { - private HttpListener _listener; - - private readonly ILogger _logger; - private readonly string _certificatePath; - private readonly IMemoryStreamProvider _memoryStreamProvider; - - public WebSocketSharpListener(ILogger logger, string certificatePath, IMemoryStreamProvider memoryStreamProvider) - { - _logger = logger; - _certificatePath = certificatePath; - _memoryStreamProvider = memoryStreamProvider; - } - - public Action ErrorHandler { get; set; } - - public Func RequestHandler { get; set; } - - public Action WebSocketConnecting { get; set; } - - public Action WebSocketConnected { get; set; } - - public void Start(IEnumerable urlPrefixes) - { - if (_listener == null) - _listener = new HttpListener(new PatternsLogger(_logger), _certificatePath); - - foreach (var prefix in urlPrefixes) - { - _logger.Info("Adding HttpListener prefix " + prefix); - _listener.Prefixes.Add(prefix); - } - - _listener.OnContext = ProcessContext; - - _listener.Start(); - } - - private void ProcessContext(HttpListenerContext context) - { - Task.Factory.StartNew(() => InitTask(context)); - } - - private void InitTask(HttpListenerContext context) - { - try - { - var task = this.ProcessRequestAsync(context); - task.ContinueWith(x => HandleError(x.Exception, context), TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.AttachedToParent); - - //if (task.Status == TaskStatus.Created) - //{ - // task.RunSynchronously(); - //} - } - catch (Exception ex) - { - HandleError(ex, context); - } - } - - private Task ProcessRequestAsync(HttpListenerContext context) - { - var request = context.Request; - - if (request.IsWebSocketRequest) - { - LoggerUtils.LogRequest(_logger, request); - - ProcessWebSocketRequest(context); - return Task.FromResult(true); - } - - if (string.IsNullOrEmpty(context.Request.RawUrl)) - return ((object)null).AsTaskResult(); - - var httpReq = GetRequest(context); - - return RequestHandler(httpReq, request.Url); - } - - private void ProcessWebSocketRequest(HttpListenerContext ctx) - { - try - { - var endpoint = ctx.Request.RemoteEndPoint.ToString(); - var url = ctx.Request.RawUrl; - var queryString = ctx.Request.QueryString ?? new NameValueCollection(); - - var queryParamCollection = new QueryParamCollection(); - - foreach (var key in queryString.AllKeys) - { - queryParamCollection[key] = queryString[key]; - } - - var connectingArgs = new WebSocketConnectingEventArgs - { - Url = url, - QueryString = queryParamCollection, - Endpoint = endpoint - }; - - if (WebSocketConnecting != null) - { - WebSocketConnecting(connectingArgs); - } - - if (connectingArgs.AllowConnection) - { - _logger.Debug("Web socket connection allowed"); - - var webSocketContext = ctx.AcceptWebSocket(null); - - if (WebSocketConnected != null) - { - WebSocketConnected(new WebSocketConnectEventArgs - { - Url = url, - QueryString = queryParamCollection, - WebSocket = new SharpWebSocket(webSocketContext.WebSocket, _logger), - Endpoint = endpoint - }); - } - } - else - { - _logger.Warn("Web socket connection not allowed"); - ctx.Response.StatusCode = 401; - ctx.Response.Close(); - } - } - catch (Exception ex) - { - _logger.ErrorException("AcceptWebSocketAsync error", ex); - ctx.Response.StatusCode = 500; - ctx.Response.Close(); - } - } - - private IHttpRequest GetRequest(HttpListenerContext httpContext) - { - var operationName = httpContext.Request.GetOperationName(); - - var req = new WebSocketSharpRequest(httpContext, operationName, RequestAttributes.None, _logger, _memoryStreamProvider); - - return req; - } - - private void HandleError(Exception ex, HttpListenerContext context) - { - var httpReq = GetRequest(context); - - if (ErrorHandler != null) - { - ErrorHandler(ex, httpReq); - } - } - - public void Stop() - { - if (_listener != null) - { - foreach (var prefix in _listener.Prefixes.ToList()) - { - _listener.Prefixes.Remove(prefix); - } - - _listener.Close(); - } - } - - public void Dispose() - { - Dispose(true); - } - - private bool _disposed; - private readonly object _disposeLock = new object(); - protected virtual void Dispose(bool disposing) - { - if (_disposed) return; - - lock (_disposeLock) - { - if (_disposed) return; - - if (disposing) - { - Stop(); - } - - //release unmanaged resources here... - _disposed = true; - } - } - } -} \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpRequest.cs b/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpRequest.cs index 72047609d..6f44fcce7 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpRequest.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpRequest.cs @@ -1,17 +1,14 @@ using System; using System.Collections.Generic; -using System.Collections.Specialized; using System.IO; using System.Text; using Emby.Server.Implementations.HttpServer.SocketSharp; using Funq; -using MediaBrowser.Common.IO; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Services; using ServiceStack; using ServiceStack.Host; -using ServiceStack.Web; using SocketHttpListener.Net; using IHttpFile = MediaBrowser.Model.Services.IHttpFile; using IHttpRequest = MediaBrowser.Model.Services.IHttpRequest; @@ -25,9 +22,9 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp public Container Container { get; set; } private readonly HttpListenerRequest request; private readonly IHttpResponse response; - private readonly IMemoryStreamProvider _memoryStreamProvider; + private readonly IMemoryStreamFactory _memoryStreamProvider; - public WebSocketSharpRequest(HttpListenerContext httpContext, string operationName, RequestAttributes requestAttributes, ILogger logger, IMemoryStreamProvider memoryStreamProvider) + public WebSocketSharpRequest(HttpListenerContext httpContext, string operationName, ILogger logger, IMemoryStreamFactory memoryStreamProvider) { this.OperationName = operationName; _memoryStreamProvider = memoryStreamProvider; @@ -55,36 +52,10 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp get { return response; } } - public T TryResolve() - { - if (typeof(T) == typeof(IHttpRequest)) - throw new Exception("You don't need to use IHttpRequest.TryResolve to resolve itself"); - - if (typeof(T) == typeof(IHttpResponse)) - throw new Exception("Resolve IHttpResponse with 'Response' property instead of IHttpRequest.TryResolve"); - - return Container == null - ? HostContext.TryResolve() - : Container.TryResolve(); - } - public string OperationName { get; set; } public object Dto { get; set; } - public string GetRawBody() - { - if (bufferedStream != null) - { - return bufferedStream.ToArray().FromUtf8Bytes(); - } - - using (var reader = new StreamReader(InputStream)) - { - return reader.ReadToEnd(); - } - } - public string RawUrl { get { return request.RawUrl; } @@ -104,7 +75,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp { get { - return String.IsNullOrEmpty(request.Headers[HttpHeaders.XForwardedFor]) ? null : request.Headers[HttpHeaders.XForwardedFor]; + return String.IsNullOrEmpty(request.Headers["X-Forwarded-For"]) ? null : request.Headers["X-Forwarded-For"]; } } @@ -112,7 +83,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp { get { - return string.IsNullOrEmpty(request.Headers[HttpHeaders.XForwardedPort]) ? (int?)null : int.Parse(request.Headers[HttpHeaders.XForwardedPort]); + return string.IsNullOrEmpty(request.Headers["X-Forwarded-Port"]) ? (int?)null : int.Parse(request.Headers["X-Forwarded-Port"]); } } @@ -120,7 +91,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp { get { - return string.IsNullOrEmpty(request.Headers[HttpHeaders.XForwardedProtocol]) ? null : request.Headers[HttpHeaders.XForwardedProtocol]; + return string.IsNullOrEmpty(request.Headers["X-Forwarded-Proto"]) ? null : request.Headers["X-Forwarded-Proto"]; } } @@ -128,7 +99,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp { get { - return String.IsNullOrEmpty(request.Headers[HttpHeaders.XRealIp]) ? null : request.Headers[HttpHeaders.XRealIp]; + return String.IsNullOrEmpty(request.Headers["X-Real-IP"]) ? null : request.Headers["X-Real-IP"]; } } @@ -140,7 +111,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp return remoteIp ?? (remoteIp = (CheckBadChars(XForwardedFor)) ?? (NormalizeIp(CheckBadChars(XRealIp)) ?? - (request.RemoteEndPoint != null ? NormalizeIp(request.RemoteEndPoint.Address.ToString()) : null))); + (request.RemoteEndPoint != null ? NormalizeIp(request.RemoteEndPoint.IpAddress.ToString()) : null))); } } @@ -280,7 +251,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp defaultContentType = HostContext.Config.DefaultContentType; } - var customContentTypes = HostContext.ContentTypes.ContentTypeFormats.Values; + var customContentTypes = ContentTypes.Instance.ContentTypeFormats.Values; var preferredContentTypes = new string[] {}; var acceptsAnything = false; @@ -328,11 +299,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp } } - if (httpReq.ContentType.MatchesContentType(MimeTypes.Soap12)) - { - return MimeTypes.Soap12; - } - if (acceptContentTypes == null && httpReq.ContentType == MimeTypes.Soap11) { return MimeTypes.Soap11; @@ -344,10 +310,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp private static string GetQueryStringContentType(IRequest httpReq) { - var callback = httpReq.QueryString[Keywords.Callback]; - if (!string.IsNullOrEmpty(callback)) return MimeTypes.Json; - - var format = httpReq.QueryString[Keywords.Format]; + var format = httpReq.QueryString["format"]; if (format == null) { const int formatMaxLength = 4; @@ -359,12 +322,11 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp } format = format.LeftPart('.').ToLower(); - if (format.Contains("json")) return MimeTypes.Json; + if (format.Contains("json")) return "application/json"; if (format.Contains("xml")) return MimeTypes.Xml; - if (format.Contains("jsv")) return MimeTypes.Jsv; string contentType; - HostContext.ContentTypes.ContentTypeFormats.TryGetValue(format, out contentType); + ContentTypes.Instance.ContentTypeFormats.TryGetValue(format, out contentType); return contentType; } @@ -474,10 +436,9 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp get { return request.UserAgent; } } - private QueryParamCollection headers; public QueryParamCollection Headers { - get { return headers ?? (headers = ToQueryParams(request.Headers)); } + get { return request.Headers; } } private QueryParamCollection queryString; @@ -492,18 +453,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp get { return formData ?? (formData = this.Form); } } - private QueryParamCollection ToQueryParams(NameValueCollection collection) - { - var result = new QueryParamCollection(); - - foreach (var key in collection.AllKeys) - { - result[key] = collection[key]; - } - - return result; - } - public bool IsLocal { get { return request.IsLocal; } @@ -563,21 +512,9 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp } } - public bool UseBufferedStream - { - get { return bufferedStream != null; } - set - { - bufferedStream = value - ? bufferedStream ?? _memoryStreamProvider.CreateNew(request.InputStream.ReadFully()) - : null; - } - } - - private MemoryStream bufferedStream; public Stream InputStream { - get { return bufferedStream ?? request.InputStream; } + get { return request.InputStream; } } public long ContentLength @@ -613,7 +550,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp } } - static Stream GetSubStream(Stream stream, IMemoryStreamProvider streamProvider) + static Stream GetSubStream(Stream stream, IMemoryStreamFactory streamProvider) { if (stream is MemoryStream) { @@ -654,4 +591,13 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp return pathInfo; } } + + public class HttpFile : IHttpFile + { + public string Name { get; set; } + public string FileName { get; set; } + public long ContentLength { get; set; } + public string ContentType { get; set; } + public Stream InputStream { get; set; } + } } diff --git a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs b/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs deleted file mode 100644 index 3aae6c9ca..000000000 --- a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs +++ /dev/null @@ -1,152 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Net; -using MediaBrowser.Model.Logging; -using ServiceStack; -using ServiceStack.Host; -using HttpListenerResponse = SocketHttpListener.Net.HttpListenerResponse; -using IHttpResponse = MediaBrowser.Model.Services.IHttpResponse; -using IRequest = MediaBrowser.Model.Services.IRequest; - -namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp -{ - public class WebSocketSharpResponse : IHttpResponse - { - private readonly ILogger _logger; - private readonly HttpListenerResponse _response; - - public WebSocketSharpResponse(ILogger logger, HttpListenerResponse response, IRequest request) - { - _logger = logger; - this._response = response; - Items = new Dictionary(); - Request = request; - } - - public IRequest Request { get; private set; } - public bool UseBufferedStream { get; set; } - public Dictionary Items { get; private set; } - public object OriginalResponse - { - get { return _response; } - } - - public int StatusCode - { - get { return this._response.StatusCode; } - set { this._response.StatusCode = value; } - } - - public string StatusDescription - { - get { return this._response.StatusDescription; } - set { this._response.StatusDescription = value; } - } - - public string ContentType - { - get { return _response.ContentType; } - set { _response.ContentType = value; } - } - - //public ICookies Cookies { get; set; } - - public void AddHeader(string name, string value) - { - if (string.Equals(name, "Content-Type", StringComparison.OrdinalIgnoreCase)) - { - ContentType = value; - return; - } - - _response.AddHeader(name, value); - } - - public string GetHeader(string name) - { - return _response.Headers[name]; - } - - public void Redirect(string url) - { - _response.Redirect(url); - } - - public Stream OutputStream - { - get { return _response.OutputStream; } - } - - public object Dto { get; set; } - - public void Write(string text) - { - var bOutput = System.Text.Encoding.UTF8.GetBytes(text); - _response.ContentLength64 = bOutput.Length; - - var outputStream = _response.OutputStream; - outputStream.Write(bOutput, 0, bOutput.Length); - Close(); - } - - public void Close() - { - if (!this.IsClosed) - { - this.IsClosed = true; - - try - { - this._response.CloseOutputStream(_logger); - } - catch (Exception ex) - { - _logger.ErrorException("Error closing HttpListener output stream", ex); - } - } - } - - public void End() - { - Close(); - } - - public void Flush() - { - _response.OutputStream.Flush(); - } - - public bool IsClosed - { - get; - private set; - } - - public void SetContentLength(long contentLength) - { - //you can happily set the Content-Length header in Asp.Net - //but HttpListener will complain if you do - you have to set ContentLength64 on the response. - //workaround: HttpListener throws "The parameter is incorrect" exceptions when we try to set the Content-Length header - _response.ContentLength64 = contentLength; - } - - public void SetCookie(Cookie cookie) - { - var cookieStr = cookie.AsHeaderValue(); - _response.Headers.Add(HttpHeaders.SetCookie, cookieStr); - } - - public bool SendChunked - { - get { return _response.SendChunked; } - set { _response.SendChunked = value; } - } - - public bool KeepAlive { get; set; } - - public void ClearCookies() - { - } - } -} diff --git a/MediaBrowser.Server.Implementations/IO/MemoryStreamProvider.cs b/MediaBrowser.Server.Implementations/IO/MemoryStreamProvider.cs index 0a0a04d5d..cb62ffa98 100644 --- a/MediaBrowser.Server.Implementations/IO/MemoryStreamProvider.cs +++ b/MediaBrowser.Server.Implementations/IO/MemoryStreamProvider.cs @@ -4,7 +4,7 @@ using Microsoft.IO; namespace MediaBrowser.Server.Implementations.IO { - public class RecyclableMemoryStreamProvider : IMemoryStreamProvider + public class RecyclableMemoryStreamProvider : IMemoryStreamFactory { readonly RecyclableMemoryStreamManager _manager = new RecyclableMemoryStreamManager(); @@ -22,9 +22,15 @@ namespace MediaBrowser.Server.Implementations.IO { return _manager.GetStream("RecyclableMemoryStream", buffer, 0, buffer.Length); } + + public bool TryGetBuffer(MemoryStream stream, out byte[] buffer) + { + buffer = stream.GetBuffer(); + return true; + } } - public class MemoryStreamProvider : IMemoryStreamProvider + public class MemoryStreamProvider : IMemoryStreamFactory { public MemoryStream CreateNew() { @@ -40,5 +46,11 @@ namespace MediaBrowser.Server.Implementations.IO { return new MemoryStream(buffer); } + + public bool TryGetBuffer(MemoryStream stream, out byte[] buffer) + { + buffer = stream.GetBuffer(); + return true; + } } } diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 42e2e0d7b..4096d71dc 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -69,16 +69,12 @@ ..\packages\Patterns.Logging.1.0.0.6\lib\portable-net45+win8\Patterns.Logging.dll True - - ..\ThirdParty\ServiceStack\ServiceStack.Api.Swagger.dll - False ..\ThirdParty\SharpCompress\SharpCompress.dll - - ..\packages\SocketHttpListener.1.0.0.44\lib\net45\SocketHttpListener.dll - True + + ..\ThirdParty\emby\SocketHttpListener.Portable.dll @@ -88,7 +84,6 @@ - ..\ThirdParty\ServiceStack\ServiceStack.dll @@ -119,18 +114,9 @@ - - - - - - - - - diff --git a/MediaBrowser.Server.Implementations/Persistence/DataExtensions.cs b/MediaBrowser.Server.Implementations/Persistence/DataExtensions.cs index 86ecbd24d..179101ca2 100644 --- a/MediaBrowser.Server.Implementations/Persistence/DataExtensions.cs +++ b/MediaBrowser.Server.Implementations/Persistence/DataExtensions.cs @@ -55,7 +55,7 @@ namespace MediaBrowser.Server.Implementations.Persistence /// /// Stream. /// reader - public static Stream GetMemoryStream(this IDataReader reader, int ordinal, IMemoryStreamProvider streamProvider) + public static Stream GetMemoryStream(this IDataReader reader, int ordinal, IMemoryStreamFactory streamProvider) { if (reader == null) { @@ -134,7 +134,7 @@ namespace MediaBrowser.Server.Implementations.Persistence /// /// System.Byte[][]. /// obj - public static byte[] SerializeToBytes(this IJsonSerializer json, object obj, IMemoryStreamProvider streamProvider) + public static byte[] SerializeToBytes(this IJsonSerializer json, object obj, IMemoryStreamFactory streamProvider) { if (obj == null) { diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteDisplayPreferencesRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteDisplayPreferencesRepository.cs index 3d20cad36..c97ba8792 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteDisplayPreferencesRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteDisplayPreferencesRepository.cs @@ -20,9 +20,9 @@ namespace MediaBrowser.Server.Implementations.Persistence /// public class SqliteDisplayPreferencesRepository : BaseSqliteRepository, IDisplayPreferencesRepository { - private readonly IMemoryStreamProvider _memoryStreamProvider; + private readonly IMemoryStreamFactory _memoryStreamProvider; - public SqliteDisplayPreferencesRepository(ILogManager logManager, IJsonSerializer jsonSerializer, IApplicationPaths appPaths, IDbConnector dbConnector, IMemoryStreamProvider memoryStreamProvider) + public SqliteDisplayPreferencesRepository(ILogManager logManager, IJsonSerializer jsonSerializer, IApplicationPaths appPaths, IDbConnector dbConnector, IMemoryStreamFactory memoryStreamProvider) : base(logManager, dbConnector) { _jsonSerializer = jsonSerializer; diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs index 5fcd38f87..3577d1883 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs @@ -99,12 +99,12 @@ namespace MediaBrowser.Server.Implementations.Persistence private IDbCommand _updateInheritedTagsCommand; public const int LatestSchemaVersion = 109; - private readonly IMemoryStreamProvider _memoryStreamProvider; + private readonly IMemoryStreamFactory _memoryStreamProvider; /// /// Initializes a new instance of the class. /// - public SqliteItemRepository(IServerConfigurationManager config, IJsonSerializer jsonSerializer, ILogManager logManager, IDbConnector connector, IMemoryStreamProvider memoryStreamProvider) + public SqliteItemRepository(IServerConfigurationManager config, IJsonSerializer jsonSerializer, ILogManager logManager, IDbConnector connector, IMemoryStreamFactory memoryStreamProvider) : base(logManager, connector) { if (config == null) diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteUserRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteUserRepository.cs index c3cf4acc4..0c1367e0a 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteUserRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteUserRepository.cs @@ -20,9 +20,9 @@ namespace MediaBrowser.Server.Implementations.Persistence public class SqliteUserRepository : BaseSqliteRepository, IUserRepository { private readonly IJsonSerializer _jsonSerializer; - private readonly IMemoryStreamProvider _memoryStreamProvider; + private readonly IMemoryStreamFactory _memoryStreamProvider; - public SqliteUserRepository(ILogManager logManager, IServerApplicationPaths appPaths, IJsonSerializer jsonSerializer, IDbConnector dbConnector, IMemoryStreamProvider memoryStreamProvider) : base(logManager, dbConnector) + public SqliteUserRepository(ILogManager logManager, IServerApplicationPaths appPaths, IJsonSerializer jsonSerializer, IDbConnector dbConnector, IMemoryStreamFactory memoryStreamProvider) : base(logManager, dbConnector) { _jsonSerializer = jsonSerializer; _memoryStreamProvider = memoryStreamProvider; diff --git a/MediaBrowser.Server.Implementations/packages.config b/MediaBrowser.Server.Implementations/packages.config index 909246f68..84a5d5a1d 100644 --- a/MediaBrowser.Server.Implementations/packages.config +++ b/MediaBrowser.Server.Implementations/packages.config @@ -5,6 +5,5 @@ - \ No newline at end of file diff --git a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs index fd84940dd..c0f184bef 100644 --- a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs +++ b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs @@ -126,7 +126,7 @@ using MediaBrowser.Model.Reflection; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Services; using MediaBrowser.Model.Social; -using MediaBrowser.Model.TextEncoding; +using MediaBrowser.Model.Text; using MediaBrowser.Model.Xml; using MediaBrowser.Server.Implementations.Archiving; using MediaBrowser.Server.Implementations.Serialization; @@ -252,6 +252,8 @@ namespace MediaBrowser.Server.Startup.Common /// The zip client. protected IZipClient ZipClient { get; private set; } + protected IAuthService AuthService { get; private set; } + private readonly StartupOptions _startupOptions; private readonly string _releaseAssetFilename; @@ -410,7 +412,7 @@ namespace MediaBrowser.Server.Startup.Common LogManager.RemoveConsoleOutput(); } - protected override IMemoryStreamProvider CreateMemoryStreamProvider() + protected override IMemoryStreamFactory CreateMemoryStreamProvider() { if (Environment.OSVersion.Platform == PlatformID.Win32NT) { @@ -555,7 +557,7 @@ namespace MediaBrowser.Server.Startup.Common StringExtensions.LocalizationManager = LocalizationManager; RegisterSingleInstance(LocalizationManager); - IEncoding textEncoding = new TextEncoding(FileSystemManager); + ITextEncoding textEncoding = new TextEncoding(FileSystemManager); RegisterSingleInstance(textEncoding); Utilities.EncodingHelper = textEncoding; RegisterSingleInstance(() => new BdInfoExaminer(FileSystemManager, textEncoding)); @@ -601,7 +603,7 @@ namespace MediaBrowser.Server.Startup.Common RegisterSingleInstance(() => new SearchEngine(LogManager, LibraryManager, UserManager)); - HttpServer = ServerFactory.CreateServer(this, LogManager, ServerConfigurationManager, NetworkManager, MemoryStreamProvider, "Emby", "web/index.html"); + HttpServer = ServerFactory.CreateServer(this, LogManager, ServerConfigurationManager, NetworkManager, MemoryStreamProvider, "Emby", "web/index.html", textEncoding, SocketFactory, CryptographyProvider); HttpServer.GlobalResponse = LocalizationManager.GetLocalizedString("StartupEmbyServerIsLoading"); RegisterSingleInstance(HttpServer, false); progress.Report(10); @@ -702,7 +704,9 @@ namespace MediaBrowser.Server.Startup.Common var authContext = new AuthorizationContext(AuthenticationRepository, ConnectManager); RegisterSingleInstance(authContext); RegisterSingleInstance(new SessionContext(UserManager, authContext, SessionManager)); - RegisterSingleInstance(new AuthService(UserManager, authContext, ServerConfigurationManager, ConnectManager, SessionManager, DeviceManager)); + + AuthService = new AuthService(UserManager, authContext, ServerConfigurationManager, ConnectManager, SessionManager, DeviceManager); + RegisterSingleInstance(AuthService); SubtitleEncoder = new SubtitleEncoder(LibraryManager, LogManager.GetLogger("SubtitleEncoder"), ApplicationPaths, FileSystemManager, MediaEncoder, JsonSerializer, HttpClient, MediaSourceManager, MemoryStreamProvider, ProcessFactory, textEncoding); RegisterSingleInstance(SubtitleEncoder); @@ -900,6 +904,7 @@ namespace MediaBrowser.Server.Startup.Common BaseStreamingService.AppHost = this; BaseStreamingService.HttpClient = HttpClient; Utilities.CryptographyProvider = CryptographyProvider; + AuthenticatedAttribute.AuthService = AuthService; } /// @@ -1291,7 +1296,7 @@ namespace MediaBrowser.Server.Startup.Common try { // Return the first matched address, if found, or the first known local address - var address = (await GetLocalIpAddressesInternal().ConfigureAwait(false)).FirstOrDefault(i => !IPAddress.IsLoopback(i)); + var address = (await GetLocalIpAddresses().ConfigureAwait(false)).FirstOrDefault(i => !i.Equals(IpAddressInfo.Loopback) && !i.Equals(IpAddressInfo.IPv6Loopback)); if (address != null) { @@ -1308,19 +1313,14 @@ namespace MediaBrowser.Server.Startup.Common return null; } - public string GetLocalApiUrl(IPAddress ipAddress) - { - return GetLocalApiUrl(ipAddress.ToString(), ipAddress.AddressFamily == AddressFamily.InterNetworkV6); - } - - public string GetLocalApiUrl(string ipAddress, bool isIpv6) + public string GetLocalApiUrl(IpAddressInfo ipAddress) { - if (isIpv6) + if (ipAddress.AddressFamily == IpAddressFamily.InterNetworkV6) { - return GetLocalApiUrl("[" + ipAddress + "]"); + return GetLocalApiUrl("[" + ipAddress.Address + "]"); } - return GetLocalApiUrl(ipAddress); + return GetLocalApiUrl(ipAddress.Address); } public string GetLocalApiUrl(string host) @@ -1332,23 +1332,8 @@ namespace MediaBrowser.Server.Startup.Common public async Task> GetLocalIpAddresses() { - var list = await GetLocalIpAddressesInternal().ConfigureAwait(false); - - return list.Select(i => new IpAddressInfo - { - Address = i.ToString(), - IsIpv6 = i.AddressFamily == AddressFamily.InterNetworkV6 - - }).ToList(); - } - - private async Task> GetLocalIpAddressesInternal() - { - // Need to do this until Common will compile with this method - var nativeNetworkManager = (BaseNetworkManager)NetworkManager; - - var addresses = nativeNetworkManager.GetLocalIpAddresses().ToList(); - var list = new List(); + var addresses = NetworkManager.GetLocalIpAddresses().ToList(); + var list = new List(); foreach (var address in addresses) { @@ -1364,9 +1349,10 @@ namespace MediaBrowser.Server.Startup.Common private readonly ConcurrentDictionary _validAddressResults = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); private DateTime _lastAddressCacheClear; - private async Task IsIpAddressValidAsync(IPAddress address) + private async Task IsIpAddressValidAsync(IpAddressInfo address) { - if (IPAddress.IsLoopback(address)) + if (address.Equals(IpAddressInfo.Loopback) || + address.Equals(IpAddressInfo.IPv6Loopback)) { return true; } diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index e813c0a0a..7b949fcb1 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common - 3.0.684 + 3.0.688 Emby.Common Emby Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index 9b0c10cbb..96986de9b 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Server.Core - 3.0.684 + 3.0.688 Emby.Server.Core Emby Team ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains core components required to build plugins for Emby Server. Copyright © Emby 2013 - + diff --git a/OpenSubtitlesHandler/Utilities.cs b/OpenSubtitlesHandler/Utilities.cs index b2ad2d0f1..3fe606c78 100644 --- a/OpenSubtitlesHandler/Utilities.cs +++ b/OpenSubtitlesHandler/Utilities.cs @@ -24,7 +24,7 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Net; using MediaBrowser.Model.Cryptography; -using MediaBrowser.Model.TextEncoding; +using MediaBrowser.Model.Text; namespace OpenSubtitlesHandler { @@ -33,9 +33,9 @@ namespace OpenSubtitlesHandler /// public sealed class Utilities { - public static ICryptographyProvider CryptographyProvider { get; set; } + public static ICryptoProvider CryptographyProvider { get; set; } public static IHttpClient HttpClient { get; set; } - public static IEncoding EncodingHelper { get; set; } + public static ITextEncoding EncodingHelper { get; set; } private const string XML_RPC_SERVER = "https://api.opensubtitles.org/xml-rpc"; @@ -124,13 +124,13 @@ namespace OpenSubtitlesHandler data.Add((byte)r); } var bytes = data.ToArray(); - return EncodingHelper.GetASCIIString(bytes, 0, bytes.Length); + return EncodingHelper.GetASCIIEncoding().GetString(bytes, 0, bytes.Length); } } public static byte[] GetASCIIBytes(string text) { - return EncodingHelper.GetASCIIBytes(text); + return EncodingHelper.GetASCIIEncoding().GetBytes(text); } /// -- cgit v1.2.3 From 227dd0a42d398978ca085a607345a7877de87950 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 10 Nov 2016 09:41:24 -0500 Subject: rework result factory --- .../HttpServer/SwaggerService.cs | 9 +- Emby.Server.sln | 36 +- MediaBrowser.Api/ApiEntryPoint.cs | 7 +- MediaBrowser.Api/BaseApiService.cs | 43 +- MediaBrowser.Api/ConnectService.cs | 6 +- MediaBrowser.Api/GamesService.cs | 7 +- MediaBrowser.Api/Images/ImageByNameService.cs | 14 +- MediaBrowser.Api/Images/ImageService.cs | 8 +- MediaBrowser.Api/Library/LibraryService.cs | 38 +- MediaBrowser.Api/LiveTv/LiveTvService.cs | 26 +- MediaBrowser.Api/Movies/CollectionService.cs | 8 +- MediaBrowser.Api/Movies/MoviesService.cs | 8 +- MediaBrowser.Api/Movies/TrailersService.cs | 10 +- MediaBrowser.Api/Music/AlbumsService.cs | 8 +- MediaBrowser.Api/Music/InstantMixService.cs | 6 +- MediaBrowser.Api/Playback/BaseStreamingService.cs | 5 +- MediaBrowser.Api/Playback/Hls/BaseHlsService.cs | 10 +- MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs | 4 +- MediaBrowser.Api/Playback/Hls/VideoHlsService.cs | 9 +- MediaBrowser.Api/Playback/MediaInfoService.cs | 8 +- .../Playback/Progressive/AudioService.cs | 9 +- .../Progressive/BaseProgressiveStreamingService.cs | 2 +- .../Playback/Progressive/VideoService.cs | 4 +- MediaBrowser.Api/PlaylistService.cs | 6 +- MediaBrowser.Api/Session/SessionsService.cs | 22 +- MediaBrowser.Api/Social/SharingService.cs | 8 +- MediaBrowser.Api/Subtitles/SubtitleService.cs | 6 +- MediaBrowser.Api/Sync/SyncService.cs | 6 +- MediaBrowser.Api/TvShowsService.cs | 14 +- MediaBrowser.Api/UserLibrary/ArtistsService.cs | 20 +- .../UserLibrary/BaseItemsByNameService.cs | 9 +- MediaBrowser.Api/UserLibrary/GameGenresService.cs | 13 +- MediaBrowser.Api/UserLibrary/GenresService.cs | 13 +- MediaBrowser.Api/UserLibrary/ItemsService.cs | 6 +- MediaBrowser.Api/UserLibrary/MusicGenresService.cs | 13 +- MediaBrowser.Api/UserLibrary/PersonsService.cs | 20 +- MediaBrowser.Api/UserLibrary/PlaystateService.cs | 18 +- MediaBrowser.Api/UserLibrary/StudiosService.cs | 13 +- MediaBrowser.Api/UserLibrary/UserLibraryService.cs | 20 +- MediaBrowser.Api/UserLibrary/UserViewsService.cs | 9 +- MediaBrowser.Api/UserLibrary/YearsService.cs | 13 +- MediaBrowser.Api/UserService.cs | 22 +- MediaBrowser.Api/VideosService.cs | 6 +- .../MediaBrowser.Controller.csproj | 2 - MediaBrowser.Controller/Net/IHasAuthorization.cs | 12 - MediaBrowser.Controller/Net/IHasSession.cs | 12 - MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 5 + MediaBrowser.Model/Serialization/IXmlSerializer.cs | 2 +- .../HttpServer/ContainerAdapter.cs | 44 - .../HttpServer/HttpListenerHost.cs | 102 +- .../HttpServer/HttpResultFactory.cs | 158 +- .../HttpServer/ServerFactory.cs | 7 +- .../SocketSharp/WebSocketSharpRequest.cs | 32 +- .../MediaBrowser.Server.Implementations.csproj | 4 - .../MediaBrowser.Server.Mono.csproj | 21 +- MediaBrowser.Server.Mono/Native/BaseMonoApp.cs | 271 --- MediaBrowser.Server.Mono/Native/MonoApp.cs | 275 +++ MediaBrowser.Server.Mono/Native/NativeApp.cs | 45 - .../Networking/CertificateGenerator.cs | 68 - .../Networking/NetworkManager.cs | 49 - MediaBrowser.Server.Mono/Program.cs | 2 +- MediaBrowser.Server.Mono/Security/ASN1.cs | 339 ---- MediaBrowser.Server.Mono/Security/ASN1Convert.cs | 206 --- .../Security/BitConverterLE.cs | 239 --- MediaBrowser.Server.Mono/Security/CryptoConvert.cs | 744 -------- MediaBrowser.Server.Mono/Security/PKCS1.cs | 490 ----- MediaBrowser.Server.Mono/Security/PKCS12.cs | 1933 ------------------- MediaBrowser.Server.Mono/Security/PKCS7.cs | 1011 ---------- MediaBrowser.Server.Mono/Security/PKCS8.cs | 494 ----- MediaBrowser.Server.Mono/Security/X501Name.cs | 392 ---- MediaBrowser.Server.Mono/Security/X509Builder.cs | 152 -- .../Security/X509Certificate.cs | 562 ------ .../Security/X509CertificateBuilder.cs | 244 --- .../Security/X509CertificateCollection.cs | 200 -- MediaBrowser.Server.Mono/Security/X509Extension.cs | 207 --- .../Security/X509Extensions.cs | 194 -- .../Security/X520Attributes.cs | 345 ---- .../ApplicationHost.cs | 4 +- .../MediaBrowser.Server.Startup.Common.csproj | 24 +- .../Networking/NetworkManager.cs | 50 + .../Security/ASN1.cs | 339 ++++ .../Security/ASN1Convert.cs | 207 +++ .../Security/BitConverterLE.cs | 239 +++ .../Security/CertificateGenerator.cs | 67 + .../Security/CryptoConvert.cs | 745 ++++++++ .../Security/PKCS1.cs | 491 +++++ .../Security/PKCS12.cs | 1934 ++++++++++++++++++++ .../Security/PKCS7.cs | 1012 ++++++++++ .../Security/PKCS8.cs | 495 +++++ .../Security/PfxGenerator.cs | 75 + .../Security/X501Name.cs | 393 ++++ .../Security/X509Builder.cs | 153 ++ .../Security/X509Certificate.cs | 563 ++++++ .../Security/X509CertificateBuilder.cs | 245 +++ .../Security/X509CertificateCollection.cs | 201 ++ .../Security/X509Extension.cs | 208 +++ .../Security/X509Extensions.cs | 195 ++ .../Security/X520Attributes.cs | 346 ++++ .../MediaBrowser.ServerApplication.csproj | 1 - .../Networking/CertificateGenerator.cs | 244 --- .../Networking/NetworkManager.cs | 16 +- MediaBrowser.WebDashboard/Api/DashboardService.cs | 22 +- 102 files changed, 8789 insertions(+), 8885 deletions(-) delete mode 100644 MediaBrowser.Controller/Net/IHasAuthorization.cs delete mode 100644 MediaBrowser.Controller/Net/IHasSession.cs delete mode 100644 MediaBrowser.Server.Implementations/HttpServer/ContainerAdapter.cs delete mode 100644 MediaBrowser.Server.Mono/Native/BaseMonoApp.cs create mode 100644 MediaBrowser.Server.Mono/Native/MonoApp.cs delete mode 100644 MediaBrowser.Server.Mono/Native/NativeApp.cs delete mode 100644 MediaBrowser.Server.Mono/Networking/CertificateGenerator.cs delete mode 100644 MediaBrowser.Server.Mono/Networking/NetworkManager.cs delete mode 100644 MediaBrowser.Server.Mono/Security/ASN1.cs delete mode 100644 MediaBrowser.Server.Mono/Security/ASN1Convert.cs delete mode 100644 MediaBrowser.Server.Mono/Security/BitConverterLE.cs delete mode 100644 MediaBrowser.Server.Mono/Security/CryptoConvert.cs delete mode 100644 MediaBrowser.Server.Mono/Security/PKCS1.cs delete mode 100644 MediaBrowser.Server.Mono/Security/PKCS12.cs delete mode 100644 MediaBrowser.Server.Mono/Security/PKCS7.cs delete mode 100644 MediaBrowser.Server.Mono/Security/PKCS8.cs delete mode 100644 MediaBrowser.Server.Mono/Security/X501Name.cs delete mode 100644 MediaBrowser.Server.Mono/Security/X509Builder.cs delete mode 100644 MediaBrowser.Server.Mono/Security/X509Certificate.cs delete mode 100644 MediaBrowser.Server.Mono/Security/X509CertificateBuilder.cs delete mode 100644 MediaBrowser.Server.Mono/Security/X509CertificateCollection.cs delete mode 100644 MediaBrowser.Server.Mono/Security/X509Extension.cs delete mode 100644 MediaBrowser.Server.Mono/Security/X509Extensions.cs delete mode 100644 MediaBrowser.Server.Mono/Security/X520Attributes.cs create mode 100644 MediaBrowser.Server.Startup.Common/Networking/NetworkManager.cs create mode 100644 MediaBrowser.Server.Startup.Common/Security/ASN1.cs create mode 100644 MediaBrowser.Server.Startup.Common/Security/ASN1Convert.cs create mode 100644 MediaBrowser.Server.Startup.Common/Security/BitConverterLE.cs create mode 100644 MediaBrowser.Server.Startup.Common/Security/CertificateGenerator.cs create mode 100644 MediaBrowser.Server.Startup.Common/Security/CryptoConvert.cs create mode 100644 MediaBrowser.Server.Startup.Common/Security/PKCS1.cs create mode 100644 MediaBrowser.Server.Startup.Common/Security/PKCS12.cs create mode 100644 MediaBrowser.Server.Startup.Common/Security/PKCS7.cs create mode 100644 MediaBrowser.Server.Startup.Common/Security/PKCS8.cs create mode 100644 MediaBrowser.Server.Startup.Common/Security/PfxGenerator.cs create mode 100644 MediaBrowser.Server.Startup.Common/Security/X501Name.cs create mode 100644 MediaBrowser.Server.Startup.Common/Security/X509Builder.cs create mode 100644 MediaBrowser.Server.Startup.Common/Security/X509Certificate.cs create mode 100644 MediaBrowser.Server.Startup.Common/Security/X509CertificateBuilder.cs create mode 100644 MediaBrowser.Server.Startup.Common/Security/X509CertificateCollection.cs create mode 100644 MediaBrowser.Server.Startup.Common/Security/X509Extension.cs create mode 100644 MediaBrowser.Server.Startup.Common/Security/X509Extensions.cs create mode 100644 MediaBrowser.Server.Startup.Common/Security/X520Attributes.cs delete mode 100644 MediaBrowser.ServerApplication/Networking/CertificateGenerator.cs (limited to 'MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs') diff --git a/Emby.Server.Implementations/HttpServer/SwaggerService.cs b/Emby.Server.Implementations/HttpServer/SwaggerService.cs index 3c181e425..d41946645 100644 --- a/Emby.Server.Implementations/HttpServer/SwaggerService.cs +++ b/Emby.Server.Implementations/HttpServer/SwaggerService.cs @@ -6,15 +6,16 @@ using MediaBrowser.Model.Services; namespace Emby.Server.Implementations.HttpServer { - public class SwaggerService : IHasResultFactory, IService + public class SwaggerService : IService, IRequiresRequest { private readonly IServerApplicationPaths _appPaths; private readonly IFileSystem _fileSystem; - public SwaggerService(IServerApplicationPaths appPaths, IFileSystem fileSystem) + public SwaggerService(IServerApplicationPaths appPaths, IFileSystem fileSystem, IHttpResultFactory resultFactory) { _appPaths = appPaths; _fileSystem = fileSystem; + _resultFactory = resultFactory; } /// @@ -28,14 +29,14 @@ namespace Emby.Server.Implementations.HttpServer var requestedFile = Path.Combine(swaggerDirectory, request.ResourceName.Replace('/', _fileSystem.DirectorySeparatorChar)); - return ResultFactory.GetStaticFileResult(Request, requestedFile).Result; + return _resultFactory.GetStaticFileResult(Request, requestedFile).Result; } /// /// Gets or sets the result factory. /// /// The result factory. - public IHttpResultFactory ResultFactory { get; set; } + private readonly IHttpResultFactory _resultFactory; /// /// Gets or sets the request context. diff --git a/Emby.Server.sln b/Emby.Server.sln index d28a49273..dce5e562e 100644 --- a/Emby.Server.sln +++ b/Emby.Server.sln @@ -36,10 +36,6 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Emby.Common.Implementations EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Mono.Nat", "Mono.Nat\Mono.Nat.xproj", "{0A82260B-4C22-4FD2-869A-E510044E3502}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "RSSDP", "RSSDP\RSSDP.xproj", "{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}" -EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Emby.Dlna", "Emby.Dlna\Emby.Dlna.xproj", "{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Photos", "Emby.Photos\Emby.Photos.csproj", "{89AB4548-770D-41FD-A891-8DAFF44F452C}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Api", "MediaBrowser.Api\MediaBrowser.Api.csproj", "{4FD51AC5-2C16-4308-A993-C3A84F3B4582}" @@ -48,6 +44,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.MediaEncoding" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Server.Implementations", "Emby.Server.Implementations\Emby.Server.Implementations.csproj", "{D08B8079-08B3-48F2-83C4-E9CCCE48AFF1}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RSSDP", "RSSDP\RSSDP.csproj", "{21002819-C39A-4D3E-BE83-2A276A77FB1F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Dlna", "Emby.Dlna\Emby.Dlna.csproj", "{805844AB-E92F-45E6-9D99-4F6D48D129A5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -133,18 +133,6 @@ Global {0A82260B-4C22-4FD2-869A-E510044E3502}.Release Mono|Any CPU.Build.0 = Release|Any CPU {0A82260B-4C22-4FD2-869A-E510044E3502}.Release|Any CPU.ActiveCfg = Release|Any CPU {0A82260B-4C22-4FD2-869A-E510044E3502}.Release|Any CPU.Build.0 = Release|Any CPU - {C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU - {C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Release Mono|Any CPU.Build.0 = Release|Any CPU - {C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Release|Any CPU.Build.0 = Release|Any CPU - {F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU - {F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Release Mono|Any CPU.Build.0 = Release|Any CPU - {F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Release|Any CPU.Build.0 = Release|Any CPU {89AB4548-770D-41FD-A891-8DAFF44F452C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {89AB4548-770D-41FD-A891-8DAFF44F452C}.Debug|Any CPU.Build.0 = Debug|Any CPU {89AB4548-770D-41FD-A891-8DAFF44F452C}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU @@ -169,6 +157,18 @@ Global {D08B8079-08B3-48F2-83C4-E9CCCE48AFF1}.Release Mono|Any CPU.Build.0 = Release|Any CPU {D08B8079-08B3-48F2-83C4-E9CCCE48AFF1}.Release|Any CPU.ActiveCfg = Release|Any CPU {D08B8079-08B3-48F2-83C4-E9CCCE48AFF1}.Release|Any CPU.Build.0 = Release|Any CPU + {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU + {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release Mono|Any CPU.Build.0 = Release|Any CPU + {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release|Any CPU.Build.0 = Release|Any CPU + {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU + {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release Mono|Any CPU.Build.0 = Release|Any CPU + {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -187,11 +187,11 @@ Global {4A4402D4-E910-443B-B8FC-2C18286A2CA0} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839} {5A27010A-09C6-4E86-93EA-437484C10917} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839} {0A82260B-4C22-4FD2-869A-E510044E3502} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839} - {C227ADB7-E256-4E70-A8B9-22B9E0CF4F55} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839} - {F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839} {89AB4548-770D-41FD-A891-8DAFF44F452C} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839} {4FD51AC5-2C16-4308-A993-C3A84F3B4582} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839} {0BD82FA6-EB8A-4452-8AF5-74F9C3849451} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839} {D08B8079-08B3-48F2-83C4-E9CCCE48AFF1} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839} + {21002819-C39A-4D3E-BE83-2A276A77FB1F} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839} + {805844AB-E92F-45E6-9D99-4F6D48D129A5} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839} EndGlobalSection EndGlobal diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs index 8f5b5eaaf..bc0241766 100644 --- a/MediaBrowser.Api/ApiEntryPoint.cs +++ b/MediaBrowser.Api/ApiEntryPoint.cs @@ -16,6 +16,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Net; using MediaBrowser.Model.Diagnostics; using MediaBrowser.Model.IO; using MediaBrowser.Model.Dto; @@ -37,7 +38,8 @@ namespace MediaBrowser.Api /// Gets or sets the logger. /// /// The logger. - private ILogger Logger { get; set; } + internal ILogger Logger { get; private set; } + internal IHttpResultFactory ResultFactory { get; private set; } /// /// The application paths @@ -66,7 +68,7 @@ namespace MediaBrowser.Api /// The configuration. /// The file system. /// The media source manager. - public ApiEntryPoint(ILogger logger, ISessionManager sessionManager, IServerConfigurationManager config, IFileSystem fileSystem, IMediaSourceManager mediaSourceManager, ITimerFactory timerFactory, IProcessFactory processFactory) + public ApiEntryPoint(ILogger logger, ISessionManager sessionManager, IServerConfigurationManager config, IFileSystem fileSystem, IMediaSourceManager mediaSourceManager, ITimerFactory timerFactory, IProcessFactory processFactory, IHttpResultFactory resultFactory) { Logger = logger; _sessionManager = sessionManager; @@ -75,6 +77,7 @@ namespace MediaBrowser.Api _mediaSourceManager = mediaSourceManager; TimerFactory = timerFactory; ProcessFactory = processFactory; + ResultFactory = resultFactory; Instance = this; _sessionManager.PlaybackProgress += _sessionManager_PlaybackProgress; diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs index 4810d4e9c..73a2bedb9 100644 --- a/MediaBrowser.Api/BaseApiService.cs +++ b/MediaBrowser.Api/BaseApiService.cs @@ -17,19 +17,31 @@ namespace MediaBrowser.Api /// /// Class BaseApiService /// - public class BaseApiService : IHasResultFactory, IService, IHasSession + public class BaseApiService : IService, IRequiresRequest { /// /// Gets or sets the logger. /// /// The logger. - public ILogger Logger { get; set; } + public ILogger Logger + { + get + { + return ApiEntryPoint.Instance.Logger; + } + } /// /// Gets or sets the HTTP result factory. /// /// The HTTP result factory. - public IHttpResultFactory ResultFactory { get; set; } + public IHttpResultFactory ResultFactory + { + get + { + return ApiEntryPoint.Instance.ResultFactory; + } + } /// /// Gets or sets the request context. @@ -37,9 +49,6 @@ namespace MediaBrowser.Api /// The request context. public IRequest Request { get; set; } - public ISessionContext SessionContext { get; set; } - public IAuthorizationContext AuthorizationContext { get; set; } - public string GetHeader(string name) { return Request.Headers[name]; @@ -57,9 +66,9 @@ namespace MediaBrowser.Api return ResultFactory.GetOptimizedResult(Request, result); } - protected void AssertCanUpdateUser(IUserManager userManager, string userId) + protected void AssertCanUpdateUser(IAuthorizationContext authContext, IUserManager userManager, string userId) { - var auth = AuthorizationContext.GetAuthorizationInfo(Request); + var auth = authContext.GetAuthorizationInfo(Request); var authenticatedUser = userManager.GetUserById(auth.UserId); @@ -96,9 +105,9 @@ namespace MediaBrowser.Api /// Gets the session. /// /// SessionInfo. - protected async Task GetSession() + protected async Task GetSession(ISessionContext sessionContext) { - var session = await SessionContext.GetSession(Request).ConfigureAwait(false); + var session = await sessionContext.GetSession(Request).ConfigureAwait(false); if (session == null) { @@ -108,21 +117,11 @@ namespace MediaBrowser.Api return session; } - /// - /// To the static file result. - /// - /// The path. - /// System.Object. - protected object ToStaticFileResult(string path) - { - return ResultFactory.GetStaticFileResult(Request, path).Result; - } - - protected DtoOptions GetDtoOptions(object request) + protected DtoOptions GetDtoOptions(IAuthorizationContext authContext, object request) { var options = new DtoOptions(); - options.DeviceId = AuthorizationContext.GetAuthorizationInfo(Request).DeviceId; + options.DeviceId = authContext.GetAuthorizationInfo(Request).DeviceId; var hasFields = request as IHasItemFields; if (hasFields != null) diff --git a/MediaBrowser.Api/ConnectService.cs b/MediaBrowser.Api/ConnectService.cs index ad3649cd2..304dc366b 100644 --- a/MediaBrowser.Api/ConnectService.cs +++ b/MediaBrowser.Api/ConnectService.cs @@ -78,11 +78,13 @@ namespace MediaBrowser.Api { private readonly IConnectManager _connectManager; private readonly ISessionManager _sessionManager; + private readonly IAuthorizationContext _authContext; - public ConnectService(IConnectManager connectManager, ISessionManager sessionManager) + public ConnectService(IConnectManager connectManager, ISessionManager sessionManager, IAuthorizationContext authContext) { _connectManager = connectManager; _sessionManager = sessionManager; + _authContext = authContext; } public object Post(CreateConnectLink request) @@ -142,7 +144,7 @@ namespace MediaBrowser.Api throw new ResourceNotFoundException(); } - var auth = AuthorizationContext.GetAuthorizationInfo(Request); + var auth = _authContext.GetAuthorizationInfo(Request); if (string.IsNullOrWhiteSpace(auth.Client)) { diff --git a/MediaBrowser.Api/GamesService.cs b/MediaBrowser.Api/GamesService.cs index 0c033fe0d..a9394b52e 100644 --- a/MediaBrowser.Api/GamesService.cs +++ b/MediaBrowser.Api/GamesService.cs @@ -80,6 +80,8 @@ namespace MediaBrowser.Api /// private readonly IDtoService _dtoService; + private readonly IAuthorizationContext _authContext; + /// /// Initializes a new instance of the class. /// @@ -88,13 +90,14 @@ namespace MediaBrowser.Api /// The library manager. /// The item repo. /// The dto service. - public GamesService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService) + public GamesService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService, IAuthorizationContext authContext) { _userManager = userManager; _userDataRepository = userDataRepository; _libraryManager = libraryManager; _itemRepo = itemRepo; _dtoService = dtoService; + _authContext = authContext; } /// @@ -200,7 +203,7 @@ namespace MediaBrowser.Api (!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder : _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id); - var dtoOptions = GetDtoOptions(request); + var dtoOptions = GetDtoOptions(_authContext, request); var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user) { diff --git a/MediaBrowser.Api/Images/ImageByNameService.cs b/MediaBrowser.Api/Images/ImageByNameService.cs index e0a9246c1..8fbc56ce3 100644 --- a/MediaBrowser.Api/Images/ImageByNameService.cs +++ b/MediaBrowser.Api/Images/ImageByNameService.cs @@ -102,15 +102,17 @@ namespace MediaBrowser.Api.Images private readonly IServerApplicationPaths _appPaths; private readonly IFileSystem _fileSystem; + private readonly IHttpResultFactory _resultFactory; /// /// Initializes a new instance of the class. /// /// The app paths. - public ImageByNameService(IServerApplicationPaths appPaths, IFileSystem fileSystem) + public ImageByNameService(IServerApplicationPaths appPaths, IFileSystem fileSystem, IHttpResultFactory resultFactory) { _appPaths = appPaths; _fileSystem = fileSystem; + _resultFactory = resultFactory; } public object Get(GetMediaInfoImages request) @@ -187,7 +189,7 @@ namespace MediaBrowser.Api.Images var path = paths.FirstOrDefault(_fileSystem.FileExists) ?? paths.FirstOrDefault(); - return ToStaticFileResult(path); + return _resultFactory.GetStaticFileResult(Request, path); } /// @@ -207,7 +209,7 @@ namespace MediaBrowser.Api.Images if (!string.IsNullOrEmpty(path)) { - return ToStaticFileResult(path); + return _resultFactory.GetStaticFileResult(Request, path); } } @@ -224,7 +226,7 @@ namespace MediaBrowser.Api.Images if (!string.IsNullOrEmpty(path)) { - return ToStaticFileResult(path); + return _resultFactory.GetStaticFileResult(Request, path); } } @@ -247,7 +249,7 @@ namespace MediaBrowser.Api.Images if (!string.IsNullOrEmpty(path)) { - return ToStaticFileResult(path); + return _resultFactory.GetStaticFileResult(Request, path); } } @@ -263,7 +265,7 @@ namespace MediaBrowser.Api.Images if (!string.IsNullOrEmpty(path)) { - return ToStaticFileResult(path); + return _resultFactory.GetStaticFileResult(Request, path); } } diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index c41907a87..f3ad462c4 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -236,11 +236,12 @@ namespace MediaBrowser.Api.Images private readonly IItemRepository _itemRepo; private readonly IImageProcessor _imageProcessor; private readonly IFileSystem _fileSystem; + private readonly IAuthorizationContext _authContext; /// /// Initializes a new instance of the class. /// - public ImageService(IUserManager userManager, ILibraryManager libraryManager, IProviderManager providerManager, IItemRepository itemRepo, IImageProcessor imageProcessor, IFileSystem fileSystem) + public ImageService(IUserManager userManager, ILibraryManager libraryManager, IProviderManager providerManager, IItemRepository itemRepo, IImageProcessor imageProcessor, IFileSystem fileSystem, IAuthorizationContext authContext) { _userManager = userManager; _libraryManager = libraryManager; @@ -248,6 +249,7 @@ namespace MediaBrowser.Api.Images _itemRepo = itemRepo; _imageProcessor = imageProcessor; _fileSystem = fileSystem; + _authContext = authContext; } /// @@ -425,7 +427,7 @@ namespace MediaBrowser.Api.Images public void Post(PostUserImage request) { var userId = GetPathValue(1); - AssertCanUpdateUser(_userManager, userId); + AssertCanUpdateUser(_authContext, _userManager, userId); request.Type = (ImageType)Enum.Parse(typeof(ImageType), GetPathValue(3), true); @@ -460,7 +462,7 @@ namespace MediaBrowser.Api.Images public void Delete(DeleteUserImage request) { var userId = request.Id; - AssertCanUpdateUser(_userManager, userId); + AssertCanUpdateUser(_authContext, _userManager, userId); var item = _userManager.GetUserById(userId); diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs index 912387fda..36a58cc20 100644 --- a/MediaBrowser.Api/Library/LibraryService.cs +++ b/MediaBrowser.Api/Library/LibraryService.cs @@ -323,13 +323,9 @@ namespace MediaBrowser.Api.Library if (item is Game) { - return new GamesService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService) + return new GamesService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService, _authContext) { - AuthorizationContext = AuthorizationContext, - Logger = Logger, Request = Request, - SessionContext = SessionContext, - ResultFactory = ResultFactory }.Get(new GetSimilarGames { @@ -341,13 +337,9 @@ namespace MediaBrowser.Api.Library } if (item is MusicAlbum) { - return new AlbumsService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService) + return new AlbumsService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService, _authContext) { - AuthorizationContext = AuthorizationContext, - Logger = Logger, Request = Request, - SessionContext = SessionContext, - ResultFactory = ResultFactory }.Get(new GetSimilarAlbums { @@ -360,13 +352,9 @@ namespace MediaBrowser.Api.Library } if (item is MusicArtist) { - return new AlbumsService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService) + return new AlbumsService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService, _authContext) { - AuthorizationContext = AuthorizationContext, - Logger = Logger, Request = Request, - SessionContext = SessionContext, - ResultFactory = ResultFactory }.Get(new GetSimilarArtists { @@ -381,13 +369,9 @@ namespace MediaBrowser.Api.Library if (item is Movie || (program != null && program.IsMovie) || item is Trailer) { - return new MoviesService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService, _config) + return new MoviesService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService, _config, _authContext) { - AuthorizationContext = AuthorizationContext, - Logger = Logger, Request = Request, - SessionContext = SessionContext, - ResultFactory = ResultFactory }.Get(new GetSimilarMovies { @@ -400,13 +384,9 @@ namespace MediaBrowser.Api.Library if (item is Series || (program != null && program.IsSeries)) { - return new TvShowsService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService, _tvManager) + return new TvShowsService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService, _tvManager, _authContext) { - AuthorizationContext = AuthorizationContext, - Logger = Logger, Request = Request, - SessionContext = SessionContext, - ResultFactory = ResultFactory }.Get(new GetSimilarShows { @@ -431,7 +411,7 @@ namespace MediaBrowser.Api.Library items = items.Where(i => i.IsHidden == val).ToList(); } - var dtoOptions = GetDtoOptions(request); + var dtoOptions = GetDtoOptions(_authContext, request); var result = new ItemsResult { @@ -612,7 +592,7 @@ namespace MediaBrowser.Api.Library var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; - var dtoOptions = GetDtoOptions(request); + var dtoOptions = GetDtoOptions(_authContext, request); BaseItem parent = item.GetParent(); @@ -841,7 +821,7 @@ namespace MediaBrowser.Api.Library item = item.GetParent(); } - var dtoOptions = GetDtoOptions(request); + var dtoOptions = GetDtoOptions(_authContext, request); var dtos = item.ThemeSongIds.Select(_libraryManager.GetItemById) .Where(i => i != null) @@ -885,7 +865,7 @@ namespace MediaBrowser.Api.Library item = item.GetParent(); } - var dtoOptions = GetDtoOptions(request); + var dtoOptions = GetDtoOptions(_authContext, request); var dtos = item.ThemeVideoIds.Select(_libraryManager.GetItemById) .Where(i => i != null) diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index fffd7ad7e..c829ad2ab 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs @@ -695,8 +695,10 @@ namespace MediaBrowser.Api.LiveTv private readonly ILibraryManager _libraryManager; private readonly IDtoService _dtoService; private readonly IFileSystem _fileSystem; + private readonly IAuthorizationContext _authContext; + private readonly ISessionContext _sessionContext; - public LiveTvService(ILiveTvManager liveTvManager, IUserManager userManager, IServerConfigurationManager config, IHttpClient httpClient, ILibraryManager libraryManager, IDtoService dtoService, IFileSystem fileSystem) + public LiveTvService(ILiveTvManager liveTvManager, IUserManager userManager, IServerConfigurationManager config, IHttpClient httpClient, ILibraryManager libraryManager, IDtoService dtoService, IFileSystem fileSystem, IAuthorizationContext authContext, ISessionContext sessionContext) { _liveTvManager = liveTvManager; _userManager = userManager; @@ -705,6 +707,8 @@ namespace MediaBrowser.Api.LiveTv _libraryManager = libraryManager; _dtoService = dtoService; _fileSystem = fileSystem; + _authContext = authContext; + _sessionContext = sessionContext; } public object Get(GetLiveRecordingFile request) @@ -819,7 +823,7 @@ namespace MediaBrowser.Api.LiveTv private void AssertUserCanManageLiveTv() { - var user = SessionContext.GetUser(Request).Result; + var user = _sessionContext.GetUser(Request).Result; if (user == null) { @@ -907,7 +911,7 @@ namespace MediaBrowser.Api.LiveTv var user = string.IsNullOrEmpty(request.UserId) ? null : _userManager.GetUserById(request.UserId); - var options = GetDtoOptions(request); + var options = GetDtoOptions(_authContext, request); RemoveFields(options); options.AddCurrentProgram = request.AddCurrentProgram; @@ -937,7 +941,7 @@ namespace MediaBrowser.Api.LiveTv var item = _libraryManager.GetItemById(request.Id); - var dtoOptions = GetDtoOptions(request); + var dtoOptions = GetDtoOptions(_authContext, request); var result = _dtoService.GetBaseItemDto(item, dtoOptions, user); @@ -1002,7 +1006,7 @@ namespace MediaBrowser.Api.LiveTv } } - var result = await _liveTvManager.GetPrograms(query, GetDtoOptions(request), CancellationToken.None).ConfigureAwait(false); + var result = await _liveTvManager.GetPrograms(query, GetDtoOptions(_authContext, request), CancellationToken.None).ConfigureAwait(false); return ToOptimizedResult(result); } @@ -1023,7 +1027,7 @@ namespace MediaBrowser.Api.LiveTv EnableTotalRecordCount = request.EnableTotalRecordCount }; - var result = await _liveTvManager.GetRecommendedPrograms(query, GetDtoOptions(request), CancellationToken.None).ConfigureAwait(false); + var result = await _liveTvManager.GetRecommendedPrograms(query, GetDtoOptions(_authContext, request), CancellationToken.None).ConfigureAwait(false); return ToOptimizedResult(result); } @@ -1035,8 +1039,8 @@ namespace MediaBrowser.Api.LiveTv public async Task Get(GetRecordings request) { - var options = GetDtoOptions(request); - options.DeviceId = AuthorizationContext.GetAuthorizationInfo(Request).DeviceId; + var options = GetDtoOptions(_authContext, request); + options.DeviceId = _authContext.GetAuthorizationInfo(Request).DeviceId; var result = await _liveTvManager.GetRecordings(new RecordingQuery { @@ -1062,8 +1066,8 @@ namespace MediaBrowser.Api.LiveTv public async Task Get(GetRecordingSeries request) { - var options = GetDtoOptions(request); - options.DeviceId = AuthorizationContext.GetAuthorizationInfo(Request).DeviceId; + var options = GetDtoOptions(_authContext, request); + options.DeviceId = _authContext.GetAuthorizationInfo(Request).DeviceId; var result = await _liveTvManager.GetRecordingSeries(new RecordingQuery { @@ -1087,7 +1091,7 @@ namespace MediaBrowser.Api.LiveTv var user = string.IsNullOrEmpty(request.UserId) ? null : _userManager.GetUserById(request.UserId); var options = new DtoOptions(); - options.DeviceId = AuthorizationContext.GetAuthorizationInfo(Request).DeviceId; + options.DeviceId = _authContext.GetAuthorizationInfo(Request).DeviceId; var result = await _liveTvManager.GetRecording(request.Id, options, CancellationToken.None, user).ConfigureAwait(false); diff --git a/MediaBrowser.Api/Movies/CollectionService.cs b/MediaBrowser.Api/Movies/CollectionService.cs index da8018a35..917a3bc0b 100644 --- a/MediaBrowser.Api/Movies/CollectionService.cs +++ b/MediaBrowser.Api/Movies/CollectionService.cs @@ -51,16 +51,18 @@ namespace MediaBrowser.Api.Movies { private readonly ICollectionManager _collectionManager; private readonly IDtoService _dtoService; + private readonly IAuthorizationContext _authContext; - public CollectionService(ICollectionManager collectionManager, IDtoService dtoService) + public CollectionService(ICollectionManager collectionManager, IDtoService dtoService, IAuthorizationContext authContext) { _collectionManager = collectionManager; _dtoService = dtoService; + _authContext = authContext; } public async Task Post(CreateCollection request) { - var userId = AuthorizationContext.GetAuthorizationInfo(Request).UserId; + var userId = _authContext.GetAuthorizationInfo(Request).UserId; var parentId = string.IsNullOrWhiteSpace(request.ParentId) ? (Guid?)null : new Guid(request.ParentId); @@ -74,7 +76,7 @@ namespace MediaBrowser.Api.Movies }).ConfigureAwait(false); - var dtoOptions = GetDtoOptions(request); + var dtoOptions = GetDtoOptions(_authContext, request); var dto = _dtoService.GetBaseItemDto(item, dtoOptions); diff --git a/MediaBrowser.Api/Movies/MoviesService.cs b/MediaBrowser.Api/Movies/MoviesService.cs index 66a83d271..1b2fa4fff 100644 --- a/MediaBrowser.Api/Movies/MoviesService.cs +++ b/MediaBrowser.Api/Movies/MoviesService.cs @@ -90,6 +90,7 @@ namespace MediaBrowser.Api.Movies private readonly IItemRepository _itemRepo; private readonly IDtoService _dtoService; private readonly IServerConfigurationManager _config; + private readonly IAuthorizationContext _authContext; /// /// Initializes a new instance of the class. @@ -99,7 +100,7 @@ namespace MediaBrowser.Api.Movies /// The library manager. /// The item repo. /// The dto service. - public MoviesService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService, IServerConfigurationManager config) + public MoviesService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService, IServerConfigurationManager config, IAuthorizationContext authContext) { _userManager = userManager; _userDataRepository = userDataRepository; @@ -107,6 +108,7 @@ namespace MediaBrowser.Api.Movies _itemRepo = itemRepo; _dtoService = dtoService; _config = config; + _authContext = authContext; } /// @@ -132,7 +134,7 @@ namespace MediaBrowser.Api.Movies { var user = _userManager.GetUserById(request.UserId); - var dtoOptions = GetDtoOptions(request); + var dtoOptions = GetDtoOptions(_authContext, request); dtoOptions.Fields = request.GetItemFields().ToList(); @@ -156,7 +158,7 @@ namespace MediaBrowser.Api.Movies itemTypes.Add(typeof(LiveTvProgram).Name); } - var dtoOptions = GetDtoOptions(request); + var dtoOptions = GetDtoOptions(_authContext, request); var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user) { diff --git a/MediaBrowser.Api/Movies/TrailersService.cs b/MediaBrowser.Api/Movies/TrailersService.cs index 19f46b3d6..eb5365ab8 100644 --- a/MediaBrowser.Api/Movies/TrailersService.cs +++ b/MediaBrowser.Api/Movies/TrailersService.cs @@ -39,8 +39,9 @@ namespace MediaBrowser.Api.Movies private readonly ICollectionManager _collectionManager; private readonly ILocalizationManager _localizationManager; private readonly IJsonSerializer _json; + private readonly IAuthorizationContext _authContext; - public TrailersService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IDtoService dtoService, ICollectionManager collectionManager, ILocalizationManager localizationManager, IJsonSerializer json) + public TrailersService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IDtoService dtoService, ICollectionManager collectionManager, ILocalizationManager localizationManager, IJsonSerializer json, IAuthorizationContext authContext) { _userManager = userManager; _userDataRepository = userDataRepository; @@ -49,6 +50,7 @@ namespace MediaBrowser.Api.Movies _collectionManager = collectionManager; _localizationManager = localizationManager; _json = json; + _authContext = authContext; } public object Get(Getrailers request) @@ -58,13 +60,9 @@ namespace MediaBrowser.Api.Movies getItems.IncludeItemTypes = "Trailer"; - return new ItemsService(_userManager, _libraryManager, _localizationManager, _dtoService) + return new ItemsService(_userManager, _libraryManager, _localizationManager, _dtoService, _authContext) { - AuthorizationContext = AuthorizationContext, - Logger = Logger, Request = Request, - ResultFactory = ResultFactory, - SessionContext = SessionContext }.Get(getItems); } diff --git a/MediaBrowser.Api/Music/AlbumsService.cs b/MediaBrowser.Api/Music/AlbumsService.cs index f35f3be88..bc7ae2be2 100644 --- a/MediaBrowser.Api/Music/AlbumsService.cs +++ b/MediaBrowser.Api/Music/AlbumsService.cs @@ -40,19 +40,21 @@ namespace MediaBrowser.Api.Music private readonly ILibraryManager _libraryManager; private readonly IItemRepository _itemRepo; private readonly IDtoService _dtoService; + private readonly IAuthorizationContext _authContext; - public AlbumsService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService) + public AlbumsService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService, IAuthorizationContext authContext) { _userManager = userManager; _userDataRepository = userDataRepository; _libraryManager = libraryManager; _itemRepo = itemRepo; _dtoService = dtoService; + _authContext = authContext; } public async Task Get(GetSimilarArtists request) { - var dtoOptions = GetDtoOptions(request); + var dtoOptions = GetDtoOptions(_authContext, request); var result = await SimilarItemsHelper.GetSimilarItemsResult(dtoOptions, _userManager, _itemRepo, @@ -73,7 +75,7 @@ namespace MediaBrowser.Api.Music /// System.Object. public async Task Get(GetSimilarAlbums request) { - var dtoOptions = GetDtoOptions(request); + var dtoOptions = GetDtoOptions(_authContext, request); var result = await SimilarItemsHelper.GetSimilarItemsResult(dtoOptions, _userManager, _itemRepo, diff --git a/MediaBrowser.Api/Music/InstantMixService.cs b/MediaBrowser.Api/Music/InstantMixService.cs index 8b9331802..d735dd7cd 100644 --- a/MediaBrowser.Api/Music/InstantMixService.cs +++ b/MediaBrowser.Api/Music/InstantMixService.cs @@ -68,13 +68,15 @@ namespace MediaBrowser.Api.Music private readonly IDtoService _dtoService; private readonly ILibraryManager _libraryManager; private readonly IMusicManager _musicManager; + private readonly IAuthorizationContext _authContext; - public InstantMixService(IUserManager userManager, IDtoService dtoService, IMusicManager musicManager, ILibraryManager libraryManager) + public InstantMixService(IUserManager userManager, IDtoService dtoService, IMusicManager musicManager, ILibraryManager libraryManager, IAuthorizationContext authContext) { _userManager = userManager; _dtoService = dtoService; _musicManager = musicManager; _libraryManager = libraryManager; + _authContext = authContext; } public Task Get(GetInstantMixFromItem request) @@ -171,7 +173,7 @@ namespace MediaBrowser.Api.Music TotalRecordCount = list.Count }; - var dtoOptions = GetDtoOptions(request); + var dtoOptions = GetDtoOptions(_authContext, request); result.Items = (await _dtoService.GetBaseItemDtos(list.Take(request.Limit ?? list.Count), dtoOptions, user).ConfigureAwait(false)).ToArray(); diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 4fa663b19..bac612a5b 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -21,6 +21,7 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Net; using MediaBrowser.Controller; +using MediaBrowser.Controller.Net; using MediaBrowser.Model.Diagnostics; namespace MediaBrowser.Api.Playback @@ -71,13 +72,15 @@ namespace MediaBrowser.Api.Playback public static IServerApplicationHost AppHost; public static IHttpClient HttpClient; + protected IAuthorizationContext AuthorizationContext { get; private set; } /// /// Initializes a new instance of the class. /// - protected BaseStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer) + protected BaseStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext) { JsonSerializer = jsonSerializer; + AuthorizationContext = authorizationContext; ZipClient = zipClient; MediaSourceManager = mediaSourceManager; DeviceManager = deviceManager; diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index 55f61f7c7..0c1cdf53e 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -15,6 +15,7 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.IO; using MediaBrowser.Controller.IO; +using MediaBrowser.Controller.Net; using MediaBrowser.Model.IO; namespace MediaBrowser.Api.Playback.Hls @@ -24,11 +25,6 @@ namespace MediaBrowser.Api.Playback.Hls /// public abstract class BaseHlsService : BaseStreamingService { - protected BaseHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer) - : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer) - { - } - /// /// Gets the audio arguments. /// @@ -292,5 +288,9 @@ namespace MediaBrowser.Api.Playback.Hls return isLiveStream; } + + public BaseHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, authorizationContext) + { + } } } \ No newline at end of file diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index 353e83205..471bfe604 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -95,8 +95,8 @@ namespace MediaBrowser.Api.Playback.Hls public class DynamicHlsService : BaseHlsService { - public DynamicHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, INetworkManager networkManager) - : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer) + + public DynamicHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext, INetworkManager networkManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, authorizationContext) { NetworkManager = networkManager; } diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs index fcff2ae29..8a2047de4 100644 --- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs @@ -8,6 +8,7 @@ using MediaBrowser.Model.Serialization; using System; using MediaBrowser.Common.IO; using MediaBrowser.Controller.IO; +using MediaBrowser.Controller.Net; using MediaBrowser.Model.IO; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Services; @@ -24,10 +25,6 @@ namespace MediaBrowser.Api.Playback.Hls /// public class VideoHlsService : BaseHlsService { - public VideoHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer) - { - } - public object Get(GetLiveHlsStream request) { return ProcessRequest(request, true); @@ -129,5 +126,9 @@ namespace MediaBrowser.Api.Playback.Hls { return ".ts"; } + + public VideoHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, authorizationContext) + { + } } } diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 373c522da..8fb78b6e5 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -72,8 +72,9 @@ namespace MediaBrowser.Api.Playback private readonly IMediaEncoder _mediaEncoder; private readonly IUserManager _userManager; private readonly IJsonSerializer _json; + private readonly IAuthorizationContext _authContext; - public MediaInfoService(IMediaSourceManager mediaSourceManager, IDeviceManager deviceManager, ILibraryManager libraryManager, IServerConfigurationManager config, INetworkManager networkManager, IMediaEncoder mediaEncoder, IUserManager userManager, IJsonSerializer json) + public MediaInfoService(IMediaSourceManager mediaSourceManager, IDeviceManager deviceManager, ILibraryManager libraryManager, IServerConfigurationManager config, INetworkManager networkManager, IMediaEncoder mediaEncoder, IUserManager userManager, IJsonSerializer json, IAuthorizationContext authContext) { _mediaSourceManager = mediaSourceManager; _deviceManager = deviceManager; @@ -83,6 +84,7 @@ namespace MediaBrowser.Api.Playback _mediaEncoder = mediaEncoder; _userManager = userManager; _json = json; + _authContext = authContext; } public object Get(GetBitrateTestBytes request) @@ -105,7 +107,7 @@ namespace MediaBrowser.Api.Playback public async Task Post(OpenMediaSource request) { - var authInfo = AuthorizationContext.GetAuthorizationInfo(Request); + var authInfo = _authContext.GetAuthorizationInfo(Request); var result = await _mediaSourceManager.OpenLiveStream(request, true, CancellationToken.None).ConfigureAwait(false); @@ -146,7 +148,7 @@ namespace MediaBrowser.Api.Playback public async Task Post(GetPostedPlaybackInfo request) { - var authInfo = AuthorizationContext.GetAuthorizationInfo(Request); + var authInfo = _authContext.GetAuthorizationInfo(Request); var profile = request.DeviceProfile; diff --git a/MediaBrowser.Api/Playback/Progressive/AudioService.cs b/MediaBrowser.Api/Playback/Progressive/AudioService.cs index e4544370e..082d6b2f4 100644 --- a/MediaBrowser.Api/Playback/Progressive/AudioService.cs +++ b/MediaBrowser.Api/Playback/Progressive/AudioService.cs @@ -11,6 +11,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using MediaBrowser.Common.IO; using MediaBrowser.Controller.IO; +using MediaBrowser.Controller.Net; using MediaBrowser.Model.IO; using MediaBrowser.Model.Services; @@ -34,10 +35,6 @@ namespace MediaBrowser.Api.Playback.Progressive /// public class AudioService : BaseProgressiveStreamingService { - public AudioService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, imageProcessor, httpClient) - { - } - /// /// Gets the specified request. /// @@ -97,5 +94,9 @@ namespace MediaBrowser.Api.Playback.Progressive string.Join(" ", audioTranscodeParams.ToArray()), outputPath).Trim(); } + + public AudioService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, authorizationContext, imageProcessor) + { + } } } diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs index 8302c1d42..23a84e480 100644 --- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs +++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs @@ -29,7 +29,7 @@ namespace MediaBrowser.Api.Playback.Progressive { protected readonly IImageProcessor ImageProcessor; - protected BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer) + public BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, authorizationContext) { ImageProcessor = imageProcessor; } diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs index f32a4e895..285d09cdc 100644 --- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs +++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs @@ -12,6 +12,7 @@ using System.IO; using System.Threading.Tasks; using MediaBrowser.Common.IO; using MediaBrowser.Controller.IO; +using MediaBrowser.Controller.Net; using MediaBrowser.Model.IO; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Services; @@ -68,8 +69,7 @@ namespace MediaBrowser.Api.Playback.Progressive /// public class VideoService : BaseProgressiveStreamingService { - public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IImageProcessor imageProcessor, IHttpClient httpClient) - : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, imageProcessor, httpClient) + public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, authorizationContext, imageProcessor) { } diff --git a/MediaBrowser.Api/PlaylistService.cs b/MediaBrowser.Api/PlaylistService.cs index 685465a6f..bb2bc449b 100644 --- a/MediaBrowser.Api/PlaylistService.cs +++ b/MediaBrowser.Api/PlaylistService.cs @@ -125,13 +125,15 @@ namespace MediaBrowser.Api private readonly IDtoService _dtoService; private readonly IUserManager _userManager; private readonly ILibraryManager _libraryManager; + private readonly IAuthorizationContext _authContext; - public PlaylistService(IDtoService dtoService, IPlaylistManager playlistManager, IUserManager userManager, ILibraryManager libraryManager) + public PlaylistService(IDtoService dtoService, IPlaylistManager playlistManager, IUserManager userManager, ILibraryManager libraryManager, IAuthorizationContext authContext) { _dtoService = dtoService; _playlistManager = playlistManager; _userManager = userManager; _libraryManager = libraryManager; + _authContext = authContext; } public void Post(MoveItem request) @@ -188,7 +190,7 @@ namespace MediaBrowser.Api items = items.Take(request.Limit.Value).ToArray(); } - var dtoOptions = GetDtoOptions(request); + var dtoOptions = GetDtoOptions(_authContext, request); var dtos = (await _dtoService.GetBaseItemDtos(items.Select(i => i.Item2), dtoOptions, user).ConfigureAwait(false)) .ToArray(); diff --git a/MediaBrowser.Api/Session/SessionsService.cs b/MediaBrowser.Api/Session/SessionsService.cs index e2f16df2d..70e0d3c45 100644 --- a/MediaBrowser.Api/Session/SessionsService.cs +++ b/MediaBrowser.Api/Session/SessionsService.cs @@ -306,6 +306,7 @@ namespace MediaBrowser.Api.Session private readonly IAuthorizationContext _authContext; private readonly IAuthenticationRepository _authRepo; private readonly IDeviceManager _deviceManager; + private readonly ISessionContext _sessionContext; /// /// Initializes a new instance of the class. @@ -314,13 +315,14 @@ namespace MediaBrowser.Api.Session /// The user manager. /// The authentication context. /// The authentication repo. - public SessionsService(ISessionManager sessionManager, IUserManager userManager, IAuthorizationContext authContext, IAuthenticationRepository authRepo, IDeviceManager deviceManager) + public SessionsService(ISessionManager sessionManager, IUserManager userManager, IAuthorizationContext authContext, IAuthenticationRepository authRepo, IDeviceManager deviceManager, ISessionContext sessionContext) { _sessionManager = sessionManager; _userManager = userManager; _authContext = authContext; _authRepo = authRepo; _deviceManager = deviceManager; + _sessionContext = sessionContext; } public void Delete(RevokeKey request) @@ -419,7 +421,7 @@ namespace MediaBrowser.Api.Session SeekPositionTicks = request.SeekPositionTicks }; - var task = _sessionManager.SendPlaystateCommand(GetSession().Result.Id, request.Id, command, CancellationToken.None); + var task = _sessionManager.SendPlaystateCommand(GetSession(_sessionContext).Result.Id, request.Id, command, CancellationToken.None); Task.WaitAll(task); } @@ -437,7 +439,7 @@ namespace MediaBrowser.Api.Session ItemType = request.ItemType }; - var task = _sessionManager.SendBrowseCommand(GetSession().Result.Id, request.Id, command, CancellationToken.None); + var task = _sessionManager.SendBrowseCommand(GetSession(_sessionContext).Result.Id, request.Id, command, CancellationToken.None); Task.WaitAll(task); } @@ -456,7 +458,7 @@ namespace MediaBrowser.Api.Session name = commandType.ToString(); } - var currentSession = GetSession().Result; + var currentSession = GetSession(_sessionContext).Result; var command = new GeneralCommand { @@ -482,7 +484,7 @@ namespace MediaBrowser.Api.Session Text = request.Text }; - var task = _sessionManager.SendMessageCommand(GetSession().Result.Id, request.Id, command, CancellationToken.None); + var task = _sessionManager.SendMessageCommand(GetSession(_sessionContext).Result.Id, request.Id, command, CancellationToken.None); Task.WaitAll(task); } @@ -501,14 +503,14 @@ namespace MediaBrowser.Api.Session StartPositionTicks = request.StartPositionTicks }; - var task = _sessionManager.SendPlayCommand(GetSession().Result.Id, request.Id, command, CancellationToken.None); + var task = _sessionManager.SendPlayCommand(GetSession(_sessionContext).Result.Id, request.Id, command, CancellationToken.None); Task.WaitAll(task); } public void Post(SendGeneralCommand request) { - var currentSession = GetSession().Result; + var currentSession = GetSession(_sessionContext).Result; var command = new GeneralCommand { @@ -523,7 +525,7 @@ namespace MediaBrowser.Api.Session public void Post(SendFullGeneralCommand request) { - var currentSession = GetSession().Result; + var currentSession = GetSession(_sessionContext).Result; request.ControllingUserId = currentSession.UserId.HasValue ? currentSession.UserId.Value.ToString("N") : null; @@ -546,7 +548,7 @@ namespace MediaBrowser.Api.Session { if (string.IsNullOrWhiteSpace(request.Id)) { - request.Id = GetSession().Result.Id; + request.Id = GetSession(_sessionContext).Result.Id; } _sessionManager.ReportCapabilities(request.Id, new ClientCapabilities { @@ -570,7 +572,7 @@ namespace MediaBrowser.Api.Session { if (string.IsNullOrWhiteSpace(request.Id)) { - request.Id = GetSession().Result.Id; + request.Id = GetSession(_sessionContext).Result.Id; } _sessionManager.ReportCapabilities(request.Id, request); } diff --git a/MediaBrowser.Api/Social/SharingService.cs b/MediaBrowser.Api/Social/SharingService.cs index 94b0c6390..86fe0a136 100644 --- a/MediaBrowser.Api/Social/SharingService.cs +++ b/MediaBrowser.Api/Social/SharingService.cs @@ -66,13 +66,15 @@ namespace MediaBrowser.Api.Social private readonly ILibraryManager _libraryManager; private readonly IDlnaManager _dlnaManager; private readonly IDtoService _dtoService; + private readonly IHttpResultFactory _resultFactory; - public SharingService(ISharingManager sharingManager, IDlnaManager dlnaManager, ILibraryManager libraryManager, IDtoService dtoService) + public SharingService(ISharingManager sharingManager, IDlnaManager dlnaManager, ILibraryManager libraryManager, IDtoService dtoService, IHttpResultFactory resultFactory) { _sharingManager = sharingManager; _dlnaManager = dlnaManager; _libraryManager = libraryManager; _dtoService = dtoService; + _resultFactory = resultFactory; } public object Get(GetSocialShareInfo request) @@ -144,14 +146,14 @@ namespace MediaBrowser.Api.Social { if (image.IsLocalFile) { - return ToStaticFileResult(image.Path); + return _resultFactory.GetStaticFileResult(Request, image.Path); } try { // Don't fail the request over this var updatedImage = await _libraryManager.ConvertImageToLocal(item, image, 0).ConfigureAwait(false); - return ToStaticFileResult(updatedImage.Path); + return _resultFactory.GetStaticFileResult(Request, updatedImage.Path); } catch { diff --git a/MediaBrowser.Api/Subtitles/SubtitleService.cs b/MediaBrowser.Api/Subtitles/SubtitleService.cs index f7ff3d162..47d442e79 100644 --- a/MediaBrowser.Api/Subtitles/SubtitleService.cs +++ b/MediaBrowser.Api/Subtitles/SubtitleService.cs @@ -135,8 +135,9 @@ namespace MediaBrowser.Api.Subtitles private readonly IMediaSourceManager _mediaSourceManager; private readonly IProviderManager _providerManager; private readonly IFileSystem _fileSystem; + private readonly IAuthorizationContext _authContext; - public SubtitleService(ILibraryManager libraryManager, ISubtitleManager subtitleManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager, IProviderManager providerManager, IFileSystem fileSystem) + public SubtitleService(ILibraryManager libraryManager, ISubtitleManager subtitleManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager, IProviderManager providerManager, IFileSystem fileSystem, IAuthorizationContext authContext) { _libraryManager = libraryManager; _subtitleManager = subtitleManager; @@ -144,6 +145,7 @@ namespace MediaBrowser.Api.Subtitles _mediaSourceManager = mediaSourceManager; _providerManager = providerManager; _fileSystem = fileSystem; + _authContext = authContext; } public async Task Get(GetSubtitlePlaylist request) @@ -170,7 +172,7 @@ namespace MediaBrowser.Api.Subtitles long positionTicks = 0; var segmentLengthTicks = TimeSpan.FromSeconds(request.SegmentLength).Ticks; - var accessToken = AuthorizationContext.GetAuthorizationInfo(Request).Token; + var accessToken = _authContext.GetAuthorizationInfo(Request).Token; while (positionTicks < runtime) { diff --git a/MediaBrowser.Api/Sync/SyncService.cs b/MediaBrowser.Api/Sync/SyncService.cs index 3665bea0c..e50d2b77f 100644 --- a/MediaBrowser.Api/Sync/SyncService.cs +++ b/MediaBrowser.Api/Sync/SyncService.cs @@ -161,13 +161,15 @@ namespace MediaBrowser.Api.Sync private readonly IDtoService _dtoService; private readonly ILibraryManager _libraryManager; private readonly IUserManager _userManager; + private readonly IAuthorizationContext _authContext; - public SyncService(ISyncManager syncManager, IDtoService dtoService, ILibraryManager libraryManager, IUserManager userManager) + public SyncService(ISyncManager syncManager, IDtoService dtoService, ILibraryManager libraryManager, IUserManager userManager, IAuthorizationContext authContext) { _syncManager = syncManager; _dtoService = dtoService; _libraryManager = libraryManager; _userManager = userManager; + _authContext = authContext; } public object Get(GetSyncTargets request) @@ -263,7 +265,7 @@ namespace MediaBrowser.Api.Sync result.Targets = _syncManager.GetSyncTargets(request.UserId) .ToList(); - var auth = AuthorizationContext.GetAuthorizationInfo(Request); + var auth = _authContext.GetAuthorizationInfo(Request); var authenticatedUser = _userManager.GetUserById(auth.UserId); if (!string.IsNullOrWhiteSpace(request.TargetId)) diff --git a/MediaBrowser.Api/TvShowsService.cs b/MediaBrowser.Api/TvShowsService.cs index 9ba489583..71c5e732a 100644 --- a/MediaBrowser.Api/TvShowsService.cs +++ b/MediaBrowser.Api/TvShowsService.cs @@ -265,6 +265,7 @@ namespace MediaBrowser.Api private readonly IItemRepository _itemRepo; private readonly IDtoService _dtoService; private readonly ITVSeriesManager _tvSeriesManager; + private readonly IAuthorizationContext _authContext; /// /// Initializes a new instance of the class. @@ -272,7 +273,7 @@ namespace MediaBrowser.Api /// The user manager. /// The user data repository. /// The library manager. - public TvShowsService(IUserManager userManager, IUserDataManager userDataManager, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService, ITVSeriesManager tvSeriesManager) + public TvShowsService(IUserManager userManager, IUserDataManager userDataManager, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService, ITVSeriesManager tvSeriesManager, IAuthorizationContext authContext) { _userManager = userManager; _userDataManager = userDataManager; @@ -280,6 +281,7 @@ namespace MediaBrowser.Api _itemRepo = itemRepo; _dtoService = dtoService; _tvSeriesManager = tvSeriesManager; + _authContext = authContext; } /// @@ -302,7 +304,7 @@ namespace MediaBrowser.Api (!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder : _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id); - var dtoOptions = GetDtoOptions(request); + var dtoOptions = GetDtoOptions(_authContext, request); var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user) { @@ -334,7 +336,7 @@ namespace MediaBrowser.Api var parentIdGuid = string.IsNullOrWhiteSpace(request.ParentId) ? (Guid?)null : new Guid(request.ParentId); - var options = GetDtoOptions(request); + var options = GetDtoOptions(_authContext, request); var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user) { @@ -379,7 +381,7 @@ namespace MediaBrowser.Api var user = _userManager.GetUserById(request.UserId); - var options = GetDtoOptions(request); + var options = GetDtoOptions(_authContext, request); var returnItems = (await _dtoService.GetBaseItemDtos(result.Items, options, user).ConfigureAwait(false)).ToArray(); @@ -434,7 +436,7 @@ namespace MediaBrowser.Api }).ConfigureAwait(false)).Items.OfType(); - var dtoOptions = GetDtoOptions(request); + var dtoOptions = GetDtoOptions(_authContext, request); var returnItems = (await _dtoService.GetBaseItemDtos(seasons, dtoOptions, user).ConfigureAwait(false)) .ToArray(); @@ -526,7 +528,7 @@ namespace MediaBrowser.Api var pagedItems = ApplyPaging(returnList, request.StartIndex, request.Limit); - var dtoOptions = GetDtoOptions(request); + var dtoOptions = GetDtoOptions(_authContext, request); var dtos = (await _dtoService.GetBaseItemDtos(pagedItems, dtoOptions, user).ConfigureAwait(false)) .ToArray(); diff --git a/MediaBrowser.Api/UserLibrary/ArtistsService.cs b/MediaBrowser.Api/UserLibrary/ArtistsService.cs index 36a62b237..5bbd96c7c 100644 --- a/MediaBrowser.Api/UserLibrary/ArtistsService.cs +++ b/MediaBrowser.Api/UserLibrary/ArtistsService.cs @@ -49,18 +49,6 @@ namespace MediaBrowser.Api.UserLibrary [Authenticated] public class ArtistsService : BaseItemsByNameService { - /// - /// Initializes a new instance of the class. - /// - /// The user manager. - /// The library manager. - /// The user data repository. - /// The item repo. - public ArtistsService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepo, IDtoService dtoService) - : base(userManager, libraryManager, userDataRepository, itemRepo, dtoService) - { - } - /// /// Gets the specified request. /// @@ -81,8 +69,8 @@ namespace MediaBrowser.Api.UserLibrary private BaseItemDto GetItem(GetArtist request) { var item = GetArtist(request.Name, LibraryManager); - - var dtoOptions = GetDtoOptions(request); + + var dtoOptions = GetDtoOptions(AuthorizationContext, request); if (!string.IsNullOrWhiteSpace(request.UserId)) { @@ -148,5 +136,9 @@ namespace MediaBrowser.Api.UserLibrary { throw new NotImplementedException(); } + + public ArtistsService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepository, IDtoService dtoService, IAuthorizationContext authorizationContext) : base(userManager, libraryManager, userDataRepository, itemRepository, dtoService, authorizationContext) + { + } } } diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs index 971d1a45c..ff285b605 100644 --- a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs +++ b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs @@ -7,6 +7,7 @@ using MediaBrowser.Model.Querying; using System; using System.Collections.Generic; using System.Linq; +using MediaBrowser.Controller.Net; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Services; @@ -30,6 +31,7 @@ namespace MediaBrowser.Api.UserLibrary protected readonly IUserDataManager UserDataRepository; protected readonly IItemRepository ItemRepository; protected IDtoService DtoService { get; private set; } + protected IAuthorizationContext AuthorizationContext { get; private set; } /// /// Initializes a new instance of the class. @@ -39,13 +41,14 @@ namespace MediaBrowser.Api.UserLibrary /// The user data repository. /// The item repository. /// The dto service. - protected BaseItemsByNameService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepository, IDtoService dtoService) + protected BaseItemsByNameService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepository, IDtoService dtoService, IAuthorizationContext authorizationContext) { UserManager = userManager; LibraryManager = libraryManager; UserDataRepository = userDataRepository; ItemRepository = itemRepository; DtoService = dtoService; + AuthorizationContext = authorizationContext; } protected BaseItem GetParentItem(GetItemsByName request) @@ -86,7 +89,7 @@ namespace MediaBrowser.Api.UserLibrary protected ItemsResult GetResultSlim(GetItemsByName request) { - var dtoOptions = GetDtoOptions(request); + var dtoOptions = GetDtoOptions(AuthorizationContext, request); User user = null; BaseItem parentItem; @@ -223,7 +226,7 @@ namespace MediaBrowser.Api.UserLibrary /// Task{ItemsResult}. protected ItemsResult GetResult(GetItemsByName request) { - var dtoOptions = GetDtoOptions(request); + var dtoOptions = GetDtoOptions(AuthorizationContext, request); User user = null; BaseItem parentItem; diff --git a/MediaBrowser.Api/UserLibrary/GameGenresService.cs b/MediaBrowser.Api/UserLibrary/GameGenresService.cs index 809df2531..2eef1ab2f 100644 --- a/MediaBrowser.Api/UserLibrary/GameGenresService.cs +++ b/MediaBrowser.Api/UserLibrary/GameGenresService.cs @@ -37,11 +37,6 @@ namespace MediaBrowser.Api.UserLibrary [Authenticated] public class GameGenresService : BaseItemsByNameService { - public GameGenresService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepo, IDtoService dtoService) - : base(userManager, libraryManager, userDataRepository, itemRepo, dtoService) - { - } - /// /// Gets the specified request. /// @@ -63,8 +58,8 @@ namespace MediaBrowser.Api.UserLibrary { var item = GetGameGenre(request.Name, LibraryManager); - var dtoOptions = GetDtoOptions(request); - + var dtoOptions = GetDtoOptions(AuthorizationContext, request); + if (!string.IsNullOrWhiteSpace(request.UserId)) { var user = UserManager.GetUserById(request.UserId); @@ -102,5 +97,9 @@ namespace MediaBrowser.Api.UserLibrary { throw new NotImplementedException(); } + + public GameGenresService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepository, IDtoService dtoService, IAuthorizationContext authorizationContext) : base(userManager, libraryManager, userDataRepository, itemRepository, dtoService, authorizationContext) + { + } } } diff --git a/MediaBrowser.Api/UserLibrary/GenresService.cs b/MediaBrowser.Api/UserLibrary/GenresService.cs index 25447f832..664efac14 100644 --- a/MediaBrowser.Api/UserLibrary/GenresService.cs +++ b/MediaBrowser.Api/UserLibrary/GenresService.cs @@ -47,11 +47,6 @@ namespace MediaBrowser.Api.UserLibrary [Authenticated] public class GenresService : BaseItemsByNameService { - public GenresService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepo, IDtoService dtoService) - : base(userManager, libraryManager, userDataRepository, itemRepo, dtoService) - { - } - /// /// Gets the specified request. /// @@ -72,8 +67,8 @@ namespace MediaBrowser.Api.UserLibrary private BaseItemDto GetItem(GetGenre request) { var item = GetGenre(request.Name, LibraryManager); - - var dtoOptions = GetDtoOptions(request); + + var dtoOptions = GetDtoOptions(AuthorizationContext ,request); if (!string.IsNullOrWhiteSpace(request.UserId)) { @@ -124,5 +119,9 @@ namespace MediaBrowser.Api.UserLibrary { throw new NotImplementedException(); } + + public GenresService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepository, IDtoService dtoService, IAuthorizationContext authorizationContext) : base(userManager, libraryManager, userDataRepository, itemRepository, dtoService, authorizationContext) + { + } } } diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index d75e42850..a07128f74 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -41,6 +41,7 @@ namespace MediaBrowser.Api.UserLibrary private readonly ILocalizationManager _localization; private readonly IDtoService _dtoService; + private readonly IAuthorizationContext _authContext; /// /// Initializes a new instance of the class. @@ -49,7 +50,7 @@ namespace MediaBrowser.Api.UserLibrary /// The library manager. /// The localization. /// The dto service. - public ItemsService(IUserManager userManager, ILibraryManager libraryManager, ILocalizationManager localization, IDtoService dtoService) + public ItemsService(IUserManager userManager, ILibraryManager libraryManager, ILocalizationManager localization, IDtoService dtoService, IAuthorizationContext authContext) { if (userManager == null) { @@ -72,6 +73,7 @@ namespace MediaBrowser.Api.UserLibrary _libraryManager = libraryManager; _localization = localization; _dtoService = dtoService; + _authContext = authContext; } /// @@ -100,7 +102,7 @@ namespace MediaBrowser.Api.UserLibrary { var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; - var dtoOptions = GetDtoOptions(request); + var dtoOptions = GetDtoOptions(_authContext, request); var result = await GetQueryResult(request, dtoOptions, user).ConfigureAwait(false); diff --git a/MediaBrowser.Api/UserLibrary/MusicGenresService.cs b/MediaBrowser.Api/UserLibrary/MusicGenresService.cs index 0d1f9e325..305c136df 100644 --- a/MediaBrowser.Api/UserLibrary/MusicGenresService.cs +++ b/MediaBrowser.Api/UserLibrary/MusicGenresService.cs @@ -38,11 +38,6 @@ namespace MediaBrowser.Api.UserLibrary [Authenticated] public class MusicGenresService : BaseItemsByNameService { - public MusicGenresService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepo, IDtoService dtoService) - : base(userManager, libraryManager, userDataRepository, itemRepo, dtoService) - { - } - /// /// Gets the specified request. /// @@ -63,8 +58,8 @@ namespace MediaBrowser.Api.UserLibrary private BaseItemDto GetItem(GetMusicGenre request) { var item = GetMusicGenre(request.Name, LibraryManager); - - var dtoOptions = GetDtoOptions(request); + + var dtoOptions = GetDtoOptions(AuthorizationContext, request); if (!string.IsNullOrWhiteSpace(request.UserId)) { @@ -103,5 +98,9 @@ namespace MediaBrowser.Api.UserLibrary { throw new NotImplementedException(); } + + public MusicGenresService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepository, IDtoService dtoService, IAuthorizationContext authorizationContext) : base(userManager, libraryManager, userDataRepository, itemRepository, dtoService, authorizationContext) + { + } } } diff --git a/MediaBrowser.Api/UserLibrary/PersonsService.cs b/MediaBrowser.Api/UserLibrary/PersonsService.cs index 1979bcd67..dbce22578 100644 --- a/MediaBrowser.Api/UserLibrary/PersonsService.cs +++ b/MediaBrowser.Api/UserLibrary/PersonsService.cs @@ -45,18 +45,6 @@ namespace MediaBrowser.Api.UserLibrary [Authenticated] public class PersonsService : BaseItemsByNameService { - /// - /// Initializes a new instance of the class. - /// - /// The user manager. - /// The library manager. - /// The user data repository. - /// The item repo. - public PersonsService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepo, IDtoService dtoService) - : base(userManager, libraryManager, userDataRepository, itemRepo, dtoService) - { - } - /// /// Gets the specified request. /// @@ -77,8 +65,8 @@ namespace MediaBrowser.Api.UserLibrary private BaseItemDto GetItem(GetPerson request) { var item = GetPerson(request.Name, LibraryManager); - - var dtoOptions = GetDtoOptions(request); + + var dtoOptions = GetDtoOptions(AuthorizationContext, request); if (!string.IsNullOrWhiteSpace(request.UserId)) { @@ -155,5 +143,9 @@ namespace MediaBrowser.Api.UserLibrary return allPeople.Where(i => allIds.Contains(i.ItemId)).OrderBy(p => p.SortOrder ?? int.MaxValue).ThenBy(p => p.Type); } + + public PersonsService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepository, IDtoService dtoService, IAuthorizationContext authorizationContext) : base(userManager, libraryManager, userDataRepository, itemRepository, dtoService, authorizationContext) + { + } } } diff --git a/MediaBrowser.Api/UserLibrary/PlaystateService.cs b/MediaBrowser.Api/UserLibrary/PlaystateService.cs index aafc0b125..5fe6c1771 100644 --- a/MediaBrowser.Api/UserLibrary/PlaystateService.cs +++ b/MediaBrowser.Api/UserLibrary/PlaystateService.cs @@ -234,13 +234,17 @@ namespace MediaBrowser.Api.UserLibrary private readonly IUserDataManager _userDataRepository; private readonly ILibraryManager _libraryManager; private readonly ISessionManager _sessionManager; + private readonly ISessionContext _sessionContext; + private readonly IAuthorizationContext _authContext; - public PlaystateService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, ISessionManager sessionManager) + public PlaystateService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, ISessionManager sessionManager, ISessionContext sessionContext, IAuthorizationContext authContext) { _userManager = userManager; _userDataRepository = userDataRepository; _libraryManager = libraryManager; _sessionManager = sessionManager; + _sessionContext = sessionContext; + _authContext = authContext; } /// @@ -265,7 +269,7 @@ namespace MediaBrowser.Api.UserLibrary datePlayed = DateTime.ParseExact(request.DatePlayed, "yyyyMMddHHmmss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal); } - var session = await GetSession().ConfigureAwait(false); + var session = await GetSession(_sessionContext).ConfigureAwait(false); var dto = await UpdatePlayedStatus(user, request.Id, true, datePlayed).ConfigureAwait(false); @@ -303,7 +307,7 @@ namespace MediaBrowser.Api.UserLibrary public void Post(ReportPlaybackStart request) { - request.SessionId = GetSession().Result.Id; + request.SessionId = GetSession(_sessionContext).Result.Id; var task = _sessionManager.OnPlaybackStart(request); @@ -335,7 +339,7 @@ namespace MediaBrowser.Api.UserLibrary public void Post(ReportPlaybackProgress request) { - request.SessionId = GetSession().Result.Id; + request.SessionId = GetSession(_sessionContext).Result.Id; var task = _sessionManager.OnPlaybackProgress(request); @@ -369,10 +373,10 @@ namespace MediaBrowser.Api.UserLibrary if (!string.IsNullOrWhiteSpace(request.PlaySessionId)) { - ApiEntryPoint.Instance.KillTranscodingJobs(AuthorizationContext.GetAuthorizationInfo(Request).DeviceId, request.PlaySessionId, s => true); + ApiEntryPoint.Instance.KillTranscodingJobs(_authContext.GetAuthorizationInfo(Request).DeviceId, request.PlaySessionId, s => true); } - request.SessionId = GetSession().Result.Id; + request.SessionId = GetSession(_sessionContext).Result.Id; var task = _sessionManager.OnPlaybackStopped(request); @@ -394,7 +398,7 @@ namespace MediaBrowser.Api.UserLibrary { var user = _userManager.GetUserById(request.UserId); - var session = await GetSession().ConfigureAwait(false); + var session = await GetSession(_sessionContext).ConfigureAwait(false); var dto = await UpdatePlayedStatus(user, request.Id, false, null).ConfigureAwait(false); diff --git a/MediaBrowser.Api/UserLibrary/StudiosService.cs b/MediaBrowser.Api/UserLibrary/StudiosService.cs index d63fd8b16..f4debcf48 100644 --- a/MediaBrowser.Api/UserLibrary/StudiosService.cs +++ b/MediaBrowser.Api/UserLibrary/StudiosService.cs @@ -47,11 +47,6 @@ namespace MediaBrowser.Api.UserLibrary [Authenticated] public class StudiosService : BaseItemsByNameService { - public StudiosService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepo, IDtoService dtoService) - : base(userManager, libraryManager, userDataRepository, itemRepo, dtoService) - { - } - /// /// Gets the specified request. /// @@ -73,8 +68,8 @@ namespace MediaBrowser.Api.UserLibrary { var item = GetStudio(request.Name, LibraryManager); - var dtoOptions = GetDtoOptions(request); - + var dtoOptions = GetDtoOptions(AuthorizationContext, request); + if (!string.IsNullOrWhiteSpace(request.UserId)) { var user = UserManager.GetUserById(request.UserId); @@ -117,5 +112,9 @@ namespace MediaBrowser.Api.UserLibrary .DistinctNames() .Select(name => LibraryManager.GetStudio(name)); } + + public StudiosService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepository, IDtoService dtoService, IAuthorizationContext authorizationContext) : base(userManager, libraryManager, userDataRepository, itemRepository, dtoService, authorizationContext) + { + } } } diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs index fbc2c0ccd..4121cc295 100644 --- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs +++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs @@ -270,8 +270,9 @@ namespace MediaBrowser.Api.UserLibrary private readonly IDtoService _dtoService; private readonly IUserViewManager _userViewManager; private readonly IFileSystem _fileSystem; + private readonly IAuthorizationContext _authContext; - public UserLibraryService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IDtoService dtoService, IUserViewManager userViewManager, IFileSystem fileSystem) + public UserLibraryService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IDtoService dtoService, IUserViewManager userViewManager, IFileSystem fileSystem, IAuthorizationContext authContext) { _userManager = userManager; _libraryManager = libraryManager; @@ -279,6 +280,7 @@ namespace MediaBrowser.Api.UserLibrary _dtoService = dtoService; _userViewManager = userViewManager; _fileSystem = fileSystem; + _authContext = authContext; } /// @@ -315,7 +317,7 @@ namespace MediaBrowser.Api.UserLibrary UserId = request.UserId }); - var options = GetDtoOptions(request); + var dtoOptions = GetDtoOptions(_authContext, request); var dtos = list.Select(i => { @@ -328,7 +330,7 @@ namespace MediaBrowser.Api.UserLibrary childCount = i.Item2.Count; } - var dto = _dtoService.GetBaseItemDto(item, options, user); + var dto = _dtoService.GetBaseItemDto(item, dtoOptions, user); dto.ChildCount = childCount; @@ -351,7 +353,7 @@ namespace MediaBrowser.Api.UserLibrary // Get them from the child tree if (series != null) { - var dtoOptions = GetDtoOptions(request); + var dtoOptions = GetDtoOptions(_authContext, request); // Avoid implicitly captured closure var currentUser = user; @@ -382,7 +384,7 @@ namespace MediaBrowser.Api.UserLibrary // Get them from the db if (movie != null) { - var dtoOptions = GetDtoOptions(request); + var dtoOptions = GetDtoOptions(_authContext, request); var dtos = movie.SpecialFeatureIds .Select(_libraryManager.GetItemById) @@ -421,7 +423,7 @@ namespace MediaBrowser.Api.UserLibrary trailerIds = hasTrailers.GetTrailerIds(); } - var dtoOptions = GetDtoOptions(request); + var dtoOptions = GetDtoOptions(_authContext, request); var dtos = trailerIds .Select(_libraryManager.GetItemById) @@ -443,7 +445,7 @@ namespace MediaBrowser.Api.UserLibrary await RefreshItemOnDemandIfNeeded(item).ConfigureAwait(false); - var dtoOptions = GetDtoOptions(request); + var dtoOptions = GetDtoOptions(_authContext, request); var result = _dtoService.GetBaseItemDto(item, dtoOptions, user); @@ -482,7 +484,7 @@ namespace MediaBrowser.Api.UserLibrary var item = user.RootFolder; - var dtoOptions = GetDtoOptions(request); + var dtoOptions = GetDtoOptions(_authContext, request); var result = _dtoService.GetBaseItemDto(item, dtoOptions, user); @@ -502,7 +504,7 @@ namespace MediaBrowser.Api.UserLibrary var items = await _libraryManager.GetIntros(item, user).ConfigureAwait(false); - var dtoOptions = GetDtoOptions(request); + var dtoOptions = GetDtoOptions(_authContext, request); var dtos = items.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)) .ToArray(); diff --git a/MediaBrowser.Api/UserLibrary/UserViewsService.cs b/MediaBrowser.Api/UserLibrary/UserViewsService.cs index 51783065c..6210b8b05 100644 --- a/MediaBrowser.Api/UserLibrary/UserViewsService.cs +++ b/MediaBrowser.Api/UserLibrary/UserViewsService.cs @@ -10,6 +10,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Controller.Net; using MediaBrowser.Model.Services; namespace MediaBrowser.Api.UserLibrary @@ -57,12 +58,14 @@ namespace MediaBrowser.Api.UserLibrary private readonly IUserManager _userManager; private readonly IUserViewManager _userViewManager; private readonly IDtoService _dtoService; + private readonly IAuthorizationContext _authContext; - public UserViewsService(IUserManager userManager, IUserViewManager userViewManager, IDtoService dtoService) + public UserViewsService(IUserManager userManager, IUserViewManager userViewManager, IDtoService dtoService, IAuthorizationContext authContext) { _userManager = userManager; _userViewManager = userViewManager; _dtoService = dtoService; + _authContext = authContext; } public async Task Get(GetUserViews request) @@ -82,7 +85,7 @@ namespace MediaBrowser.Api.UserLibrary query.PresetViews = request.PresetViews.Split(','); } - var app = AuthorizationContext.GetAuthorizationInfo(Request).Client ?? string.Empty; + var app = _authContext.GetAuthorizationInfo(Request).Client ?? string.Empty; if (app.IndexOf("emby rt", StringComparison.OrdinalIgnoreCase) != -1) { query.PresetViews = new[] { CollectionType.Music, CollectionType.Movies, CollectionType.TvShows }; @@ -91,7 +94,7 @@ namespace MediaBrowser.Api.UserLibrary var folders = await _userViewManager.GetUserViews(query, CancellationToken.None).ConfigureAwait(false); - var dtoOptions = GetDtoOptions(request); + var dtoOptions = GetDtoOptions(_authContext, request); dtoOptions.Fields = new List(); dtoOptions.Fields.Add(ItemFields.PrimaryImageAspectRatio); dtoOptions.Fields.Add(ItemFields.DisplayPreferencesId); diff --git a/MediaBrowser.Api/UserLibrary/YearsService.cs b/MediaBrowser.Api/UserLibrary/YearsService.cs index 2d2ca523d..1059b72cb 100644 --- a/MediaBrowser.Api/UserLibrary/YearsService.cs +++ b/MediaBrowser.Api/UserLibrary/YearsService.cs @@ -45,11 +45,6 @@ namespace MediaBrowser.Api.UserLibrary [Authenticated] public class YearsService : BaseItemsByNameService { - public YearsService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepo, IDtoService dtoService) - : base(userManager, libraryManager, userDataRepository, itemRepo, dtoService) - { - } - /// /// Gets the specified request. /// @@ -70,8 +65,8 @@ namespace MediaBrowser.Api.UserLibrary private BaseItemDto GetItem(GetYear request) { var item = LibraryManager.GetYear(request.Year); - - var dtoOptions = GetDtoOptions(request); + + var dtoOptions = GetDtoOptions(AuthorizationContext, request); if (!string.IsNullOrWhiteSpace(request.UserId)) { @@ -111,5 +106,9 @@ namespace MediaBrowser.Api.UserLibrary .Distinct() .Select(year => LibraryManager.GetYear(year)); } + + public YearsService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepository, IDtoService dtoService, IAuthorizationContext authorizationContext) : base(userManager, libraryManager, userDataRepository, itemRepository, dtoService, authorizationContext) + { + } } } diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs index f87cad0ff..96c7fc111 100644 --- a/MediaBrowser.Api/UserService.cs +++ b/MediaBrowser.Api/UserService.cs @@ -246,7 +246,7 @@ namespace MediaBrowser.Api /// /// Class UsersService /// - public class UserService : BaseApiService, IHasAuthorization + public class UserService : BaseApiService { /// /// The _user manager @@ -256,14 +256,16 @@ namespace MediaBrowser.Api private readonly IServerConfigurationManager _config; private readonly INetworkManager _networkManager; private readonly IDeviceManager _deviceManager; + private readonly IAuthorizationContext _authContext; - public UserService(IUserManager userManager, ISessionManager sessionMananger, IServerConfigurationManager config, INetworkManager networkManager, IDeviceManager deviceManager) + public UserService(IUserManager userManager, ISessionManager sessionMananger, IServerConfigurationManager config, INetworkManager networkManager, IDeviceManager deviceManager, IAuthorizationContext authContext) { _userManager = userManager; _sessionMananger = sessionMananger; _config = config; _networkManager = networkManager; _deviceManager = deviceManager; + _authContext = authContext; } public object Get(GetPublicUsers request) @@ -316,7 +318,7 @@ namespace MediaBrowser.Api if (filterByDevice) { - var deviceId = AuthorizationContext.GetAuthorizationInfo(Request).DeviceId; + var deviceId = _authContext.GetAuthorizationInfo(Request).DeviceId; if (!string.IsNullOrWhiteSpace(deviceId)) { @@ -412,7 +414,7 @@ namespace MediaBrowser.Api public async Task Post(AuthenticateUserByName request) { - var auth = AuthorizationContext.GetAuthorizationInfo(Request); + var auth = _authContext.GetAuthorizationInfo(Request); var result = await _sessionMananger.AuthenticateNewSession(new AuthenticationRequest { @@ -442,7 +444,7 @@ namespace MediaBrowser.Api public async Task PostAsync(UpdateUserPassword request) { - AssertCanUpdateUser(_userManager, request.Id); + AssertCanUpdateUser(_authContext, _userManager, request.Id); var user = _userManager.GetUserById(request.Id); @@ -466,7 +468,7 @@ namespace MediaBrowser.Api await _userManager.ChangePassword(user, request.NewPassword).ConfigureAwait(false); - var currentToken = AuthorizationContext.GetAuthorizationInfo(Request).Token; + var currentToken = _authContext.GetAuthorizationInfo(Request).Token; await _sessionMananger.RevokeUserTokens(user.Id.ToString("N"), currentToken).ConfigureAwait(false); } @@ -480,7 +482,7 @@ namespace MediaBrowser.Api public async Task PostAsync(UpdateUserEasyPassword request) { - AssertCanUpdateUser(_userManager, request.Id); + AssertCanUpdateUser(_authContext, _userManager, request.Id); var user = _userManager.GetUserById(request.Id); @@ -516,7 +518,7 @@ namespace MediaBrowser.Api // https://code.google.com/p/servicestack/source/browse/trunk/Common/ServiceStack.Text/ServiceStack.Text/Controller/PathInfo.cs var id = GetPathValue(1); - AssertCanUpdateUser(_userManager, id); + AssertCanUpdateUser(_authContext, _userManager, id); var dtoUser = request; @@ -566,7 +568,7 @@ namespace MediaBrowser.Api public void Post(UpdateUserConfiguration request) { - AssertCanUpdateUser(_userManager, request.Id); + AssertCanUpdateUser(_authContext, _userManager, request.Id); var task = _userManager.UpdateConfiguration(request.Id, request); @@ -606,7 +608,7 @@ namespace MediaBrowser.Api throw new ArgumentException("There must be at least one enabled user in the system."); } - var currentToken = AuthorizationContext.GetAuthorizationInfo(Request).Token; + var currentToken = _authContext.GetAuthorizationInfo(Request).Token; await _sessionMananger.RevokeUserTokens(user.Id.ToString("N"), currentToken).ConfigureAwait(false); } diff --git a/MediaBrowser.Api/VideosService.cs b/MediaBrowser.Api/VideosService.cs index a1aec667e..f7b83f23b 100644 --- a/MediaBrowser.Api/VideosService.cs +++ b/MediaBrowser.Api/VideosService.cs @@ -56,8 +56,9 @@ namespace MediaBrowser.Api private readonly IFileSystem _fileSystem; private readonly IItemRepository _itemRepo; private readonly IServerConfigurationManager _config; + private readonly IAuthorizationContext _authContext; - public VideosService(ILibraryManager libraryManager, IUserManager userManager, IDtoService dtoService, IItemRepository itemRepo, IFileSystem fileSystem, IServerConfigurationManager config) + public VideosService(ILibraryManager libraryManager, IUserManager userManager, IDtoService dtoService, IItemRepository itemRepo, IFileSystem fileSystem, IServerConfigurationManager config, IAuthorizationContext authContext) { _libraryManager = libraryManager; _userManager = userManager; @@ -65,6 +66,7 @@ namespace MediaBrowser.Api _itemRepo = itemRepo; _fileSystem = fileSystem; _config = config; + _authContext = authContext; } /// @@ -82,7 +84,7 @@ namespace MediaBrowser.Api : _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id); - var dtoOptions = GetDtoOptions(request); + var dtoOptions = GetDtoOptions(_authContext, request); var video = item as Video; BaseItemDto[] items; diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 6801a4639..518daa6d7 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -193,9 +193,7 @@ - - diff --git a/MediaBrowser.Controller/Net/IHasAuthorization.cs b/MediaBrowser.Controller/Net/IHasAuthorization.cs deleted file mode 100644 index 6fc70159d..000000000 --- a/MediaBrowser.Controller/Net/IHasAuthorization.cs +++ /dev/null @@ -1,12 +0,0 @@ - -namespace MediaBrowser.Controller.Net -{ - public interface IHasAuthorization - { - /// - /// Gets or sets the authorization context. - /// - /// The authorization context. - IAuthorizationContext AuthorizationContext { get; set; } - } -} diff --git a/MediaBrowser.Controller/Net/IHasSession.cs b/MediaBrowser.Controller/Net/IHasSession.cs deleted file mode 100644 index e762c1e84..000000000 --- a/MediaBrowser.Controller/Net/IHasSession.cs +++ /dev/null @@ -1,12 +0,0 @@ - -namespace MediaBrowser.Controller.Net -{ - public interface IHasSession - { - /// - /// Gets or sets the session context. - /// - /// The session context. - ISessionContext SessionContext { get; set; } - } -} diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 0d7a7912a..bae367d82 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -1218,6 +1218,11 @@ namespace MediaBrowser.MediaEncoding.Encoder public string EscapeSubtitleFilterPath(string path) { + // https://ffmpeg.org/ffmpeg-filters.html#Notes-on-filtergraph-escaping + // We need to double escape + + var escapeChars = new[] {':', '\'', ','}; + return path.Replace('\\', '/').Replace(":/", "\\:/").Replace("'", "'\\\\\\''"); } diff --git a/MediaBrowser.Model/Serialization/IXmlSerializer.cs b/MediaBrowser.Model/Serialization/IXmlSerializer.cs index eb23d784f..b26b673f3 100644 --- a/MediaBrowser.Model/Serialization/IXmlSerializer.cs +++ b/MediaBrowser.Model/Serialization/IXmlSerializer.cs @@ -34,7 +34,7 @@ namespace MediaBrowser.Model.Serialization /// The file. /// System.Object. object DeserializeFromFile(Type type, string file); - + /// /// Deserializes from bytes. /// diff --git a/MediaBrowser.Server.Implementations/HttpServer/ContainerAdapter.cs b/MediaBrowser.Server.Implementations/HttpServer/ContainerAdapter.cs deleted file mode 100644 index 235b62f69..000000000 --- a/MediaBrowser.Server.Implementations/HttpServer/ContainerAdapter.cs +++ /dev/null @@ -1,44 +0,0 @@ -using MediaBrowser.Common; -using ServiceStack.Configuration; - -namespace MediaBrowser.Server.Implementations.HttpServer -{ - /// - /// Class ContainerAdapter - /// - class ContainerAdapter : IContainerAdapter - { - /// - /// The _app host - /// - private readonly IApplicationHost _appHost; - - /// - /// Initializes a new instance of the class. - /// - /// The app host. - public ContainerAdapter(IApplicationHost appHost) - { - _appHost = appHost; - } - /// - /// Resolves this instance. - /// - /// - /// ``0. - public T Resolve() - { - return _appHost.Resolve(); - } - - /// - /// Tries the resolve. - /// - /// - /// ``0. - public T TryResolve() - { - return _appHost.TryResolve(); - } - } -} diff --git a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs index 5d9d05034..d86e4da37 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -1,13 +1,10 @@ -using Funq; -using MediaBrowser.Common; -using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Logging; using MediaBrowser.Server.Implementations.HttpServer.SocketSharp; using ServiceStack; using ServiceStack.Host; -using ServiceStack.Host.Handlers; using ServiceStack.Web; using System; using System.Collections.Generic; @@ -17,7 +14,6 @@ using System.Net.Security; using System.Net.Sockets; using System.Reflection; using System.Security.Cryptography.X509Certificates; -using System.Threading; using System.Threading.Tasks; using Emby.Common.Implementations.Net; using Emby.Server.Implementations.HttpServer; @@ -29,6 +25,7 @@ using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Extensions; using MediaBrowser.Model.IO; using MediaBrowser.Model.Net; +using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Services; using MediaBrowser.Model.Text; using SocketHttpListener.Net; @@ -47,8 +44,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer private IHttpListener _listener; - private readonly ContainerAdapter _containerAdapter; - public event EventHandler WebSocketConnected; public event EventHandler WebSocketConnecting; @@ -64,11 +59,14 @@ namespace MediaBrowser.Server.Implementations.HttpServer private readonly ISocketFactory _socketFactory; private readonly ICryptoProvider _cryptoProvider; + private readonly IJsonSerializer _jsonSerializer; + private readonly IXmlSerializer _xmlSerializer; + public HttpListenerHost(IServerApplicationHost applicationHost, ILogManager logManager, IServerConfigurationManager config, string serviceName, - string defaultRedirectPath, INetworkManager networkManager, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding, ISocketFactory socketFactory, ICryptoProvider cryptoProvider) + string defaultRedirectPath, INetworkManager networkManager, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding, ISocketFactory socketFactory, ICryptoProvider cryptoProvider, IJsonSerializer jsonSerializer, IXmlSerializer xmlSerializer) : base(serviceName, new Assembly[] { }) { _appHost = applicationHost; @@ -78,11 +76,11 @@ namespace MediaBrowser.Server.Implementations.HttpServer _textEncoding = textEncoding; _socketFactory = socketFactory; _cryptoProvider = cryptoProvider; + _jsonSerializer = jsonSerializer; + _xmlSerializer = xmlSerializer; _config = config; _logger = logManager.GetLogger("HttpServer"); - - _containerAdapter = new ContainerAdapter(applicationHost); } public string GlobalResponse { get; set; } @@ -106,19 +104,13 @@ namespace MediaBrowser.Server.Implementations.HttpServer {typeof (NotSupportedException), 500} }; - // The Markdown feature causes slow startup times (5 mins+) on cold boots for some users - // Custom format allows images - HostConfig.Instance.EnableFeatures = Feature.Html | Feature.Json | Feature.Xml | Feature.CustomFormat; - - Container.Adapter = _containerAdapter; - var requestFilters = _appHost.GetExports().ToList(); foreach (var filter in requestFilters) { - HostContext.GlobalRequestFilters.Add(filter.Filter); + GlobalRequestFilters.Add(filter.Filter); } - HostContext.GlobalResponseFilters.Add(new ResponseFilter(_logger).FilterResponse); + GlobalResponseFilters.Add(new ResponseFilter(_logger).FilterResponse); } protected override ILogger Logger @@ -129,6 +121,21 @@ namespace MediaBrowser.Server.Implementations.HttpServer } } + public override T Resolve() + { + return _appHost.Resolve(); + } + + public override T TryResolve() + { + return _appHost.TryResolve(); + } + + public override object CreateInstance(Type type) + { + return _appHost.CreateInstance(type); + } + public override void OnConfigLoad() { base.OnConfigLoad(); @@ -241,6 +248,8 @@ namespace MediaBrowser.Server.Implementations.HttpServer { try { + _logger.ErrorException("Error processing request", ex); + var httpRes = httpReq.Response; if (httpRes.IsClosed) @@ -248,39 +257,10 @@ namespace MediaBrowser.Server.Implementations.HttpServer return; } - var errorResponse = new ErrorResponse - { - ResponseStatus = new ResponseStatus - { - ErrorCode = ex.GetType().GetOperationName(), - Message = ex.Message, - StackTrace = ex.StackTrace - } - }; - - var contentType = httpReq.ResponseContentType; - - var serializer = ContentTypes.Instance.GetResponseSerializer(contentType); - if (serializer == null) - { - contentType = HostContext.Config.DefaultContentType; - serializer = ContentTypes.Instance.GetResponseSerializer(contentType); - } - - var httpError = ex as IHttpError; - if (httpError != null) - { - httpRes.StatusCode = httpError.Status; - httpRes.StatusDescription = httpError.StatusDescription; - } - else - { - httpRes.StatusCode = 500; - } - - httpRes.ContentType = contentType; + httpRes.StatusCode = 500; - serializer(httpReq, errorResponse, httpRes); + httpRes.ContentType = "text/html"; + httpRes.Write(ex.Message); httpRes.Close(); } @@ -571,7 +551,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer public static void RedirectToUrl(IResponse httpRes, string url) { httpRes.StatusCode = 302; - httpRes.AddHeader(HttpHeaders.Location, url); + httpRes.AddHeader("Location", url); } @@ -622,6 +602,26 @@ namespace MediaBrowser.Server.Implementations.HttpServer return routes.ToArray(); } + public override void SerializeToJson(object o, Stream stream) + { + _jsonSerializer.SerializeToStream(o, stream); + } + + public override void SerializeToXml(object o, Stream stream) + { + _xmlSerializer.SerializeToStream(o, stream); + } + + public override object DeserializeXml(Type type, Stream stream) + { + return _xmlSerializer.DeserializeFromStream(type, stream); + } + + public override object DeserializeJson(Type type, Stream stream) + { + return _jsonSerializer.DeserializeFromStream(stream, type); + } + private string NormalizeEmbyRoutePath(string path) { if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase)) diff --git a/MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs b/MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs index 4c251ba24..b013a0952 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs @@ -6,13 +6,17 @@ using System; using System.Collections.Generic; using System.Globalization; using System.IO; +using System.IO.Compression; using System.Net; +using System.Runtime.Serialization; using System.Text; using System.Threading.Tasks; +using System.Xml; using Emby.Server.Implementations.HttpServer; using MediaBrowser.Model.IO; using MediaBrowser.Model.Services; using ServiceStack; +using ServiceStack.Host; using IRequest = MediaBrowser.Model.Services.IRequest; using MimeTypes = MediaBrowser.Model.Net.MimeTypes; using StreamWriter = Emby.Server.Implementations.HttpServer.StreamWriter; @@ -30,6 +34,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer private readonly ILogger _logger; private readonly IFileSystem _fileSystem; private readonly IJsonSerializer _jsonSerializer; + private readonly IXmlSerializer _xmlSerializer; /// /// Initializes a new instance of the class. @@ -37,10 +42,11 @@ namespace MediaBrowser.Server.Implementations.HttpServer /// The log manager. /// The file system. /// The json serializer. - public HttpResultFactory(ILogManager logManager, IFileSystem fileSystem, IJsonSerializer jsonSerializer) + public HttpResultFactory(ILogManager logManager, IFileSystem fileSystem, IJsonSerializer jsonSerializer, IXmlSerializer xmlSerializer) { _fileSystem = fileSystem; _jsonSerializer = jsonSerializer; + _xmlSerializer = xmlSerializer; _logger = logManager.GetLogger("HttpResultFactory"); } @@ -130,7 +136,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer throw new ArgumentNullException("result"); } - var optimizedResult = requestContext.ToOptimizedResult(result); + var optimizedResult = ToOptimizedResult(requestContext, result); if (responseHeaders == null) { @@ -152,7 +158,99 @@ namespace MediaBrowser.Server.Implementations.HttpServer return optimizedResult; } - + + public static string GetCompressionType(IRequest request) + { + var prefs = new RequestPreferences(request); + + if (prefs.AcceptsDeflate) + return "deflate"; + + if (prefs.AcceptsGzip) + return "gzip"; + + return null; + } + + /// + /// Returns the optimized result for the IRequestContext. + /// Does not use or store results in any cache. + /// + /// + /// + /// + public object ToOptimizedResult(IRequest request, T dto) + { + request.Response.Dto = dto; + + var compressionType = GetCompressionType(request); + if (compressionType == null) + { + var contentType = request.ResponseContentType; + var contentTypeAttr = ContentFormat.GetEndpointAttributes(contentType); + + switch (contentTypeAttr) + { + case RequestAttributes.Xml: + return SerializeToXmlString(dto); + + case RequestAttributes.Json: + return _jsonSerializer.SerializeToString(dto); + } + } + + using (var ms = new MemoryStream()) + { + using (var compressionStream = GetCompressionStream(ms, compressionType)) + { + ContentTypes.Instance.SerializeToStream(request, dto, compressionStream); + compressionStream.Close(); + + var compressedBytes = ms.ToArray(); + + var httpResult = new HttpResult(compressedBytes, request.ResponseContentType) + { + Status = request.Response.StatusCode + }; + + httpResult.Headers["Content-Length"] = compressedBytes.Length.ToString(UsCulture); + httpResult.Headers["Content-Encoding"] = compressionType; + + return httpResult; + } + } + } + + public static string SerializeToXmlString(object from) + { + using (var ms = new MemoryStream()) + { + var xwSettings = new XmlWriterSettings(); + xwSettings.Encoding = new UTF8Encoding(false); + xwSettings.OmitXmlDeclaration = false; + + using (var xw = XmlWriter.Create(ms, xwSettings)) + { + var serializer = new DataContractSerializer(from.GetType()); + serializer.WriteObject(xw, from); + xw.Flush(); + ms.Seek(0, SeekOrigin.Begin); + var reader = new StreamReader(ms); + return reader.ReadToEnd(); + } + } + } + + private static Stream GetCompressionStream(Stream outputStream, string compressionType) + { + if (compressionType == "deflate") + return new DeflateStream(outputStream, CompressionMode.Compress); + if (compressionType == "gzip") + return new GZipStream(outputStream, CompressionMode.Compress); + + throw new NotSupportedException(compressionType); + } + /// /// Gets the optimized result using cache. /// @@ -471,7 +569,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer var contentType = options.ContentType; var responseHeaders = options.ResponseHeaders; - var requestedCompressionType = requestContext.GetCompressionType(); + var requestedCompressionType = GetCompressionType(requestContext); if (!compress || string.IsNullOrEmpty(requestedCompressionType)) { @@ -513,16 +611,64 @@ namespace MediaBrowser.Server.Implementations.HttpServer } } - var contents = content.Compress(requestedCompressionType); + var contents = Compress(content, requestedCompressionType); responseHeaders["Content-Length"] = contents.Length.ToString(UsCulture); + responseHeaders["Content-Encoding"] = requestedCompressionType; if (isHeadRequest) { return GetHttpResult(new byte[] { }, contentType); } - return new CompressedResult(contents, requestedCompressionType, contentType); + return GetHttpResult(contents, contentType, responseHeaders); + } + + public static byte[] Compress(string text, string compressionType) + { + if (compressionType == "deflate") + return Deflate(text); + + if (compressionType == "gzip") + return GZip(text); + + throw new NotSupportedException(compressionType); + } + + public static byte[] Deflate(string text) + { + return Deflate(Encoding.UTF8.GetBytes(text)); + } + + public static byte[] Deflate(byte[] bytes) + { + // In .NET FX incompat-ville, you can't access compressed bytes without closing DeflateStream + // Which means we must use MemoryStream since you have to use ToArray() on a closed Stream + using (var ms = new MemoryStream()) + using (var zipStream = new DeflateStream(ms, CompressionMode.Compress)) + { + zipStream.Write(bytes, 0, bytes.Length); + zipStream.Close(); + + return ms.ToArray(); + } + } + + public static byte[] GZip(string text) + { + return GZip(Encoding.UTF8.GetBytes(text)); + } + + public static byte[] GZip(byte[] buffer) + { + using (var ms = new MemoryStream()) + using (var zipStream = new GZipStream(ms, CompressionMode.Compress)) + { + zipStream.Write(buffer, 0, buffer.Length); + zipStream.Close(); + + return ms.ToArray(); + } } /// diff --git a/MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs b/MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs index 5da515900..abcf84abd 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs @@ -6,6 +6,7 @@ using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Net; +using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Text; namespace MediaBrowser.Server.Implementations.HttpServer @@ -28,9 +29,11 @@ namespace MediaBrowser.Server.Implementations.HttpServer string defaultRedirectpath, ITextEncoding textEncoding, ISocketFactory socketFactory, - ICryptoProvider cryptoProvider) + ICryptoProvider cryptoProvider, + IJsonSerializer json, + IXmlSerializer xml) { - return new HttpListenerHost(applicationHost, logManager, config, serverName, defaultRedirectpath, networkmanager, streamProvider, textEncoding, socketFactory, cryptoProvider); + return new HttpListenerHost(applicationHost, logManager, config, serverName, defaultRedirectpath, networkmanager, streamProvider, textEncoding, socketFactory, cryptoProvider, json, xml); } } } diff --git a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpRequest.cs b/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpRequest.cs index 6f44fcce7..95b2ccaba 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpRequest.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpRequest.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.IO; using System.Text; using Emby.Server.Implementations.HttpServer.SocketSharp; -using Funq; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Services; @@ -19,7 +18,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp { public partial class WebSocketSharpRequest : IHttpRequest { - public Container Container { get; set; } private readonly HttpListenerRequest request; private readonly IHttpResponse response; private readonly IMemoryStreamFactory _memoryStreamProvider; @@ -239,6 +237,8 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp } } + public const string FormUrlEncoded = "application/x-www-form-urlencoded"; + public const string MultiPartFormData = "multipart/form-data"; private static string GetResponseContentType(IRequest httpReq) { var specifiedContentType = GetQueryStringContentType(httpReq); @@ -246,7 +246,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp var acceptContentTypes = httpReq.AcceptTypes; var defaultContentType = httpReq.ContentType; - if (httpReq.HasAnyOfContentTypes(MimeTypes.FormUrlEncoded, MimeTypes.MultiPartFormData)) + if (HasAnyOfContentTypes(httpReq, FormUrlEncoded, MultiPartFormData)) { defaultContentType = HostContext.Config.DefaultContentType; } @@ -299,15 +299,32 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp } } - if (acceptContentTypes == null && httpReq.ContentType == MimeTypes.Soap11) + if (acceptContentTypes == null && httpReq.ContentType == Soap11) { - return MimeTypes.Soap11; + return Soap11; } //We could also send a '406 Not Acceptable', but this is allowed also return HostContext.Config.DefaultContentType; } + public const string Soap11 = "text/xml; charset=utf-8"; + public static bool HasAnyOfContentTypes(IRequest request, params string[] contentTypes) + { + if (contentTypes == null || request.ContentType == null) return false; + foreach (var contentType in contentTypes) + { + if (IsContentType(request, contentType)) return true; + } + return false; + } + + public static bool IsContentType(IRequest request, string contentType) + { + return request.ContentType.StartsWith(contentType, StringComparison.OrdinalIgnoreCase); + } + + public const string Xml = "application/xml"; private static string GetQueryStringContentType(IRequest httpReq) { var format = httpReq.QueryString["format"]; @@ -323,7 +340,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp format = format.LeftPart('.').ToLower(); if (format.Contains("json")) return "application/json"; - if (format.Contains("xml")) return MimeTypes.Xml; + if (format.Contains("xml")) return Xml; string contentType; ContentTypes.Instance.ContentTypeFormats.TryGetValue(format, out contentType); @@ -464,8 +481,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp get { return httpMethod - ?? (httpMethod = Param(HttpHeaders.XHttpMethodOverride) - ?? request.HttpMethod); + ?? (httpMethod = request.HttpMethod); } } diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 3d38c2e3c..a2edc7aa8 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -88,9 +88,6 @@ ..\ThirdParty\ServiceStack\ServiceStack.dll - - ..\ThirdParty\ServiceStack\ServiceStack.Interfaces.dll - ..\ThirdParty\ServiceStack.Text\ServiceStack.Text.dll @@ -111,7 +108,6 @@ - diff --git a/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj b/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj index b7dbe4dc7..dcc409d3a 100644 --- a/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj +++ b/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj @@ -98,30 +98,11 @@ Properties\SharedVersion.cs - + - - - - - - - - - - - - - - - - - - - diff --git a/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs b/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs deleted file mode 100644 index 46605e19f..000000000 --- a/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs +++ /dev/null @@ -1,271 +0,0 @@ -using MediaBrowser.Common.Net; -using MediaBrowser.IsoMounter; -using MediaBrowser.Model.Logging; -using MediaBrowser.Server.Mono.Networking; -using MediaBrowser.Server.Startup.Common; -using Mono.Unix.Native; -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Text.RegularExpressions; -using MediaBrowser.Model.System; -using MediaBrowser.Server.Implementations.Persistence; -using MediaBrowser.Server.Startup.Common.FFMpeg; -using OperatingSystem = MediaBrowser.Server.Startup.Common.OperatingSystem; - -namespace MediaBrowser.Server.Mono.Native -{ - public abstract class BaseMonoApp : INativeApp - { - protected StartupOptions StartupOptions { get; private set; } - protected ILogger Logger { get; private set; } - - protected BaseMonoApp(StartupOptions startupOptions, ILogger logger) - { - StartupOptions = startupOptions; - Logger = logger; - } - - /// - /// Shutdowns this instance. - /// - public abstract void Shutdown(); - - /// - /// Restarts this instance. - /// - public virtual void Restart(StartupOptions startupOptions) - { - throw new NotImplementedException(); - } - - /// - /// Determines whether this instance [can self restart]. - /// - /// true if this instance [can self restart]; otherwise, false. - public virtual bool CanSelfRestart - { - get - { - return false; - } - } - - /// - /// Gets a value indicating whether this instance can self update. - /// - /// true if this instance can self update; otherwise, false. - public bool CanSelfUpdate - { - get - { - return false; - } - } - - public bool SupportsAutoRunAtStartup - { - get { return false; } - } - - public List GetAssembliesWithParts() - { - var list = new List(); - - if (Environment.OperatingSystem == Startup.Common.OperatingSystem.Linux) - { - list.AddRange(GetLinuxAssemblies()); - } - - list.Add(GetType().Assembly); - - return list; - } - - private IEnumerable GetLinuxAssemblies() - { - var list = new List(); - - list.Add(typeof(LinuxIsoManager).Assembly); - - return list; - } - - public void AuthorizeServer(int udpPort, int httpServerPort, int httpsPort, string applicationPath, string tempDirectory) - { - } - - private NativeEnvironment _nativeEnvironment; - public NativeEnvironment Environment - { - get { return _nativeEnvironment ?? (_nativeEnvironment = GetEnvironmentInfo()); } - } - - public bool SupportsRunningAsService - { - get - { - return false; - } - } - - public bool IsRunningAsService - { - get - { - return false; - } - } - - public bool SupportsLibraryMonitor - { - get - { - return Environment.OperatingSystem != Startup.Common.OperatingSystem.Osx; - } - } - - public void ConfigureAutoRun(bool autorun) - { - } - - public INetworkManager CreateNetworkManager(ILogger logger) - { - return new NetworkManager(logger); - } - - private NativeEnvironment GetEnvironmentInfo() - { - var info = new NativeEnvironment - { - OperatingSystem = Startup.Common.OperatingSystem.Linux - }; - - var uname = GetUnixName(); - - var sysName = uname.sysname ?? string.Empty; - - if (string.Equals(sysName, "Darwin", StringComparison.OrdinalIgnoreCase)) - { - info.OperatingSystem = Startup.Common.OperatingSystem.Osx; - } - else if (string.Equals(sysName, "Linux", StringComparison.OrdinalIgnoreCase)) - { - info.OperatingSystem = Startup.Common.OperatingSystem.Linux; - } - else if (string.Equals(sysName, "BSD", StringComparison.OrdinalIgnoreCase)) - { - info.OperatingSystem = Startup.Common.OperatingSystem.Bsd; - } - - var archX86 = new Regex("(i|I)[3-6]86"); - - if (archX86.IsMatch(uname.machine)) - { - info.SystemArchitecture = Architecture.X86; - } - else if (string.Equals(uname.machine, "x86_64", StringComparison.OrdinalIgnoreCase)) - { - info.SystemArchitecture = Architecture.X64; - } - else if (uname.machine.StartsWith("arm", StringComparison.OrdinalIgnoreCase)) - { - info.SystemArchitecture = Architecture.Arm; - } - else if (System.Environment.Is64BitOperatingSystem) - { - info.SystemArchitecture = Architecture.X64; - } - else - { - info.SystemArchitecture = Architecture.X86; - } - - info.OperatingSystemVersionString = string.IsNullOrWhiteSpace(sysName) ? - System.Environment.OSVersion.VersionString : - sysName; - - return info; - } - - private Uname _unixName; - - private Uname GetUnixName() - { - if (_unixName == null) - { - var uname = new Uname(); - try - { - Utsname utsname; - var callResult = Syscall.uname(out utsname); - if (callResult == 0) - { - uname.sysname = utsname.sysname ?? string.Empty; - uname.machine = utsname.machine ?? string.Empty; - } - - } - catch (Exception ex) - { - Logger.ErrorException("Error getting unix name", ex); - } - _unixName = uname; - } - return _unixName; - } - - public class Uname - { - public string sysname = string.Empty; - public string machine = string.Empty; - } - - public FFMpegInstallInfo GetFfmpegInstallInfo() - { - return GetInfo(Environment); - } - - public void LaunchUrl(string url) - { - throw new NotImplementedException(); - } - - public IDbConnector GetDbConnector() - { - return new DbConnector(Logger); - } - - public static FFMpegInstallInfo GetInfo(NativeEnvironment environment) - { - var info = new FFMpegInstallInfo(); - - // Windows builds: http://ffmpeg.zeranoe.com/builds/ - // Linux builds: http://johnvansickle.com/ffmpeg/ - // OS X builds: http://ffmpegmac.net/ - // OS X x64: http://www.evermeet.cx/ffmpeg/ - - switch (environment.OperatingSystem) - { - case OperatingSystem.Osx: - case OperatingSystem.Bsd: - break; - case OperatingSystem.Linux: - - info.ArchiveType = "7z"; - info.Version = "20160215"; - break; - } - - // No version available - user requirement - info.DownloadUrls = new string[] { }; - - return info; - } - - public void EnableLoopback(string appName) - { - - } - } -} diff --git a/MediaBrowser.Server.Mono/Native/MonoApp.cs b/MediaBrowser.Server.Mono/Native/MonoApp.cs new file mode 100644 index 000000000..bc4a9978e --- /dev/null +++ b/MediaBrowser.Server.Mono/Native/MonoApp.cs @@ -0,0 +1,275 @@ +using MediaBrowser.Common.Net; +using MediaBrowser.IsoMounter; +using MediaBrowser.Model.Logging; +using MediaBrowser.Server.Startup.Common; +using Mono.Unix.Native; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text.RegularExpressions; +using MediaBrowser.Model.System; +using MediaBrowser.Server.Implementations.Persistence; +using MediaBrowser.Server.Startup.Common.FFMpeg; +using MediaBrowser.Server.Startup.Common.Networking; +using OperatingSystem = MediaBrowser.Server.Startup.Common.OperatingSystem; + +namespace MediaBrowser.Server.Mono.Native +{ + public class MonoApp : INativeApp + { + protected StartupOptions StartupOptions { get; private set; } + protected ILogger Logger { get; private set; } + + public MonoApp(StartupOptions startupOptions, ILogger logger) + { + StartupOptions = startupOptions; + Logger = logger; + } + + /// + /// Shutdowns this instance. + /// + public void Shutdown() + { + MainClass.Shutdown(); + } + + /// + /// Determines whether this instance [can self restart]. + /// + /// true if this instance can self restart; otherwise, false. + public bool CanSelfRestart + { + get + { + // A restart script must be provided + return StartupOptions.ContainsOption("-restartpath"); + } + } + + /// + /// Restarts this instance. + /// + public void Restart(StartupOptions startupOptions) + { + MainClass.Restart(startupOptions); + } + + /// + /// Gets a value indicating whether this instance can self update. + /// + /// true if this instance can self update; otherwise, false. + public bool CanSelfUpdate + { + get + { + return false; + } + } + + public bool SupportsAutoRunAtStartup + { + get { return false; } + } + + public List GetAssembliesWithParts() + { + var list = new List(); + + if (Environment.OperatingSystem == Startup.Common.OperatingSystem.Linux) + { + list.AddRange(GetLinuxAssemblies()); + } + + list.Add(GetType().Assembly); + + return list; + } + + private IEnumerable GetLinuxAssemblies() + { + var list = new List(); + + list.Add(typeof(LinuxIsoManager).Assembly); + + return list; + } + + public void AuthorizeServer(int udpPort, int httpServerPort, int httpsPort, string applicationPath, string tempDirectory) + { + } + + private NativeEnvironment _nativeEnvironment; + public NativeEnvironment Environment + { + get { return _nativeEnvironment ?? (_nativeEnvironment = GetEnvironmentInfo()); } + } + + public bool SupportsRunningAsService + { + get + { + return false; + } + } + + public bool IsRunningAsService + { + get + { + return false; + } + } + + public bool SupportsLibraryMonitor + { + get + { + return Environment.OperatingSystem != Startup.Common.OperatingSystem.Osx; + } + } + + public void ConfigureAutoRun(bool autorun) + { + } + + public INetworkManager CreateNetworkManager(ILogger logger) + { + return new NetworkManager(logger); + } + + private NativeEnvironment GetEnvironmentInfo() + { + var info = new NativeEnvironment + { + OperatingSystem = Startup.Common.OperatingSystem.Linux + }; + + var uname = GetUnixName(); + + var sysName = uname.sysname ?? string.Empty; + + if (string.Equals(sysName, "Darwin", StringComparison.OrdinalIgnoreCase)) + { + info.OperatingSystem = Startup.Common.OperatingSystem.Osx; + } + else if (string.Equals(sysName, "Linux", StringComparison.OrdinalIgnoreCase)) + { + info.OperatingSystem = Startup.Common.OperatingSystem.Linux; + } + else if (string.Equals(sysName, "BSD", StringComparison.OrdinalIgnoreCase)) + { + info.OperatingSystem = Startup.Common.OperatingSystem.Bsd; + } + + var archX86 = new Regex("(i|I)[3-6]86"); + + if (archX86.IsMatch(uname.machine)) + { + info.SystemArchitecture = Architecture.X86; + } + else if (string.Equals(uname.machine, "x86_64", StringComparison.OrdinalIgnoreCase)) + { + info.SystemArchitecture = Architecture.X64; + } + else if (uname.machine.StartsWith("arm", StringComparison.OrdinalIgnoreCase)) + { + info.SystemArchitecture = Architecture.Arm; + } + else if (System.Environment.Is64BitOperatingSystem) + { + info.SystemArchitecture = Architecture.X64; + } + else + { + info.SystemArchitecture = Architecture.X86; + } + + info.OperatingSystemVersionString = string.IsNullOrWhiteSpace(sysName) ? + System.Environment.OSVersion.VersionString : + sysName; + + return info; + } + + private Uname _unixName; + + private Uname GetUnixName() + { + if (_unixName == null) + { + var uname = new Uname(); + try + { + Utsname utsname; + var callResult = Syscall.uname(out utsname); + if (callResult == 0) + { + uname.sysname = utsname.sysname ?? string.Empty; + uname.machine = utsname.machine ?? string.Empty; + } + + } + catch (Exception ex) + { + Logger.ErrorException("Error getting unix name", ex); + } + _unixName = uname; + } + return _unixName; + } + + public class Uname + { + public string sysname = string.Empty; + public string machine = string.Empty; + } + + public FFMpegInstallInfo GetFfmpegInstallInfo() + { + return GetInfo(Environment); + } + + public void LaunchUrl(string url) + { + throw new NotImplementedException(); + } + + public IDbConnector GetDbConnector() + { + return new DbConnector(Logger); + } + + public static FFMpegInstallInfo GetInfo(NativeEnvironment environment) + { + var info = new FFMpegInstallInfo(); + + // Windows builds: http://ffmpeg.zeranoe.com/builds/ + // Linux builds: http://johnvansickle.com/ffmpeg/ + // OS X builds: http://ffmpegmac.net/ + // OS X x64: http://www.evermeet.cx/ffmpeg/ + + switch (environment.OperatingSystem) + { + case OperatingSystem.Osx: + case OperatingSystem.Bsd: + break; + case OperatingSystem.Linux: + + info.ArchiveType = "7z"; + info.Version = "20160215"; + break; + } + + // No version available - user requirement + info.DownloadUrls = new string[] { }; + + return info; + } + + public void EnableLoopback(string appName) + { + + } + } +} diff --git a/MediaBrowser.Server.Mono/Native/NativeApp.cs b/MediaBrowser.Server.Mono/Native/NativeApp.cs deleted file mode 100644 index c0874a1d8..000000000 --- a/MediaBrowser.Server.Mono/Native/NativeApp.cs +++ /dev/null @@ -1,45 +0,0 @@ -using MediaBrowser.Model.Logging; -using MediaBrowser.Server.Startup.Common; - -namespace MediaBrowser.Server.Mono.Native -{ - /// - /// Class NativeApp - /// - internal class NativeApp : BaseMonoApp - { - public NativeApp(StartupOptions startupOptions, ILogger logger) - : base(startupOptions, logger) - { - } - - /// - /// Shutdowns this instance. - /// - public override void Shutdown() - { - MainClass.Shutdown(); - } - - /// - /// Determines whether this instance [can self restart]. - /// - /// true if this instance can self restart; otherwise, false. - public override bool CanSelfRestart - { - get - { - // A restart script must be provided - return StartupOptions.ContainsOption("-restartpath"); - } - } - - /// - /// Restarts this instance. - /// - public override void Restart(StartupOptions startupOptions) - { - MainClass.Restart(startupOptions); - } - } -} diff --git a/MediaBrowser.Server.Mono/Networking/CertificateGenerator.cs b/MediaBrowser.Server.Mono/Networking/CertificateGenerator.cs deleted file mode 100644 index 58c5bba2d..000000000 --- a/MediaBrowser.Server.Mono/Networking/CertificateGenerator.cs +++ /dev/null @@ -1,68 +0,0 @@ -using MediaBrowser.Model.Logging; -using System; -using System.Collections; -using System.Security.Cryptography; -using MediaBrowser.Server.Mono.Security; - -namespace MediaBrowser.Server.Mono.Networking -{ - internal class CertificateGenerator - { - private const string MonoTestRootAgency = "v/4nALBxCE+9JgEC0LnDUvKh6e96PwTpN4Rj+vWnqKT7IAp1iK/JjuqvAg6DQ2vTfv0dTlqffmHH51OyioprcT5nzxcSTsZb/9jcHScG0s3/FRIWnXeLk/fgm7mSYhjUaHNI0m1/NTTktipicjKxo71hGIg9qucCWnDum+Krh/k=AQAB

9jbKxMXEruW2CfZrzhxtull4O8P47+mNsEL+9gf9QsRO1jJ77C+jmzfU6zbzjf8+ViK+q62tCMdC1ZzulwdpXQ==

x5+p198l1PkK0Ga2mRh0SIYSykENpY2aLXoyZD/iUpKYAvATm0/wvKNrE4dKJyPCA+y3hfTdgVag+SP9avvDTQ==ISSjCvXsUfbOGG05eddN1gXxL2pj+jegQRfjpk7RAsnWKvNExzhqd5x+ZuNQyc6QH5wxun54inP4RTUI0P/IaQ==R815VQmR3RIbPqzDXzv5j6CSH6fYlcTiQRtkBsUnzhWmkd/y3XmamO+a8zJFjOCCx9CcjpVuGziivBqi65lVPQ==iYiu0KwMWI/dyqN3RJYUzuuLj02/oTD1pYpwo2rvNCXU1Q5VscOeu2DpNg1gWqI+1RrRCsEoaTNzXB1xtKNlSw==nIfh1LYF8fjRBgMdAH/zt9UKHWiaCnc+jXzq5tkR8HVSKTVdzitD8bl1JgAfFQD8VjSXiCJqluexy/B5SGrCXQ49c78NIQj0hD+J13Y8/E0fUbW1QYbhj6Ff7oHyhaYe1WOQfkp2t/h+llHOdt1HRf7bt7dUknYp7m8bQKGxoYE=
"; - - internal static void CreateSelfSignCertificatePfx( - string fileName, - string hostname, - ILogger logger) - { - if (string.IsNullOrWhiteSpace(fileName)) - { - throw new ArgumentNullException("fileName"); - } - - byte[] sn = Guid.NewGuid().ToByteArray(); - string subject = string.Format("CN={0}", hostname); - string issuer = subject; - DateTime notBefore = DateTime.Now.AddDays(-2); - DateTime notAfter = DateTime.Now.AddYears(10); - - RSA issuerKey = RSA.Create(); - issuerKey.FromXmlString(MonoTestRootAgency); - RSA subjectKey = RSA.Create(); - - // serial number MUST be positive - if ((sn[0] & 0x80) == 0x80) - sn[0] -= 0x80; - - issuer = subject; - issuerKey = subjectKey; - - X509CertificateBuilder cb = new X509CertificateBuilder(3); - cb.SerialNumber = sn; - cb.IssuerName = issuer; - cb.NotBefore = notBefore; - cb.NotAfter = notAfter; - cb.SubjectName = subject; - cb.SubjectPublicKey = subjectKey; - - // signature - cb.Hash = "SHA256"; - byte[] rawcert = cb.Sign(issuerKey); - - PKCS12 p12 = new PKCS12(); - - - ArrayList list = new ArrayList(); - // we use a fixed array to avoid endianess issues - // (in case some tools requires the ID to be 1). - list.Add(new byte[4] { 1, 0, 0, 0 }); - Hashtable attributes = new Hashtable(1); - attributes.Add(PKCS9.localKeyId, list); - - p12.AddCertificate(new X509Certificate(rawcert), attributes); - - p12.AddPkcs8ShroudedKeyBag(subjectKey, attributes); - p12.SaveToFile(fileName); - } - } -} diff --git a/MediaBrowser.Server.Mono/Networking/NetworkManager.cs b/MediaBrowser.Server.Mono/Networking/NetworkManager.cs deleted file mode 100644 index 3ef68b665..000000000 --- a/MediaBrowser.Server.Mono/Networking/NetworkManager.cs +++ /dev/null @@ -1,49 +0,0 @@ -using MediaBrowser.Common.Net; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Net; -using System.Collections.Generic; -using Emby.Common.Implementations.Networking; - -namespace MediaBrowser.Server.Mono.Networking -{ - /// - /// Class NetUtils - /// - public class NetworkManager : BaseNetworkManager, INetworkManager - { - public NetworkManager(ILogger logger) - : base(logger) - { - } - - /// - /// Gets the network shares. - /// - /// The path. - /// IEnumerable{NetworkShare}. - public IEnumerable GetNetworkShares(string path) - { - return new List(); - } - - /// - /// Gets available devices within the domain - /// - /// PC's in the Domain - public IEnumerable GetNetworkDevices() - { - return new List(); - } - - /// - /// Generates a self signed certificate at the locatation specified by . - /// - /// The path to generate the certificate. - /// The common name for the certificate. - public void GenerateSelfSignedSslCertificate(string certificatePath, string hostname) - { - CertificateGenerator.CreateSelfSignCertificatePfx(certificatePath, hostname, Logger); - } - } -} diff --git a/MediaBrowser.Server.Mono/Program.cs b/MediaBrowser.Server.Mono/Program.cs index 662d82ff0..77fa6fd91 100644 --- a/MediaBrowser.Server.Mono/Program.cs +++ b/MediaBrowser.Server.Mono/Program.cs @@ -78,7 +78,7 @@ namespace MediaBrowser.Server.Mono var fileSystem = new ManagedFileSystem(logManager.GetLogger("FileSystem"), false, false); fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem)); - var nativeApp = new NativeApp(options, logManager.GetLogger("App")); + var nativeApp = new MonoApp(options, logManager.GetLogger("App")); _appHost = new ApplicationHost(appPaths, logManager, options, fileSystem, nativeApp, new PowerManagement(), "emby.mono.zip"); diff --git a/MediaBrowser.Server.Mono/Security/ASN1.cs b/MediaBrowser.Server.Mono/Security/ASN1.cs deleted file mode 100644 index 2fcbde7c1..000000000 --- a/MediaBrowser.Server.Mono/Security/ASN1.cs +++ /dev/null @@ -1,339 +0,0 @@ -// -// ASN1.cs: Abstract Syntax Notation 1 - micro-parser and generator -// -// Authors: -// Sebastien Pouliot -// Jesper Pedersen -// -// (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com) -// Copyright (C) 2004 Novell, Inc (http://www.novell.com) -// (C) 2004 IT+ A/S (http://www.itplus.dk) -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; -using System.Collections; -using System.IO; -using System.Text; - -namespace MediaBrowser.Server.Mono.Security { - - // References: - // a. ITU ASN.1 standards (free download) - // http://www.itu.int/ITU-T/studygroups/com17/languages/ - - public class ASN1 { - - private byte m_nTag; - private byte[] m_aValue; - private ArrayList elist; - - public ASN1 () : this (0x00, null) {} - - public ASN1 (byte tag) : this (tag, null) {} - - public ASN1 (byte tag, byte[] data) - { - m_nTag = tag; - m_aValue = data; - } - - public ASN1 (byte[] data) - { - m_nTag = data [0]; - - int nLenLength = 0; - int nLength = data [1]; - - if (nLength > 0x80) { - // composed length - nLenLength = nLength - 0x80; - nLength = 0; - for (int i = 0; i < nLenLength; i++) { - nLength *= 256; - nLength += data [i + 2]; - } - } - else if (nLength == 0x80) { - // undefined length encoding - throw new NotSupportedException ("Undefined length encoding."); - } - - m_aValue = new byte [nLength]; - Buffer.BlockCopy (data, (2 + nLenLength), m_aValue, 0, nLength); - - if ((m_nTag & 0x20) == 0x20) { - int nStart = (2 + nLenLength); - Decode (data, ref nStart, data.Length); - } - } - - public int Count { - get { - if (elist == null) - return 0; - return elist.Count; - } - } - - public byte Tag { - get { return m_nTag; } - } - - public int Length { - get { - if (m_aValue != null) - return m_aValue.Length; - else - return 0; - } - } - - public byte[] Value { - get { - if (m_aValue == null) - GetBytes (); - return (byte[]) m_aValue.Clone (); - } - set { - if (value != null) - m_aValue = (byte[]) value.Clone (); - } - } - - private bool CompareArray (byte[] array1, byte[] array2) - { - bool bResult = (array1.Length == array2.Length); - if (bResult) { - for (int i = 0; i < array1.Length; i++) { - if (array1[i] != array2[i]) - return false; - } - } - return bResult; - } - - public bool Equals (byte[] asn1) - { - return CompareArray (this.GetBytes (), asn1); - } - - public bool CompareValue (byte[] value) - { - return CompareArray (m_aValue, value); - } - - public ASN1 Add (ASN1 asn1) - { - if (asn1 != null) { - if (elist == null) - elist = new ArrayList (); - elist.Add (asn1); - } - return asn1; - } - - public virtual byte[] GetBytes () - { - byte[] val = null; - - if (Count > 0) { - int esize = 0; - ArrayList al = new ArrayList (); - foreach (ASN1 a in elist) { - byte[] item = a.GetBytes (); - al.Add (item); - esize += item.Length; - } - val = new byte [esize]; - int pos = 0; - for (int i=0; i < elist.Count; i++) { - byte[] item = (byte[]) al[i]; - Buffer.BlockCopy (item, 0, val, pos, item.Length); - pos += item.Length; - } - } else if (m_aValue != null) { - val = m_aValue; - } - - byte[] der; - int nLengthLen = 0; - - if (val != null) { - int nLength = val.Length; - // special for length > 127 - if (nLength > 127) { - if (nLength <= Byte.MaxValue) { - der = new byte [3 + nLength]; - Buffer.BlockCopy (val, 0, der, 3, nLength); - nLengthLen = 0x81; - der[2] = (byte)(nLength); - } - else if (nLength <= UInt16.MaxValue) { - der = new byte [4 + nLength]; - Buffer.BlockCopy (val, 0, der, 4, nLength); - nLengthLen = 0x82; - der[2] = (byte)(nLength >> 8); - der[3] = (byte)(nLength); - } - else if (nLength <= 0xFFFFFF) { - // 24 bits - der = new byte [5 + nLength]; - Buffer.BlockCopy (val, 0, der, 5, nLength); - nLengthLen = 0x83; - der [2] = (byte)(nLength >> 16); - der [3] = (byte)(nLength >> 8); - der [4] = (byte)(nLength); - } - else { - // max (Length is an integer) 32 bits - der = new byte [6 + nLength]; - Buffer.BlockCopy (val, 0, der, 6, nLength); - nLengthLen = 0x84; - der [2] = (byte)(nLength >> 24); - der [3] = (byte)(nLength >> 16); - der [4] = (byte)(nLength >> 8); - der [5] = (byte)(nLength); - } - } - else { - // basic case (no encoding) - der = new byte [2 + nLength]; - Buffer.BlockCopy (val, 0, der, 2, nLength); - nLengthLen = nLength; - } - if (m_aValue == null) - m_aValue = val; - } - else - der = new byte[2]; - - der[0] = m_nTag; - der[1] = (byte)nLengthLen; - - return der; - } - - // Note: Recursive - protected void Decode (byte[] asn1, ref int anPos, int anLength) - { - byte nTag; - int nLength; - byte[] aValue; - - // minimum is 2 bytes (tag + length of 0) - while (anPos < anLength - 1) { - DecodeTLV (asn1, ref anPos, out nTag, out nLength, out aValue); - // sometimes we get trailing 0 - if (nTag == 0) - continue; - - ASN1 elm = Add (new ASN1 (nTag, aValue)); - - if ((nTag & 0x20) == 0x20) { - int nConstructedPos = anPos; - elm.Decode (asn1, ref nConstructedPos, nConstructedPos + nLength); - } - anPos += nLength; // value length - } - } - - // TLV : Tag - Length - Value - protected void DecodeTLV (byte[] asn1, ref int pos, out byte tag, out int length, out byte[] content) - { - tag = asn1 [pos++]; - length = asn1 [pos++]; - - // special case where L contains the Length of the Length + 0x80 - if ((length & 0x80) == 0x80) { - int nLengthLen = length & 0x7F; - length = 0; - for (int i = 0; i < nLengthLen; i++) - length = length * 256 + asn1 [pos++]; - } - - content = new byte [length]; - Buffer.BlockCopy (asn1, pos, content, 0, length); - } - - public ASN1 this [int index] { - get { - try { - if ((elist == null) || (index >= elist.Count)) - return null; - return (ASN1) elist [index]; - } - catch (ArgumentOutOfRangeException) { - return null; - } - } - } - - public ASN1 Element (int index, byte anTag) - { - try { - if ((elist == null) || (index >= elist.Count)) - return null; - - ASN1 elm = (ASN1) elist [index]; - if (elm.Tag == anTag) - return elm; - else - return null; - } - catch (ArgumentOutOfRangeException) { - return null; - } - } - - public override string ToString() - { - StringBuilder hexLine = new StringBuilder (); - - // Add tag - hexLine.AppendFormat ("Tag: {0} {1}", m_nTag.ToString ("X2"), Environment.NewLine); - - // Add length - hexLine.AppendFormat ("Length: {0} {1}", Value.Length, Environment.NewLine); - - // Add value - hexLine.Append ("Value: "); - hexLine.Append (Environment.NewLine); - for (int i = 0; i < Value.Length; i++) { - hexLine.AppendFormat ("{0} ", Value [i].ToString ("X2")); - if ((i+1) % 16 == 0) - hexLine.AppendFormat (Environment.NewLine); - } - return hexLine.ToString (); - } - - public void SaveToFile (string filename) - { - if (filename == null) - throw new ArgumentNullException ("filename"); - - using (FileStream fs = File.Create (filename)) { - byte[] data = GetBytes (); - fs.Write (data, 0, data.Length); - } - } - } -} diff --git a/MediaBrowser.Server.Mono/Security/ASN1Convert.cs b/MediaBrowser.Server.Mono/Security/ASN1Convert.cs deleted file mode 100644 index f25a5275a..000000000 --- a/MediaBrowser.Server.Mono/Security/ASN1Convert.cs +++ /dev/null @@ -1,206 +0,0 @@ -// -// ASN1Convert.cs: Abstract Syntax Notation 1 convertion routines -// -// Authors: -// Sebastien Pouliot -// Jesper Pedersen -// -// (C) 2003 Motus Technologies Inc. (http://www.motus.com) -// (C) 2004 IT+ A/S (http://www.itplus.dk) -// Copyright (C) 2004-2007 Novell, Inc (http://www.novell.com) -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; -using System.Globalization; -using System.Security.Cryptography; -using System.Text; - -namespace MediaBrowser.Server.Mono.Security { - - // References: - // a. ITU ASN.1 standards (free download) - // http://www.itu.int/ITU-T/studygroups/com17/languages/ - - public static class ASN1Convert { - // RFC3280, section 4.2.1.5 - // CAs conforming to this profile MUST always encode certificate - // validity dates through the year 2049 as UTCTime; certificate validity - // dates in 2050 or later MUST be encoded as GeneralizedTime. - - // Under 1.x this API requires a Local datetime to be provided - // Under 2.0 it will also accept a Utc datetime - static public ASN1 FromDateTime (DateTime dt) - { - if (dt.Year < 2050) { - // UTCTIME - return new ASN1 (0x17, Encoding.ASCII.GetBytes ( - dt.ToUniversalTime ().ToString ("yyMMddHHmmss", - CultureInfo.InvariantCulture) + "Z")); - } - else { - // GENERALIZEDTIME - return new ASN1 (0x18, Encoding.ASCII.GetBytes ( - dt.ToUniversalTime ().ToString ("yyyyMMddHHmmss", - CultureInfo.InvariantCulture) + "Z")); - } - } - - static public ASN1 FromInt32 (Int32 value) - { - byte[] integer = BitConverterLE.GetBytes (value); - Array.Reverse (integer); - int x = 0; - while ((x < integer.Length) && (integer [x] == 0x00)) - x++; - ASN1 asn1 = new ASN1 (0x02); - switch (x) { - case 0: - asn1.Value = integer; - break; - case 4: - asn1.Value = new byte [1]; - break; - default: - byte[] smallerInt = new byte [4 - x]; - Buffer.BlockCopy (integer, x, smallerInt, 0, smallerInt.Length); - asn1.Value = smallerInt; - break; - } - return asn1; - } - - static public ASN1 FromOid (string oid) - { - if (oid == null) - throw new ArgumentNullException ("oid"); - - return new ASN1 (CryptoConfig.EncodeOID (oid)); - } - - static public ASN1 FromUnsignedBigInteger (byte[] big) - { - if (big == null) - throw new ArgumentNullException ("big"); - - // check for numbers that could be interpreted as negative (first bit) - if (big [0] >= 0x80) { - // in thie cas we add a new, empty, byte (position 0) so we're - // sure this will always be interpreted an unsigned integer. - // However we can't feed it into RSAParameters or DSAParameters - int length = big.Length + 1; - byte[] uinteger = new byte [length]; - Buffer.BlockCopy (big, 0, uinteger, 1, length - 1); - big = uinteger; - } - return new ASN1 (0x02, big); - } - - static public int ToInt32 (ASN1 asn1) - { - if (asn1 == null) - throw new ArgumentNullException ("asn1"); - if (asn1.Tag != 0x02) - throw new FormatException ("Only integer can be converted"); - - int x = 0; - for (int i=0; i < asn1.Value.Length; i++) - x = (x << 8) + asn1.Value [i]; - return x; - } - - // Convert a binary encoded OID to human readable string representation of - // an OID (IETF style). Based on DUMPASN1.C from Peter Gutmann. - static public string ToOid (ASN1 asn1) - { - if (asn1 == null) - throw new ArgumentNullException ("asn1"); - - byte[] aOID = asn1.Value; - StringBuilder sb = new StringBuilder (); - // Pick apart the OID - byte x = (byte) (aOID[0] / 40); - byte y = (byte) (aOID[0] % 40); - if (x > 2) { - // Handle special case for large y if x = 2 - y += (byte) ((x - 2) * 40); - x = 2; - } - sb.Append (x.ToString (CultureInfo.InvariantCulture)); - sb.Append ("."); - sb.Append (y.ToString (CultureInfo.InvariantCulture)); - ulong val = 0; - for (x = 1; x < aOID.Length; x++) { - val = ((val << 7) | ((byte) (aOID [x] & 0x7F))); - if ( !((aOID [x] & 0x80) == 0x80)) { - sb.Append ("."); - sb.Append (val.ToString (CultureInfo.InvariantCulture)); - val = 0; - } - } - return sb.ToString (); - } - - static public DateTime ToDateTime (ASN1 time) - { - if (time == null) - throw new ArgumentNullException ("time"); - - string t = Encoding.ASCII.GetString (time.Value); - // to support both UTCTime and GeneralizedTime (and not so common format) - string mask = null; - int year; - switch (t.Length) { - case 11: - // illegal format, still it's supported for compatibility - mask = "yyMMddHHmmZ"; - break; - case 13: - // RFC3280: 4.1.2.5.1 UTCTime - year = Convert.ToInt16 (t.Substring (0, 2), CultureInfo.InvariantCulture); - // Where YY is greater than or equal to 50, the - // year SHALL be interpreted as 19YY; and - // Where YY is less than 50, the year SHALL be - // interpreted as 20YY. - if (year >= 50) - t = "19" + t; - else - t = "20" + t; - mask = "yyyyMMddHHmmssZ"; - break; - case 15: - mask = "yyyyMMddHHmmssZ"; // GeneralizedTime - break; - case 17: - // another illegal format (990630000000+1000), again supported for compatibility - year = Convert.ToInt16 (t.Substring (0, 2), CultureInfo.InvariantCulture); - string century = (year >= 50) ? "19" : "20"; - // ASN.1 (see ITU X.680 section 43.3) deals with offset differently than .NET - char sign = (t[12] == '+') ? '-' : '+'; - t = String.Format ("{0}{1}{2}{3}{4}:{5}{6}", century, t.Substring (0, 12), sign, - t[13], t[14], t[15], t[16]); - mask = "yyyyMMddHHmmsszzz"; - break; - } - return DateTime.ParseExact (t, mask, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal); - } - } -} diff --git a/MediaBrowser.Server.Mono/Security/BitConverterLE.cs b/MediaBrowser.Server.Mono/Security/BitConverterLE.cs deleted file mode 100644 index 29b6ee023..000000000 --- a/MediaBrowser.Server.Mono/Security/BitConverterLE.cs +++ /dev/null @@ -1,239 +0,0 @@ -// -// Mono.Security.BitConverterLE.cs -// Like System.BitConverter but always little endian -// -// Author: -// Bernie Solomon -// - -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; - -namespace MediaBrowser.Server.Mono.Security -{ - internal sealed class BitConverterLE - { - private BitConverterLE () - { - } - - unsafe private static byte[] GetUShortBytes (byte *bytes) - { - if (BitConverter.IsLittleEndian) - return new byte [] { bytes [0], bytes [1] }; - else - return new byte [] { bytes [1], bytes [0] }; - } - - unsafe private static byte[] GetUIntBytes (byte *bytes) - { - if (BitConverter.IsLittleEndian) - return new byte [] { bytes [0], bytes [1], bytes [2], bytes [3] }; - else - return new byte [] { bytes [3], bytes [2], bytes [1], bytes [0] }; - } - - unsafe private static byte[] GetULongBytes (byte *bytes) - { - if (BitConverter.IsLittleEndian) - return new byte [] { bytes [0], bytes [1], bytes [2], bytes [3], - bytes [4], bytes [5], bytes [6], bytes [7] }; - else - return new byte [] { bytes [7], bytes [6], bytes [5], bytes [4], - bytes [3], bytes [2], bytes [1], bytes [0] }; - } - - unsafe internal static byte[] GetBytes (bool value) - { - return new byte [] { value ? (byte)1 : (byte)0 }; - } - - unsafe internal static byte[] GetBytes (char value) - { - return GetUShortBytes ((byte *) &value); - } - - unsafe internal static byte[] GetBytes (short value) - { - return GetUShortBytes ((byte *) &value); - } - - unsafe internal static byte[] GetBytes (int value) - { - return GetUIntBytes ((byte *) &value); - } - - unsafe internal static byte[] GetBytes (long value) - { - return GetULongBytes ((byte *) &value); - } - - unsafe internal static byte[] GetBytes (ushort value) - { - return GetUShortBytes ((byte *) &value); - } - - unsafe internal static byte[] GetBytes (uint value) - { - return GetUIntBytes ((byte *) &value); - } - - unsafe internal static byte[] GetBytes (ulong value) - { - return GetULongBytes ((byte *) &value); - } - - unsafe internal static byte[] GetBytes (float value) - { - return GetUIntBytes ((byte *) &value); - } - - unsafe internal static byte[] GetBytes (double value) - { - return GetULongBytes ((byte *) &value); - } - - unsafe private static void UShortFromBytes (byte *dst, byte[] src, int startIndex) - { - if (BitConverter.IsLittleEndian) { - dst [0] = src [startIndex]; - dst [1] = src [startIndex + 1]; - } else { - dst [0] = src [startIndex + 1]; - dst [1] = src [startIndex]; - } - } - - unsafe private static void UIntFromBytes (byte *dst, byte[] src, int startIndex) - { - if (BitConverter.IsLittleEndian) { - dst [0] = src [startIndex]; - dst [1] = src [startIndex + 1]; - dst [2] = src [startIndex + 2]; - dst [3] = src [startIndex + 3]; - } else { - dst [0] = src [startIndex + 3]; - dst [1] = src [startIndex + 2]; - dst [2] = src [startIndex + 1]; - dst [3] = src [startIndex]; - } - } - - unsafe private static void ULongFromBytes (byte *dst, byte[] src, int startIndex) - { - if (BitConverter.IsLittleEndian) { - for (int i = 0; i < 8; ++i) - dst [i] = src [startIndex + i]; - } else { - for (int i = 0; i < 8; ++i) - dst [i] = src [startIndex + (7 - i)]; - } - } - - unsafe internal static bool ToBoolean (byte[] value, int startIndex) - { - return value [startIndex] != 0; - } - - unsafe internal static char ToChar (byte[] value, int startIndex) - { - char ret; - - UShortFromBytes ((byte *) &ret, value, startIndex); - - return ret; - } - - unsafe internal static short ToInt16 (byte[] value, int startIndex) - { - short ret; - - UShortFromBytes ((byte *) &ret, value, startIndex); - - return ret; - } - - unsafe internal static int ToInt32 (byte[] value, int startIndex) - { - int ret; - - UIntFromBytes ((byte *) &ret, value, startIndex); - - return ret; - } - - unsafe internal static long ToInt64 (byte[] value, int startIndex) - { - long ret; - - ULongFromBytes ((byte *) &ret, value, startIndex); - - return ret; - } - - unsafe internal static ushort ToUInt16 (byte[] value, int startIndex) - { - ushort ret; - - UShortFromBytes ((byte *) &ret, value, startIndex); - - return ret; - } - - unsafe internal static uint ToUInt32 (byte[] value, int startIndex) - { - uint ret; - - UIntFromBytes ((byte *) &ret, value, startIndex); - - return ret; - } - - unsafe internal static ulong ToUInt64 (byte[] value, int startIndex) - { - ulong ret; - - ULongFromBytes ((byte *) &ret, value, startIndex); - - return ret; - } - - unsafe internal static float ToSingle (byte[] value, int startIndex) - { - float ret; - - UIntFromBytes ((byte *) &ret, value, startIndex); - - return ret; - } - - unsafe internal static double ToDouble (byte[] value, int startIndex) - { - double ret; - - ULongFromBytes ((byte *) &ret, value, startIndex); - - return ret; - } - } -} diff --git a/MediaBrowser.Server.Mono/Security/CryptoConvert.cs b/MediaBrowser.Server.Mono/Security/CryptoConvert.cs deleted file mode 100644 index 62c28bd27..000000000 --- a/MediaBrowser.Server.Mono/Security/CryptoConvert.cs +++ /dev/null @@ -1,744 +0,0 @@ -// -// CryptoConvert.cs - Crypto Convertion Routines -// -// Author: -// Sebastien Pouliot -// -// (C) 2003 Motus Technologies Inc. (http://www.motus.com) -// Copyright (C) 2004-2006 Novell Inc. (http://www.novell.com) -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; -using System.Globalization; -using System.Security.Cryptography; -using System.Text; - -namespace MediaBrowser.Server.Mono.Security { - - public sealed class CryptoConvert { - - private CryptoConvert () - { - } - - static private int ToInt32LE (byte [] bytes, int offset) - { - return (bytes [offset+3] << 24) | (bytes [offset+2] << 16) | (bytes [offset+1] << 8) | bytes [offset]; - } - - static private uint ToUInt32LE (byte [] bytes, int offset) - { - return (uint)((bytes [offset+3] << 24) | (bytes [offset+2] << 16) | (bytes [offset+1] << 8) | bytes [offset]); - } - - static private byte [] GetBytesLE (int val) - { - return new byte [] { - (byte) (val & 0xff), - (byte) ((val >> 8) & 0xff), - (byte) ((val >> 16) & 0xff), - (byte) ((val >> 24) & 0xff) - }; - } - - static private byte[] Trim (byte[] array) - { - for (int i=0; i < array.Length; i++) { - if (array [i] != 0x00) { - byte[] result = new byte [array.Length - i]; - Buffer.BlockCopy (array, i, result, 0, result.Length); - return result; - } - } - return null; - } - - // convert the key from PRIVATEKEYBLOB to RSA - // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/security/Security/private_key_blobs.asp - // e.g. SNK files, PVK files - static public RSA FromCapiPrivateKeyBlob (byte[] blob) - { - return FromCapiPrivateKeyBlob (blob, 0); - } - - static public RSA FromCapiPrivateKeyBlob (byte[] blob, int offset) - { - if (blob == null) - throw new ArgumentNullException ("blob"); - if (offset >= blob.Length) - throw new ArgumentException ("blob is too small."); - - RSAParameters rsap = new RSAParameters (); - try { - if ((blob [offset] != 0x07) || // PRIVATEKEYBLOB (0x07) - (blob [offset+1] != 0x02) || // Version (0x02) - (blob [offset+2] != 0x00) || // Reserved (word) - (blob [offset+3] != 0x00) || - (ToUInt32LE (blob, offset+8) != 0x32415352)) // DWORD magic = RSA2 - throw new CryptographicException ("Invalid blob header"); - - // ALGID (CALG_RSA_SIGN, CALG_RSA_KEYX, ...) - // int algId = ToInt32LE (blob, offset+4); - - // DWORD bitlen - int bitLen = ToInt32LE (blob, offset+12); - - // DWORD public exponent - byte[] exp = new byte [4]; - Buffer.BlockCopy (blob, offset+16, exp, 0, 4); - Array.Reverse (exp); - rsap.Exponent = Trim (exp); - - int pos = offset+20; - // BYTE modulus[rsapubkey.bitlen/8]; - int byteLen = (bitLen >> 3); - rsap.Modulus = new byte [byteLen]; - Buffer.BlockCopy (blob, pos, rsap.Modulus, 0, byteLen); - Array.Reverse (rsap.Modulus); - pos += byteLen; - - // BYTE prime1[rsapubkey.bitlen/16]; - int byteHalfLen = (byteLen >> 1); - rsap.P = new byte [byteHalfLen]; - Buffer.BlockCopy (blob, pos, rsap.P, 0, byteHalfLen); - Array.Reverse (rsap.P); - pos += byteHalfLen; - - // BYTE prime2[rsapubkey.bitlen/16]; - rsap.Q = new byte [byteHalfLen]; - Buffer.BlockCopy (blob, pos, rsap.Q, 0, byteHalfLen); - Array.Reverse (rsap.Q); - pos += byteHalfLen; - - // BYTE exponent1[rsapubkey.bitlen/16]; - rsap.DP = new byte [byteHalfLen]; - Buffer.BlockCopy (blob, pos, rsap.DP, 0, byteHalfLen); - Array.Reverse (rsap.DP); - pos += byteHalfLen; - - // BYTE exponent2[rsapubkey.bitlen/16]; - rsap.DQ = new byte [byteHalfLen]; - Buffer.BlockCopy (blob, pos, rsap.DQ, 0, byteHalfLen); - Array.Reverse (rsap.DQ); - pos += byteHalfLen; - - // BYTE coefficient[rsapubkey.bitlen/16]; - rsap.InverseQ = new byte [byteHalfLen]; - Buffer.BlockCopy (blob, pos, rsap.InverseQ, 0, byteHalfLen); - Array.Reverse (rsap.InverseQ); - pos += byteHalfLen; - - // ok, this is hackish but CryptoAPI support it so... - // note: only works because CRT is used by default - // http://bugzilla.ximian.com/show_bug.cgi?id=57941 - rsap.D = new byte [byteLen]; // must be allocated - if (pos + byteLen + offset <= blob.Length) { - // BYTE privateExponent[rsapubkey.bitlen/8]; - Buffer.BlockCopy (blob, pos, rsap.D, 0, byteLen); - Array.Reverse (rsap.D); - } - } - catch (Exception e) { - throw new CryptographicException ("Invalid blob.", e); - } - - RSA rsa = null; - try - { - rsa = RSA.Create(); - rsa.ImportParameters(rsap); - } - catch (CryptographicException ce) - { - // this may cause problem when this code is run under - // the SYSTEM identity on Windows (e.g. ASP.NET). See - // http://bugzilla.ximian.com/show_bug.cgi?id=77559 - try - { - CspParameters csp = new CspParameters(); - csp.Flags = CspProviderFlags.UseMachineKeyStore; - rsa = new RSACryptoServiceProvider(csp); - rsa.ImportParameters(rsap); - } - catch - { - // rethrow original, not the later, exception if this fails - throw ce; - } - } - return rsa; - } - - static public DSA FromCapiPrivateKeyBlobDSA (byte[] blob) - { - return FromCapiPrivateKeyBlobDSA (blob, 0); - } - - static public DSA FromCapiPrivateKeyBlobDSA (byte[] blob, int offset) - { - if (blob == null) - throw new ArgumentNullException ("blob"); - if (offset >= blob.Length) - throw new ArgumentException ("blob is too small."); - - DSAParameters dsap = new DSAParameters (); - try { - if ((blob [offset] != 0x07) || // PRIVATEKEYBLOB (0x07) - (blob [offset + 1] != 0x02) || // Version (0x02) - (blob [offset + 2] != 0x00) || // Reserved (word) - (blob [offset + 3] != 0x00) || - (ToUInt32LE (blob, offset + 8) != 0x32535344)) // DWORD magic - throw new CryptographicException ("Invalid blob header"); - - int bitlen = ToInt32LE (blob, offset + 12); - int bytelen = bitlen >> 3; - int pos = offset + 16; - - dsap.P = new byte [bytelen]; - Buffer.BlockCopy (blob, pos, dsap.P, 0, bytelen); - Array.Reverse (dsap.P); - pos += bytelen; - - dsap.Q = new byte [20]; - Buffer.BlockCopy (blob, pos, dsap.Q, 0, 20); - Array.Reverse (dsap.Q); - pos += 20; - - dsap.G = new byte [bytelen]; - Buffer.BlockCopy (blob, pos, dsap.G, 0, bytelen); - Array.Reverse (dsap.G); - pos += bytelen; - - dsap.X = new byte [20]; - Buffer.BlockCopy (blob, pos, dsap.X, 0, 20); - Array.Reverse (dsap.X); - pos += 20; - - dsap.Counter = ToInt32LE (blob, pos); - pos += 4; - - dsap.Seed = new byte [20]; - Buffer.BlockCopy (blob, pos, dsap.Seed, 0, 20); - Array.Reverse (dsap.Seed); - pos += 20; - } - catch (Exception e) { - throw new CryptographicException ("Invalid blob.", e); - } - - DSA dsa = null; - try - { - dsa = (DSA)DSA.Create(); - dsa.ImportParameters(dsap); - } - catch (CryptographicException ce) - { - // this may cause problem when this code is run under - // the SYSTEM identity on Windows (e.g. ASP.NET). See - // http://bugzilla.ximian.com/show_bug.cgi?id=77559 - try - { - CspParameters csp = new CspParameters(); - csp.Flags = CspProviderFlags.UseMachineKeyStore; - dsa = new DSACryptoServiceProvider(csp); - dsa.ImportParameters(dsap); - } - catch - { - // rethrow original, not the later, exception if this fails - throw ce; - } - } - return dsa; - } - - static public byte[] ToCapiPrivateKeyBlob (RSA rsa) - { - RSAParameters p = rsa.ExportParameters (true); - int keyLength = p.Modulus.Length; // in bytes - byte[] blob = new byte [20 + (keyLength << 2) + (keyLength >> 1)]; - - blob [0] = 0x07; // Type - PRIVATEKEYBLOB (0x07) - blob [1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02) - // [2], [3] // RESERVED - Always 0 - blob [5] = 0x24; // ALGID - Always 00 24 00 00 (for CALG_RSA_SIGN) - blob [8] = 0x52; // Magic - RSA2 (ASCII in hex) - blob [9] = 0x53; - blob [10] = 0x41; - blob [11] = 0x32; - - byte[] bitlen = GetBytesLE (keyLength << 3); - blob [12] = bitlen [0]; // bitlen - blob [13] = bitlen [1]; - blob [14] = bitlen [2]; - blob [15] = bitlen [3]; - - // public exponent (DWORD) - int pos = 16; - int n = p.Exponent.Length; - while (n > 0) - blob [pos++] = p.Exponent [--n]; - // modulus - pos = 20; - byte[] part = p.Modulus; - int len = part.Length; - Array.Reverse (part, 0, len); - Buffer.BlockCopy (part, 0, blob, pos, len); - pos += len; - // private key - part = p.P; - len = part.Length; - Array.Reverse (part, 0, len); - Buffer.BlockCopy (part, 0, blob, pos, len); - pos += len; - - part = p.Q; - len = part.Length; - Array.Reverse (part, 0, len); - Buffer.BlockCopy (part, 0, blob, pos, len); - pos += len; - - part = p.DP; - len = part.Length; - Array.Reverse (part, 0, len); - Buffer.BlockCopy (part, 0, blob, pos, len); - pos += len; - - part = p.DQ; - len = part.Length; - Array.Reverse (part, 0, len); - Buffer.BlockCopy (part, 0, blob, pos, len); - pos += len; - - part = p.InverseQ; - len = part.Length; - Array.Reverse (part, 0, len); - Buffer.BlockCopy (part, 0, blob, pos, len); - pos += len; - - part = p.D; - len = part.Length; - Array.Reverse (part, 0, len); - Buffer.BlockCopy (part, 0, blob, pos, len); - - return blob; - } - - static public byte[] ToCapiPrivateKeyBlob (DSA dsa) - { - DSAParameters p = dsa.ExportParameters (true); - int keyLength = p.P.Length; // in bytes - - // header + P + Q + G + X + count + seed - byte[] blob = new byte [16 + keyLength + 20 + keyLength + 20 + 4 + 20]; - - blob [0] = 0x07; // Type - PRIVATEKEYBLOB (0x07) - blob [1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02) - // [2], [3] // RESERVED - Always 0 - blob [5] = 0x22; // ALGID - blob [8] = 0x44; // Magic - blob [9] = 0x53; - blob [10] = 0x53; - blob [11] = 0x32; - - byte[] bitlen = GetBytesLE (keyLength << 3); - blob [12] = bitlen [0]; - blob [13] = bitlen [1]; - blob [14] = bitlen [2]; - blob [15] = bitlen [3]; - - int pos = 16; - byte[] part = p.P; - Array.Reverse (part); - Buffer.BlockCopy (part, 0, blob, pos, keyLength); - pos += keyLength; - - part = p.Q; - Array.Reverse (part); - Buffer.BlockCopy (part, 0, blob, pos, 20); - pos += 20; - - part = p.G; - Array.Reverse (part); - Buffer.BlockCopy (part, 0, blob, pos, keyLength); - pos += keyLength; - - part = p.X; - Array.Reverse (part); - Buffer.BlockCopy (part, 0, blob, pos, 20); - pos += 20; - - Buffer.BlockCopy (GetBytesLE (p.Counter), 0, blob, pos, 4); - pos += 4; - - part = p.Seed; - Array.Reverse (part); - Buffer.BlockCopy (part, 0, blob, pos, 20); - - return blob; - } - - static public RSA FromCapiPublicKeyBlob (byte[] blob) - { - return FromCapiPublicKeyBlob (blob, 0); - } - - static public RSA FromCapiPublicKeyBlob (byte[] blob, int offset) - { - if (blob == null) - throw new ArgumentNullException ("blob"); - if (offset >= blob.Length) - throw new ArgumentException ("blob is too small."); - - try { - if ((blob [offset] != 0x06) || // PUBLICKEYBLOB (0x06) - (blob [offset+1] != 0x02) || // Version (0x02) - (blob [offset+2] != 0x00) || // Reserved (word) - (blob [offset+3] != 0x00) || - (ToUInt32LE (blob, offset+8) != 0x31415352)) // DWORD magic = RSA1 - throw new CryptographicException ("Invalid blob header"); - - // ALGID (CALG_RSA_SIGN, CALG_RSA_KEYX, ...) - // int algId = ToInt32LE (blob, offset+4); - - // DWORD bitlen - int bitLen = ToInt32LE (blob, offset+12); - - // DWORD public exponent - RSAParameters rsap = new RSAParameters (); - rsap.Exponent = new byte [3]; - rsap.Exponent [0] = blob [offset+18]; - rsap.Exponent [1] = blob [offset+17]; - rsap.Exponent [2] = blob [offset+16]; - - int pos = offset+20; - // BYTE modulus[rsapubkey.bitlen/8]; - int byteLen = (bitLen >> 3); - rsap.Modulus = new byte [byteLen]; - Buffer.BlockCopy (blob, pos, rsap.Modulus, 0, byteLen); - Array.Reverse (rsap.Modulus); - RSA rsa = null; - try - { - rsa = RSA.Create(); - rsa.ImportParameters(rsap); - } - catch (CryptographicException) - { - // this may cause problem when this code is run under - // the SYSTEM identity on Windows (e.g. ASP.NET). See - // http://bugzilla.ximian.com/show_bug.cgi?id=77559 - CspParameters csp = new CspParameters(); - csp.Flags = CspProviderFlags.UseMachineKeyStore; - rsa = new RSACryptoServiceProvider(csp); - rsa.ImportParameters(rsap); - } - return rsa; - } - catch (Exception e) { - throw new CryptographicException ("Invalid blob.", e); - } - } - - static public DSA FromCapiPublicKeyBlobDSA (byte[] blob) - { - return FromCapiPublicKeyBlobDSA (blob, 0); - } - - static public DSA FromCapiPublicKeyBlobDSA (byte[] blob, int offset) - { - if (blob == null) - throw new ArgumentNullException ("blob"); - if (offset >= blob.Length) - throw new ArgumentException ("blob is too small."); - - try { - if ((blob [offset] != 0x06) || // PUBLICKEYBLOB (0x06) - (blob [offset + 1] != 0x02) || // Version (0x02) - (blob [offset + 2] != 0x00) || // Reserved (word) - (blob [offset + 3] != 0x00) || - (ToUInt32LE (blob, offset + 8) != 0x31535344)) // DWORD magic - throw new CryptographicException ("Invalid blob header"); - - int bitlen = ToInt32LE (blob, offset + 12); - DSAParameters dsap = new DSAParameters (); - int bytelen = bitlen >> 3; - int pos = offset + 16; - - dsap.P = new byte [bytelen]; - Buffer.BlockCopy (blob, pos, dsap.P, 0, bytelen); - Array.Reverse (dsap.P); - pos += bytelen; - - dsap.Q = new byte [20]; - Buffer.BlockCopy (blob, pos, dsap.Q, 0, 20); - Array.Reverse (dsap.Q); - pos += 20; - - dsap.G = new byte [bytelen]; - Buffer.BlockCopy (blob, pos, dsap.G, 0, bytelen); - Array.Reverse (dsap.G); - pos += bytelen; - - dsap.Y = new byte [bytelen]; - Buffer.BlockCopy (blob, pos, dsap.Y, 0, bytelen); - Array.Reverse (dsap.Y); - pos += bytelen; - - dsap.Counter = ToInt32LE (blob, pos); - pos += 4; - - dsap.Seed = new byte [20]; - Buffer.BlockCopy (blob, pos, dsap.Seed, 0, 20); - Array.Reverse (dsap.Seed); - pos += 20; - - DSA dsa = (DSA)DSA.Create (); - dsa.ImportParameters (dsap); - return dsa; - } - catch (Exception e) { - throw new CryptographicException ("Invalid blob.", e); - } - } - - static public byte[] ToCapiPublicKeyBlob (RSA rsa) - { - RSAParameters p = rsa.ExportParameters (false); - int keyLength = p.Modulus.Length; // in bytes - byte[] blob = new byte [20 + keyLength]; - - blob [0] = 0x06; // Type - PUBLICKEYBLOB (0x06) - blob [1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02) - // [2], [3] // RESERVED - Always 0 - blob [5] = 0x24; // ALGID - Always 00 24 00 00 (for CALG_RSA_SIGN) - blob [8] = 0x52; // Magic - RSA1 (ASCII in hex) - blob [9] = 0x53; - blob [10] = 0x41; - blob [11] = 0x31; - - byte[] bitlen = GetBytesLE (keyLength << 3); - blob [12] = bitlen [0]; // bitlen - blob [13] = bitlen [1]; - blob [14] = bitlen [2]; - blob [15] = bitlen [3]; - - // public exponent (DWORD) - int pos = 16; - int n = p.Exponent.Length; - while (n > 0) - blob [pos++] = p.Exponent [--n]; - // modulus - pos = 20; - byte[] part = p.Modulus; - int len = part.Length; - Array.Reverse (part, 0, len); - Buffer.BlockCopy (part, 0, blob, pos, len); - pos += len; - return blob; - } - - static public byte[] ToCapiPublicKeyBlob (DSA dsa) - { - DSAParameters p = dsa.ExportParameters (false); - int keyLength = p.P.Length; // in bytes - - // header + P + Q + G + Y + count + seed - byte[] blob = new byte [16 + keyLength + 20 + keyLength + keyLength + 4 + 20]; - - blob [0] = 0x06; // Type - PUBLICKEYBLOB (0x06) - blob [1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02) - // [2], [3] // RESERVED - Always 0 - blob [5] = 0x22; // ALGID - blob [8] = 0x44; // Magic - blob [9] = 0x53; - blob [10] = 0x53; - blob [11] = 0x31; - - byte[] bitlen = GetBytesLE (keyLength << 3); - blob [12] = bitlen [0]; - blob [13] = bitlen [1]; - blob [14] = bitlen [2]; - blob [15] = bitlen [3]; - - int pos = 16; - byte[] part; - - part = p.P; - Array.Reverse (part); - Buffer.BlockCopy (part, 0, blob, pos, keyLength); - pos += keyLength; - - part = p.Q; - Array.Reverse (part); - Buffer.BlockCopy (part, 0, blob, pos, 20); - pos += 20; - - part = p.G; - Array.Reverse (part); - Buffer.BlockCopy (part, 0, blob, pos, keyLength); - pos += keyLength; - - part = p.Y; - Array.Reverse (part); - Buffer.BlockCopy (part, 0, blob, pos, keyLength); - pos += keyLength; - - Buffer.BlockCopy (GetBytesLE (p.Counter), 0, blob, pos, 4); - pos += 4; - - part = p.Seed; - Array.Reverse (part); - Buffer.BlockCopy (part, 0, blob, pos, 20); - - return blob; - } - - // PRIVATEKEYBLOB - // PUBLICKEYBLOB - static public RSA FromCapiKeyBlob (byte[] blob) - { - return FromCapiKeyBlob (blob, 0); - } - - static public RSA FromCapiKeyBlob (byte[] blob, int offset) - { - if (blob == null) - throw new ArgumentNullException ("blob"); - if (offset >= blob.Length) - throw new ArgumentException ("blob is too small."); - - switch (blob [offset]) { - case 0x00: - // this could be a public key inside an header - // like "sn -e" would produce - if (blob [offset + 12] == 0x06) { - return FromCapiPublicKeyBlob (blob, offset + 12); - } - break; - case 0x06: - return FromCapiPublicKeyBlob (blob, offset); - case 0x07: - return FromCapiPrivateKeyBlob (blob, offset); - } - throw new CryptographicException ("Unknown blob format."); - } - - static public DSA FromCapiKeyBlobDSA (byte[] blob) - { - return FromCapiKeyBlobDSA (blob, 0); - } - - static public DSA FromCapiKeyBlobDSA (byte[] blob, int offset) - { - if (blob == null) - throw new ArgumentNullException ("blob"); - if (offset >= blob.Length) - throw new ArgumentException ("blob is too small."); - - switch (blob [offset]) { - case 0x06: - return FromCapiPublicKeyBlobDSA (blob, offset); - case 0x07: - return FromCapiPrivateKeyBlobDSA (blob, offset); - } - throw new CryptographicException ("Unknown blob format."); - } - - static public byte[] ToCapiKeyBlob (AsymmetricAlgorithm keypair, bool includePrivateKey) - { - if (keypair == null) - throw new ArgumentNullException ("keypair"); - - // check between RSA and DSA (and potentially others like DH) - if (keypair is RSA) - return ToCapiKeyBlob ((RSA)keypair, includePrivateKey); - else if (keypair is DSA) - return ToCapiKeyBlob ((DSA)keypair, includePrivateKey); - else - return null; // TODO - } - - static public byte[] ToCapiKeyBlob (RSA rsa, bool includePrivateKey) - { - if (rsa == null) - throw new ArgumentNullException ("rsa"); - - if (includePrivateKey) - return ToCapiPrivateKeyBlob (rsa); - else - return ToCapiPublicKeyBlob (rsa); - } - - static public byte[] ToCapiKeyBlob (DSA dsa, bool includePrivateKey) - { - if (dsa == null) - throw new ArgumentNullException ("dsa"); - - if (includePrivateKey) - return ToCapiPrivateKeyBlob (dsa); - else - return ToCapiPublicKeyBlob (dsa); - } - - static public string ToHex (byte[] input) - { - if (input == null) - return null; - - StringBuilder sb = new StringBuilder (input.Length * 2); - foreach (byte b in input) { - sb.Append (b.ToString ("X2", CultureInfo.InvariantCulture)); - } - return sb.ToString (); - } - - static private byte FromHexChar (char c) - { - if ((c >= 'a') && (c <= 'f')) - return (byte) (c - 'a' + 10); - if ((c >= 'A') && (c <= 'F')) - return (byte) (c - 'A' + 10); - if ((c >= '0') && (c <= '9')) - return (byte) (c - '0'); - throw new ArgumentException ("invalid hex char"); - } - - static public byte[] FromHex (string hex) - { - if (hex == null) - return null; - if ((hex.Length & 0x1) == 0x1) - throw new ArgumentException ("Length must be a multiple of 2"); - - byte[] result = new byte [hex.Length >> 1]; - int n = 0; - int i = 0; - while (n < result.Length) { - result [n] = (byte) (FromHexChar (hex [i++]) << 4); - result [n++] += FromHexChar (hex [i++]); - } - return result; - } - } -} diff --git a/MediaBrowser.Server.Mono/Security/PKCS1.cs b/MediaBrowser.Server.Mono/Security/PKCS1.cs deleted file mode 100644 index 86ed6f2d8..000000000 --- a/MediaBrowser.Server.Mono/Security/PKCS1.cs +++ /dev/null @@ -1,490 +0,0 @@ -// -// PKCS1.cs - Implements PKCS#1 primitives. -// -// Author: -// Sebastien Pouliot -// -// (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com) -// Copyright (C) 2004 Novell, Inc (http://www.novell.com) -// Copyright 2013 Xamarin Inc. (http://www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; -using System.Security.Cryptography; - -namespace MediaBrowser.Server.Mono.Security { - - // References: - // a. PKCS#1: RSA Cryptography Standard - // http://www.rsasecurity.com/rsalabs/pkcs/pkcs-1/index.html - - public sealed class PKCS1 { - - private PKCS1 () - { - } - - private static bool Compare (byte[] array1, byte[] array2) - { - bool result = (array1.Length == array2.Length); - if (result) { - for (int i=0; i < array1.Length; i++) - if (array1[i] != array2[i]) - return false; - } - return result; - } - - private static byte[] xor (byte[] array1, byte[] array2) - { - byte[] result = new byte [array1.Length]; - for (int i=0; i < result.Length; i++) - result[i] = (byte) (array1[i] ^ array2[i]); - return result; - } - - private static byte[] emptySHA1 = { 0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09 }; - private static byte[] emptySHA256 = { 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55 }; - private static byte[] emptySHA384 = { 0x38, 0xb0, 0x60, 0xa7, 0x51, 0xac, 0x96, 0x38, 0x4c, 0xd9, 0x32, 0x7e, 0xb1, 0xb1, 0xe3, 0x6a, 0x21, 0xfd, 0xb7, 0x11, 0x14, 0xbe, 0x07, 0x43, 0x4c, 0x0c, 0xc7, 0xbf, 0x63, 0xf6, 0xe1, 0xda, 0x27, 0x4e, 0xde, 0xbf, 0xe7, 0x6f, 0x65, 0xfb, 0xd5, 0x1a, 0xd2, 0xf1, 0x48, 0x98, 0xb9, 0x5b }; - private static byte[] emptySHA512 = { 0xcf, 0x83, 0xe1, 0x35, 0x7e, 0xef, 0xb8, 0xbd, 0xf1, 0x54, 0x28, 0x50, 0xd6, 0x6d, 0x80, 0x07, 0xd6, 0x20, 0xe4, 0x05, 0x0b, 0x57, 0x15, 0xdc, 0x83, 0xf4, 0xa9, 0x21, 0xd3, 0x6c, 0xe9, 0xce, 0x47, 0xd0, 0xd1, 0x3c, 0x5d, 0x85, 0xf2, 0xb0, 0xff, 0x83, 0x18, 0xd2, 0x87, 0x7e, 0xec, 0x2f, 0x63, 0xb9, 0x31, 0xbd, 0x47, 0x41, 0x7a, 0x81, 0xa5, 0x38, 0x32, 0x7a, 0xf9, 0x27, 0xda, 0x3e }; - - private static byte[] GetEmptyHash (HashAlgorithm hash) - { - if (hash is SHA1) - return emptySHA1; - else if (hash is SHA256) - return emptySHA256; - else if (hash is SHA384) - return emptySHA384; - else if (hash is SHA512) - return emptySHA512; - else - return hash.ComputeHash ((byte[])null); - } - - // PKCS #1 v.2.1, Section 4.1 - // I2OSP converts a non-negative integer to an octet string of a specified length. - public static byte[] I2OSP (int x, int size) - { - byte[] array = BitConverterLE.GetBytes (x); - Array.Reverse (array, 0, array.Length); - return I2OSP (array, size); - } - - public static byte[] I2OSP (byte[] x, int size) - { - byte[] result = new byte [size]; - Buffer.BlockCopy (x, 0, result, (result.Length - x.Length), x.Length); - return result; - } - - // PKCS #1 v.2.1, Section 4.2 - // OS2IP converts an octet string to a nonnegative integer. - public static byte[] OS2IP (byte[] x) - { - int i = 0; - while ((x [i++] == 0x00) && (i < x.Length)) { - // confuse compiler into reporting a warning with {} - } - i--; - if (i > 0) { - byte[] result = new byte [x.Length - i]; - Buffer.BlockCopy (x, i, result, 0, result.Length); - return result; - } - else - return x; - } - - // PKCS #1 v.2.1, Section 5.1.1 - public static byte[] RSAEP (RSA rsa, byte[] m) - { - // c = m^e mod n - return rsa.EncryptValue (m); - } - - // PKCS #1 v.2.1, Section 5.1.2 - public static byte[] RSADP (RSA rsa, byte[] c) - { - // m = c^d mod n - // Decrypt value may apply CRT optimizations - return rsa.DecryptValue (c); - } - - // PKCS #1 v.2.1, Section 5.2.1 - public static byte[] RSASP1 (RSA rsa, byte[] m) - { - // first form: s = m^d mod n - // Decrypt value may apply CRT optimizations - return rsa.DecryptValue (m); - } - - // PKCS #1 v.2.1, Section 5.2.2 - public static byte[] RSAVP1 (RSA rsa, byte[] s) - { - // m = s^e mod n - return rsa.EncryptValue (s); - } - - // PKCS #1 v.2.1, Section 7.1.1 - // RSAES-OAEP-ENCRYPT ((n, e), M, L) - public static byte[] Encrypt_OAEP (RSA rsa, HashAlgorithm hash, RandomNumberGenerator rng, byte[] M) - { - int size = rsa.KeySize / 8; - int hLen = hash.HashSize / 8; - if (M.Length > size - 2 * hLen - 2) - throw new CryptographicException ("message too long"); - // empty label L SHA1 hash - byte[] lHash = GetEmptyHash (hash); - int PSLength = (size - M.Length - 2 * hLen - 2); - // DB = lHash || PS || 0x01 || M - byte[] DB = new byte [lHash.Length + PSLength + 1 + M.Length]; - Buffer.BlockCopy (lHash, 0, DB, 0, lHash.Length); - DB [(lHash.Length + PSLength)] = 0x01; - Buffer.BlockCopy (M, 0, DB, (DB.Length - M.Length), M.Length); - - byte[] seed = new byte [hLen]; - rng.GetBytes (seed); - - byte[] dbMask = MGF1 (hash, seed, size - hLen - 1); - byte[] maskedDB = xor (DB, dbMask); - byte[] seedMask = MGF1 (hash, maskedDB, hLen); - byte[] maskedSeed = xor (seed, seedMask); - // EM = 0x00 || maskedSeed || maskedDB - byte[] EM = new byte [maskedSeed.Length + maskedDB.Length + 1]; - Buffer.BlockCopy (maskedSeed, 0, EM, 1, maskedSeed.Length); - Buffer.BlockCopy (maskedDB, 0, EM, maskedSeed.Length + 1, maskedDB.Length); - - byte[] m = OS2IP (EM); - byte[] c = RSAEP (rsa, m); - return I2OSP (c, size); - } - - // PKCS #1 v.2.1, Section 7.1.2 - // RSAES-OAEP-DECRYPT (K, C, L) - public static byte[] Decrypt_OAEP (RSA rsa, HashAlgorithm hash, byte[] C) - { - int size = rsa.KeySize / 8; - int hLen = hash.HashSize / 8; - if ((size < (2 * hLen + 2)) || (C.Length != size)) - throw new CryptographicException ("decryption error"); - - byte[] c = OS2IP (C); - byte[] m = RSADP (rsa, c); - byte[] EM = I2OSP (m, size); - - // split EM = Y || maskedSeed || maskedDB - byte[] maskedSeed = new byte [hLen]; - Buffer.BlockCopy (EM, 1, maskedSeed, 0, maskedSeed.Length); - byte[] maskedDB = new byte [size - hLen - 1]; - Buffer.BlockCopy (EM, (EM.Length - maskedDB.Length), maskedDB, 0, maskedDB.Length); - - byte[] seedMask = MGF1 (hash, maskedDB, hLen); - byte[] seed = xor (maskedSeed, seedMask); - byte[] dbMask = MGF1 (hash, seed, size - hLen - 1); - byte[] DB = xor (maskedDB, dbMask); - - byte[] lHash = GetEmptyHash (hash); - // split DB = lHash' || PS || 0x01 || M - byte[] dbHash = new byte [lHash.Length]; - Buffer.BlockCopy (DB, 0, dbHash, 0, dbHash.Length); - bool h = Compare (lHash, dbHash); - - // find separator 0x01 - int nPos = lHash.Length; - while (DB[nPos] == 0) - nPos++; - - int Msize = DB.Length - nPos - 1; - byte[] M = new byte [Msize]; - Buffer.BlockCopy (DB, (nPos + 1), M, 0, Msize); - - // we could have returned EM[0] sooner but would be helping a timing attack - if ((EM[0] != 0) || (!h) || (DB[nPos] != 0x01)) - return null; - return M; - } - - // PKCS #1 v.2.1, Section 7.2.1 - // RSAES-PKCS1-V1_5-ENCRYPT ((n, e), M) - public static byte[] Encrypt_v15 (RSA rsa, RandomNumberGenerator rng, byte[] M) - { - int size = rsa.KeySize / 8; - if (M.Length > size - 11) - throw new CryptographicException ("message too long"); - int PSLength = System.Math.Max (8, (size - M.Length - 3)); - byte[] PS = new byte [PSLength]; - rng.GetNonZeroBytes (PS); - byte[] EM = new byte [size]; - EM [1] = 0x02; - Buffer.BlockCopy (PS, 0, EM, 2, PSLength); - Buffer.BlockCopy (M, 0, EM, (size - M.Length), M.Length); - - byte[] m = OS2IP (EM); - byte[] c = RSAEP (rsa, m); - byte[] C = I2OSP (c, size); - return C; - } - - // PKCS #1 v.2.1, Section 7.2.2 - // RSAES-PKCS1-V1_5-DECRYPT (K, C) - public static byte[] Decrypt_v15 (RSA rsa, byte[] C) - { - int size = rsa.KeySize >> 3; // div by 8 - if ((size < 11) || (C.Length > size)) - throw new CryptographicException ("decryption error"); - byte[] c = OS2IP (C); - byte[] m = RSADP (rsa, c); - byte[] EM = I2OSP (m, size); - - if ((EM [0] != 0x00) || (EM [1] != 0x02)) - return null; - - int mPos = 10; - // PS is a minimum of 8 bytes + 2 bytes for header - while ((EM [mPos] != 0x00) && (mPos < EM.Length)) - mPos++; - if (EM [mPos] != 0x00) - return null; - mPos++; - byte[] M = new byte [EM.Length - mPos]; - Buffer.BlockCopy (EM, mPos, M, 0, M.Length); - return M; - } - - // PKCS #1 v.2.1, Section 8.2.1 - // RSASSA-PKCS1-V1_5-SIGN (K, M) - public static byte[] Sign_v15 (RSA rsa, HashAlgorithm hash, byte[] hashValue) - { - int size = (rsa.KeySize >> 3); // div 8 - byte[] EM = Encode_v15 (hash, hashValue, size); - byte[] m = OS2IP (EM); - byte[] s = RSASP1 (rsa, m); - byte[] S = I2OSP (s, size); - return S; - } - - internal static byte[] Sign_v15 (RSA rsa, string hashName, byte[] hashValue) - { - using (var hash = CreateFromName (hashName)) - return Sign_v15 (rsa, hash, hashValue); - } - - // PKCS #1 v.2.1, Section 8.2.2 - // RSASSA-PKCS1-V1_5-VERIFY ((n, e), M, S) - public static bool Verify_v15 (RSA rsa, HashAlgorithm hash, byte[] hashValue, byte[] signature) - { - return Verify_v15 (rsa, hash, hashValue, signature, false); - } - - internal static bool Verify_v15 (RSA rsa, string hashName, byte[] hashValue, byte[] signature) - { - using (var hash = CreateFromName (hashName)) - return Verify_v15 (rsa, hash, hashValue, signature, false); - } - - // DO NOT USE WITHOUT A VERY GOOD REASON - public static bool Verify_v15 (RSA rsa, HashAlgorithm hash, byte [] hashValue, byte [] signature, bool tryNonStandardEncoding) - { - int size = (rsa.KeySize >> 3); // div 8 - byte[] s = OS2IP (signature); - byte[] m = RSAVP1 (rsa, s); - byte[] EM2 = I2OSP (m, size); - byte[] EM = Encode_v15 (hash, hashValue, size); - bool result = Compare (EM, EM2); - if (result || !tryNonStandardEncoding) - return result; - - // NOTE: some signatures don't include the hash OID (pretty lame but real) - // and compatible with MS implementation. E.g. Verisign Authenticode Timestamps - - // we're making this "as safe as possible" - if ((EM2 [0] != 0x00) || (EM2 [1] != 0x01)) - return false; - int i; - for (i = 2; i < EM2.Length - hashValue.Length - 1; i++) { - if (EM2 [i] != 0xFF) - return false; - } - if (EM2 [i++] != 0x00) - return false; - - byte [] decryptedHash = new byte [hashValue.Length]; - Buffer.BlockCopy (EM2, i, decryptedHash, 0, decryptedHash.Length); - return Compare (decryptedHash, hashValue); - } - - // PKCS #1 v.2.1, Section 9.2 - // EMSA-PKCS1-v1_5-Encode - public static byte[] Encode_v15 (HashAlgorithm hash, byte[] hashValue, int emLength) - { - if (hashValue.Length != (hash.HashSize >> 3)) - throw new CryptographicException ("bad hash length for " + hash.ToString ()); - - // DigestInfo ::= SEQUENCE { - // digestAlgorithm AlgorithmIdentifier, - // digest OCTET STRING - // } - - byte[] t = null; - - string oid = CryptoConfig.MapNameToOID (hash.ToString ()); - if (oid != null) - { - ASN1 digestAlgorithm = new ASN1 (0x30); - digestAlgorithm.Add (new ASN1 (CryptoConfig.EncodeOID (oid))); - digestAlgorithm.Add (new ASN1 (0x05)); // NULL - ASN1 digest = new ASN1 (0x04, hashValue); - ASN1 digestInfo = new ASN1 (0x30); - digestInfo.Add (digestAlgorithm); - digestInfo.Add (digest); - - t = digestInfo.GetBytes (); - } - else - { - // There are no valid OID, in this case t = hashValue - // This is the case of the MD5SHA hash algorithm - t = hashValue; - } - - Buffer.BlockCopy (hashValue, 0, t, t.Length - hashValue.Length, hashValue.Length); - - int PSLength = System.Math.Max (8, emLength - t.Length - 3); - // PS = PSLength of 0xff - - // EM = 0x00 | 0x01 | PS | 0x00 | T - byte[] EM = new byte [PSLength + t.Length + 3]; - EM [1] = 0x01; - for (int i=2; i < PSLength + 2; i++) - EM[i] = 0xff; - Buffer.BlockCopy (t, 0, EM, PSLength + 3, t.Length); - - return EM; - } - - // PKCS #1 v.2.1, Section B.2.1 - public static byte[] MGF1 (HashAlgorithm hash, byte[] mgfSeed, int maskLen) - { - // 1. If maskLen > 2^32 hLen, output "mask too long" and stop. - // easy - this is impossible by using a int (31bits) as parameter ;-) - // BUT with a signed int we do have to check for negative values! - if (maskLen < 0) - throw new OverflowException(); - - int mgfSeedLength = mgfSeed.Length; - int hLen = (hash.HashSize >> 3); // from bits to bytes - int iterations = (maskLen / hLen); - if (maskLen % hLen != 0) - iterations++; - // 2. Let T be the empty octet string. - byte[] T = new byte [iterations * hLen]; - - byte[] toBeHashed = new byte [mgfSeedLength + 4]; - int pos = 0; - // 3. For counter from 0 to \ceil (maskLen / hLen) - 1, do the following: - for (int counter = 0; counter < iterations; counter++) { - // a. Convert counter to an octet string C of length 4 octets - byte[] C = I2OSP (counter, 4); - - // b. Concatenate the hash of the seed mgfSeed and C to the octet string T: - // T = T || Hash (mgfSeed || C) - Buffer.BlockCopy (mgfSeed, 0, toBeHashed, 0, mgfSeedLength); - Buffer.BlockCopy (C, 0, toBeHashed, mgfSeedLength, 4); - byte[] output = hash.ComputeHash (toBeHashed); - Buffer.BlockCopy (output, 0, T, pos, hLen); - pos += hLen; - } - - // 4. Output the leading maskLen octets of T as the octet string mask. - byte[] mask = new byte [maskLen]; - Buffer.BlockCopy (T, 0, mask, 0, maskLen); - return mask; - } - - static internal string HashNameFromOid (string oid, bool throwOnError = true) - { - switch (oid) { - case "1.2.840.113549.1.1.2": // MD2 with RSA encryption - return "MD2"; - case "1.2.840.113549.1.1.3": // MD4 with RSA encryption - return "MD4"; - case "1.2.840.113549.1.1.4": // MD5 with RSA encryption - return "MD5"; - case "1.2.840.113549.1.1.5": // SHA-1 with RSA Encryption - case "1.3.14.3.2.29": // SHA1 with RSA signature - case "1.2.840.10040.4.3": // SHA1-1 with DSA - return "SHA1"; - case "1.2.840.113549.1.1.11": // SHA-256 with RSA Encryption - return "SHA256"; - case "1.2.840.113549.1.1.12": // SHA-384 with RSA Encryption - return "SHA384"; - case "1.2.840.113549.1.1.13": // SHA-512 with RSA Encryption - return "SHA512"; - case "1.3.36.3.3.1.2": - return "RIPEMD160"; - default: - if (throwOnError) - throw new CryptographicException ("Unsupported hash algorithm: " + oid); - return null; - } - } - - static internal HashAlgorithm CreateFromOid (string oid) - { - return CreateFromName (HashNameFromOid (oid)); - } - - static internal HashAlgorithm CreateFromName (string name) - { -#if FULL_AOT_RUNTIME - switch (name) { - case "MD2": - return MD2.Create (); - case "MD4": - return MD4.Create (); - case "MD5": - return MD5.Create (); - case "SHA1": - return SHA1.Create (); - case "SHA256": - return SHA256.Create (); - case "SHA384": - return SHA384.Create (); - case "SHA512": - return SHA512.Create (); - case "RIPEMD160": - return RIPEMD160.Create (); - default: - try { - return (HashAlgorithm) Activator.CreateInstance (Type.GetType (name)); - } - catch { - throw new CryptographicException ("Unsupported hash algorithm: " + name); - } - } -#else - return HashAlgorithm.Create (name); -#endif - } - } -} diff --git a/MediaBrowser.Server.Mono/Security/PKCS12.cs b/MediaBrowser.Server.Mono/Security/PKCS12.cs deleted file mode 100644 index b5da09c63..000000000 --- a/MediaBrowser.Server.Mono/Security/PKCS12.cs +++ /dev/null @@ -1,1933 +0,0 @@ -// -// PKCS12.cs: PKCS 12 - Personal Information Exchange Syntax -// -// Author: -// Sebastien Pouliot -// -// (C) 2003 Motus Technologies Inc. (http://www.motus.com) -// Copyright (C) 2004,2005,2006 Novell Inc. (http://www.novell.com) -// Copyright 2013 Xamarin Inc. (http://www.xamarin.com) -// -// Key derivation translated from Bouncy Castle JCE (http://www.bouncycastle.org/) -// See bouncycastle.txt for license. -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; -using System.Collections; -using System.IO; -using System.Security.Cryptography; -using System.Text; - -namespace MediaBrowser.Server.Mono.Security { - - public class PKCS5 { - - public const string pbeWithMD2AndDESCBC = "1.2.840.113549.1.5.1"; - public const string pbeWithMD5AndDESCBC = "1.2.840.113549.1.5.3"; - public const string pbeWithMD2AndRC2CBC = "1.2.840.113549.1.5.4"; - public const string pbeWithMD5AndRC2CBC = "1.2.840.113549.1.5.6"; - public const string pbeWithSHA1AndDESCBC = "1.2.840.113549.1.5.10"; - public const string pbeWithSHA1AndRC2CBC = "1.2.840.113549.1.5.11"; - - public PKCS5 () {} - } - - public class PKCS9 { - - public const string friendlyName = "1.2.840.113549.1.9.20"; - public const string localKeyId = "1.2.840.113549.1.9.21"; - - public PKCS9 () {} - } - - - internal class SafeBag { - private string _bagOID; - private ASN1 _asn1; - - public SafeBag(string bagOID, ASN1 asn1) { - _bagOID = bagOID; - _asn1 = asn1; - } - - public string BagOID { - get { return _bagOID; } - } - - public ASN1 ASN1 { - get { return _asn1; } - } - } - - - public class PKCS12 : ICloneable { - - public const string pbeWithSHAAnd128BitRC4 = "1.2.840.113549.1.12.1.1"; - public const string pbeWithSHAAnd40BitRC4 = "1.2.840.113549.1.12.1.2"; - public const string pbeWithSHAAnd3KeyTripleDESCBC = "1.2.840.113549.1.12.1.3"; - public const string pbeWithSHAAnd2KeyTripleDESCBC = "1.2.840.113549.1.12.1.4"; - public const string pbeWithSHAAnd128BitRC2CBC = "1.2.840.113549.1.12.1.5"; - public const string pbeWithSHAAnd40BitRC2CBC = "1.2.840.113549.1.12.1.6"; - - // bags - public const string keyBag = "1.2.840.113549.1.12.10.1.1"; - public const string pkcs8ShroudedKeyBag = "1.2.840.113549.1.12.10.1.2"; - public const string certBag = "1.2.840.113549.1.12.10.1.3"; - public const string crlBag = "1.2.840.113549.1.12.10.1.4"; - public const string secretBag = "1.2.840.113549.1.12.10.1.5"; - public const string safeContentsBag = "1.2.840.113549.1.12.10.1.6"; - - // types - public const string x509Certificate = "1.2.840.113549.1.9.22.1"; - public const string sdsiCertificate = "1.2.840.113549.1.9.22.2"; - public const string x509Crl = "1.2.840.113549.1.9.23.1"; - - // Adapted from BouncyCastle PKCS12ParametersGenerator.java - public class DeriveBytes { - - public enum Purpose { - Key, - IV, - MAC - } - - static private byte[] keyDiversifier = { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 }; - static private byte[] ivDiversifier = { 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 }; - static private byte[] macDiversifier = { 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 }; - - private string _hashName; - private int _iterations; - private byte[] _password; - private byte[] _salt; - - public DeriveBytes () {} - - public string HashName { - get { return _hashName; } - set { _hashName = value; } - } - - public int IterationCount { - get { return _iterations; } - set { _iterations = value; } - } - - public byte[] Password { - get { return (byte[]) _password.Clone (); } - set { - if (value == null) - _password = new byte [0]; - else - _password = (byte[]) value.Clone (); - } - } - - public byte[] Salt { - get { return (byte[]) _salt.Clone (); } - set { - if (value != null) - _salt = (byte[]) value.Clone (); - else - _salt = null; - } - } - - private void Adjust (byte[] a, int aOff, byte[] b) - { - int x = (b[b.Length - 1] & 0xff) + (a [aOff + b.Length - 1] & 0xff) + 1; - - a [aOff + b.Length - 1] = (byte) x; - x >>= 8; - - for (int i = b.Length - 2; i >= 0; i--) { - x += (b [i] & 0xff) + (a [aOff + i] & 0xff); - a [aOff + i] = (byte) x; - x >>= 8; - } - } - - private byte[] Derive (byte[] diversifier, int n) - { - HashAlgorithm digest = PKCS1.CreateFromName (_hashName); - int u = (digest.HashSize >> 3); // div 8 - int v = 64; - byte[] dKey = new byte [n]; - - byte[] S; - if ((_salt != null) && (_salt.Length != 0)) { - S = new byte[v * ((_salt.Length + v - 1) / v)]; - - for (int i = 0; i != S.Length; i++) { - S[i] = _salt[i % _salt.Length]; - } - } - else { - S = new byte[0]; - } - - byte[] P; - if ((_password != null) && (_password.Length != 0)) { - P = new byte[v * ((_password.Length + v - 1) / v)]; - - for (int i = 0; i != P.Length; i++) { - P[i] = _password[i % _password.Length]; - } - } - else { - P = new byte[0]; - } - - byte[] I = new byte [S.Length + P.Length]; - - Buffer.BlockCopy (S, 0, I, 0, S.Length); - Buffer.BlockCopy (P, 0, I, S.Length, P.Length); - - byte[] B = new byte[v]; - int c = (n + u - 1) / u; - - for (int i = 1; i <= c; i++) { - digest.TransformBlock (diversifier, 0, diversifier.Length, diversifier, 0); - digest.TransformFinalBlock (I, 0, I.Length); - byte[] A = digest.Hash; - digest.Initialize (); - for (int j = 1; j != _iterations; j++) { - A = digest.ComputeHash (A, 0, A.Length); - } - - for (int j = 0; j != B.Length; j++) { - B [j] = A [j % A.Length]; - } - - for (int j = 0; j != I.Length / v; j++) { - Adjust (I, j * v, B); - } - - if (i == c) { - Buffer.BlockCopy(A, 0, dKey, (i - 1) * u, dKey.Length - ((i - 1) * u)); - } - else { - Buffer.BlockCopy(A, 0, dKey, (i - 1) * u, A.Length); - } - } - - return dKey; - } - - public byte[] DeriveKey (int size) - { - return Derive (keyDiversifier, size); - } - - public byte[] DeriveIV (int size) - { - return Derive (ivDiversifier, size); - } - - public byte[] DeriveMAC (int size) - { - return Derive (macDiversifier, size); - } - } - - const int recommendedIterationCount = 2000; - - //private int _version; - private byte[] _password; - private ArrayList _keyBags; - private ArrayList _secretBags; - private X509CertificateCollection _certs; - private bool _keyBagsChanged; - private bool _secretBagsChanged; - private bool _certsChanged; - private int _iterations; - private ArrayList _safeBags; - private RandomNumberGenerator _rng; - - // constructors - - public PKCS12 () - { - _iterations = recommendedIterationCount; - _keyBags = new ArrayList (); - _secretBags = new ArrayList (); - _certs = new X509CertificateCollection (); - _keyBagsChanged = false; - _secretBagsChanged = false; - _certsChanged = false; - _safeBags = new ArrayList (); - } - - public PKCS12 (byte[] data) - : this () - { - Password = null; - Decode (data); - } - - /* - * PFX ::= SEQUENCE { - * version INTEGER {v3(3)}(v3,...), - * authSafe ContentInfo, - * macData MacData OPTIONAL - * } - * - * MacData ::= SEQUENCE { - * mac DigestInfo, - * macSalt OCTET STRING, - * iterations INTEGER DEFAULT 1 - * -- Note: The default is for historical reasons and its use is deprecated. A higher - * -- value, like 1024 is recommended. - * } - * - * SafeContents ::= SEQUENCE OF SafeBag - * - * SafeBag ::= SEQUENCE { - * bagId BAG-TYPE.&id ({PKCS12BagSet}), - * bagValue [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}), - * bagAttributes SET OF PKCS12Attribute OPTIONAL - * } - */ - public PKCS12 (byte[] data, string password) - : this () - { - Password = password; - Decode (data); - } - - public PKCS12 (byte[] data, byte[] password) - : this () - { - _password = password; - Decode (data); - } - - private void Decode (byte[] data) - { - ASN1 pfx = new ASN1 (data); - if (pfx.Tag != 0x30) - throw new ArgumentException ("invalid data"); - - ASN1 version = pfx [0]; - if (version.Tag != 0x02) - throw new ArgumentException ("invalid PFX version"); - //_version = version.Value [0]; - - PKCS7.ContentInfo authSafe = new PKCS7.ContentInfo (pfx [1]); - if (authSafe.ContentType != PKCS7.Oid.data) - throw new ArgumentException ("invalid authenticated safe"); - - // now that we know it's a PKCS#12 file, check the (optional) MAC - // before decoding anything else in the file - if (pfx.Count > 2) { - ASN1 macData = pfx [2]; - if (macData.Tag != 0x30) - throw new ArgumentException ("invalid MAC"); - - ASN1 mac = macData [0]; - if (mac.Tag != 0x30) - throw new ArgumentException ("invalid MAC"); - ASN1 macAlgorithm = mac [0]; - string macOid = ASN1Convert.ToOid (macAlgorithm [0]); - if (macOid != "1.3.14.3.2.26") - throw new ArgumentException ("unsupported HMAC"); - byte[] macValue = mac [1].Value; - - ASN1 macSalt = macData [1]; - if (macSalt.Tag != 0x04) - throw new ArgumentException ("missing MAC salt"); - - _iterations = 1; // default value - if (macData.Count > 2) { - ASN1 iters = macData [2]; - if (iters.Tag != 0x02) - throw new ArgumentException ("invalid MAC iteration"); - _iterations = ASN1Convert.ToInt32 (iters); - } - - byte[] authSafeData = authSafe.Content [0].Value; - byte[] calculatedMac = MAC (_password, macSalt.Value, _iterations, authSafeData); - if (!Compare (macValue, calculatedMac)) { - byte[] nullPassword = {0, 0}; - calculatedMac = MAC(nullPassword, macSalt.Value, _iterations, authSafeData); - if (!Compare (macValue, calculatedMac)) - throw new CryptographicException ("Invalid MAC - file may have been tampe red!"); - _password = nullPassword; - } - } - - // we now returns to our original presentation - PFX - ASN1 authenticatedSafe = new ASN1 (authSafe.Content [0].Value); - for (int i=0; i < authenticatedSafe.Count; i++) { - PKCS7.ContentInfo ci = new PKCS7.ContentInfo (authenticatedSafe [i]); - switch (ci.ContentType) { - case PKCS7.Oid.data: - // unencrypted (by PKCS#12) - ASN1 safeContents = new ASN1 (ci.Content [0].Value); - for (int j=0; j < safeContents.Count; j++) { - ASN1 safeBag = safeContents [j]; - ReadSafeBag (safeBag); - } - break; - case PKCS7.Oid.encryptedData: - // password encrypted - PKCS7.EncryptedData ed = new PKCS7.EncryptedData (ci.Content [0]); - ASN1 decrypted = new ASN1 (Decrypt (ed)); - for (int j=0; j < decrypted.Count; j++) { - ASN1 safeBag = decrypted [j]; - ReadSafeBag (safeBag); - } - break; - case PKCS7.Oid.envelopedData: - // public key encrypted - throw new NotImplementedException ("public key encrypted"); - default: - throw new ArgumentException ("unknown authenticatedSafe"); - } - } - } - - ~PKCS12 () - { - if (_password != null) { - Array.Clear (_password, 0, _password.Length); - } - _password = null; - } - - // properties - - public string Password { - set { - // Clear old password. - if (_password != null) - Array.Clear (_password, 0, _password.Length); - _password = null; - if (value != null) { - if (value.Length > 0) { - int size = value.Length; - int nul = 0; - if (size < MaximumPasswordLength) { - // if not present, add space for a NULL (0x00) character - if (value[size - 1] != 0x00) - nul = 1; - } else { - size = MaximumPasswordLength; - } - _password = new byte[(size + nul) << 1]; // double for unicode - Encoding.BigEndianUnicode.GetBytes (value, 0, size, _password, 0); - } else { - // double-byte (Unicode) NULL (0x00) - see bug #79617 - _password = new byte[2]; - } - } - } - } - - public int IterationCount { - get { return _iterations; } - set { _iterations = value; } - } - - public ArrayList Keys { - get { - if (_keyBagsChanged) { - _keyBags.Clear (); - foreach (SafeBag sb in _safeBags) { - if (sb.BagOID.Equals (keyBag)) { - ASN1 safeBag = sb.ASN1; - ASN1 bagValue = safeBag [1]; - PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value); - byte[] privateKey = pki.PrivateKey; - switch (privateKey [0]) { - case 0x02: - DSAParameters p = new DSAParameters (); // FIXME - _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p)); - break; - case 0x30: - _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeRSA (privateKey)); - break; - default: - break; - } - Array.Clear (privateKey, 0, privateKey.Length); - - } else if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) { - ASN1 safeBag = sb.ASN1; - ASN1 bagValue = safeBag [1]; - PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value); - byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData); - PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted); - byte[] privateKey = pki.PrivateKey; - switch (privateKey [0]) { - case 0x02: - DSAParameters p = new DSAParameters (); // FIXME - _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p)); - break; - case 0x30: - _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeRSA (privateKey)); - break; - default: - break; - } - Array.Clear (privateKey, 0, privateKey.Length); - Array.Clear (decrypted, 0, decrypted.Length); - } - } - _keyBagsChanged = false; - } - return ArrayList.ReadOnly(_keyBags); - } - } - - public ArrayList Secrets { - get { - if (_secretBagsChanged) { - _secretBags.Clear (); - foreach (SafeBag sb in _safeBags) { - if (sb.BagOID.Equals (secretBag)) { - ASN1 safeBag = sb.ASN1; - ASN1 bagValue = safeBag [1]; - byte[] secret = bagValue.Value; - _secretBags.Add(secret); - } - } - _secretBagsChanged = false; - } - return ArrayList.ReadOnly(_secretBags); - } - } - - public X509CertificateCollection Certificates { - get { - if (_certsChanged) { - _certs.Clear (); - foreach (SafeBag sb in _safeBags) { - if (sb.BagOID.Equals (certBag)) { - ASN1 safeBag = sb.ASN1; - ASN1 bagValue = safeBag [1]; - PKCS7.ContentInfo cert = new PKCS7.ContentInfo (bagValue.Value); - _certs.Add (new X509Certificate (cert.Content [0].Value)); - } - } - _certsChanged = false; - } - return _certs; - } - } - - internal RandomNumberGenerator RNG { - get { - if (_rng == null) - _rng = RandomNumberGenerator.Create (); - return _rng; - } - } - - // private methods - - private bool Compare (byte[] expected, byte[] actual) - { - bool compare = false; - if (expected.Length == actual.Length) { - for (int i=0; i < expected.Length; i++) { - if (expected [i] != actual [i]) - return false; - } - compare = true; - } - return compare; - } - - private SymmetricAlgorithm GetSymmetricAlgorithm (string algorithmOid, byte[] salt, int iterationCount) - { - string algorithm = null; - int keyLength = 8; // 64 bits (default) - int ivLength = 8; // 64 bits (default) - - PKCS12.DeriveBytes pd = new PKCS12.DeriveBytes (); - pd.Password = _password; - pd.Salt = salt; - pd.IterationCount = iterationCount; - - switch (algorithmOid) { - case PKCS5.pbeWithMD2AndDESCBC: // no unit test available - pd.HashName = "MD2"; - algorithm = "DES"; - break; - case PKCS5.pbeWithMD5AndDESCBC: // no unit test available - pd.HashName = "MD5"; - algorithm = "DES"; - break; - case PKCS5.pbeWithMD2AndRC2CBC: // no unit test available - // TODO - RC2-CBC-Parameter (PKCS5) - // if missing default to 32 bits !!! - pd.HashName = "MD2"; - algorithm = "RC2"; - keyLength = 4; // default - break; - case PKCS5.pbeWithMD5AndRC2CBC: // no unit test available - // TODO - RC2-CBC-Parameter (PKCS5) - // if missing default to 32 bits !!! - pd.HashName = "MD5"; - algorithm = "RC2"; - keyLength = 4; // default - break; - case PKCS5.pbeWithSHA1AndDESCBC: // no unit test available - pd.HashName = "SHA1"; - algorithm = "DES"; - break; - case PKCS5.pbeWithSHA1AndRC2CBC: // no unit test available - // TODO - RC2-CBC-Parameter (PKCS5) - // if missing default to 32 bits !!! - pd.HashName = "SHA1"; - algorithm = "RC2"; - keyLength = 4; // default - break; - case PKCS12.pbeWithSHAAnd128BitRC4: // no unit test available - pd.HashName = "SHA1"; - algorithm = "RC4"; - keyLength = 16; - ivLength = 0; // N/A - break; - case PKCS12.pbeWithSHAAnd40BitRC4: // no unit test available - pd.HashName = "SHA1"; - algorithm = "RC4"; - keyLength = 5; - ivLength = 0; // N/A - break; - case PKCS12.pbeWithSHAAnd3KeyTripleDESCBC: - pd.HashName = "SHA1"; - algorithm = "TripleDES"; - keyLength = 24; - break; - case PKCS12.pbeWithSHAAnd2KeyTripleDESCBC: // no unit test available - pd.HashName = "SHA1"; - algorithm = "TripleDES"; - keyLength = 16; - break; - case PKCS12.pbeWithSHAAnd128BitRC2CBC: // no unit test available - pd.HashName = "SHA1"; - algorithm = "RC2"; - keyLength = 16; - break; - case PKCS12.pbeWithSHAAnd40BitRC2CBC: - pd.HashName = "SHA1"; - algorithm = "RC2"; - keyLength = 5; - break; - default: - throw new NotSupportedException ("unknown oid " + algorithm); - } - - SymmetricAlgorithm sa = null; - sa = SymmetricAlgorithm.Create(algorithm); - sa.Key = pd.DeriveKey (keyLength); - // IV required only for block ciphers (not stream ciphers) - if (ivLength > 0) { - sa.IV = pd.DeriveIV (ivLength); - sa.Mode = CipherMode.CBC; - } - return sa; - } - - public byte[] Decrypt (string algorithmOid, byte[] salt, int iterationCount, byte[] encryptedData) - { - SymmetricAlgorithm sa = null; - byte[] result = null; - try { - sa = GetSymmetricAlgorithm (algorithmOid, salt, iterationCount); - ICryptoTransform ct = sa.CreateDecryptor (); - result = ct.TransformFinalBlock (encryptedData, 0, encryptedData.Length); - } - finally { - if (sa != null) - sa.Clear (); - } - return result; - } - - public byte[] Decrypt (PKCS7.EncryptedData ed) - { - return Decrypt (ed.EncryptionAlgorithm.ContentType, - ed.EncryptionAlgorithm.Content [0].Value, - ASN1Convert.ToInt32 (ed.EncryptionAlgorithm.Content [1]), - ed.EncryptedContent); - } - - public byte[] Encrypt (string algorithmOid, byte[] salt, int iterationCount, byte[] data) - { - byte[] result = null; - using (SymmetricAlgorithm sa = GetSymmetricAlgorithm (algorithmOid, salt, iterationCount)) { - ICryptoTransform ct = sa.CreateEncryptor (); - result = ct.TransformFinalBlock (data, 0, data.Length); - } - return result; - } - - private DSAParameters GetExistingParameters (out bool found) - { - foreach (X509Certificate cert in Certificates) { - // FIXME: that won't work if parts of the parameters are missing - if (cert.KeyAlgorithmParameters != null) { - DSA dsa = cert.DSA; - if (dsa != null) { - found = true; - return dsa.ExportParameters (false); - } - } - } - found = false; - return new DSAParameters (); - } - - private void AddPrivateKey (PKCS8.PrivateKeyInfo pki) - { - byte[] privateKey = pki.PrivateKey; - switch (privateKey [0]) { - case 0x02: - bool found; - DSAParameters p = GetExistingParameters (out found); - if (found) { - _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p)); - } - break; - case 0x30: - _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeRSA (privateKey)); - break; - default: - Array.Clear (privateKey, 0, privateKey.Length); - throw new CryptographicException ("Unknown private key format"); - } - Array.Clear (privateKey, 0, privateKey.Length); - } - - private void ReadSafeBag (ASN1 safeBag) - { - if (safeBag.Tag != 0x30) - throw new ArgumentException ("invalid safeBag"); - - ASN1 bagId = safeBag [0]; - if (bagId.Tag != 0x06) - throw new ArgumentException ("invalid safeBag id"); - - ASN1 bagValue = safeBag [1]; - string oid = ASN1Convert.ToOid (bagId); - switch (oid) { - case keyBag: - // NEED UNIT TEST - AddPrivateKey (new PKCS8.PrivateKeyInfo (bagValue.Value)); - break; - case pkcs8ShroudedKeyBag: - PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value); - byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData); - AddPrivateKey (new PKCS8.PrivateKeyInfo (decrypted)); - Array.Clear (decrypted, 0, decrypted.Length); - break; - case certBag: - PKCS7.ContentInfo cert = new PKCS7.ContentInfo (bagValue.Value); - if (cert.ContentType != x509Certificate) - throw new NotSupportedException ("unsupport certificate type"); - X509Certificate x509 = new X509Certificate (cert.Content [0].Value); - _certs.Add (x509); - break; - case crlBag: - // TODO - break; - case secretBag: - byte[] secret = bagValue.Value; - _secretBags.Add(secret); - break; - case safeContentsBag: - // TODO - ? recurse ? - break; - default: - throw new ArgumentException ("unknown safeBag oid"); - } - - if (safeBag.Count > 2) { - ASN1 bagAttributes = safeBag [2]; - if (bagAttributes.Tag != 0x31) - throw new ArgumentException ("invalid safeBag attributes id"); - - for (int i = 0; i < bagAttributes.Count; i++) { - ASN1 pkcs12Attribute = bagAttributes[i]; - - if (pkcs12Attribute.Tag != 0x30) - throw new ArgumentException ("invalid PKCS12 attributes id"); - - ASN1 attrId = pkcs12Attribute [0]; - if (attrId.Tag != 0x06) - throw new ArgumentException ("invalid attribute id"); - - string attrOid = ASN1Convert.ToOid (attrId); - - ASN1 attrValues = pkcs12Attribute[1]; - for (int j = 0; j < attrValues.Count; j++) { - ASN1 attrValue = attrValues[j]; - - switch (attrOid) { - case PKCS9.friendlyName: - if (attrValue.Tag != 0x1e) - throw new ArgumentException ("invalid attribute value id"); - break; - case PKCS9.localKeyId: - if (attrValue.Tag != 0x04) - throw new ArgumentException ("invalid attribute value id"); - break; - default: - // Unknown OID -- don't check Tag - break; - } - } - } - } - - _safeBags.Add (new SafeBag(oid, safeBag)); - } - - private ASN1 Pkcs8ShroudedKeyBagSafeBag (AsymmetricAlgorithm aa, IDictionary attributes) - { - PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (); - if (aa is RSA) { - pki.Algorithm = "1.2.840.113549.1.1.1"; - pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((RSA)aa); - } - else if (aa is DSA) { - pki.Algorithm = null; - pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((DSA)aa); - } - else - throw new CryptographicException ("Unknown asymmetric algorithm {0}", aa.ToString ()); - - PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (); - epki.Algorithm = pbeWithSHAAnd3KeyTripleDESCBC; - epki.IterationCount = _iterations; - epki.EncryptedData = Encrypt (pbeWithSHAAnd3KeyTripleDESCBC, epki.Salt, _iterations, pki.GetBytes ()); - - ASN1 safeBag = new ASN1 (0x30); - safeBag.Add (ASN1Convert.FromOid (pkcs8ShroudedKeyBag)); - ASN1 bagValue = new ASN1 (0xA0); - bagValue.Add (new ASN1 (epki.GetBytes ())); - safeBag.Add (bagValue); - - if (attributes != null) { - ASN1 bagAttributes = new ASN1 (0x31); - IDictionaryEnumerator de = attributes.GetEnumerator (); - - while (de.MoveNext ()) { - string oid = (string)de.Key; - switch (oid) { - case PKCS9.friendlyName: - ArrayList names = (ArrayList)de.Value; - if (names.Count > 0) { - ASN1 pkcs12Attribute = new ASN1 (0x30); - pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName)); - ASN1 attrValues = new ASN1 (0x31); - foreach (byte[] name in names) { - ASN1 attrValue = new ASN1 (0x1e); - attrValue.Value = name; - attrValues.Add (attrValue); - } - pkcs12Attribute.Add (attrValues); - bagAttributes.Add (pkcs12Attribute); - } - break; - case PKCS9.localKeyId: - ArrayList keys = (ArrayList)de.Value; - if (keys.Count > 0) { - ASN1 pkcs12Attribute = new ASN1 (0x30); - pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId)); - ASN1 attrValues = new ASN1 (0x31); - foreach (byte[] key in keys) { - ASN1 attrValue = new ASN1 (0x04); - attrValue.Value = key; - attrValues.Add (attrValue); - } - pkcs12Attribute.Add (attrValues); - bagAttributes.Add (pkcs12Attribute); - } - break; - default: - break; - } - } - - if (bagAttributes.Count > 0) { - safeBag.Add (bagAttributes); - } - } - - return safeBag; - } - - private ASN1 KeyBagSafeBag (AsymmetricAlgorithm aa, IDictionary attributes) - { - PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (); - if (aa is RSA) { - pki.Algorithm = "1.2.840.113549.1.1.1"; - pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((RSA)aa); - } - else if (aa is DSA) { - pki.Algorithm = null; - pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((DSA)aa); - } - else - throw new CryptographicException ("Unknown asymmetric algorithm {0}", aa.ToString ()); - - ASN1 safeBag = new ASN1 (0x30); - safeBag.Add (ASN1Convert.FromOid (keyBag)); - ASN1 bagValue = new ASN1 (0xA0); - bagValue.Add (new ASN1 (pki.GetBytes ())); - safeBag.Add (bagValue); - - if (attributes != null) { - ASN1 bagAttributes = new ASN1 (0x31); - IDictionaryEnumerator de = attributes.GetEnumerator (); - - while (de.MoveNext ()) { - string oid = (string)de.Key; - switch (oid) { - case PKCS9.friendlyName: - ArrayList names = (ArrayList)de.Value; - if (names.Count > 0) { - ASN1 pkcs12Attribute = new ASN1 (0x30); - pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName)); - ASN1 attrValues = new ASN1 (0x31); - foreach (byte[] name in names) { - ASN1 attrValue = new ASN1 (0x1e); - attrValue.Value = name; - attrValues.Add (attrValue); - } - pkcs12Attribute.Add (attrValues); - bagAttributes.Add (pkcs12Attribute); - } - break; - case PKCS9.localKeyId: - ArrayList keys = (ArrayList)de.Value; - if (keys.Count > 0) { - ASN1 pkcs12Attribute = new ASN1 (0x30); - pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId)); - ASN1 attrValues = new ASN1 (0x31); - foreach (byte[] key in keys) { - ASN1 attrValue = new ASN1 (0x04); - attrValue.Value = key; - attrValues.Add (attrValue); - } - pkcs12Attribute.Add (attrValues); - bagAttributes.Add (pkcs12Attribute); - } - break; - default: - break; - } - } - - if (bagAttributes.Count > 0) { - safeBag.Add (bagAttributes); - } - } - - return safeBag; - } - - private ASN1 SecretBagSafeBag (byte[] secret, IDictionary attributes) - { - ASN1 safeBag = new ASN1 (0x30); - safeBag.Add (ASN1Convert.FromOid (secretBag)); - ASN1 bagValue = new ASN1 (0x80, secret); - safeBag.Add (bagValue); - - if (attributes != null) { - ASN1 bagAttributes = new ASN1 (0x31); - IDictionaryEnumerator de = attributes.GetEnumerator (); - - while (de.MoveNext ()) { - string oid = (string)de.Key; - switch (oid) { - case PKCS9.friendlyName: - ArrayList names = (ArrayList)de.Value; - if (names.Count > 0) { - ASN1 pkcs12Attribute = new ASN1 (0x30); - pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName)); - ASN1 attrValues = new ASN1 (0x31); - foreach (byte[] name in names) { - ASN1 attrValue = new ASN1 (0x1e); - attrValue.Value = name; - attrValues.Add (attrValue); - } - pkcs12Attribute.Add (attrValues); - bagAttributes.Add (pkcs12Attribute); - } - break; - case PKCS9.localKeyId: - ArrayList keys = (ArrayList)de.Value; - if (keys.Count > 0) { - ASN1 pkcs12Attribute = new ASN1 (0x30); - pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId)); - ASN1 attrValues = new ASN1 (0x31); - foreach (byte[] key in keys) { - ASN1 attrValue = new ASN1 (0x04); - attrValue.Value = key; - attrValues.Add (attrValue); - } - pkcs12Attribute.Add (attrValues); - bagAttributes.Add (pkcs12Attribute); - } - break; - default: - break; - } - } - - if (bagAttributes.Count > 0) { - safeBag.Add (bagAttributes); - } - } - - return safeBag; - } - - private ASN1 CertificateSafeBag (X509Certificate x509, IDictionary attributes) - { - ASN1 encapsulatedCertificate = new ASN1 (0x04, x509.RawData); - - PKCS7.ContentInfo ci = new PKCS7.ContentInfo (); - ci.ContentType = x509Certificate; - ci.Content.Add (encapsulatedCertificate); - - ASN1 bagValue = new ASN1 (0xA0); - bagValue.Add (ci.ASN1); - - ASN1 safeBag = new ASN1 (0x30); - safeBag.Add (ASN1Convert.FromOid (certBag)); - safeBag.Add (bagValue); - - if (attributes != null) { - ASN1 bagAttributes = new ASN1 (0x31); - IDictionaryEnumerator de = attributes.GetEnumerator (); - - while (de.MoveNext ()) { - string oid = (string)de.Key; - switch (oid) { - case PKCS9.friendlyName: - ArrayList names = (ArrayList)de.Value; - if (names.Count > 0) { - ASN1 pkcs12Attribute = new ASN1 (0x30); - pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName)); - ASN1 attrValues = new ASN1 (0x31); - foreach (byte[] name in names) { - ASN1 attrValue = new ASN1 (0x1e); - attrValue.Value = name; - attrValues.Add (attrValue); - } - pkcs12Attribute.Add (attrValues); - bagAttributes.Add (pkcs12Attribute); - } - break; - case PKCS9.localKeyId: - ArrayList keys = (ArrayList)de.Value; - if (keys.Count > 0) { - ASN1 pkcs12Attribute = new ASN1 (0x30); - pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId)); - ASN1 attrValues = new ASN1 (0x31); - foreach (byte[] key in keys) { - ASN1 attrValue = new ASN1 (0x04); - attrValue.Value = key; - attrValues.Add (attrValue); - } - pkcs12Attribute.Add (attrValues); - bagAttributes.Add (pkcs12Attribute); - } - break; - default: - break; - } - } - - if (bagAttributes.Count > 0) { - safeBag.Add (bagAttributes); - } - } - - return safeBag; - } - - private byte[] MAC (byte[] password, byte[] salt, int iterations, byte[] data) - { - PKCS12.DeriveBytes pd = new PKCS12.DeriveBytes (); - pd.HashName = "SHA1"; - pd.Password = password; - pd.Salt = salt; - pd.IterationCount = iterations; - - HMACSHA1 hmac = (HMACSHA1) HMACSHA1.Create (); - hmac.Key = pd.DeriveMAC (20); - return hmac.ComputeHash (data, 0, data.Length); - } - - /* - * SafeContents ::= SEQUENCE OF SafeBag - * - * SafeBag ::= SEQUENCE { - * bagId BAG-TYPE.&id ({PKCS12BagSet}), - * bagValue [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}), - * bagAttributes SET OF PKCS12Attribute OPTIONAL - * } - */ - public byte[] GetBytes () - { - // TODO (incomplete) - ASN1 safeBagSequence = new ASN1 (0x30); - - // Sync Safe Bag list since X509CertificateCollection may be updated - ArrayList scs = new ArrayList (); - foreach (SafeBag sb in _safeBags) { - if (sb.BagOID.Equals (certBag)) { - ASN1 safeBag = sb.ASN1; - ASN1 bagValue = safeBag [1]; - PKCS7.ContentInfo cert = new PKCS7.ContentInfo (bagValue.Value); - scs.Add (new X509Certificate (cert.Content [0].Value)); - } - } - - ArrayList addcerts = new ArrayList (); - ArrayList removecerts = new ArrayList (); - - foreach (X509Certificate c in Certificates) { - bool found = false; - - foreach (X509Certificate lc in scs) { - if (Compare (c.RawData, lc.RawData)) { - found = true; - } - } - - if (!found) { - addcerts.Add (c); - } - } - foreach (X509Certificate c in scs) { - bool found = false; - - foreach (X509Certificate lc in Certificates) { - if (Compare (c.RawData, lc.RawData)) { - found = true; - } - } - - if (!found) { - removecerts.Add (c); - } - } - - foreach (X509Certificate c in removecerts) { - RemoveCertificate (c); - } - - foreach (X509Certificate c in addcerts) { - AddCertificate (c); - } - // Sync done - - if (_safeBags.Count > 0) { - ASN1 certsSafeBag = new ASN1 (0x30); - foreach (SafeBag sb in _safeBags) { - if (sb.BagOID.Equals (certBag)) { - certsSafeBag.Add (sb.ASN1); - } - } - - if (certsSafeBag.Count > 0) { - PKCS7.ContentInfo contentInfo = EncryptedContentInfo (certsSafeBag, pbeWithSHAAnd3KeyTripleDESCBC); - safeBagSequence.Add (contentInfo.ASN1); - } - } - - if (_safeBags.Count > 0) { - ASN1 safeContents = new ASN1 (0x30); - foreach (SafeBag sb in _safeBags) { - if (sb.BagOID.Equals (keyBag) || - sb.BagOID.Equals (pkcs8ShroudedKeyBag)) { - safeContents.Add (sb.ASN1); - } - } - if (safeContents.Count > 0) { - ASN1 content = new ASN1 (0xA0); - content.Add (new ASN1 (0x04, safeContents.GetBytes ())); - - PKCS7.ContentInfo keyBag = new PKCS7.ContentInfo (PKCS7.Oid.data); - keyBag.Content = content; - safeBagSequence.Add (keyBag.ASN1); - } - } - - // Doing SecretBags separately in case we want to change their encryption independently. - if (_safeBags.Count > 0) { - ASN1 secretsSafeBag = new ASN1 (0x30); - foreach (SafeBag sb in _safeBags) { - if (sb.BagOID.Equals (secretBag)) { - secretsSafeBag.Add (sb.ASN1); - } - } - - if (secretsSafeBag.Count > 0) { - PKCS7.ContentInfo contentInfo = EncryptedContentInfo (secretsSafeBag, pbeWithSHAAnd3KeyTripleDESCBC); - safeBagSequence.Add (contentInfo.ASN1); - } - } - - - ASN1 encapsulates = new ASN1 (0x04, safeBagSequence.GetBytes ()); - ASN1 ci = new ASN1 (0xA0); - ci.Add (encapsulates); - PKCS7.ContentInfo authSafe = new PKCS7.ContentInfo (PKCS7.Oid.data); - authSafe.Content = ci; - - ASN1 macData = new ASN1 (0x30); - if (_password != null) { - // only for password based encryption - byte[] salt = new byte [20]; - RNG.GetBytes (salt); - byte[] macValue = MAC (_password, salt, _iterations, authSafe.Content [0].Value); - ASN1 oidSeq = new ASN1 (0x30); - oidSeq.Add (ASN1Convert.FromOid ("1.3.14.3.2.26")); // SHA1 - oidSeq.Add (new ASN1 (0x05)); - - ASN1 mac = new ASN1 (0x30); - mac.Add (oidSeq); - mac.Add (new ASN1 (0x04, macValue)); - - macData.Add (mac); - macData.Add (new ASN1 (0x04, salt)); - macData.Add (ASN1Convert.FromInt32 (_iterations)); - } - - ASN1 version = new ASN1 (0x02, new byte [1] { 0x03 }); - - ASN1 pfx = new ASN1 (0x30); - pfx.Add (version); - pfx.Add (authSafe.ASN1); - if (macData.Count > 0) { - // only for password based encryption - pfx.Add (macData); - } - - return pfx.GetBytes (); - } - - // Creates an encrypted PKCS#7 ContentInfo with safeBags as its SafeContents. Used in GetBytes(), above. - private PKCS7.ContentInfo EncryptedContentInfo(ASN1 safeBags, string algorithmOid) - { - byte[] salt = new byte [8]; - RNG.GetBytes (salt); - - ASN1 seqParams = new ASN1 (0x30); - seqParams.Add (new ASN1 (0x04, salt)); - seqParams.Add (ASN1Convert.FromInt32 (_iterations)); - - ASN1 seqPbe = new ASN1 (0x30); - seqPbe.Add (ASN1Convert.FromOid (algorithmOid)); - seqPbe.Add (seqParams); - - byte[] encrypted = Encrypt (algorithmOid, salt, _iterations, safeBags.GetBytes ()); - ASN1 encryptedContent = new ASN1 (0x80, encrypted); - - ASN1 seq = new ASN1 (0x30); - seq.Add (ASN1Convert.FromOid (PKCS7.Oid.data)); - seq.Add (seqPbe); - seq.Add (encryptedContent); - - ASN1 version = new ASN1 (0x02, new byte [1] { 0x00 }); - ASN1 encData = new ASN1 (0x30); - encData.Add (version); - encData.Add (seq); - - ASN1 finalContent = new ASN1 (0xA0); - finalContent.Add (encData); - - PKCS7.ContentInfo bag = new PKCS7.ContentInfo (PKCS7.Oid.encryptedData); - bag.Content = finalContent; - return bag; - } - - public void AddCertificate (X509Certificate cert) - { - AddCertificate (cert, null); - } - - public void AddCertificate (X509Certificate cert, IDictionary attributes) - { - bool found = false; - - for (int i = 0; !found && i < _safeBags.Count; i++) { - SafeBag sb = (SafeBag)_safeBags [i]; - - if (sb.BagOID.Equals (certBag)) { - ASN1 safeBag = sb.ASN1; - ASN1 bagValue = safeBag [1]; - PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value); - X509Certificate c = new X509Certificate (crt.Content [0].Value); - if (Compare (cert.RawData, c.RawData)) { - found = true; - } - } - } - - if (!found) { - _safeBags.Add (new SafeBag (certBag, CertificateSafeBag (cert, attributes))); - _certsChanged = true; - } - } - - public void RemoveCertificate (X509Certificate cert) - { - RemoveCertificate (cert, null); - } - - public void RemoveCertificate (X509Certificate cert, IDictionary attrs) - { - int certIndex = -1; - - for (int i = 0; certIndex == -1 && i < _safeBags.Count; i++) { - SafeBag sb = (SafeBag)_safeBags [i]; - - if (sb.BagOID.Equals (certBag)) { - ASN1 safeBag = sb.ASN1; - ASN1 bagValue = safeBag [1]; - PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value); - X509Certificate c = new X509Certificate (crt.Content [0].Value); - if (Compare (cert.RawData, c.RawData)) { - if (attrs != null) { - if (safeBag.Count == 3) { - ASN1 bagAttributes = safeBag [2]; - int bagAttributesFound = 0; - for (int j = 0; j < bagAttributes.Count; j++) { - ASN1 pkcs12Attribute = bagAttributes [j]; - ASN1 attrId = pkcs12Attribute [0]; - string ao = ASN1Convert.ToOid (attrId); - ArrayList dattrValues = (ArrayList)attrs [ao]; - - if (dattrValues != null) { - ASN1 attrValues = pkcs12Attribute [1]; - - if (dattrValues.Count == attrValues.Count) { - int attrValuesFound = 0; - for (int k = 0; k < attrValues.Count; k++) { - ASN1 attrValue = attrValues [k]; - byte[] value = (byte[])dattrValues [k]; - - if (Compare (value, attrValue.Value)) { - attrValuesFound += 1; - } - } - if (attrValuesFound == attrValues.Count) { - bagAttributesFound += 1; - } - } - } - } - if (bagAttributesFound == bagAttributes.Count) { - certIndex = i; - } - } - } else { - certIndex = i; - } - } - } - } - - if (certIndex != -1) { - _safeBags.RemoveAt (certIndex); - _certsChanged = true; - } - } - - private bool CompareAsymmetricAlgorithm (AsymmetricAlgorithm a1, AsymmetricAlgorithm a2) - { - // fast path - if (a1.KeySize != a2.KeySize) - return false; - // compare public keys - if they match we can assume the private match too - return (a1.ToXmlString (false) == a2.ToXmlString (false)); - } - - public void AddPkcs8ShroudedKeyBag (AsymmetricAlgorithm aa) - { - AddPkcs8ShroudedKeyBag (aa, null); - } - - public void AddPkcs8ShroudedKeyBag (AsymmetricAlgorithm aa, IDictionary attributes) - { - bool found = false; - - for (int i = 0; !found && i < _safeBags.Count; i++) { - SafeBag sb = (SafeBag)_safeBags [i]; - - if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) { - ASN1 bagValue = sb.ASN1 [1]; - PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value); - byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData); - PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted); - byte[] privateKey = pki.PrivateKey; - - AsymmetricAlgorithm saa = null; - switch (privateKey [0]) { - case 0x02: - DSAParameters p = new DSAParameters (); // FIXME - saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p); - break; - case 0x30: - saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey); - break; - default: - Array.Clear (decrypted, 0, decrypted.Length); - Array.Clear (privateKey, 0, privateKey.Length); - throw new CryptographicException ("Unknown private key format"); - } - - Array.Clear (decrypted, 0, decrypted.Length); - Array.Clear (privateKey, 0, privateKey.Length); - - if (CompareAsymmetricAlgorithm (aa , saa)) { - found = true; - } - } - } - - if (!found) { - _safeBags.Add (new SafeBag (pkcs8ShroudedKeyBag, Pkcs8ShroudedKeyBagSafeBag (aa, attributes))); - _keyBagsChanged = true; - } - } - - public void RemovePkcs8ShroudedKeyBag (AsymmetricAlgorithm aa) - { - int aaIndex = -1; - - for (int i = 0; aaIndex == -1 && i < _safeBags.Count; i++) { - SafeBag sb = (SafeBag)_safeBags [i]; - - if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) { - ASN1 bagValue = sb.ASN1 [1]; - PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value); - byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData); - PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted); - byte[] privateKey = pki.PrivateKey; - - AsymmetricAlgorithm saa = null; - switch (privateKey [0]) { - case 0x02: - DSAParameters p = new DSAParameters (); // FIXME - saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p); - break; - case 0x30: - saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey); - break; - default: - Array.Clear (decrypted, 0, decrypted.Length); - Array.Clear (privateKey, 0, privateKey.Length); - throw new CryptographicException ("Unknown private key format"); - } - - Array.Clear (decrypted, 0, decrypted.Length); - Array.Clear (privateKey, 0, privateKey.Length); - - if (CompareAsymmetricAlgorithm (aa, saa)) { - aaIndex = i; - } - } - } - - if (aaIndex != -1) { - _safeBags.RemoveAt (aaIndex); - _keyBagsChanged = true; - } - } - - public void AddKeyBag (AsymmetricAlgorithm aa) - { - AddKeyBag (aa, null); - } - - public void AddKeyBag (AsymmetricAlgorithm aa, IDictionary attributes) - { - bool found = false; - - for (int i = 0; !found && i < _safeBags.Count; i++) { - SafeBag sb = (SafeBag)_safeBags [i]; - - if (sb.BagOID.Equals (keyBag)) { - ASN1 bagValue = sb.ASN1 [1]; - PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value); - byte[] privateKey = pki.PrivateKey; - - AsymmetricAlgorithm saa = null; - switch (privateKey [0]) { - case 0x02: - DSAParameters p = new DSAParameters (); // FIXME - saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p); - break; - case 0x30: - saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey); - break; - default: - Array.Clear (privateKey, 0, privateKey.Length); - throw new CryptographicException ("Unknown private key format"); - } - - Array.Clear (privateKey, 0, privateKey.Length); - - if (CompareAsymmetricAlgorithm (aa, saa)) { - found = true; - } - } - } - - if (!found) { - _safeBags.Add (new SafeBag (keyBag, KeyBagSafeBag (aa, attributes))); - _keyBagsChanged = true; - } - } - - public void RemoveKeyBag (AsymmetricAlgorithm aa) - { - int aaIndex = -1; - - for (int i = 0; aaIndex == -1 && i < _safeBags.Count; i++) { - SafeBag sb = (SafeBag)_safeBags [i]; - - if (sb.BagOID.Equals (keyBag)) { - ASN1 bagValue = sb.ASN1 [1]; - PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value); - byte[] privateKey = pki.PrivateKey; - - AsymmetricAlgorithm saa = null; - switch (privateKey [0]) { - case 0x02: - DSAParameters p = new DSAParameters (); // FIXME - saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p); - break; - case 0x30: - saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey); - break; - default: - Array.Clear (privateKey, 0, privateKey.Length); - throw new CryptographicException ("Unknown private key format"); - } - - Array.Clear (privateKey, 0, privateKey.Length); - - if (CompareAsymmetricAlgorithm (aa, saa)) { - aaIndex = i; - } - } - } - - if (aaIndex != -1) { - _safeBags.RemoveAt (aaIndex); - _keyBagsChanged = true; - } - } - - public void AddSecretBag (byte[] secret) - { - AddSecretBag (secret, null); - } - - public void AddSecretBag (byte[] secret, IDictionary attributes) - { - bool found = false; - - for (int i = 0; !found && i < _safeBags.Count; i++) { - SafeBag sb = (SafeBag)_safeBags [i]; - - if (sb.BagOID.Equals (secretBag)) { - ASN1 bagValue = sb.ASN1 [1]; - byte[] ssecret = bagValue.Value; - - if (Compare (secret, ssecret)) { - found = true; - } - } - } - - if (!found) { - _safeBags.Add (new SafeBag (secretBag, SecretBagSafeBag (secret, attributes))); - _secretBagsChanged = true; - } - } - - public void RemoveSecretBag (byte[] secret) - { - int sIndex = -1; - - for (int i = 0; sIndex == -1 && i < _safeBags.Count; i++) { - SafeBag sb = (SafeBag)_safeBags [i]; - - if (sb.BagOID.Equals (secretBag)) { - ASN1 bagValue = sb.ASN1 [1]; - byte[] ssecret = bagValue.Value; - - if (Compare (secret, ssecret)) { - sIndex = i; - } - } - } - - if (sIndex != -1) { - _safeBags.RemoveAt (sIndex); - _secretBagsChanged = true; - } - } - - public AsymmetricAlgorithm GetAsymmetricAlgorithm (IDictionary attrs) - { - foreach (SafeBag sb in _safeBags) { - if (sb.BagOID.Equals (keyBag) || sb.BagOID.Equals (pkcs8ShroudedKeyBag)) { - ASN1 safeBag = sb.ASN1; - - if (safeBag.Count == 3) { - ASN1 bagAttributes = safeBag [2]; - - int bagAttributesFound = 0; - for (int i = 0; i < bagAttributes.Count; i++) { - ASN1 pkcs12Attribute = bagAttributes [i]; - ASN1 attrId = pkcs12Attribute [0]; - string ao = ASN1Convert.ToOid (attrId); - ArrayList dattrValues = (ArrayList)attrs [ao]; - - if (dattrValues != null) { - ASN1 attrValues = pkcs12Attribute [1]; - - if (dattrValues.Count == attrValues.Count) { - int attrValuesFound = 0; - for (int j = 0; j < attrValues.Count; j++) { - ASN1 attrValue = attrValues [j]; - byte[] value = (byte[])dattrValues [j]; - - if (Compare (value, attrValue.Value)) { - attrValuesFound += 1; - } - } - if (attrValuesFound == attrValues.Count) { - bagAttributesFound += 1; - } - } - } - } - if (bagAttributesFound == bagAttributes.Count) { - ASN1 bagValue = safeBag [1]; - AsymmetricAlgorithm aa = null; - if (sb.BagOID.Equals (keyBag)) { - PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value); - byte[] privateKey = pki.PrivateKey; - switch (privateKey [0]) { - case 0x02: - DSAParameters p = new DSAParameters (); // FIXME - aa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p); - break; - case 0x30: - aa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey); - break; - default: - break; - } - Array.Clear (privateKey, 0, privateKey.Length); - } else if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) { - PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value); - byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData); - PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted); - byte[] privateKey = pki.PrivateKey; - switch (privateKey [0]) { - case 0x02: - DSAParameters p = new DSAParameters (); // FIXME - aa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p); - break; - case 0x30: - aa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey); - break; - default: - break; - } - Array.Clear (privateKey, 0, privateKey.Length); - Array.Clear (decrypted, 0, decrypted.Length); - } - return aa; - } - } - } - } - - return null; - } - - public byte[] GetSecret (IDictionary attrs) - { - foreach (SafeBag sb in _safeBags) { - if (sb.BagOID.Equals (secretBag)) { - ASN1 safeBag = sb.ASN1; - - if (safeBag.Count == 3) { - ASN1 bagAttributes = safeBag [2]; - - int bagAttributesFound = 0; - for (int i = 0; i < bagAttributes.Count; i++) { - ASN1 pkcs12Attribute = bagAttributes [i]; - ASN1 attrId = pkcs12Attribute [0]; - string ao = ASN1Convert.ToOid (attrId); - ArrayList dattrValues = (ArrayList)attrs [ao]; - - if (dattrValues != null) { - ASN1 attrValues = pkcs12Attribute [1]; - - if (dattrValues.Count == attrValues.Count) { - int attrValuesFound = 0; - for (int j = 0; j < attrValues.Count; j++) { - ASN1 attrValue = attrValues [j]; - byte[] value = (byte[])dattrValues [j]; - - if (Compare (value, attrValue.Value)) { - attrValuesFound += 1; - } - } - if (attrValuesFound == attrValues.Count) { - bagAttributesFound += 1; - } - } - } - } - if (bagAttributesFound == bagAttributes.Count) { - ASN1 bagValue = safeBag [1]; - return bagValue.Value; - } - } - } - } - - return null; - } - - public X509Certificate GetCertificate (IDictionary attrs) - { - foreach (SafeBag sb in _safeBags) { - if (sb.BagOID.Equals (certBag)) { - ASN1 safeBag = sb.ASN1; - - if (safeBag.Count == 3) { - ASN1 bagAttributes = safeBag [2]; - - int bagAttributesFound = 0; - for (int i = 0; i < bagAttributes.Count; i++) { - ASN1 pkcs12Attribute = bagAttributes [i]; - ASN1 attrId = pkcs12Attribute [0]; - string ao = ASN1Convert.ToOid (attrId); - ArrayList dattrValues = (ArrayList)attrs [ao]; - - if (dattrValues != null) { - ASN1 attrValues = pkcs12Attribute [1]; - - if (dattrValues.Count == attrValues.Count) { - int attrValuesFound = 0; - for (int j = 0; j < attrValues.Count; j++) { - ASN1 attrValue = attrValues [j]; - byte[] value = (byte[])dattrValues [j]; - - if (Compare (value, attrValue.Value)) { - attrValuesFound += 1; - } - } - if (attrValuesFound == attrValues.Count) { - bagAttributesFound += 1; - } - } - } - } - if (bagAttributesFound == bagAttributes.Count) { - ASN1 bagValue = safeBag [1]; - PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value); - return new X509Certificate (crt.Content [0].Value); - } - } - } - } - - return null; - } - - public IDictionary GetAttributes (AsymmetricAlgorithm aa) - { - IDictionary result = new Hashtable (); - - foreach (SafeBag sb in _safeBags) { - if (sb.BagOID.Equals (keyBag) || sb.BagOID.Equals (pkcs8ShroudedKeyBag)) { - ASN1 safeBag = sb.ASN1; - - ASN1 bagValue = safeBag [1]; - AsymmetricAlgorithm saa = null; - if (sb.BagOID.Equals (keyBag)) { - PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value); - byte[] privateKey = pki.PrivateKey; - switch (privateKey [0]) { - case 0x02: - DSAParameters p = new DSAParameters (); // FIXME - saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p); - break; - case 0x30: - saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey); - break; - default: - break; - } - Array.Clear (privateKey, 0, privateKey.Length); - } else if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) { - PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value); - byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData); - PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted); - byte[] privateKey = pki.PrivateKey; - switch (privateKey [0]) { - case 0x02: - DSAParameters p = new DSAParameters (); // FIXME - saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p); - break; - case 0x30: - saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey); - break; - default: - break; - } - Array.Clear (privateKey, 0, privateKey.Length); - Array.Clear (decrypted, 0, decrypted.Length); - } - - if (saa != null && CompareAsymmetricAlgorithm (saa, aa)) { - if (safeBag.Count == 3) { - ASN1 bagAttributes = safeBag [2]; - - for (int i = 0; i < bagAttributes.Count; i++) { - ASN1 pkcs12Attribute = bagAttributes [i]; - ASN1 attrId = pkcs12Attribute [0]; - string aOid = ASN1Convert.ToOid (attrId); - ArrayList aValues = new ArrayList (); - - ASN1 attrValues = pkcs12Attribute [1]; - - for (int j = 0; j < attrValues.Count; j++) { - ASN1 attrValue = attrValues [j]; - aValues.Add (attrValue.Value); - } - result.Add (aOid, aValues); - } - } - } - } - } - - return result; - } - - public IDictionary GetAttributes (X509Certificate cert) - { - IDictionary result = new Hashtable (); - - foreach (SafeBag sb in _safeBags) { - if (sb.BagOID.Equals (certBag)) { - ASN1 safeBag = sb.ASN1; - ASN1 bagValue = safeBag [1]; - PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value); - X509Certificate xc = new X509Certificate (crt.Content [0].Value); - - if (Compare (cert.RawData, xc.RawData)) { - if (safeBag.Count == 3) { - ASN1 bagAttributes = safeBag [2]; - - for (int i = 0; i < bagAttributes.Count; i++) { - ASN1 pkcs12Attribute = bagAttributes [i]; - ASN1 attrId = pkcs12Attribute [0]; - - string aOid = ASN1Convert.ToOid (attrId); - ArrayList aValues = new ArrayList (); - - ASN1 attrValues = pkcs12Attribute [1]; - - for (int j = 0; j < attrValues.Count; j++) { - ASN1 attrValue = attrValues [j]; - aValues.Add (attrValue.Value); - } - result.Add (aOid, aValues); - } - } - } - } - } - - return result; - } - - public void SaveToFile (string filename) - { - if (filename == null) - throw new ArgumentNullException ("filename"); - - using (FileStream fs = File.Create (filename)) { - byte[] data = GetBytes (); - fs.Write (data, 0, data.Length); - } - } - - public object Clone () - { - PKCS12 clone = null; - if (_password != null) { - clone = new PKCS12 (GetBytes (), Encoding.BigEndianUnicode.GetString (_password)); - } else { - clone = new PKCS12 (GetBytes ()); - } - clone.IterationCount = this.IterationCount; - - return clone; - } - - // static - - public const int CryptoApiPasswordLimit = 32; - - static private int password_max_length = Int32.MaxValue; - - // static properties - - // MS CryptoAPI limits the password to a maximum of 31 characters - // other implementations, like OpenSSL, have no such limitation. - // Setting a maximum value will truncate the password length to - // ensure compatibility with MS's PFXImportCertStore API. - static public int MaximumPasswordLength { - get { return password_max_length; } - set { - if (value < CryptoApiPasswordLimit) { - string msg = string.Format ("Maximum password length cannot be less than {0}.", CryptoApiPasswordLimit); - throw new ArgumentOutOfRangeException (msg); - } - password_max_length = value; - } - } - } -} diff --git a/MediaBrowser.Server.Mono/Security/PKCS7.cs b/MediaBrowser.Server.Mono/Security/PKCS7.cs deleted file mode 100644 index 7a34580e9..000000000 --- a/MediaBrowser.Server.Mono/Security/PKCS7.cs +++ /dev/null @@ -1,1011 +0,0 @@ -// -// PKCS7.cs: PKCS #7 - Cryptographic Message Syntax Standard -// http://www.rsasecurity.com/rsalabs/pkcs/pkcs-7/index.html -// -// Authors: -// Sebastien Pouliot -// Daniel Granath -// -// (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com) -// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com) -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; -using System.Collections; -using System.Security.Cryptography; - -namespace MediaBrowser.Server.Mono.Security { - - public sealed class PKCS7 { - - public class Oid { - // pkcs 1 - public const string rsaEncryption = "1.2.840.113549.1.1.1"; - // pkcs 7 - public const string data = "1.2.840.113549.1.7.1"; - public const string signedData = "1.2.840.113549.1.7.2"; - public const string envelopedData = "1.2.840.113549.1.7.3"; - public const string signedAndEnvelopedData = "1.2.840.113549.1.7.4"; - public const string digestedData = "1.2.840.113549.1.7.5"; - public const string encryptedData = "1.2.840.113549.1.7.6"; - // pkcs 9 - public const string contentType = "1.2.840.113549.1.9.3"; - public const string messageDigest = "1.2.840.113549.1.9.4"; - public const string signingTime = "1.2.840.113549.1.9.5"; - public const string countersignature = "1.2.840.113549.1.9.6"; - - public Oid () - { - } - } - - private PKCS7 () - { - } - - static public ASN1 Attribute (string oid, ASN1 value) - { - ASN1 attr = new ASN1 (0x30); - attr.Add (ASN1Convert.FromOid (oid)); - ASN1 aset = attr.Add (new ASN1 (0x31)); - aset.Add (value); - return attr; - } - - static public ASN1 AlgorithmIdentifier (string oid) - { - ASN1 ai = new ASN1 (0x30); - ai.Add (ASN1Convert.FromOid (oid)); - ai.Add (new ASN1 (0x05)); // NULL - return ai; - } - - static public ASN1 AlgorithmIdentifier (string oid, ASN1 parameters) - { - ASN1 ai = new ASN1 (0x30); - ai.Add (ASN1Convert.FromOid (oid)); - ai.Add (parameters); - return ai; - } - - /* - * IssuerAndSerialNumber ::= SEQUENCE { - * issuer Name, - * serialNumber CertificateSerialNumber - * } - */ - static public ASN1 IssuerAndSerialNumber (X509Certificate x509) - { - ASN1 issuer = null; - ASN1 serial = null; - ASN1 cert = new ASN1 (x509.RawData); - int tbs = 0; - bool flag = false; - while (tbs < cert[0].Count) { - ASN1 e = cert[0][tbs++]; - if (e.Tag == 0x02) - serial = e; - else if (e.Tag == 0x30) { - if (flag) { - issuer = e; - break; - } - flag = true; - } - } - ASN1 iasn = new ASN1 (0x30); - iasn.Add (issuer); - iasn.Add (serial); - return iasn; - } - - /* - * ContentInfo ::= SEQUENCE { - * contentType ContentType, - * content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL - * } - * ContentType ::= OBJECT IDENTIFIER - */ - public class ContentInfo { - - private string contentType; - private ASN1 content; - - public ContentInfo () - { - content = new ASN1 (0xA0); - } - - public ContentInfo (string oid) : this () - { - contentType = oid; - } - - public ContentInfo (byte[] data) - : this (new ASN1 (data)) {} - - public ContentInfo (ASN1 asn1) - { - // SEQUENCE with 1 or 2 elements - if ((asn1.Tag != 0x30) || ((asn1.Count < 1) && (asn1.Count > 2))) - throw new ArgumentException ("Invalid ASN1"); - if (asn1[0].Tag != 0x06) - throw new ArgumentException ("Invalid contentType"); - contentType = ASN1Convert.ToOid (asn1[0]); - if (asn1.Count > 1) { - if (asn1[1].Tag != 0xA0) - throw new ArgumentException ("Invalid content"); - content = asn1[1]; - } - } - - public ASN1 ASN1 { - get { return GetASN1(); } - } - - public ASN1 Content { - get { return content; } - set { content = value; } - } - - public string ContentType { - get { return contentType; } - set { contentType = value; } - } - - internal ASN1 GetASN1 () - { - // ContentInfo ::= SEQUENCE { - ASN1 contentInfo = new ASN1 (0x30); - // contentType ContentType, -> ContentType ::= OBJECT IDENTIFIER - contentInfo.Add (ASN1Convert.FromOid (contentType)); - // content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL - if ((content != null) && (content.Count > 0)) - contentInfo.Add (content); - return contentInfo; - } - - public byte[] GetBytes () - { - return GetASN1 ().GetBytes (); - } - } - - /* - * EncryptedData ::= SEQUENCE { - * version INTEGER {edVer0(0)} (edVer0), - * encryptedContentInfo EncryptedContentInfo - * } - */ - public class EncryptedData { - private byte _version; - private ContentInfo _content; - private ContentInfo _encryptionAlgorithm; - private byte[] _encrypted; - - public EncryptedData () - { - _version = 0; - } - - public EncryptedData (byte[] data) - : this (new ASN1 (data)) - { - } - - public EncryptedData (ASN1 asn1) : this () - { - if ((asn1.Tag != 0x30) || (asn1.Count < 2)) - throw new ArgumentException ("Invalid EncryptedData"); - - if (asn1 [0].Tag != 0x02) - throw new ArgumentException ("Invalid version"); - _version = asn1 [0].Value [0]; - - ASN1 encryptedContentInfo = asn1 [1]; - if (encryptedContentInfo.Tag != 0x30) - throw new ArgumentException ("missing EncryptedContentInfo"); - - ASN1 contentType = encryptedContentInfo [0]; - if (contentType.Tag != 0x06) - throw new ArgumentException ("missing EncryptedContentInfo.ContentType"); - _content = new ContentInfo (ASN1Convert.ToOid (contentType)); - - ASN1 contentEncryptionAlgorithm = encryptedContentInfo [1]; - if (contentEncryptionAlgorithm.Tag != 0x30) - throw new ArgumentException ("missing EncryptedContentInfo.ContentEncryptionAlgorithmIdentifier"); - _encryptionAlgorithm = new ContentInfo (ASN1Convert.ToOid (contentEncryptionAlgorithm [0])); - _encryptionAlgorithm.Content = contentEncryptionAlgorithm [1]; - - ASN1 encryptedContent = encryptedContentInfo [2]; - if (encryptedContent.Tag != 0x80) - throw new ArgumentException ("missing EncryptedContentInfo.EncryptedContent"); - _encrypted = encryptedContent.Value; - } - - public ASN1 ASN1 { - get { return GetASN1(); } - } - - public ContentInfo ContentInfo { - get { return _content; } - } - - public ContentInfo EncryptionAlgorithm { - get { return _encryptionAlgorithm; } - } - - public byte[] EncryptedContent { - get { - if (_encrypted == null) - return null; - return (byte[]) _encrypted.Clone (); - } - } - - public byte Version { - get { return _version; } - set { _version = value; } - } - - // methods - - internal ASN1 GetASN1 () - { - return null; - } - - public byte[] GetBytes () - { - return GetASN1 ().GetBytes (); - } - } - - /* - * EnvelopedData ::= SEQUENCE { - * version Version, - * recipientInfos RecipientInfos, - * encryptedContentInfo EncryptedContentInfo - * } - * - * RecipientInfos ::= SET OF RecipientInfo - * - * EncryptedContentInfo ::= SEQUENCE { - * contentType ContentType, - * contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier, - * encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL - * } - * - * EncryptedContent ::= OCTET STRING - * - */ - public class EnvelopedData { - private byte _version; - private ContentInfo _content; - private ContentInfo _encryptionAlgorithm; - private ArrayList _recipientInfos; - private byte[] _encrypted; - - public EnvelopedData () - { - _version = 0; - _content = new ContentInfo (); - _encryptionAlgorithm = new ContentInfo (); - _recipientInfos = new ArrayList (); - } - - public EnvelopedData (byte[] data) - : this (new ASN1 (data)) - { - } - - public EnvelopedData (ASN1 asn1) : this () - { - if ((asn1[0].Tag != 0x30) || (asn1[0].Count < 3)) - throw new ArgumentException ("Invalid EnvelopedData"); - - if (asn1[0][0].Tag != 0x02) - throw new ArgumentException ("Invalid version"); - _version = asn1[0][0].Value[0]; - - // recipientInfos - - ASN1 recipientInfos = asn1 [0][1]; - if (recipientInfos.Tag != 0x31) - throw new ArgumentException ("missing RecipientInfos"); - for (int i=0; i < recipientInfos.Count; i++) { - ASN1 recipientInfo = recipientInfos [i]; - _recipientInfos.Add (new RecipientInfo (recipientInfo)); - } - - ASN1 encryptedContentInfo = asn1[0][2]; - if (encryptedContentInfo.Tag != 0x30) - throw new ArgumentException ("missing EncryptedContentInfo"); - - ASN1 contentType = encryptedContentInfo [0]; - if (contentType.Tag != 0x06) - throw new ArgumentException ("missing EncryptedContentInfo.ContentType"); - _content = new ContentInfo (ASN1Convert.ToOid (contentType)); - - ASN1 contentEncryptionAlgorithm = encryptedContentInfo [1]; - if (contentEncryptionAlgorithm.Tag != 0x30) - throw new ArgumentException ("missing EncryptedContentInfo.ContentEncryptionAlgorithmIdentifier"); - _encryptionAlgorithm = new ContentInfo (ASN1Convert.ToOid (contentEncryptionAlgorithm [0])); - _encryptionAlgorithm.Content = contentEncryptionAlgorithm [1]; - - ASN1 encryptedContent = encryptedContentInfo [2]; - if (encryptedContent.Tag != 0x80) - throw new ArgumentException ("missing EncryptedContentInfo.EncryptedContent"); - _encrypted = encryptedContent.Value; - } - - public ArrayList RecipientInfos { - get { return _recipientInfos; } - } - - public ASN1 ASN1 { - get { return GetASN1(); } - } - - public ContentInfo ContentInfo { - get { return _content; } - } - - public ContentInfo EncryptionAlgorithm { - get { return _encryptionAlgorithm; } - } - - public byte[] EncryptedContent { - get { - if (_encrypted == null) - return null; - return (byte[]) _encrypted.Clone (); - } - } - - public byte Version { - get { return _version; } - set { _version = value; } - } - - internal ASN1 GetASN1 () - { - // SignedData ::= SEQUENCE { - ASN1 signedData = new ASN1 (0x30); - // version Version -> Version ::= INTEGER -/* byte[] ver = { _version }; - signedData.Add (new ASN1 (0x02, ver)); - // digestAlgorithms DigestAlgorithmIdentifiers -> DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier - ASN1 digestAlgorithms = signedData.Add (new ASN1 (0x31)); - if (hashAlgorithm != null) { - string hashOid = CryptoConfig.MapNameToOid (hashAlgorithm); - digestAlgorithms.Add (AlgorithmIdentifier (hashOid)); - } - - // contentInfo ContentInfo, - ASN1 ci = contentInfo.ASN1; - signedData.Add (ci); - if ((mda == null) && (hashAlgorithm != null)) { - // automatically add the messageDigest authenticated attribute - HashAlgorithm ha = HashAlgorithm.Create (hashAlgorithm); - byte[] idcHash = ha.ComputeHash (ci[1][0].Value); - ASN1 md = new ASN1 (0x30); - mda = Attribute (messageDigest, md.Add (new ASN1 (0x04, idcHash))); - signerInfo.AuthenticatedAttributes.Add (mda); - } - - // certificates [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL, - if (certs.Count > 0) { - ASN1 a0 = signedData.Add (new ASN1 (0xA0)); - foreach (X509Certificate x in certs) - a0.Add (new ASN1 (x.RawData)); - } - // crls [1] IMPLICIT CertificateRevocationLists OPTIONAL, - if (crls.Count > 0) { - ASN1 a1 = signedData.Add (new ASN1 (0xA1)); - foreach (byte[] crl in crls) - a1.Add (new ASN1 (crl)); - } - // signerInfos SignerInfos -> SignerInfos ::= SET OF SignerInfo - ASN1 signerInfos = signedData.Add (new ASN1 (0x31)); - if (signerInfo.Key != null) - signerInfos.Add (signerInfo.ASN1);*/ - return signedData; - } - - public byte[] GetBytes () { - return GetASN1 ().GetBytes (); - } - } - - /* RecipientInfo ::= SEQUENCE { - * version Version, - * issuerAndSerialNumber IssuerAndSerialNumber, - * keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier, - * encryptedKey EncryptedKey - * } - * - * KeyEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier - * - * EncryptedKey ::= OCTET STRING - */ - public class RecipientInfo { - - private int _version; - private string _oid; - private byte[] _key; - private byte[] _ski; - private string _issuer; - private byte[] _serial; - - public RecipientInfo () {} - - public RecipientInfo (ASN1 data) - { - if (data.Tag != 0x30) - throw new ArgumentException ("Invalid RecipientInfo"); - - ASN1 version = data [0]; - if (version.Tag != 0x02) - throw new ArgumentException ("missing Version"); - _version = version.Value [0]; - - // issuerAndSerialNumber IssuerAndSerialNumber - ASN1 subjectIdentifierType = data [1]; - if ((subjectIdentifierType.Tag == 0x80) && (_version == 3)) { - _ski = subjectIdentifierType.Value; - } - else { - _issuer = X501.ToString (subjectIdentifierType [0]); - _serial = subjectIdentifierType [1].Value; - } - - ASN1 keyEncryptionAlgorithm = data [2]; - _oid = ASN1Convert.ToOid (keyEncryptionAlgorithm [0]); - - ASN1 encryptedKey = data [3]; - _key = encryptedKey.Value; - } - - public string Oid { - get { return _oid; } - } - - public byte[] Key { - get { - if (_key == null) - return null; - return (byte[]) _key.Clone (); - } - } - - public byte[] SubjectKeyIdentifier { - get { - if (_ski == null) - return null; - return (byte[]) _ski.Clone (); - } - } - - public string Issuer { - get { return _issuer; } - } - - public byte[] Serial { - get { - if (_serial == null) - return null; - return (byte[]) _serial.Clone (); - } - } - - public int Version { - get { return _version; } - } - } - - /* - * SignedData ::= SEQUENCE { - * version Version, - * digestAlgorithms DigestAlgorithmIdentifiers, - * contentInfo ContentInfo, - * certificates [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL, - * crls [1] IMPLICIT CertificateRevocationLists OPTIONAL, - * signerInfos SignerInfos - * } - */ - public class SignedData { - private byte version; - private string hashAlgorithm; - private ContentInfo contentInfo; - private X509CertificateCollection certs; - private ArrayList crls; - private SignerInfo signerInfo; - private bool mda; - private bool signed; - - public SignedData () - { - version = 1; - contentInfo = new ContentInfo (); - certs = new X509CertificateCollection (); - crls = new ArrayList (); - signerInfo = new SignerInfo (); - mda = true; - signed = false; - } - - public SignedData (byte[] data) - : this (new ASN1 (data)) - { - } - - public SignedData (ASN1 asn1) - { - if ((asn1[0].Tag != 0x30) || (asn1[0].Count < 4)) - throw new ArgumentException ("Invalid SignedData"); - - if (asn1[0][0].Tag != 0x02) - throw new ArgumentException ("Invalid version"); - version = asn1[0][0].Value[0]; - - contentInfo = new ContentInfo (asn1[0][2]); - - int n = 3; - certs = new X509CertificateCollection (); - if (asn1[0][n].Tag == 0xA0) { - for (int i=0; i < asn1[0][n].Count; i++) - certs.Add (new X509Certificate (asn1[0][n][i].GetBytes ())); - n++; - } - - crls = new ArrayList (); - if (asn1[0][n].Tag == 0xA1) { - for (int i=0; i < asn1[0][n].Count; i++) - crls.Add (asn1[0][n][i].GetBytes ()); - n++; - } - - if (asn1[0][n].Count > 0) - signerInfo = new SignerInfo (asn1[0][n]); - else - signerInfo = new SignerInfo (); - - // Exchange hash algorithm Oid from SignerInfo - if (signerInfo.HashName != null) { - HashName = OidToName(signerInfo.HashName); - } - - // Check if SignerInfo has authenticated attributes - mda = (signerInfo.AuthenticatedAttributes.Count > 0); - } - - public ASN1 ASN1 { - get { return GetASN1(); } - } - - public X509CertificateCollection Certificates { - get { return certs; } - } - - public ContentInfo ContentInfo { - get { return contentInfo; } - } - - public ArrayList Crls { - get { return crls; } - } - - public string HashName { - get { return hashAlgorithm; } - // todo add validation - set { - hashAlgorithm = value; - signerInfo.HashName = value; - } - } - - public SignerInfo SignerInfo { - get { return signerInfo; } - } - - public byte Version { - get { return version; } - set { version = value; } - } - - public bool UseAuthenticatedAttributes { - get { return mda; } - set { mda = value; } - } - - public bool VerifySignature (AsymmetricAlgorithm aa) - { - if (aa == null) { - return false; - } - - RSAPKCS1SignatureDeformatter r = new RSAPKCS1SignatureDeformatter (aa); - r.SetHashAlgorithm (hashAlgorithm); - HashAlgorithm ha = HashAlgorithm.Create (hashAlgorithm); - - byte[] signature = signerInfo.Signature; - byte[] hash = null; - - if (mda) { - ASN1 asn = new ASN1 (0x31); - foreach (ASN1 attr in signerInfo.AuthenticatedAttributes) - asn.Add (attr); - - hash = ha.ComputeHash (asn.GetBytes ()); - } else { - hash = ha.ComputeHash (contentInfo.Content[0].Value); - } - - if (hash != null && signature != null) { - return r.VerifySignature (hash, signature); - } - return false; - } - - internal string OidToName (string oid) - { - switch (oid) { - case "1.3.14.3.2.26" : - return "SHA1"; - case "1.2.840.113549.2.2" : - return "MD2"; - case "1.2.840.113549.2.5" : - return "MD5"; - case "2.16.840.1.101.3.4.1" : - return "SHA256"; - case "2.16.840.1.101.3.4.2" : - return "SHA384"; - case "2.16.840.1.101.3.4.3" : - return "SHA512"; - default : - break; - } - // Unknown Oid - return oid; - } - - internal ASN1 GetASN1 () - { - // SignedData ::= SEQUENCE { - ASN1 signedData = new ASN1 (0x30); - // version Version -> Version ::= INTEGER - byte[] ver = { version }; - signedData.Add (new ASN1 (0x02, ver)); - // digestAlgorithms DigestAlgorithmIdentifiers -> DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier - ASN1 digestAlgorithms = signedData.Add (new ASN1 (0x31)); - if (hashAlgorithm != null) { - string hashOid = CryptoConfig.MapNameToOID (hashAlgorithm); - digestAlgorithms.Add (AlgorithmIdentifier (hashOid)); - } - - // contentInfo ContentInfo, - ASN1 ci = contentInfo.ASN1; - signedData.Add (ci); - if (!signed && (hashAlgorithm != null)) { - if (mda) { - // Use authenticated attributes for signature - - // Automatically add the contentType authenticated attribute - ASN1 ctattr = Attribute (Oid.contentType, ci[0]); - signerInfo.AuthenticatedAttributes.Add (ctattr); - - // Automatically add the messageDigest authenticated attribute - HashAlgorithm ha = HashAlgorithm.Create (hashAlgorithm); - byte[] idcHash = ha.ComputeHash (ci[1][0].Value); - ASN1 md = new ASN1 (0x30); - ASN1 mdattr = Attribute (Oid.messageDigest, md.Add (new ASN1 (0x04, idcHash))); - signerInfo.AuthenticatedAttributes.Add (mdattr); - } else { - // Don't use authenticated attributes for signature -- signature is content - RSAPKCS1SignatureFormatter r = new RSAPKCS1SignatureFormatter (signerInfo.Key); - r.SetHashAlgorithm (hashAlgorithm); - HashAlgorithm ha = HashAlgorithm.Create (hashAlgorithm); - byte[] sig = ha.ComputeHash (ci[1][0].Value); - signerInfo.Signature = r.CreateSignature (sig); - } - signed = true; - } - - // certificates [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL, - if (certs.Count > 0) { - ASN1 a0 = signedData.Add (new ASN1 (0xA0)); - foreach (X509Certificate x in certs) - a0.Add (new ASN1 (x.RawData)); - } - // crls [1] IMPLICIT CertificateRevocationLists OPTIONAL, - if (crls.Count > 0) { - ASN1 a1 = signedData.Add (new ASN1 (0xA1)); - foreach (byte[] crl in crls) - a1.Add (new ASN1 (crl)); - } - // signerInfos SignerInfos -> SignerInfos ::= SET OF SignerInfo - ASN1 signerInfos = signedData.Add (new ASN1 (0x31)); - if (signerInfo.Key != null) - signerInfos.Add (signerInfo.ASN1); - return signedData; - } - - public byte[] GetBytes () - { - return GetASN1 ().GetBytes (); - } - } - - /* - * SignerInfo ::= SEQUENCE { - * version Version, - * issuerAndSerialNumber IssuerAndSerialNumber, - * digestAlgorithm DigestAlgorithmIdentifier, - * authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL, - * digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier, - * encryptedDigest EncryptedDigest, - * unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL - * } - * - * For version == 3 issuerAndSerialNumber may be replaced by ... - */ - public class SignerInfo { - - private byte version; - private X509Certificate x509; - private string hashAlgorithm; - private AsymmetricAlgorithm key; - private ArrayList authenticatedAttributes; - private ArrayList unauthenticatedAttributes; - private byte[] signature; - private string issuer; - private byte[] serial; - private byte[] ski; - - public SignerInfo () - { - version = 1; - authenticatedAttributes = new ArrayList (); - unauthenticatedAttributes = new ArrayList (); - } - - public SignerInfo (byte[] data) - : this (new ASN1 (data)) {} - - // TODO: INCOMPLETE - public SignerInfo (ASN1 asn1) : this () - { - if ((asn1[0].Tag != 0x30) || (asn1[0].Count < 5)) - throw new ArgumentException ("Invalid SignedData"); - - // version Version - if (asn1[0][0].Tag != 0x02) - throw new ArgumentException ("Invalid version"); - version = asn1[0][0].Value[0]; - - // issuerAndSerialNumber IssuerAndSerialNumber - ASN1 subjectIdentifierType = asn1 [0][1]; - if ((subjectIdentifierType.Tag == 0x80) && (version == 3)) { - ski = subjectIdentifierType.Value; - } - else { - issuer = X501.ToString (subjectIdentifierType [0]); - serial = subjectIdentifierType [1].Value; - } - - // digestAlgorithm DigestAlgorithmIdentifier - ASN1 digestAlgorithm = asn1 [0][2]; - hashAlgorithm = ASN1Convert.ToOid (digestAlgorithm [0]); - - // authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL - int n = 3; - ASN1 authAttributes = asn1 [0][n]; - if (authAttributes.Tag == 0xA0) { - n++; - for (int i=0; i < authAttributes.Count; i++) - authenticatedAttributes.Add (authAttributes [i]); - } - - // digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier - n++; - // ASN1 digestEncryptionAlgorithm = asn1 [0][n++]; - // string digestEncryptionAlgorithmOid = ASN1Convert.ToOid (digestEncryptionAlgorithm [0]); - - // encryptedDigest EncryptedDigest - ASN1 encryptedDigest = asn1 [0][n++]; - if (encryptedDigest.Tag == 0x04) - signature = encryptedDigest.Value; - - // unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL - ASN1 unauthAttributes = asn1 [0][n]; - if ((unauthAttributes != null) && (unauthAttributes.Tag == 0xA1)) { - for (int i=0; i < unauthAttributes.Count; i++) - unauthenticatedAttributes.Add (unauthAttributes [i]); - } - } - - public string IssuerName { - get { return issuer; } - } - - public byte[] SerialNumber { - get { - if (serial == null) - return null; - return (byte[]) serial.Clone (); - } - } - - public byte[] SubjectKeyIdentifier { - get { - if (ski == null) - return null; - return (byte[]) ski.Clone (); - } - } - - public ASN1 ASN1 { - get { return GetASN1(); } - } - - public ArrayList AuthenticatedAttributes { - get { return authenticatedAttributes; } - } - - public X509Certificate Certificate { - get { return x509; } - set { x509 = value; } - } - - public string HashName { - get { return hashAlgorithm; } - set { hashAlgorithm = value; } - } - - public AsymmetricAlgorithm Key { - get { return key; } - set { key = value; } - } - - public byte[] Signature { - get { - if (signature == null) - return null; - return (byte[]) signature.Clone (); - } - - set { - if (value != null) { - signature = (byte[]) value.Clone (); - } - } - } - - public ArrayList UnauthenticatedAttributes { - get { return unauthenticatedAttributes; } - } - - public byte Version { - get { return version; } - set { version = value; } - } - - internal ASN1 GetASN1 () - { - if ((key == null) || (hashAlgorithm == null)) - return null; - byte[] ver = { version }; - ASN1 signerInfo = new ASN1 (0x30); - // version Version -> Version ::= INTEGER - signerInfo.Add (new ASN1 (0x02, ver)); - // issuerAndSerialNumber IssuerAndSerialNumber, - signerInfo.Add (PKCS7.IssuerAndSerialNumber (x509)); - // digestAlgorithm DigestAlgorithmIdentifier, - string hashOid = CryptoConfig.MapNameToOID (hashAlgorithm); - signerInfo.Add (AlgorithmIdentifier (hashOid)); - // authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL, - ASN1 aa = null; - if (authenticatedAttributes.Count > 0) { - aa = signerInfo.Add (new ASN1 (0xA0)); - authenticatedAttributes.Sort(new SortedSet ()); - foreach (ASN1 attr in authenticatedAttributes) - aa.Add (attr); - } - // digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier, - if (key is RSA) { - signerInfo.Add (AlgorithmIdentifier (PKCS7.Oid.rsaEncryption)); - - if (aa != null) { - // Calculate the signature here; otherwise it must be set from SignedData - RSAPKCS1SignatureFormatter r = new RSAPKCS1SignatureFormatter (key); - r.SetHashAlgorithm (hashAlgorithm); - byte[] tbs = aa.GetBytes (); - tbs [0] = 0x31; // not 0xA0 for signature - HashAlgorithm ha = HashAlgorithm.Create (hashAlgorithm); - byte[] tbsHash = ha.ComputeHash (tbs); - signature = r.CreateSignature (tbsHash); - } - } - else if (key is DSA) { - throw new NotImplementedException ("not yet"); - } - else - throw new CryptographicException ("Unknown assymetric algorithm"); - // encryptedDigest EncryptedDigest, - signerInfo.Add (new ASN1 (0x04, signature)); - // unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL - if (unauthenticatedAttributes.Count > 0) { - ASN1 ua = signerInfo.Add (new ASN1 (0xA1)); - unauthenticatedAttributes.Sort(new SortedSet ()); - foreach (ASN1 attr in unauthenticatedAttributes) - ua.Add (attr); - } - return signerInfo; - } - - public byte[] GetBytes () - { - return GetASN1 ().GetBytes (); - } - } - - internal class SortedSet : IComparer { - - public int Compare (object x, object y) - { - if (x == null) - return (y == null) ? 0 : -1; - else if (y == null) - return 1; - - ASN1 xx = x as ASN1; - ASN1 yy = y as ASN1; - - if ((xx == null) || (yy == null)) { - throw new ArgumentException (("Invalid objects.")); - } - - byte[] xb = xx.GetBytes (); - byte[] yb = yy.GetBytes (); - - for (int i = 0; i < xb.Length; i++) { - if (i == yb.Length) - break; - - if (xb[i] == yb[i]) - continue; - - return (xb[i] < yb[i]) ? -1 : 1; - } - - // The arrays are equal up to the shortest of them. - if (xb.Length > yb.Length) - return 1; - else if (xb.Length < yb.Length) - return -1; - - return 0; - } - } - } -} diff --git a/MediaBrowser.Server.Mono/Security/PKCS8.cs b/MediaBrowser.Server.Mono/Security/PKCS8.cs deleted file mode 100644 index b2f28f318..000000000 --- a/MediaBrowser.Server.Mono/Security/PKCS8.cs +++ /dev/null @@ -1,494 +0,0 @@ -// -// PKCS8.cs: PKCS #8 - Private-Key Information Syntax Standard -// ftp://ftp.rsasecurity.com/pub/pkcs/doc/pkcs-8.doc -// -// Author: -// Sebastien Pouliot -// -// (C) 2003 Motus Technologies Inc. (http://www.motus.com) -// Copyright (C) 2004-2006 Novell Inc. (http://www.novell.com) -// Copyright 2013 Xamarin Inc. (http://www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; -using System.Collections; -using System.Security.Cryptography; - -namespace MediaBrowser.Server.Mono.Security { - - public sealed class PKCS8 { - - public enum KeyInfo { - PrivateKey, - EncryptedPrivateKey, - Unknown - } - - private PKCS8 () - { - } - - static public KeyInfo GetType (byte[] data) - { - if (data == null) - throw new ArgumentNullException ("data"); - - KeyInfo ki = KeyInfo.Unknown; - try { - ASN1 top = new ASN1 (data); - if ((top.Tag == 0x30) && (top.Count > 0)) { - ASN1 firstLevel = top [0]; - switch (firstLevel.Tag) { - case 0x02: - ki = KeyInfo.PrivateKey; - break; - case 0x30: - ki = KeyInfo.EncryptedPrivateKey; - break; - } - } - } - catch { - throw new CryptographicException ("invalid ASN.1 data"); - } - return ki; - } - - /* - * PrivateKeyInfo ::= SEQUENCE { - * version Version, - * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, - * privateKey PrivateKey, - * attributes [0] IMPLICIT Attributes OPTIONAL - * } - * - * Version ::= INTEGER - * - * PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier - * - * PrivateKey ::= OCTET STRING - * - * Attributes ::= SET OF Attribute - */ - public class PrivateKeyInfo { - - private int _version; - private string _algorithm; - private byte[] _key; - private ArrayList _list; - - public PrivateKeyInfo () - { - _version = 0; - _list = new ArrayList (); - } - - public PrivateKeyInfo (byte[] data) : this () - { - Decode (data); - } - - // properties - - public string Algorithm { - get { return _algorithm; } - set { _algorithm = value; } - } - - public ArrayList Attributes { - get { return _list; } - } - - public byte[] PrivateKey { - get { - if (_key == null) - return null; - return (byte[]) _key.Clone (); - } - set { - if (value == null) - throw new ArgumentNullException ("PrivateKey"); - _key = (byte[]) value.Clone (); - } - } - - public int Version { - get { return _version; } - set { - if (value < 0) - throw new ArgumentOutOfRangeException ("negative version"); - _version = value; - } - } - - // methods - - private void Decode (byte[] data) - { - ASN1 privateKeyInfo = new ASN1 (data); - if (privateKeyInfo.Tag != 0x30) - throw new CryptographicException ("invalid PrivateKeyInfo"); - - ASN1 version = privateKeyInfo [0]; - if (version.Tag != 0x02) - throw new CryptographicException ("invalid version"); - _version = version.Value [0]; - - ASN1 privateKeyAlgorithm = privateKeyInfo [1]; - if (privateKeyAlgorithm.Tag != 0x30) - throw new CryptographicException ("invalid algorithm"); - - ASN1 algorithm = privateKeyAlgorithm [0]; - if (algorithm.Tag != 0x06) - throw new CryptographicException ("missing algorithm OID"); - _algorithm = ASN1Convert.ToOid (algorithm); - - ASN1 privateKey = privateKeyInfo [2]; - _key = privateKey.Value; - - // attributes [0] IMPLICIT Attributes OPTIONAL - if (privateKeyInfo.Count > 3) { - ASN1 attributes = privateKeyInfo [3]; - for (int i=0; i < attributes.Count; i++) { - _list.Add (attributes [i]); - } - } - } - - public byte[] GetBytes () - { - ASN1 privateKeyAlgorithm = new ASN1 (0x30); - privateKeyAlgorithm.Add (ASN1Convert.FromOid (_algorithm)); - privateKeyAlgorithm.Add (new ASN1 (0x05)); // ASN.1 NULL - - ASN1 pki = new ASN1 (0x30); - pki.Add (new ASN1 (0x02, new byte [1] { (byte) _version })); - pki.Add (privateKeyAlgorithm); - pki.Add (new ASN1 (0x04, _key)); - - if (_list.Count > 0) { - ASN1 attributes = new ASN1 (0xA0); - foreach (ASN1 attribute in _list) { - attributes.Add (attribute); - } - pki.Add (attributes); - } - - return pki.GetBytes (); - } - - // static methods - - static private byte[] RemoveLeadingZero (byte[] bigInt) - { - int start = 0; - int length = bigInt.Length; - if (bigInt [0] == 0x00) { - start = 1; - length--; - } - byte[] bi = new byte [length]; - Buffer.BlockCopy (bigInt, start, bi, 0, length); - return bi; - } - - static private byte[] Normalize (byte[] bigInt, int length) - { - if (bigInt.Length == length) - return bigInt; - else if (bigInt.Length > length) - return RemoveLeadingZero (bigInt); - else { - // pad with 0 - byte[] bi = new byte [length]; - Buffer.BlockCopy (bigInt, 0, bi, (length - bigInt.Length), bigInt.Length); - return bi; - } - } - - /* - * RSAPrivateKey ::= SEQUENCE { - * version Version, - * modulus INTEGER, -- n - * publicExponent INTEGER, -- e - * privateExponent INTEGER, -- d - * prime1 INTEGER, -- p - * prime2 INTEGER, -- q - * exponent1 INTEGER, -- d mod (p-1) - * exponent2 INTEGER, -- d mod (q-1) - * coefficient INTEGER, -- (inverse of q) mod p - * otherPrimeInfos OtherPrimeInfos OPTIONAL - * } - */ - static public RSA DecodeRSA (byte[] keypair) - { - ASN1 privateKey = new ASN1 (keypair); - if (privateKey.Tag != 0x30) - throw new CryptographicException ("invalid private key format"); - - ASN1 version = privateKey [0]; - if (version.Tag != 0x02) - throw new CryptographicException ("missing version"); - - if (privateKey.Count < 9) - throw new CryptographicException ("not enough key parameters"); - - RSAParameters param = new RSAParameters (); - // note: MUST remove leading 0 - else MS wont import the key - param.Modulus = RemoveLeadingZero (privateKey [1].Value); - int keysize = param.Modulus.Length; - int keysize2 = (keysize >> 1); // half-size - // size must be normalized - else MS wont import the key - param.D = Normalize (privateKey [3].Value, keysize); - param.DP = Normalize (privateKey [6].Value, keysize2); - param.DQ = Normalize (privateKey [7].Value, keysize2); - param.Exponent = RemoveLeadingZero (privateKey [2].Value); - param.InverseQ = Normalize (privateKey [8].Value, keysize2); - param.P = Normalize (privateKey [4].Value, keysize2); - param.Q = Normalize (privateKey [5].Value, keysize2); - - RSA rsa = null; - try { - rsa = RSA.Create (); - rsa.ImportParameters (param); - } - catch (CryptographicException) { - // this may cause problem when this code is run under - // the SYSTEM identity on Windows (e.g. ASP.NET). See - // http://bugzilla.ximian.com/show_bug.cgi?id=77559 - CspParameters csp = new CspParameters(); - csp.Flags = CspProviderFlags.UseMachineKeyStore; - rsa = new RSACryptoServiceProvider(csp); - rsa.ImportParameters(param); - } - return rsa; - } - - /* - * RSAPrivateKey ::= SEQUENCE { - * version Version, - * modulus INTEGER, -- n - * publicExponent INTEGER, -- e - * privateExponent INTEGER, -- d - * prime1 INTEGER, -- p - * prime2 INTEGER, -- q - * exponent1 INTEGER, -- d mod (p-1) - * exponent2 INTEGER, -- d mod (q-1) - * coefficient INTEGER, -- (inverse of q) mod p - * otherPrimeInfos OtherPrimeInfos OPTIONAL - * } - */ - static public byte[] Encode (RSA rsa) - { - RSAParameters param = rsa.ExportParameters (true); - - ASN1 rsaPrivateKey = new ASN1 (0x30); - rsaPrivateKey.Add (new ASN1 (0x02, new byte [1] { 0x00 })); - rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.Modulus)); - rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.Exponent)); - rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.D)); - rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.P)); - rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.Q)); - rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.DP)); - rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.DQ)); - rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.InverseQ)); - - return rsaPrivateKey.GetBytes (); - } - - // DSA only encode it's X private key inside an ASN.1 INTEGER (Hint: Tag == 0x02) - // which isn't enough for rebuilding the keypair. The other parameters - // can be found (98% of the time) in the X.509 certificate associated - // with the private key or (2% of the time) the parameters are in it's - // issuer X.509 certificate (not supported in the .NET framework). - static public DSA DecodeDSA (byte[] privateKey, DSAParameters dsaParameters) - { - ASN1 pvk = new ASN1 (privateKey); - if (pvk.Tag != 0x02) - throw new CryptographicException ("invalid private key format"); - - // X is ALWAYS 20 bytes (no matter if the key length is 512 or 1024 bits) - dsaParameters.X = Normalize (pvk.Value, 20); - DSA dsa = DSA.Create (); - dsa.ImportParameters (dsaParameters); - return dsa; - } - - static public byte[] Encode (DSA dsa) - { - DSAParameters param = dsa.ExportParameters (true); - return ASN1Convert.FromUnsignedBigInteger (param.X).GetBytes (); - } - - static public byte[] Encode (AsymmetricAlgorithm aa) - { - if (aa is RSA) - return Encode ((RSA)aa); - else if (aa is DSA) - return Encode ((DSA)aa); - else - throw new CryptographicException ("Unknown asymmetric algorithm {0}", aa.ToString ()); - } - } - - /* - * EncryptedPrivateKeyInfo ::= SEQUENCE { - * encryptionAlgorithm EncryptionAlgorithmIdentifier, - * encryptedData EncryptedData - * } - * - * EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier - * - * EncryptedData ::= OCTET STRING - * - * -- - * AlgorithmIdentifier ::= SEQUENCE { - * algorithm OBJECT IDENTIFIER, - * parameters ANY DEFINED BY algorithm OPTIONAL - * } - * - * -- from PKCS#5 - * PBEParameter ::= SEQUENCE { - * salt OCTET STRING SIZE(8), - * iterationCount INTEGER - * } - */ - public class EncryptedPrivateKeyInfo { - - private string _algorithm; - private byte[] _salt; - private int _iterations; - private byte[] _data; - - public EncryptedPrivateKeyInfo () {} - - public EncryptedPrivateKeyInfo (byte[] data) : this () - { - Decode (data); - } - - // properties - - public string Algorithm { - get { return _algorithm; } - set { _algorithm = value; } - } - - public byte[] EncryptedData { - get { return (_data == null) ? null : (byte[]) _data.Clone (); } - set { _data = (value == null) ? null : (byte[]) value.Clone (); } - } - - public byte[] Salt { - get { - if (_salt == null) { - RandomNumberGenerator rng = RandomNumberGenerator.Create (); - _salt = new byte [8]; - rng.GetBytes (_salt); - } - return (byte[]) _salt.Clone (); - } - set { _salt = (byte[]) value.Clone (); } - } - - public int IterationCount { - get { return _iterations; } - set { - if (value < 0) - throw new ArgumentOutOfRangeException ("IterationCount", "Negative"); - _iterations = value; - } - } - - // methods - - private void Decode (byte[] data) - { - ASN1 encryptedPrivateKeyInfo = new ASN1 (data); - if (encryptedPrivateKeyInfo.Tag != 0x30) - throw new CryptographicException ("invalid EncryptedPrivateKeyInfo"); - - ASN1 encryptionAlgorithm = encryptedPrivateKeyInfo [0]; - if (encryptionAlgorithm.Tag != 0x30) - throw new CryptographicException ("invalid encryptionAlgorithm"); - ASN1 algorithm = encryptionAlgorithm [0]; - if (algorithm.Tag != 0x06) - throw new CryptographicException ("invalid algorithm"); - _algorithm = ASN1Convert.ToOid (algorithm); - // parameters ANY DEFINED BY algorithm OPTIONAL - if (encryptionAlgorithm.Count > 1) { - ASN1 parameters = encryptionAlgorithm [1]; - if (parameters.Tag != 0x30) - throw new CryptographicException ("invalid parameters"); - - ASN1 salt = parameters [0]; - if (salt.Tag != 0x04) - throw new CryptographicException ("invalid salt"); - _salt = salt.Value; - - ASN1 iterationCount = parameters [1]; - if (iterationCount.Tag != 0x02) - throw new CryptographicException ("invalid iterationCount"); - _iterations = ASN1Convert.ToInt32 (iterationCount); - } - - ASN1 encryptedData = encryptedPrivateKeyInfo [1]; - if (encryptedData.Tag != 0x04) - throw new CryptographicException ("invalid EncryptedData"); - _data = encryptedData.Value; - } - - // Note: PKCS#8 doesn't define how to generate the key required for encryption - // so you're on your own. Just don't try to copy the big guys too much ;) - // Netscape: http://www.cs.auckland.ac.nz/~pgut001/pubs/netscape.txt - // Microsoft: http://www.cs.auckland.ac.nz/~pgut001/pubs/breakms.txt - public byte[] GetBytes () - { - if (_algorithm == null) - throw new CryptographicException ("No algorithm OID specified"); - - ASN1 encryptionAlgorithm = new ASN1 (0x30); - encryptionAlgorithm.Add (ASN1Convert.FromOid (_algorithm)); - - // parameters ANY DEFINED BY algorithm OPTIONAL - if ((_iterations > 0) || (_salt != null)) { - ASN1 salt = new ASN1 (0x04, _salt); - ASN1 iterations = ASN1Convert.FromInt32 (_iterations); - - ASN1 parameters = new ASN1 (0x30); - parameters.Add (salt); - parameters.Add (iterations); - encryptionAlgorithm.Add (parameters); - } - - // encapsulates EncryptedData into an OCTET STRING - ASN1 encryptedData = new ASN1 (0x04, _data); - - ASN1 encryptedPrivateKeyInfo = new ASN1 (0x30); - encryptedPrivateKeyInfo.Add (encryptionAlgorithm); - encryptedPrivateKeyInfo.Add (encryptedData); - - return encryptedPrivateKeyInfo.GetBytes (); - } - } - } -} diff --git a/MediaBrowser.Server.Mono/Security/X501Name.cs b/MediaBrowser.Server.Mono/Security/X501Name.cs deleted file mode 100644 index d77158e45..000000000 --- a/MediaBrowser.Server.Mono/Security/X501Name.cs +++ /dev/null @@ -1,392 +0,0 @@ -// -// X501Name.cs: X.501 Distinguished Names stuff -// -// Author: -// Sebastien Pouliot -// -// (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com) -// Copyright (C) 2004-2006 Novell, Inc (http://www.novell.com) -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; -using System.Globalization; -using System.Text; - -namespace MediaBrowser.Server.Mono.Security { - - // References: - // 1. Information technology - Open Systems Interconnection - The Directory: Models - // http://www.itu.int/rec/recommendation.asp?type=items&lang=e&parent=T-REC-X.501-200102-I - // 2. RFC2253: Lightweight Directory Access Protocol (v3): UTF-8 String Representation of Distinguished Names - // http://www.ietf.org/rfc/rfc2253.txt - - /* - * Name ::= CHOICE { RDNSequence } - * - * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName - * - * RelativeDistinguishedName ::= SET OF AttributeTypeAndValue - */ - public sealed class X501 { - - static byte[] countryName = { 0x55, 0x04, 0x06 }; - static byte[] organizationName = { 0x55, 0x04, 0x0A }; - static byte[] organizationalUnitName = { 0x55, 0x04, 0x0B }; - static byte[] commonName = { 0x55, 0x04, 0x03 }; - static byte[] localityName = { 0x55, 0x04, 0x07 }; - static byte[] stateOrProvinceName = { 0x55, 0x04, 0x08 }; - static byte[] streetAddress = { 0x55, 0x04, 0x09 }; - //static byte[] serialNumber = { 0x55, 0x04, 0x05 }; - static byte[] domainComponent = { 0x09, 0x92, 0x26, 0x89, 0x93, 0xF2, 0x2C, 0x64, 0x01, 0x19 }; - static byte[] userid = { 0x09, 0x92, 0x26, 0x89, 0x93, 0xF2, 0x2C, 0x64, 0x01, 0x01 }; - static byte[] email = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01 }; - static byte[] dnQualifier = { 0x55, 0x04, 0x2E }; - static byte[] title = { 0x55, 0x04, 0x0C }; - static byte[] surname = { 0x55, 0x04, 0x04 }; - static byte[] givenName = { 0x55, 0x04, 0x2A }; - static byte[] initial = { 0x55, 0x04, 0x2B }; - - private X501 () - { - } - - static public string ToString (ASN1 seq) - { - StringBuilder sb = new StringBuilder (); - for (int i = 0; i < seq.Count; i++) { - ASN1 entry = seq [i]; - AppendEntry (sb, entry, true); - - // separator (not on last iteration) - if (i < seq.Count - 1) - sb.Append (", "); - } - return sb.ToString (); - } - - static public string ToString (ASN1 seq, bool reversed, string separator, bool quotes) - { - StringBuilder sb = new StringBuilder (); - - if (reversed) { - for (int i = seq.Count - 1; i >= 0; i--) { - ASN1 entry = seq [i]; - AppendEntry (sb, entry, quotes); - - // separator (not on last iteration) - if (i > 0) - sb.Append (separator); - } - } else { - for (int i = 0; i < seq.Count; i++) { - ASN1 entry = seq [i]; - AppendEntry (sb, entry, quotes); - - // separator (not on last iteration) - if (i < seq.Count - 1) - sb.Append (separator); - } - } - return sb.ToString (); - } - - static private void AppendEntry (StringBuilder sb, ASN1 entry, bool quotes) - { - // multiple entries are valid - for (int k = 0; k < entry.Count; k++) { - ASN1 pair = entry [k]; - ASN1 s = pair [1]; - if (s == null) - continue; - - ASN1 poid = pair [0]; - if (poid == null) - continue; - - if (poid.CompareValue (countryName)) - sb.Append ("C="); - else if (poid.CompareValue (organizationName)) - sb.Append ("O="); - else if (poid.CompareValue (organizationalUnitName)) - sb.Append ("OU="); - else if (poid.CompareValue (commonName)) - sb.Append ("CN="); - else if (poid.CompareValue (localityName)) - sb.Append ("L="); - else if (poid.CompareValue (stateOrProvinceName)) - sb.Append ("S="); // NOTE: RFC2253 uses ST= - else if (poid.CompareValue (streetAddress)) - sb.Append ("STREET="); - else if (poid.CompareValue (domainComponent)) - sb.Append ("DC="); - else if (poid.CompareValue (userid)) - sb.Append ("UID="); - else if (poid.CompareValue (email)) - sb.Append ("E="); // NOTE: Not part of RFC2253 - else if (poid.CompareValue (dnQualifier)) - sb.Append ("dnQualifier="); - else if (poid.CompareValue (title)) - sb.Append ("T="); - else if (poid.CompareValue (surname)) - sb.Append ("SN="); - else if (poid.CompareValue (givenName)) - sb.Append ("G="); - else if (poid.CompareValue (initial)) - sb.Append ("I="); - else { - // unknown OID - sb.Append ("OID."); // NOTE: Not present as RFC2253 - sb.Append (ASN1Convert.ToOid (poid)); - sb.Append ("="); - } - - string sValue = null; - // 16bits or 8bits string ? TODO not complete (+special chars!) - if (s.Tag == 0x1E) { - // BMPSTRING - StringBuilder sb2 = new StringBuilder (); - for (int j = 1; j < s.Value.Length; j += 2) - sb2.Append ((char)s.Value[j]); - sValue = sb2.ToString (); - } else { - if (s.Tag == 0x14) - sValue = Encoding.UTF7.GetString (s.Value); - else - sValue = Encoding.UTF8.GetString (s.Value); - // in some cases we must quote (") the value - // Note: this doesn't seems to conform to RFC2253 - char[] specials = { ',', '+', '"', '\\', '<', '>', ';' }; - if (quotes) { - if ((sValue.IndexOfAny (specials, 0, sValue.Length) > 0) || - sValue.StartsWith (" ") || (sValue.EndsWith (" "))) - sValue = "\"" + sValue + "\""; - } - } - - sb.Append (sValue); - - // separator (not on last iteration) - if (k < entry.Count - 1) - sb.Append (", "); - } - } - - static private X520.AttributeTypeAndValue GetAttributeFromOid (string attributeType) - { - string s = attributeType.ToUpper (CultureInfo.InvariantCulture).Trim (); - switch (s) { - case "C": - return new X520.CountryName (); - case "O": - return new X520.OrganizationName (); - case "OU": - return new X520.OrganizationalUnitName (); - case "CN": - return new X520.CommonName (); - case "L": - return new X520.LocalityName (); - case "S": // Microsoft - case "ST": // RFC2253 - return new X520.StateOrProvinceName (); - case "E": // NOTE: Not part of RFC2253 - return new X520.EmailAddress (); - case "DC": // RFC2247 - return new X520.DomainComponent (); - case "UID": // RFC1274 - return new X520.UserId (); - case "DNQUALIFIER": - return new X520.DnQualifier (); - case "T": - return new X520.Title (); - case "SN": - return new X520.Surname (); - case "G": - return new X520.GivenName (); - case "I": - return new X520.Initial (); - default: - if (s.StartsWith ("OID.")) { - // MUST support it but it OID may be without it - return new X520.Oid (s.Substring (4)); - } else { - if (IsOid (s)) - return new X520.Oid (s); - else - return null; - } - } - } - - static private bool IsOid (string oid) - { - try { - ASN1 asn = ASN1Convert.FromOid (oid); - return (asn.Tag == 0x06); - } - catch { - return false; - } - } - - // no quote processing - static private X520.AttributeTypeAndValue ReadAttribute (string value, ref int pos) - { - while ((value[pos] == ' ') && (pos < value.Length)) - pos++; - - // get '=' position in substring - int equal = value.IndexOf ('=', pos); - if (equal == -1) { - string msg = ("No attribute found."); - throw new FormatException (msg); - } - - string s = value.Substring (pos, equal - pos); - X520.AttributeTypeAndValue atv = GetAttributeFromOid (s); - if (atv == null) { - string msg = ("Unknown attribute '{0}'."); - throw new FormatException (String.Format (msg, s)); - } - pos = equal + 1; // skip the '=' - return atv; - } - - static private bool IsHex (char c) - { - if (Char.IsDigit (c)) - return true; - char up = Char.ToUpper (c, CultureInfo.InvariantCulture); - return ((up >= 'A') && (up <= 'F')); - } - - static string ReadHex (string value, ref int pos) - { - StringBuilder sb = new StringBuilder (); - // it is (at least an) 8 bits char - sb.Append (value[pos++]); - sb.Append (value[pos]); - // look ahead for a 16 bits char - if ((pos < value.Length - 4) && (value[pos+1] == '\\') && IsHex (value[pos+2])) { - pos += 2; // pass last char and skip \ - sb.Append (value[pos++]); - sb.Append (value[pos]); - } - byte[] data = CryptoConvert.FromHex (sb.ToString ()); - return Encoding.UTF8.GetString (data); - } - - static private int ReadEscaped (StringBuilder sb, string value, int pos) - { - switch (value[pos]) { - case '\\': - case '"': - case '=': - case ';': - case '<': - case '>': - case '+': - case '#': - case ',': - sb.Append (value[pos]); - return pos; - default: - if (pos >= value.Length - 2) { - string msg = ("Malformed escaped value '{0}'."); - throw new FormatException (string.Format (msg, value.Substring (pos))); - } - // it's either a 8 bits or 16 bits char - sb.Append (ReadHex (value, ref pos)); - return pos; - } - } - - static private int ReadQuoted (StringBuilder sb, string value, int pos) - { - int original = pos; - while (pos <= value.Length) { - switch (value[pos]) { - case '"': - return pos; - case '\\': - return ReadEscaped (sb, value, pos); - default: - sb.Append (value[pos]); - pos++; - break; - } - } - string msg = ("Malformed quoted value '{0}'."); - throw new FormatException (string.Format (msg, value.Substring (original))); - } - - static private string ReadValue (string value, ref int pos) - { - int original = pos; - StringBuilder sb = new StringBuilder (); - while (pos < value.Length) { - switch (value [pos]) { - case '\\': - pos = ReadEscaped (sb, value, ++pos); - break; - case '"': - pos = ReadQuoted (sb, value, ++pos); - break; - case '=': - case ';': - case '<': - case '>': - string msg =("Malformed value '{0}' contains '{1}' outside quotes."); - throw new FormatException (string.Format (msg, value.Substring (original), value[pos])); - case '+': - case '#': - throw new NotImplementedException (); - case ',': - pos++; - return sb.ToString (); - default: - sb.Append (value[pos]); - break; - } - pos++; - } - return sb.ToString (); - } - - static public ASN1 FromString (string rdn) - { - if (rdn == null) - throw new ArgumentNullException ("rdn"); - - int pos = 0; - ASN1 asn1 = new ASN1 (0x30); - while (pos < rdn.Length) { - X520.AttributeTypeAndValue atv = ReadAttribute (rdn, ref pos); - atv.Value = ReadValue (rdn, ref pos); - - ASN1 sequence = new ASN1 (0x31); - sequence.Add (atv.GetASN1 ()); - asn1.Add (sequence); - } - return asn1; - } - } -} diff --git a/MediaBrowser.Server.Mono/Security/X509Builder.cs b/MediaBrowser.Server.Mono/Security/X509Builder.cs deleted file mode 100644 index 4801f3d8a..000000000 --- a/MediaBrowser.Server.Mono/Security/X509Builder.cs +++ /dev/null @@ -1,152 +0,0 @@ -// -// X509Builder.cs: Abstract builder class for X509 objects -// -// Author: -// Sebastien Pouliot -// -// (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com) -// (C) 2004 Novell (http://www.novell.com) -// - -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; -using System.Globalization; -using System.Security.Cryptography; - -namespace MediaBrowser.Server.Mono.Security { - - public abstract class X509Builder { - - private const string defaultHash = "SHA1"; - private string hashName; - - protected X509Builder () - { - hashName = defaultHash; - } - - protected abstract ASN1 ToBeSigned (string hashName); - - // move to PKCS1 - protected string GetOid (string hashName) - { - switch (hashName.ToLower (CultureInfo.InvariantCulture)) { - case "md2": - // md2withRSAEncryption (1 2 840 113549 1 1 2) - return "1.2.840.113549.1.1.2"; - case "md4": - // md4withRSAEncryption (1 2 840 113549 1 1 3) - return "1.2.840.113549.1.1.3"; - case "md5": - // md5withRSAEncryption (1 2 840 113549 1 1 4) - return "1.2.840.113549.1.1.4"; - case "sha1": - // sha1withRSAEncryption (1 2 840 113549 1 1 5) - return "1.2.840.113549.1.1.5"; - case "sha256": - // sha256WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 11 } - return "1.2.840.113549.1.1.11"; - case "sha384": - // sha384WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 12 } - return "1.2.840.113549.1.1.12"; - case "sha512": - // sha512WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 13 } - return "1.2.840.113549.1.1.13"; - default: - throw new NotSupportedException ("Unknown hash algorithm " + hashName); - } - } - - public string Hash { - get { return hashName; } - set { - if (hashName == null) - hashName = defaultHash; - else - hashName = value; - } - } - - public virtual byte[] Sign (AsymmetricAlgorithm aa) - { - if (aa is RSA) - return Sign (aa as RSA); - else if (aa is DSA) - return Sign (aa as DSA); - else - throw new NotSupportedException ("Unknown Asymmetric Algorithm " + aa.ToString()); - } - - private byte[] Build (ASN1 tbs, string hashoid, byte[] signature) - { - ASN1 builder = new ASN1 (0x30); - builder.Add (tbs); - builder.Add (PKCS7.AlgorithmIdentifier (hashoid)); - // first byte of BITSTRING is the number of unused bits in the first byte - byte[] bitstring = new byte [signature.Length + 1]; - Buffer.BlockCopy (signature, 0, bitstring, 1, signature.Length); - builder.Add (new ASN1 (0x03, bitstring)); - return builder.GetBytes (); - } - - public virtual byte[] Sign (RSA key) - { - string oid = GetOid (hashName); - ASN1 tbs = ToBeSigned (oid); - HashAlgorithm ha = HashAlgorithm.Create (hashName); - byte[] hash = ha.ComputeHash (tbs.GetBytes ()); - - RSAPKCS1SignatureFormatter pkcs1 = new RSAPKCS1SignatureFormatter (key); - pkcs1.SetHashAlgorithm (hashName); - byte[] signature = pkcs1.CreateSignature (hash); - - return Build (tbs, oid, signature); - } - - public virtual byte[] Sign (DSA key) - { - string oid = "1.2.840.10040.4.3"; - ASN1 tbs = ToBeSigned (oid); - HashAlgorithm ha = HashAlgorithm.Create (hashName); - if (!(ha is SHA1)) - throw new NotSupportedException ("Only SHA-1 is supported for DSA"); - byte[] hash = ha.ComputeHash (tbs.GetBytes ()); - - DSASignatureFormatter dsa = new DSASignatureFormatter (key); - dsa.SetHashAlgorithm (hashName); - byte[] rs = dsa.CreateSignature (hash); - - // split R and S - byte[] r = new byte [20]; - Buffer.BlockCopy (rs, 0, r, 0, 20); - byte[] s = new byte [20]; - Buffer.BlockCopy (rs, 20, s, 0, 20); - ASN1 signature = new ASN1 (0x30); - signature.Add (new ASN1 (0x02, r)); - signature.Add (new ASN1 (0x02, s)); - - // dsaWithSha1 (1 2 840 10040 4 3) - return Build (tbs, oid, signature.GetBytes ()); - } - } -} diff --git a/MediaBrowser.Server.Mono/Security/X509Certificate.cs b/MediaBrowser.Server.Mono/Security/X509Certificate.cs deleted file mode 100644 index fa817d959..000000000 --- a/MediaBrowser.Server.Mono/Security/X509Certificate.cs +++ /dev/null @@ -1,562 +0,0 @@ -// -// X509Certificates.cs: Handles X.509 certificates. -// -// Author: -// Sebastien Pouliot -// -// (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com) -// Copyright (C) 2004-2006 Novell, Inc (http://www.novell.com) -// Copyright 2013 Xamarin Inc. (http://www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; -using System.Runtime.Serialization; -using System.Security.Cryptography; -using System.Security.Permissions; -using System.Text; - -namespace MediaBrowser.Server.Mono.Security { - - // References: - // a. Internet X.509 Public Key Infrastructure Certificate and CRL Profile - // http://www.ietf.org/rfc/rfc3280.txt - // b. ITU ASN.1 standards (free download) - // http://www.itu.int/ITU-T/studygroups/com17/languages/ - - public class X509Certificate : ISerializable - { - - private ASN1 decoder; - - private byte[] m_encodedcert; - private DateTime m_from; - private DateTime m_until; - private ASN1 issuer; - private string m_issuername; - private string m_keyalgo; - private byte[] m_keyalgoparams; - private ASN1 subject; - private string m_subject; - private byte[] m_publickey; - private byte[] signature; - private string m_signaturealgo; - private byte[] m_signaturealgoparams; - private byte[] certhash; - private RSA _rsa; - private DSA _dsa; - - // from http://msdn.microsoft.com/en-gb/library/ff635835.aspx - private const string OID_DSA = "1.2.840.10040.4.1"; - private const string OID_RSA = "1.2.840.113549.1.1.1"; - - // from http://www.ietf.org/rfc/rfc2459.txt - // - //Certificate ::= SEQUENCE { - // tbsCertificate TBSCertificate, - // signatureAlgorithm AlgorithmIdentifier, - // signature BIT STRING } - // - //TBSCertificate ::= SEQUENCE { - // version [0] Version DEFAULT v1, - // serialNumber CertificateSerialNumber, - // signature AlgorithmIdentifier, - // issuer Name, - // validity Validity, - // subject Name, - // subjectPublicKeyInfo SubjectPublicKeyInfo, - // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, - // -- If present, version shall be v2 or v3 - // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, - // -- If present, version shall be v2 or v3 - // extensions [3] Extensions OPTIONAL - // -- If present, version shall be v3 -- } - private int version; - private byte[] serialnumber; - - private byte[] issuerUniqueID; - private byte[] subjectUniqueID; - private X509ExtensionCollection extensions; - - private static string encoding_error = ("Input data cannot be coded as a valid certificate."); - - - // that's were the real job is! - private void Parse (byte[] data) - { - try { - decoder = new ASN1 (data); - // Certificate - if (decoder.Tag != 0x30) - throw new CryptographicException (encoding_error); - // Certificate / TBSCertificate - if (decoder [0].Tag != 0x30) - throw new CryptographicException (encoding_error); - - ASN1 tbsCertificate = decoder [0]; - - int tbs = 0; - // Certificate / TBSCertificate / Version - ASN1 v = decoder [0][tbs]; - version = 1; // DEFAULT v1 - if ((v.Tag == 0xA0) && (v.Count > 0)) { - // version (optional) is present only in v2+ certs - version += v [0].Value [0]; // zero based - tbs++; - } - - // Certificate / TBSCertificate / CertificateSerialNumber - ASN1 sn = decoder [0][tbs++]; - if (sn.Tag != 0x02) - throw new CryptographicException (encoding_error); - serialnumber = sn.Value; - Array.Reverse (serialnumber, 0, serialnumber.Length); - - // Certificate / TBSCertificate / AlgorithmIdentifier - tbs++; - // ASN1 signatureAlgo = tbsCertificate.Element (tbs++, 0x30); - - issuer = tbsCertificate.Element (tbs++, 0x30); - m_issuername = X501.ToString (issuer); - - ASN1 validity = tbsCertificate.Element (tbs++, 0x30); - ASN1 notBefore = validity [0]; - m_from = ASN1Convert.ToDateTime (notBefore); - ASN1 notAfter = validity [1]; - m_until = ASN1Convert.ToDateTime (notAfter); - - subject = tbsCertificate.Element (tbs++, 0x30); - m_subject = X501.ToString (subject); - - ASN1 subjectPublicKeyInfo = tbsCertificate.Element (tbs++, 0x30); - - ASN1 algorithm = subjectPublicKeyInfo.Element (0, 0x30); - ASN1 algo = algorithm.Element (0, 0x06); - m_keyalgo = ASN1Convert.ToOid (algo); - // parameters ANY DEFINED BY algorithm OPTIONAL - // so we dont ask for a specific (Element) type and return DER - ASN1 parameters = algorithm [1]; - m_keyalgoparams = ((algorithm.Count > 1) ? parameters.GetBytes () : null); - - ASN1 subjectPublicKey = subjectPublicKeyInfo.Element (1, 0x03); - // we must drop th first byte (which is the number of unused bits - // in the BITSTRING) - int n = subjectPublicKey.Length - 1; - m_publickey = new byte [n]; - Buffer.BlockCopy (subjectPublicKey.Value, 1, m_publickey, 0, n); - - // signature processing - byte[] bitstring = decoder [2].Value; - // first byte contains unused bits in first byte - signature = new byte [bitstring.Length - 1]; - Buffer.BlockCopy (bitstring, 1, signature, 0, signature.Length); - - algorithm = decoder [1]; - algo = algorithm.Element (0, 0x06); - m_signaturealgo = ASN1Convert.ToOid (algo); - parameters = algorithm [1]; - if (parameters != null) - m_signaturealgoparams = parameters.GetBytes (); - else - m_signaturealgoparams = null; - - // Certificate / TBSCertificate / issuerUniqueID - ASN1 issuerUID = tbsCertificate.Element (tbs, 0x81); - if (issuerUID != null) { - tbs++; - issuerUniqueID = issuerUID.Value; - } - - // Certificate / TBSCertificate / subjectUniqueID - ASN1 subjectUID = tbsCertificate.Element (tbs, 0x82); - if (subjectUID != null) { - tbs++; - subjectUniqueID = subjectUID.Value; - } - - // Certificate / TBSCertificate / Extensions - ASN1 extns = tbsCertificate.Element (tbs, 0xA3); - if ((extns != null) && (extns.Count == 1)) - extensions = new X509ExtensionCollection (extns [0]); - else - extensions = new X509ExtensionCollection (null); - - // keep a copy of the original data - m_encodedcert = (byte[]) data.Clone (); - } - catch (Exception ex) { - throw new CryptographicException (encoding_error, ex); - } - } - - // constructors - - public X509Certificate (byte[] data) - { - if (data != null) { - // does it looks like PEM ? - if ((data.Length > 0) && (data [0] != 0x30)) { - try { - data = PEM ("CERTIFICATE", data); - } - catch (Exception ex) { - throw new CryptographicException (encoding_error, ex); - } - } - Parse (data); - } - } - - private byte[] GetUnsignedBigInteger (byte[] integer) - { - if (integer [0] == 0x00) { - // this first byte is added so we're sure it's an unsigned integer - // however we can't feed it into RSAParameters or DSAParameters - int length = integer.Length - 1; - byte[] uinteger = new byte [length]; - Buffer.BlockCopy (integer, 1, uinteger, 0, length); - return uinteger; - } - else - return integer; - } - - // public methods - - public DSA DSA { - get { - if (m_keyalgoparams == null) - throw new CryptographicException ("Missing key algorithm parameters."); - - if (_dsa == null && m_keyalgo == OID_DSA) { - DSAParameters dsaParams = new DSAParameters (); - // for DSA m_publickey contains 1 ASN.1 integer - Y - ASN1 pubkey = new ASN1 (m_publickey); - if ((pubkey == null) || (pubkey.Tag != 0x02)) - return null; - dsaParams.Y = GetUnsignedBigInteger (pubkey.Value); - - ASN1 param = new ASN1 (m_keyalgoparams); - if ((param == null) || (param.Tag != 0x30) || (param.Count < 3)) - return null; - if ((param [0].Tag != 0x02) || (param [1].Tag != 0x02) || (param [2].Tag != 0x02)) - return null; - dsaParams.P = GetUnsignedBigInteger (param [0].Value); - dsaParams.Q = GetUnsignedBigInteger (param [1].Value); - dsaParams.G = GetUnsignedBigInteger (param [2].Value); - - // BUG: MS BCL 1.0 can't import a key which - // isn't the same size as the one present in - // the container. - _dsa = (DSA) new DSACryptoServiceProvider (dsaParams.Y.Length << 3); - _dsa.ImportParameters (dsaParams); - } - return _dsa; - } - - set { - _dsa = value; - if (value != null) - _rsa = null; - } - } - - public X509ExtensionCollection Extensions { - get { return extensions; } - } - - public byte[] Hash { - get { - if (certhash == null) { - if ((decoder == null) || (decoder.Count < 1)) - return null; - string algo = PKCS1.HashNameFromOid (m_signaturealgo, false); - if (algo == null) - return null; - byte[] toBeSigned = decoder [0].GetBytes (); - using (var hash = PKCS1.CreateFromName (algo)) - certhash = hash.ComputeHash (toBeSigned, 0, toBeSigned.Length); - } - return (byte[]) certhash.Clone (); - } - } - - public virtual string IssuerName { - get { return m_issuername; } - } - - public virtual string KeyAlgorithm { - get { return m_keyalgo; } - } - - public virtual byte[] KeyAlgorithmParameters { - get { - if (m_keyalgoparams == null) - return null; - return (byte[]) m_keyalgoparams.Clone (); - } - set { m_keyalgoparams = value; } - } - - public virtual byte[] PublicKey { - get { - if (m_publickey == null) - return null; - return (byte[]) m_publickey.Clone (); - } - } - - public virtual RSA RSA { - get { - if (_rsa == null && m_keyalgo == OID_RSA) { - RSAParameters rsaParams = new RSAParameters (); - // for RSA m_publickey contains 2 ASN.1 integers - // the modulus and the public exponent - ASN1 pubkey = new ASN1 (m_publickey); - ASN1 modulus = pubkey [0]; - if ((modulus == null) || (modulus.Tag != 0x02)) - return null; - ASN1 exponent = pubkey [1]; - if (exponent.Tag != 0x02) - return null; - - rsaParams.Modulus = GetUnsignedBigInteger (modulus.Value); - rsaParams.Exponent = exponent.Value; - - // BUG: MS BCL 1.0 can't import a key which - // isn't the same size as the one present in - // the container. - int keySize = (rsaParams.Modulus.Length << 3); - _rsa = (RSA) new RSACryptoServiceProvider (keySize); - _rsa.ImportParameters (rsaParams); - } - return _rsa; - } - - set { - if (value != null) - _dsa = null; - _rsa = value; - } - } - - public virtual byte[] RawData { - get { - if (m_encodedcert == null) - return null; - return (byte[]) m_encodedcert.Clone (); - } - } - - public virtual byte[] SerialNumber { - get { - if (serialnumber == null) - return null; - return (byte[]) serialnumber.Clone (); - } - } - - public virtual byte[] Signature { - get { - if (signature == null) - return null; - - switch (m_signaturealgo) { - case "1.2.840.113549.1.1.2": // MD2 with RSA encryption - case "1.2.840.113549.1.1.3": // MD4 with RSA encryption - case "1.2.840.113549.1.1.4": // MD5 with RSA encryption - case "1.2.840.113549.1.1.5": // SHA-1 with RSA Encryption - case "1.3.14.3.2.29": // SHA1 with RSA signature - case "1.2.840.113549.1.1.11": // SHA-256 with RSA Encryption - case "1.2.840.113549.1.1.12": // SHA-384 with RSA Encryption - case "1.2.840.113549.1.1.13": // SHA-512 with RSA Encryption - case "1.3.36.3.3.1.2": // RIPEMD160 with RSA Encryption - return (byte[]) signature.Clone (); - - case "1.2.840.10040.4.3": // SHA-1 with DSA - ASN1 sign = new ASN1 (signature); - if ((sign == null) || (sign.Count != 2)) - return null; - byte[] part1 = sign [0].Value; - byte[] part2 = sign [1].Value; - byte[] sig = new byte [40]; - // parts may be less than 20 bytes (i.e. first bytes were 0x00) - // parts may be more than 20 bytes (i.e. first byte > 0x80, negative) - int s1 = System.Math.Max (0, part1.Length - 20); - int e1 = System.Math.Max (0, 20 - part1.Length); - Buffer.BlockCopy (part1, s1, sig, e1, part1.Length - s1); - int s2 = System.Math.Max (0, part2.Length - 20); - int e2 = System.Math.Max (20, 40 - part2.Length); - Buffer.BlockCopy (part2, s2, sig, e2, part2.Length - s2); - return sig; - - default: - throw new CryptographicException ("Unsupported hash algorithm: " + m_signaturealgo); - } - } - } - - public virtual string SignatureAlgorithm { - get { return m_signaturealgo; } - } - - public virtual byte[] SignatureAlgorithmParameters { - get { - if (m_signaturealgoparams == null) - return m_signaturealgoparams; - return (byte[]) m_signaturealgoparams.Clone (); - } - } - - public virtual string SubjectName { - get { return m_subject; } - } - - public virtual DateTime ValidFrom { - get { return m_from; } - } - - public virtual DateTime ValidUntil { - get { return m_until; } - } - - public int Version { - get { return version; } - } - - public bool IsCurrent { - get { return WasCurrent (DateTime.UtcNow); } - } - - public bool WasCurrent (DateTime instant) - { - return ((instant > ValidFrom) && (instant <= ValidUntil)); - } - - // uncommon v2 "extension" - public byte[] IssuerUniqueIdentifier { - get { - if (issuerUniqueID == null) - return null; - return (byte[]) issuerUniqueID.Clone (); - } - } - - // uncommon v2 "extension" - public byte[] SubjectUniqueIdentifier { - get { - if (subjectUniqueID == null) - return null; - return (byte[]) subjectUniqueID.Clone (); - } - } - - internal bool VerifySignature (DSA dsa) - { - // signatureOID is check by both this.Hash and this.Signature - DSASignatureDeformatter v = new DSASignatureDeformatter (dsa); - // only SHA-1 is supported - v.SetHashAlgorithm ("SHA1"); - return v.VerifySignature (this.Hash, this.Signature); - } - - internal bool VerifySignature (RSA rsa) - { - // SHA1-1 with DSA - if (m_signaturealgo == "1.2.840.10040.4.3") - return false; - RSAPKCS1SignatureDeformatter v = new RSAPKCS1SignatureDeformatter (rsa); - v.SetHashAlgorithm (PKCS1.HashNameFromOid (m_signaturealgo)); - return v.VerifySignature (this.Hash, this.Signature); - } - - public bool VerifySignature (AsymmetricAlgorithm aa) - { - if (aa == null) - throw new ArgumentNullException ("aa"); - - if (aa is RSA) - return VerifySignature (aa as RSA); - else if (aa is DSA) - return VerifySignature (aa as DSA); - else - throw new NotSupportedException ("Unknown Asymmetric Algorithm " + aa.ToString ()); - } - - public bool CheckSignature (byte[] hash, string hashAlgorithm, byte[] signature) - { - RSACryptoServiceProvider r = (RSACryptoServiceProvider) RSA; - return r.VerifyHash (hash, hashAlgorithm, signature); - } - - public bool IsSelfSigned { - get { - if (m_issuername != m_subject) - return false; - - try { - if (RSA != null) - return VerifySignature (RSA); - else if (DSA != null) - return VerifySignature (DSA); - else - return false; // e.g. a certificate with only DSA parameters - } - catch (CryptographicException) { - return false; - } - } - } - - public ASN1 GetIssuerName () - { - return issuer; - } - - public ASN1 GetSubjectName () - { - return subject; - } - - protected X509Certificate (SerializationInfo info, StreamingContext context) - { - Parse ((byte[]) info.GetValue ("raw", typeof (byte[]))); - } - - [SecurityPermission (SecurityAction.Demand, SerializationFormatter = true)] - public virtual void GetObjectData (SerializationInfo info, StreamingContext context) - { - info.AddValue ("raw", m_encodedcert); - // note: we NEVER serialize the private key - } - - static byte[] PEM (string type, byte[] data) - { - string pem = Encoding.ASCII.GetString (data); - string header = String.Format ("-----BEGIN {0}-----", type); - string footer = String.Format ("-----END {0}-----", type); - int start = pem.IndexOf (header) + header.Length; - int end = pem.IndexOf (footer, start); - string base64 = pem.Substring (start, (end - start)); - return Convert.FromBase64String (base64); - } - } -} diff --git a/MediaBrowser.Server.Mono/Security/X509CertificateBuilder.cs b/MediaBrowser.Server.Mono/Security/X509CertificateBuilder.cs deleted file mode 100644 index 9b51d9e4e..000000000 --- a/MediaBrowser.Server.Mono/Security/X509CertificateBuilder.cs +++ /dev/null @@ -1,244 +0,0 @@ -// -// X509CertificateBuilder.cs: Handles building of X.509 certificates. -// -// Author: -// Sebastien Pouliot -// -// (C) 2003 Motus Technologies Inc. (http://www.motus.com) -// (C) 2004 Novell (http://www.novell.com) -// - -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; -using System.Security.Cryptography; - -namespace MediaBrowser.Server.Mono.Security { - // From RFC3280 - /* - * Certificate ::= SEQUENCE { - * tbsCertificate TBSCertificate, - * signatureAlgorithm AlgorithmIdentifier, - * signature BIT STRING - * } - * TBSCertificate ::= SEQUENCE { - * version [0] Version DEFAULT v1, - * serialNumber CertificateSerialNumber, - * signature AlgorithmIdentifier, - * issuer Name, - * validity Validity, - * subject Name, - * subjectPublicKeyInfo SubjectPublicKeyInfo, - * issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, - * -- If present, version MUST be v2 or v3 - * subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, - * -- If present, version MUST be v2 or v3 - * extensions [3] Extensions OPTIONAL - * -- If present, version MUST be v3 -- - * } - * Version ::= INTEGER { v1(0), v2(1), v3(2) } - * CertificateSerialNumber ::= INTEGER - * Validity ::= SEQUENCE { - * notBefore Time, - * notAfter Time - * } - * Time ::= CHOICE { - * utcTime UTCTime, - * generalTime GeneralizedTime - * } - */ - public class X509CertificateBuilder : X509Builder { - - private byte version; - private byte[] sn; - private string issuer; - private DateTime notBefore; - private DateTime notAfter; - private string subject; - private AsymmetricAlgorithm aa; - private byte[] issuerUniqueID; - private byte[] subjectUniqueID; - private X509ExtensionCollection extensions; - - public X509CertificateBuilder () : this (3) {} - - public X509CertificateBuilder (byte version) - { - if (version > 3) - throw new ArgumentException ("Invalid certificate version"); - this.version = version; - extensions = new X509ExtensionCollection (); - } - - public byte Version { - get { return version; } - set { version = value; } - } - - public byte[] SerialNumber { - get { return sn; } - set { sn = value; } - } - - public string IssuerName { - get { return issuer; } - set { issuer = value; } - } - - public DateTime NotBefore { - get { return notBefore; } - set { notBefore = value; } - } - - public DateTime NotAfter { - get { return notAfter; } - set { notAfter = value; } - } - - public string SubjectName { - get { return subject; } - set { subject = value; } - } - - public AsymmetricAlgorithm SubjectPublicKey { - get { return aa; } - set { aa = value; } - } - - public byte[] IssuerUniqueId { - get { return issuerUniqueID; } - set { issuerUniqueID = value; } - } - - public byte[] SubjectUniqueId { - get { return subjectUniqueID; } - set { subjectUniqueID = value; } - } - - public X509ExtensionCollection Extensions { - get { return extensions; } - } - - - /* SubjectPublicKeyInfo ::= SEQUENCE { - * algorithm AlgorithmIdentifier, - * subjectPublicKey BIT STRING } - */ - private ASN1 SubjectPublicKeyInfo () - { - ASN1 keyInfo = new ASN1 (0x30); - if (aa is RSA) { - keyInfo.Add (PKCS7.AlgorithmIdentifier ("1.2.840.113549.1.1.1")); - RSAParameters p = (aa as RSA).ExportParameters (false); - /* RSAPublicKey ::= SEQUENCE { - * modulus INTEGER, -- n - * publicExponent INTEGER } -- e - */ - ASN1 key = new ASN1 (0x30); - key.Add (ASN1Convert.FromUnsignedBigInteger (p.Modulus)); - key.Add (ASN1Convert.FromUnsignedBigInteger (p.Exponent)); - keyInfo.Add (new ASN1 (UniqueIdentifier (key.GetBytes ()))); - } - else if (aa is DSA) { - DSAParameters p = (aa as DSA).ExportParameters (false); - /* Dss-Parms ::= SEQUENCE { - * p INTEGER, - * q INTEGER, - * g INTEGER } - */ - ASN1 param = new ASN1 (0x30); - param.Add (ASN1Convert.FromUnsignedBigInteger (p.P)); - param.Add (ASN1Convert.FromUnsignedBigInteger (p.Q)); - param.Add (ASN1Convert.FromUnsignedBigInteger (p.G)); - keyInfo.Add (PKCS7.AlgorithmIdentifier ("1.2.840.10040.4.1", param)); - ASN1 key = keyInfo.Add (new ASN1 (0x03)); - // DSAPublicKey ::= INTEGER -- public key, y - key.Add (ASN1Convert.FromUnsignedBigInteger (p.Y)); - } - else - throw new NotSupportedException ("Unknown Asymmetric Algorithm " + aa.ToString ()); - return keyInfo; - } - - private byte[] UniqueIdentifier (byte[] id) - { - // UniqueIdentifier ::= BIT STRING - ASN1 uid = new ASN1 (0x03); - // first byte in a BITSTRING is the number of unused bits in the first byte - byte[] v = new byte [id.Length + 1]; - Buffer.BlockCopy (id, 0, v, 1, id.Length); - uid.Value = v; - return uid.GetBytes (); - } - - protected override ASN1 ToBeSigned (string oid) - { - // TBSCertificate - ASN1 tbsCert = new ASN1 (0x30); - - if (version > 1) { - // TBSCertificate / [0] Version DEFAULT v1, - byte[] ver = { (byte)(version - 1) }; - ASN1 v = tbsCert.Add (new ASN1 (0xA0)); - v.Add (new ASN1 (0x02, ver)); - } - - // TBSCertificate / CertificateSerialNumber, - tbsCert.Add (new ASN1 (0x02, sn)); - - // TBSCertificate / AlgorithmIdentifier, - tbsCert.Add (PKCS7.AlgorithmIdentifier (oid)); - - // TBSCertificate / Name - tbsCert.Add (X501.FromString (issuer)); - - // TBSCertificate / Validity - ASN1 validity = tbsCert.Add (new ASN1 (0x30)); - // TBSCertificate / Validity / Time - validity.Add (ASN1Convert.FromDateTime (notBefore)); - // TBSCertificate / Validity / Time - validity.Add (ASN1Convert.FromDateTime (notAfter)); - - // TBSCertificate / Name - tbsCert.Add (X501.FromString (subject)); - - // TBSCertificate / SubjectPublicKeyInfo - tbsCert.Add (SubjectPublicKeyInfo ()); - - if (version > 1) { - // TBSCertificate / [1] IMPLICIT UniqueIdentifier OPTIONAL - if (issuerUniqueID != null) - tbsCert.Add (new ASN1 (0xA1, UniqueIdentifier (issuerUniqueID))); - - // TBSCertificate / [2] IMPLICIT UniqueIdentifier OPTIONAL - if (subjectUniqueID != null) - tbsCert.Add (new ASN1 (0xA1, UniqueIdentifier (subjectUniqueID))); - - // TBSCertificate / [3] Extensions OPTIONAL - if ((version > 2) && (extensions.Count > 0)) - tbsCert.Add (new ASN1 (0xA3, extensions.GetBytes ())); - } - - return tbsCert; - } - } -} diff --git a/MediaBrowser.Server.Mono/Security/X509CertificateCollection.cs b/MediaBrowser.Server.Mono/Security/X509CertificateCollection.cs deleted file mode 100644 index 6bb465b28..000000000 --- a/MediaBrowser.Server.Mono/Security/X509CertificateCollection.cs +++ /dev/null @@ -1,200 +0,0 @@ -// -// Based on System.Security.Cryptography.X509Certificates.X509CertificateCollection -// in System assembly -// -// Authors: -// Lawrence Pit (loz@cable.a2000.nl) -// Sebastien Pouliot -// - -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; -using System.Collections; - -namespace MediaBrowser.Server.Mono.Security { - - [Serializable] - public class X509CertificateCollection : CollectionBase, IEnumerable { - - public X509CertificateCollection () - { - } - - public X509CertificateCollection (X509Certificate [] value) - { - AddRange (value); - } - - public X509CertificateCollection (X509CertificateCollection value) - { - AddRange (value); - } - - // Properties - - public X509Certificate this [int index] { - get { return (X509Certificate) InnerList [index]; } - set { InnerList [index] = value; } - } - - // Methods - - public int Add (X509Certificate value) - { - if (value == null) - throw new ArgumentNullException ("value"); - - return InnerList.Add (value); - } - - public void AddRange (X509Certificate [] value) - { - if (value == null) - throw new ArgumentNullException ("value"); - - for (int i = 0; i < value.Length; i++) - InnerList.Add (value [i]); - } - - public void AddRange (X509CertificateCollection value) - { - if (value == null) - throw new ArgumentNullException ("value"); - - for (int i = 0; i < value.InnerList.Count; i++) - InnerList.Add (value [i]); - } - - public bool Contains (X509Certificate value) - { - return (IndexOf (value) != -1); - } - - public void CopyTo (X509Certificate[] array, int index) - { - InnerList.CopyTo (array, index); - } - - public new X509CertificateEnumerator GetEnumerator () - { - return new X509CertificateEnumerator (this); - } - - IEnumerator IEnumerable.GetEnumerator () - { - return InnerList.GetEnumerator (); - } - - public override int GetHashCode () - { - return InnerList.GetHashCode (); - } - - public int IndexOf (X509Certificate value) - { - if (value == null) - throw new ArgumentNullException ("value"); - - byte[] hash = value.Hash; - for (int i=0; i < InnerList.Count; i++) { - X509Certificate x509 = (X509Certificate) InnerList [i]; - if (Compare (x509.Hash, hash)) - return i; - } - return -1; - } - - public void Insert (int index, X509Certificate value) - { - InnerList.Insert (index, value); - } - - public void Remove (X509Certificate value) - { - InnerList.Remove (value); - } - - // private stuff - - private bool Compare (byte[] array1, byte[] array2) - { - if ((array1 == null) && (array2 == null)) - return true; - if ((array1 == null) || (array2 == null)) - return false; - if (array1.Length != array2.Length) - return false; - for (int i=0; i < array1.Length; i++) { - if (array1 [i] != array2 [i]) - return false; - } - return true; - } - - // Inner Class - - public class X509CertificateEnumerator : IEnumerator { - - private IEnumerator enumerator; - - // Constructors - - public X509CertificateEnumerator (X509CertificateCollection mappings) - { - enumerator = ((IEnumerable) mappings).GetEnumerator (); - } - - // Properties - - public X509Certificate Current { - get { return (X509Certificate) enumerator.Current; } - } - - object IEnumerator.Current { - get { return enumerator.Current; } - } - - // Methods - - bool IEnumerator.MoveNext () - { - return enumerator.MoveNext (); - } - - void IEnumerator.Reset () - { - enumerator.Reset (); - } - - public bool MoveNext () - { - return enumerator.MoveNext (); - } - - public void Reset () - { - enumerator.Reset (); - } - } - } -} diff --git a/MediaBrowser.Server.Mono/Security/X509Extension.cs b/MediaBrowser.Server.Mono/Security/X509Extension.cs deleted file mode 100644 index 984c3542b..000000000 --- a/MediaBrowser.Server.Mono/Security/X509Extension.cs +++ /dev/null @@ -1,207 +0,0 @@ -// -// X509Extension.cs: Base class for all X.509 extensions. -// -// Author: -// Sebastien Pouliot -// -// (C) 2003 Motus Technologies Inc. (http://www.motus.com) -// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com) -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; -using System.Globalization; -using System.Text; - -namespace MediaBrowser.Server.Mono.Security { - /* - * Extension ::= SEQUENCE { - * extnID OBJECT IDENTIFIER, - * critical BOOLEAN DEFAULT FALSE, - * extnValue OCTET STRING - * } - */ - public class X509Extension { - - protected string extnOid; - protected bool extnCritical; - protected ASN1 extnValue; - - protected X509Extension () - { - extnCritical = false; - } - - public X509Extension (ASN1 asn1) - { - if ((asn1.Tag != 0x30) || (asn1.Count < 2)) - throw new ArgumentException (("Invalid X.509 extension.")); - if (asn1[0].Tag != 0x06) - throw new ArgumentException (("Invalid X.509 extension.")); - - extnOid = ASN1Convert.ToOid (asn1[0]); - extnCritical = ((asn1[1].Tag == 0x01) && (asn1[1].Value[0] == 0xFF)); - // last element is an octet string which may need to be decoded - extnValue = asn1 [asn1.Count - 1]; - if ((extnValue.Tag == 0x04) && (extnValue.Length > 0) && (extnValue.Count == 0)) { - try { - ASN1 encapsulated = new ASN1 (extnValue.Value); - extnValue.Value = null; - extnValue.Add (encapsulated); - } - catch { - // data isn't ASN.1 - } - } - Decode (); - } - - public X509Extension (X509Extension extension) - { - if (extension == null) - throw new ArgumentNullException ("extension"); - if ((extension.Value == null) || (extension.Value.Tag != 0x04) || (extension.Value.Count != 1)) - throw new ArgumentException (("Invalid X.509 extension.")); - - extnOid = extension.Oid; - extnCritical = extension.Critical; - extnValue = extension.Value; - Decode (); - } - - // encode the extension *into* an OCTET STRING - protected virtual void Decode () - { - } - - // decode the extension from *inside* an OCTET STRING - protected virtual void Encode () - { - } - - public ASN1 ASN1 { - get { - ASN1 extension = new ASN1 (0x30); - extension.Add (ASN1Convert.FromOid (extnOid)); - if (extnCritical) - extension.Add (new ASN1 (0x01, new byte [1] { 0xFF })); - Encode (); - extension.Add (extnValue); - return extension; - } - } - - public string Oid { - get { return extnOid; } - } - - public bool Critical { - get { return extnCritical; } - set { extnCritical = value; } - } - - // this gets overrided with more meaningful names - public virtual string Name { - get { return extnOid; } - } - - public ASN1 Value { - get { - if (extnValue == null) { - Encode (); - } - return extnValue; - } - } - - public override bool Equals (object obj) - { - if (obj == null) - return false; - - X509Extension ex = (obj as X509Extension); - if (ex == null) - return false; - - if (extnCritical != ex.extnCritical) - return false; - if (extnOid != ex.extnOid) - return false; - if (extnValue.Length != ex.extnValue.Length) - return false; - - for (int i=0; i < extnValue.Length; i++) { - if (extnValue [i] != ex.extnValue [i]) - return false; - } - return true; - } - - public byte[] GetBytes () - { - return ASN1.GetBytes (); - } - - public override int GetHashCode () - { - // OID should be unique in a collection of extensions - return extnOid.GetHashCode (); - } - - private void WriteLine (StringBuilder sb, int n, int pos) - { - byte[] value = extnValue.Value; - int p = pos; - for (int j=0; j < 8; j++) { - if (j < n) { - sb.Append (value [p++].ToString ("X2", CultureInfo.InvariantCulture)); - sb.Append (" "); - } - else - sb.Append (" "); - } - sb.Append (" "); - p = pos; - for (int j=0; j < n; j++) { - byte b = value [p++]; - if (b < 0x20) - sb.Append ("."); - else - sb.Append (Convert.ToChar (b)); - } - sb.Append (Environment.NewLine); - } - - public override string ToString () - { - StringBuilder sb = new StringBuilder (); - int div = (extnValue.Length >> 3); - int rem = (extnValue.Length - (div << 3)); - int x = 0; - for (int i=0; i < div; i++) { - WriteLine (sb, 8, x); - x += 8; - } - WriteLine (sb, rem, x); - return sb.ToString (); - } - } -} diff --git a/MediaBrowser.Server.Mono/Security/X509Extensions.cs b/MediaBrowser.Server.Mono/Security/X509Extensions.cs deleted file mode 100644 index b86fe1c40..000000000 --- a/MediaBrowser.Server.Mono/Security/X509Extensions.cs +++ /dev/null @@ -1,194 +0,0 @@ -// -// X509Extensions.cs: Handles X.509 extensions. -// -// Author: -// Sebastien Pouliot -// -// (C) 2003 Motus Technologies Inc. (http://www.motus.com) -// (C) 2004 Novell (http://www.novell.com) -// - -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; -using System.Collections; - -namespace MediaBrowser.Server.Mono.Security { - /* - * Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension - * - * Note: 1..MAX -> There shouldn't be 0 Extensions in the ASN1 structure - */ - public sealed class X509ExtensionCollection : CollectionBase, IEnumerable { - - private bool readOnly; - - public X509ExtensionCollection () : base () - { - } - - public X509ExtensionCollection (ASN1 asn1) : this () - { - readOnly = true; - if (asn1 == null) - return; - if (asn1.Tag != 0x30) - throw new Exception ("Invalid extensions format"); - for (int i=0; i < asn1.Count; i++) { - X509Extension extension = new X509Extension (asn1 [i]); - InnerList.Add (extension); - } - } - - public int Add (X509Extension extension) - { - if (extension == null) - throw new ArgumentNullException ("extension"); - if (readOnly) - throw new NotSupportedException ("Extensions are read only"); - - return InnerList.Add (extension); - } - - public void AddRange (X509Extension[] extension) - { - if (extension == null) - throw new ArgumentNullException ("extension"); - if (readOnly) - throw new NotSupportedException ("Extensions are read only"); - - for (int i = 0; i < extension.Length; i++) - InnerList.Add (extension [i]); - } - - public void AddRange (X509ExtensionCollection collection) - { - if (collection == null) - throw new ArgumentNullException ("collection"); - if (readOnly) - throw new NotSupportedException ("Extensions are read only"); - - for (int i = 0; i < collection.InnerList.Count; i++) - InnerList.Add (collection [i]); - } - - public bool Contains (X509Extension extension) - { - return (IndexOf (extension) != -1); - } - - public bool Contains (string oid) - { - return (IndexOf (oid) != -1); - } - - public void CopyTo (X509Extension[] extensions, int index) - { - if (extensions == null) - throw new ArgumentNullException ("extensions"); - - InnerList.CopyTo (extensions, index); - } - - public int IndexOf (X509Extension extension) - { - if (extension == null) - throw new ArgumentNullException ("extension"); - - for (int i=0; i < InnerList.Count; i++) { - X509Extension ex = (X509Extension) InnerList [i]; - if (ex.Equals (extension)) - return i; - } - return -1; - } - - public int IndexOf (string oid) - { - if (oid == null) - throw new ArgumentNullException ("oid"); - - for (int i=0; i < InnerList.Count; i++) { - X509Extension ex = (X509Extension) InnerList [i]; - if (ex.Oid == oid) - return i; - } - return -1; - } - - public void Insert (int index, X509Extension extension) - { - if (extension == null) - throw new ArgumentNullException ("extension"); - - InnerList.Insert (index, extension); - } - - public void Remove (X509Extension extension) - { - if (extension == null) - throw new ArgumentNullException ("extension"); - - InnerList.Remove (extension); - } - - public void Remove (string oid) - { - if (oid == null) - throw new ArgumentNullException ("oid"); - - int index = IndexOf (oid); - if (index != -1) - InnerList.RemoveAt (index); - } - - IEnumerator IEnumerable.GetEnumerator () - { - return InnerList.GetEnumerator (); - } - - public X509Extension this [int index] { - get { return (X509Extension) InnerList [index]; } - } - - public X509Extension this [string oid] { - get { - int index = IndexOf (oid); - if (index == -1) - return null; - return (X509Extension) InnerList [index]; - } - } - - public byte[] GetBytes () - { - if (InnerList.Count < 1) - return null; - ASN1 sequence = new ASN1 (0x30); - for (int i=0; i < InnerList.Count; i++) { - X509Extension x = (X509Extension) InnerList [i]; - sequence.Add (x.ASN1); - } - return sequence.GetBytes (); - } - } -} diff --git a/MediaBrowser.Server.Mono/Security/X520Attributes.cs b/MediaBrowser.Server.Mono/Security/X520Attributes.cs deleted file mode 100644 index a61d31ad9..000000000 --- a/MediaBrowser.Server.Mono/Security/X520Attributes.cs +++ /dev/null @@ -1,345 +0,0 @@ -// -// X520.cs: X.520 related stuff (attributes, RDN) -// -// Author: -// Sebastien Pouliot -// -// (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com) -// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com) -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; -using System.Text; - -namespace MediaBrowser.Server.Mono.Security { - - // References: - // 1. Information technology - Open Systems Interconnection - The Directory: Selected attribute types - // http://www.itu.int/rec/recommendation.asp?type=folders&lang=e&parent=T-REC-X.520 - // 2. Internet X.509 Public Key Infrastructure Certificate and CRL Profile - // http://www.ietf.org/rfc/rfc3280.txt - // 3. A Summary of the X.500(96) User Schema for use with LDAPv3 - // http://www.faqs.org/rfcs/rfc2256.html - // 4. RFC 2247 - Using Domains in LDAP/X.500 Distinguished Names - // http://www.faqs.org/rfcs/rfc2247.html - - /* - * AttributeTypeAndValue ::= SEQUENCE { - * type AttributeType, - * value AttributeValue - * } - * - * AttributeType ::= OBJECT IDENTIFIER - * - * AttributeValue ::= ANY DEFINED BY AttributeType - */ - public class X520 { - - public abstract class AttributeTypeAndValue { - private string oid; - private string attrValue; - private int upperBound; - private byte encoding; - - protected AttributeTypeAndValue (string oid, int upperBound) - { - this.oid = oid; - this.upperBound = upperBound; - this.encoding = 0xFF; - } - - protected AttributeTypeAndValue (string oid, int upperBound, byte encoding) - { - this.oid = oid; - this.upperBound = upperBound; - this.encoding = encoding; - } - - public string Value { - get { return attrValue; } - set { - if ((attrValue != null) && (attrValue.Length > upperBound)) { - string msg = ("Value length bigger than upperbound ({0})."); - throw new FormatException (String.Format (msg, upperBound)); - } - attrValue = value; - } - } - - public ASN1 ASN1 { - get { return GetASN1 (); } - } - - internal ASN1 GetASN1 (byte encoding) - { - byte encode = encoding; - if (encode == 0xFF) - encode = SelectBestEncoding (); - - ASN1 asn1 = new ASN1 (0x30); - asn1.Add (ASN1Convert.FromOid (oid)); - switch (encode) { - case 0x13: - // PRINTABLESTRING - asn1.Add (new ASN1 (0x13, Encoding.ASCII.GetBytes (attrValue))); - break; - case 0x16: - // IA5STRING - asn1.Add (new ASN1 (0x16, Encoding.ASCII.GetBytes (attrValue))); - break; - case 0x1E: - // BMPSTRING - asn1.Add (new ASN1 (0x1E, Encoding.BigEndianUnicode.GetBytes (attrValue))); - break; - } - return asn1; - } - - internal ASN1 GetASN1 () - { - return GetASN1 (encoding); - } - - public byte[] GetBytes (byte encoding) - { - return GetASN1 (encoding) .GetBytes (); - } - - public byte[] GetBytes () - { - return GetASN1 () .GetBytes (); - } - - private byte SelectBestEncoding () - { - foreach (char c in attrValue) { - switch (c) { - case '@': - case '_': - return 0x1E; // BMPSTRING - default: - if (c > 127) - return 0x1E; // BMPSTRING - break; - } - } - return 0x13; // PRINTABLESTRING - } - } - - public class Name : AttributeTypeAndValue { - - public Name () : base ("2.5.4.41", 32768) - { - } - } - - public class CommonName : AttributeTypeAndValue { - - public CommonName () : base ("2.5.4.3", 64) - { - } - } - - // RFC2256, Section 5.6 - public class SerialNumber : AttributeTypeAndValue { - - // max length 64 bytes, Printable String only - public SerialNumber () - : base ("2.5.4.5", 64, 0x13) - { - } - } - - public class LocalityName : AttributeTypeAndValue { - - public LocalityName () : base ("2.5.4.7", 128) - { - } - } - - public class StateOrProvinceName : AttributeTypeAndValue { - - public StateOrProvinceName () : base ("2.5.4.8", 128) - { - } - } - - public class OrganizationName : AttributeTypeAndValue { - - public OrganizationName () : base ("2.5.4.10", 64) - { - } - } - - public class OrganizationalUnitName : AttributeTypeAndValue { - - public OrganizationalUnitName () : base ("2.5.4.11", 64) - { - } - } - - // NOTE: Not part of RFC2253 - public class EmailAddress : AttributeTypeAndValue { - - public EmailAddress () : base ("1.2.840.113549.1.9.1", 128, 0x16) - { - } - } - - // RFC2247, Section 4 - public class DomainComponent : AttributeTypeAndValue { - - // no maximum length defined - public DomainComponent () - : base ("0.9.2342.19200300.100.1.25", Int32.MaxValue, 0x16) - { - } - } - - // RFC1274, Section 9.3.1 - public class UserId : AttributeTypeAndValue { - - public UserId () - : base ("0.9.2342.19200300.100.1.1", 256) - { - } - } - - public class Oid : AttributeTypeAndValue { - - public Oid (string oid) - : base (oid, Int32.MaxValue) - { - } - } - - /* -- Naming attributes of type X520Title - * id-at-title AttributeType ::= { id-at 12 } - * - * X520Title ::= CHOICE { - * teletexString TeletexString (SIZE (1..ub-title)), - * printableString PrintableString (SIZE (1..ub-title)), - * universalString UniversalString (SIZE (1..ub-title)), - * utf8String UTF8String (SIZE (1..ub-title)), - * bmpString BMPString (SIZE (1..ub-title)) - * } - */ - public class Title : AttributeTypeAndValue { - - public Title () : base ("2.5.4.12", 64) - { - } - } - - public class CountryName : AttributeTypeAndValue { - - // (0x13) PRINTABLESTRING - public CountryName () : base ("2.5.4.6", 2, 0x13) - { - } - } - - public class DnQualifier : AttributeTypeAndValue { - - // (0x13) PRINTABLESTRING - public DnQualifier () : base ("2.5.4.46", 2, 0x13) - { - } - } - - public class Surname : AttributeTypeAndValue { - - public Surname () : base ("2.5.4.4", 32768) - { - } - } - - public class GivenName : AttributeTypeAndValue { - - public GivenName () : base ("2.5.4.42", 16) - { - } - } - - public class Initial : AttributeTypeAndValue { - - public Initial () : base ("2.5.4.43", 5) - { - } - } - - } - - /* From RFC3280 - * -- specifications of Upper Bounds MUST be regarded as mandatory - * -- from Annex B of ITU-T X.411 Reference Definition of MTS Parameter - * - * -- Upper Bounds - * - * ub-name INTEGER ::= 32768 - * ub-common-name INTEGER ::= 64 - * ub-locality-name INTEGER ::= 128 - * ub-state-name INTEGER ::= 128 - * ub-organization-name INTEGER ::= 64 - * ub-organizational-unit-name INTEGER ::= 64 - * ub-title INTEGER ::= 64 - * ub-serial-number INTEGER ::= 64 - * ub-match INTEGER ::= 128 - * ub-emailaddress-length INTEGER ::= 128 - * ub-common-name-length INTEGER ::= 64 - * ub-country-name-alpha-length INTEGER ::= 2 - * ub-country-name-numeric-length INTEGER ::= 3 - * ub-domain-defined-attributes INTEGER ::= 4 - * ub-domain-defined-attribute-type-length INTEGER ::= 8 - * ub-domain-defined-attribute-value-length INTEGER ::= 128 - * ub-domain-name-length INTEGER ::= 16 - * ub-extension-attributes INTEGER ::= 256 - * ub-e163-4-number-length INTEGER ::= 15 - * ub-e163-4-sub-address-length INTEGER ::= 40 - * ub-generation-qualifier-length INTEGER ::= 3 - * ub-given-name-length INTEGER ::= 16 - * ub-initials-length INTEGER ::= 5 - * ub-integer-options INTEGER ::= 256 - * ub-numeric-user-id-length INTEGER ::= 32 - * ub-organization-name-length INTEGER ::= 64 - * ub-organizational-unit-name-length INTEGER ::= 32 - * ub-organizational-units INTEGER ::= 4 - * ub-pds-name-length INTEGER ::= 16 - * ub-pds-parameter-length INTEGER ::= 30 - * ub-pds-physical-address-lines INTEGER ::= 6 - * ub-postal-code-length INTEGER ::= 16 - * ub-pseudonym INTEGER ::= 128 - * ub-surname-length INTEGER ::= 40 - * ub-terminal-id-length INTEGER ::= 24 - * ub-unformatted-address-length INTEGER ::= 180 - * ub-x121-address-length INTEGER ::= 16 - * - * -- Note - upper bounds on string types, such as TeletexString, are - * -- measured in characters. Excepting PrintableString or IA5String, a - * -- significantly greater number of octets will be required to hold - * -- such a value. As a minimum, 16 octets, or twice the specified - * -- upper bound, whichever is the larger, should be allowed for - * -- TeletexString. For UTF8String or UniversalString at least four - * -- times the upper bound should be allowed. - */ -} diff --git a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs index c09e028ea..e8aa4de3e 100644 --- a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs +++ b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs @@ -552,7 +552,7 @@ namespace MediaBrowser.Server.Startup.Common ZipClient = new ZipClient(FileSystemManager); RegisterSingleInstance(ZipClient); - RegisterSingleInstance(new HttpResultFactory(LogManager, FileSystemManager, JsonSerializer)); + RegisterSingleInstance(new HttpResultFactory(LogManager, FileSystemManager, JsonSerializer, XmlSerializer)); RegisterSingleInstance(this); RegisterSingleInstance(ApplicationPaths); @@ -612,7 +612,7 @@ namespace MediaBrowser.Server.Startup.Common RegisterSingleInstance(() => new SearchEngine(LogManager, LibraryManager, UserManager)); - HttpServer = ServerFactory.CreateServer(this, LogManager, ServerConfigurationManager, NetworkManager, MemoryStreamProvider, "Emby", "web/index.html", textEncoding, SocketFactory, CryptographyProvider); + HttpServer = ServerFactory.CreateServer(this, LogManager, ServerConfigurationManager, NetworkManager, MemoryStreamProvider, "Emby", "web/index.html", textEncoding, SocketFactory, CryptographyProvider, JsonSerializer, XmlSerializer); HttpServer.GlobalResponse = LocalizationManager.GetLocalizedString("StartupEmbyServerIsLoading"); RegisterSingleInstance(HttpServer, false); progress.Report(10); diff --git a/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj b/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj index 97cdddf26..00ecda476 100644 --- a/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj +++ b/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj @@ -22,6 +22,7 @@ DEBUG;TRACE prompt 4 + true None @@ -47,10 +48,6 @@ ..\packages\Patterns.Logging.1.0.0.6\lib\portable-net45+win8\Patterns.Logging.dll True - - False - ..\ThirdParty\ServiceStack\ServiceStack.Interfaces.dll - False ..\ThirdParty\ServiceStack.Text\ServiceStack.Text.dll @@ -85,7 +82,26 @@ + + + + + + + + + + + + + + + + + + + diff --git a/MediaBrowser.Server.Startup.Common/Networking/NetworkManager.cs b/MediaBrowser.Server.Startup.Common/Networking/NetworkManager.cs new file mode 100644 index 000000000..bd260bf87 --- /dev/null +++ b/MediaBrowser.Server.Startup.Common/Networking/NetworkManager.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; +using Emby.Common.Implementations.Networking; +using Emby.Common.Implementations.Security; +using MediaBrowser.Common.Net; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Net; + +namespace MediaBrowser.Server.Startup.Common.Networking +{ + /// + /// Class NetUtils + /// + public class NetworkManager : BaseNetworkManager, INetworkManager + { + public NetworkManager(ILogger logger) + : base(logger) + { + } + + /// + /// Gets the network shares. + /// + /// The path. + /// IEnumerable{NetworkShare}. + public IEnumerable GetNetworkShares(string path) + { + return new List(); + } + + /// + /// Gets available devices within the domain + /// + /// PC's in the Domain + public IEnumerable GetNetworkDevices() + { + return new List(); + } + + /// + /// Generates a self signed certificate at the locatation specified by . + /// + /// The path to generate the certificate. + /// The common name for the certificate. + public void GenerateSelfSignedSslCertificate(string certificatePath, string hostname) + { + CertificateGenerator.CreateSelfSignCertificatePfx(certificatePath, hostname, Logger); + } + } +} diff --git a/MediaBrowser.Server.Startup.Common/Security/ASN1.cs b/MediaBrowser.Server.Startup.Common/Security/ASN1.cs new file mode 100644 index 000000000..a25c27073 --- /dev/null +++ b/MediaBrowser.Server.Startup.Common/Security/ASN1.cs @@ -0,0 +1,339 @@ +// +// ASN1.cs: Abstract Syntax Notation 1 - micro-parser and generator +// +// Authors: +// Sebastien Pouliot +// Jesper Pedersen +// +// (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com) +// Copyright (C) 2004 Novell, Inc (http://www.novell.com) +// (C) 2004 IT+ A/S (http://www.itplus.dk) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections; +using System.IO; +using System.Text; + +namespace Emby.Common.Implementations.Security { + + // References: + // a. ITU ASN.1 standards (free download) + // http://www.itu.int/ITU-T/studygroups/com17/languages/ + + public class ASN1 { + + private byte m_nTag; + private byte[] m_aValue; + private ArrayList elist; + + public ASN1 () : this (0x00, null) {} + + public ASN1 (byte tag) : this (tag, null) {} + + public ASN1 (byte tag, byte[] data) + { + m_nTag = tag; + m_aValue = data; + } + + public ASN1 (byte[] data) + { + m_nTag = data [0]; + + int nLenLength = 0; + int nLength = data [1]; + + if (nLength > 0x80) { + // composed length + nLenLength = nLength - 0x80; + nLength = 0; + for (int i = 0; i < nLenLength; i++) { + nLength *= 256; + nLength += data [i + 2]; + } + } + else if (nLength == 0x80) { + // undefined length encoding + throw new NotSupportedException ("Undefined length encoding."); + } + + m_aValue = new byte [nLength]; + Buffer.BlockCopy (data, (2 + nLenLength), m_aValue, 0, nLength); + + if ((m_nTag & 0x20) == 0x20) { + int nStart = (2 + nLenLength); + Decode (data, ref nStart, data.Length); + } + } + + public int Count { + get { + if (elist == null) + return 0; + return elist.Count; + } + } + + public byte Tag { + get { return m_nTag; } + } + + public int Length { + get { + if (m_aValue != null) + return m_aValue.Length; + else + return 0; + } + } + + public byte[] Value { + get { + if (m_aValue == null) + GetBytes (); + return (byte[]) m_aValue.Clone (); + } + set { + if (value != null) + m_aValue = (byte[]) value.Clone (); + } + } + + private bool CompareArray (byte[] array1, byte[] array2) + { + bool bResult = (array1.Length == array2.Length); + if (bResult) { + for (int i = 0; i < array1.Length; i++) { + if (array1[i] != array2[i]) + return false; + } + } + return bResult; + } + + public bool Equals (byte[] asn1) + { + return CompareArray (this.GetBytes (), asn1); + } + + public bool CompareValue (byte[] value) + { + return CompareArray (m_aValue, value); + } + + public ASN1 Add (ASN1 asn1) + { + if (asn1 != null) { + if (elist == null) + elist = new ArrayList (); + elist.Add (asn1); + } + return asn1; + } + + public virtual byte[] GetBytes () + { + byte[] val = null; + + if (Count > 0) { + int esize = 0; + ArrayList al = new ArrayList (); + foreach (ASN1 a in elist) { + byte[] item = a.GetBytes (); + al.Add (item); + esize += item.Length; + } + val = new byte [esize]; + int pos = 0; + for (int i=0; i < elist.Count; i++) { + byte[] item = (byte[]) al[i]; + Buffer.BlockCopy (item, 0, val, pos, item.Length); + pos += item.Length; + } + } else if (m_aValue != null) { + val = m_aValue; + } + + byte[] der; + int nLengthLen = 0; + + if (val != null) { + int nLength = val.Length; + // special for length > 127 + if (nLength > 127) { + if (nLength <= Byte.MaxValue) { + der = new byte [3 + nLength]; + Buffer.BlockCopy (val, 0, der, 3, nLength); + nLengthLen = 0x81; + der[2] = (byte)(nLength); + } + else if (nLength <= UInt16.MaxValue) { + der = new byte [4 + nLength]; + Buffer.BlockCopy (val, 0, der, 4, nLength); + nLengthLen = 0x82; + der[2] = (byte)(nLength >> 8); + der[3] = (byte)(nLength); + } + else if (nLength <= 0xFFFFFF) { + // 24 bits + der = new byte [5 + nLength]; + Buffer.BlockCopy (val, 0, der, 5, nLength); + nLengthLen = 0x83; + der [2] = (byte)(nLength >> 16); + der [3] = (byte)(nLength >> 8); + der [4] = (byte)(nLength); + } + else { + // max (Length is an integer) 32 bits + der = new byte [6 + nLength]; + Buffer.BlockCopy (val, 0, der, 6, nLength); + nLengthLen = 0x84; + der [2] = (byte)(nLength >> 24); + der [3] = (byte)(nLength >> 16); + der [4] = (byte)(nLength >> 8); + der [5] = (byte)(nLength); + } + } + else { + // basic case (no encoding) + der = new byte [2 + nLength]; + Buffer.BlockCopy (val, 0, der, 2, nLength); + nLengthLen = nLength; + } + if (m_aValue == null) + m_aValue = val; + } + else + der = new byte[2]; + + der[0] = m_nTag; + der[1] = (byte)nLengthLen; + + return der; + } + + // Note: Recursive + protected void Decode (byte[] asn1, ref int anPos, int anLength) + { + byte nTag; + int nLength; + byte[] aValue; + + // minimum is 2 bytes (tag + length of 0) + while (anPos < anLength - 1) { + DecodeTLV (asn1, ref anPos, out nTag, out nLength, out aValue); + // sometimes we get trailing 0 + if (nTag == 0) + continue; + + ASN1 elm = Add (new ASN1 (nTag, aValue)); + + if ((nTag & 0x20) == 0x20) { + int nConstructedPos = anPos; + elm.Decode (asn1, ref nConstructedPos, nConstructedPos + nLength); + } + anPos += nLength; // value length + } + } + + // TLV : Tag - Length - Value + protected void DecodeTLV (byte[] asn1, ref int pos, out byte tag, out int length, out byte[] content) + { + tag = asn1 [pos++]; + length = asn1 [pos++]; + + // special case where L contains the Length of the Length + 0x80 + if ((length & 0x80) == 0x80) { + int nLengthLen = length & 0x7F; + length = 0; + for (int i = 0; i < nLengthLen; i++) + length = length * 256 + asn1 [pos++]; + } + + content = new byte [length]; + Buffer.BlockCopy (asn1, pos, content, 0, length); + } + + public ASN1 this [int index] { + get { + try { + if ((elist == null) || (index >= elist.Count)) + return null; + return (ASN1) elist [index]; + } + catch (ArgumentOutOfRangeException) { + return null; + } + } + } + + public ASN1 Element (int index, byte anTag) + { + try { + if ((elist == null) || (index >= elist.Count)) + return null; + + ASN1 elm = (ASN1) elist [index]; + if (elm.Tag == anTag) + return elm; + else + return null; + } + catch (ArgumentOutOfRangeException) { + return null; + } + } + + public override string ToString() + { + StringBuilder hexLine = new StringBuilder (); + + // Add tag + hexLine.AppendFormat ("Tag: {0} {1}", m_nTag.ToString ("X2"), Environment.NewLine); + + // Add length + hexLine.AppendFormat ("Length: {0} {1}", Value.Length, Environment.NewLine); + + // Add value + hexLine.Append ("Value: "); + hexLine.Append (Environment.NewLine); + for (int i = 0; i < Value.Length; i++) { + hexLine.AppendFormat ("{0} ", Value [i].ToString ("X2")); + if ((i+1) % 16 == 0) + hexLine.AppendFormat (Environment.NewLine); + } + return hexLine.ToString (); + } + + public void SaveToFile (string filename) + { + if (filename == null) + throw new ArgumentNullException ("filename"); + + using (FileStream fs = File.Create (filename)) { + byte[] data = GetBytes (); + fs.Write (data, 0, data.Length); + } + } + } +} diff --git a/MediaBrowser.Server.Startup.Common/Security/ASN1Convert.cs b/MediaBrowser.Server.Startup.Common/Security/ASN1Convert.cs new file mode 100644 index 000000000..8a2a487c8 --- /dev/null +++ b/MediaBrowser.Server.Startup.Common/Security/ASN1Convert.cs @@ -0,0 +1,207 @@ +// +// ASN1Convert.cs: Abstract Syntax Notation 1 convertion routines +// +// Authors: +// Sebastien Pouliot +// Jesper Pedersen +// +// (C) 2003 Motus Technologies Inc. (http://www.motus.com) +// (C) 2004 IT+ A/S (http://www.itplus.dk) +// Copyright (C) 2004-2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Globalization; +using System.Security.Cryptography; +using System.Text; + +namespace Emby.Common.Implementations.Security +{ + + // References: + // a. ITU ASN.1 standards (free download) + // http://www.itu.int/ITU-T/studygroups/com17/languages/ + + public static class ASN1Convert { + // RFC3280, section 4.2.1.5 + // CAs conforming to this profile MUST always encode certificate + // validity dates through the year 2049 as UTCTime; certificate validity + // dates in 2050 or later MUST be encoded as GeneralizedTime. + + // Under 1.x this API requires a Local datetime to be provided + // Under 2.0 it will also accept a Utc datetime + static public ASN1 FromDateTime (DateTime dt) + { + if (dt.Year < 2050) { + // UTCTIME + return new ASN1 (0x17, Encoding.ASCII.GetBytes ( + dt.ToUniversalTime ().ToString ("yyMMddHHmmss", + CultureInfo.InvariantCulture) + "Z")); + } + else { + // GENERALIZEDTIME + return new ASN1 (0x18, Encoding.ASCII.GetBytes ( + dt.ToUniversalTime ().ToString ("yyyyMMddHHmmss", + CultureInfo.InvariantCulture) + "Z")); + } + } + + static public ASN1 FromInt32 (Int32 value) + { + byte[] integer = BitConverterLE.GetBytes (value); + Array.Reverse (integer); + int x = 0; + while ((x < integer.Length) && (integer [x] == 0x00)) + x++; + ASN1 asn1 = new ASN1 (0x02); + switch (x) { + case 0: + asn1.Value = integer; + break; + case 4: + asn1.Value = new byte [1]; + break; + default: + byte[] smallerInt = new byte [4 - x]; + Buffer.BlockCopy (integer, x, smallerInt, 0, smallerInt.Length); + asn1.Value = smallerInt; + break; + } + return asn1; + } + + static public ASN1 FromOid (string oid) + { + if (oid == null) + throw new ArgumentNullException ("oid"); + + return new ASN1 (CryptoConfig.EncodeOID (oid)); + } + + static public ASN1 FromUnsignedBigInteger (byte[] big) + { + if (big == null) + throw new ArgumentNullException ("big"); + + // check for numbers that could be interpreted as negative (first bit) + if (big [0] >= 0x80) { + // in thie cas we add a new, empty, byte (position 0) so we're + // sure this will always be interpreted an unsigned integer. + // However we can't feed it into RSAParameters or DSAParameters + int length = big.Length + 1; + byte[] uinteger = new byte [length]; + Buffer.BlockCopy (big, 0, uinteger, 1, length - 1); + big = uinteger; + } + return new ASN1 (0x02, big); + } + + static public int ToInt32 (ASN1 asn1) + { + if (asn1 == null) + throw new ArgumentNullException ("asn1"); + if (asn1.Tag != 0x02) + throw new FormatException ("Only integer can be converted"); + + int x = 0; + for (int i=0; i < asn1.Value.Length; i++) + x = (x << 8) + asn1.Value [i]; + return x; + } + + // Convert a binary encoded OID to human readable string representation of + // an OID (IETF style). Based on DUMPASN1.C from Peter Gutmann. + static public string ToOid (ASN1 asn1) + { + if (asn1 == null) + throw new ArgumentNullException ("asn1"); + + byte[] aOID = asn1.Value; + StringBuilder sb = new StringBuilder (); + // Pick apart the OID + byte x = (byte) (aOID[0] / 40); + byte y = (byte) (aOID[0] % 40); + if (x > 2) { + // Handle special case for large y if x = 2 + y += (byte) ((x - 2) * 40); + x = 2; + } + sb.Append (x.ToString (CultureInfo.InvariantCulture)); + sb.Append ("."); + sb.Append (y.ToString (CultureInfo.InvariantCulture)); + ulong val = 0; + for (x = 1; x < aOID.Length; x++) { + val = ((val << 7) | ((byte) (aOID [x] & 0x7F))); + if ( !((aOID [x] & 0x80) == 0x80)) { + sb.Append ("."); + sb.Append (val.ToString (CultureInfo.InvariantCulture)); + val = 0; + } + } + return sb.ToString (); + } + + static public DateTime ToDateTime (ASN1 time) + { + if (time == null) + throw new ArgumentNullException ("time"); + + string t = Encoding.ASCII.GetString (time.Value); + // to support both UTCTime and GeneralizedTime (and not so common format) + string mask = null; + int year; + switch (t.Length) { + case 11: + // illegal format, still it's supported for compatibility + mask = "yyMMddHHmmZ"; + break; + case 13: + // RFC3280: 4.1.2.5.1 UTCTime + year = Convert.ToInt16 (t.Substring (0, 2), CultureInfo.InvariantCulture); + // Where YY is greater than or equal to 50, the + // year SHALL be interpreted as 19YY; and + // Where YY is less than 50, the year SHALL be + // interpreted as 20YY. + if (year >= 50) + t = "19" + t; + else + t = "20" + t; + mask = "yyyyMMddHHmmssZ"; + break; + case 15: + mask = "yyyyMMddHHmmssZ"; // GeneralizedTime + break; + case 17: + // another illegal format (990630000000+1000), again supported for compatibility + year = Convert.ToInt16 (t.Substring (0, 2), CultureInfo.InvariantCulture); + string century = (year >= 50) ? "19" : "20"; + // ASN.1 (see ITU X.680 section 43.3) deals with offset differently than .NET + char sign = (t[12] == '+') ? '-' : '+'; + t = String.Format ("{0}{1}{2}{3}{4}:{5}{6}", century, t.Substring (0, 12), sign, + t[13], t[14], t[15], t[16]); + mask = "yyyyMMddHHmmsszzz"; + break; + } + return DateTime.ParseExact (t, mask, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal); + } + } +} diff --git a/MediaBrowser.Server.Startup.Common/Security/BitConverterLE.cs b/MediaBrowser.Server.Startup.Common/Security/BitConverterLE.cs new file mode 100644 index 000000000..240c958a3 --- /dev/null +++ b/MediaBrowser.Server.Startup.Common/Security/BitConverterLE.cs @@ -0,0 +1,239 @@ +// +// Mono.Security.BitConverterLE.cs +// Like System.BitConverter but always little endian +// +// Author: +// Bernie Solomon +// + +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; + +namespace Emby.Common.Implementations.Security +{ + internal sealed class BitConverterLE + { + private BitConverterLE () + { + } + + unsafe private static byte[] GetUShortBytes (byte *bytes) + { + if (BitConverter.IsLittleEndian) + return new byte [] { bytes [0], bytes [1] }; + else + return new byte [] { bytes [1], bytes [0] }; + } + + unsafe private static byte[] GetUIntBytes (byte *bytes) + { + if (BitConverter.IsLittleEndian) + return new byte [] { bytes [0], bytes [1], bytes [2], bytes [3] }; + else + return new byte [] { bytes [3], bytes [2], bytes [1], bytes [0] }; + } + + unsafe private static byte[] GetULongBytes (byte *bytes) + { + if (BitConverter.IsLittleEndian) + return new byte [] { bytes [0], bytes [1], bytes [2], bytes [3], + bytes [4], bytes [5], bytes [6], bytes [7] }; + else + return new byte [] { bytes [7], bytes [6], bytes [5], bytes [4], + bytes [3], bytes [2], bytes [1], bytes [0] }; + } + + unsafe internal static byte[] GetBytes (bool value) + { + return new byte [] { value ? (byte)1 : (byte)0 }; + } + + unsafe internal static byte[] GetBytes (char value) + { + return GetUShortBytes ((byte *) &value); + } + + unsafe internal static byte[] GetBytes (short value) + { + return GetUShortBytes ((byte *) &value); + } + + unsafe internal static byte[] GetBytes (int value) + { + return GetUIntBytes ((byte *) &value); + } + + unsafe internal static byte[] GetBytes (long value) + { + return GetULongBytes ((byte *) &value); + } + + unsafe internal static byte[] GetBytes (ushort value) + { + return GetUShortBytes ((byte *) &value); + } + + unsafe internal static byte[] GetBytes (uint value) + { + return GetUIntBytes ((byte *) &value); + } + + unsafe internal static byte[] GetBytes (ulong value) + { + return GetULongBytes ((byte *) &value); + } + + unsafe internal static byte[] GetBytes (float value) + { + return GetUIntBytes ((byte *) &value); + } + + unsafe internal static byte[] GetBytes (double value) + { + return GetULongBytes ((byte *) &value); + } + + unsafe private static void UShortFromBytes (byte *dst, byte[] src, int startIndex) + { + if (BitConverter.IsLittleEndian) { + dst [0] = src [startIndex]; + dst [1] = src [startIndex + 1]; + } else { + dst [0] = src [startIndex + 1]; + dst [1] = src [startIndex]; + } + } + + unsafe private static void UIntFromBytes (byte *dst, byte[] src, int startIndex) + { + if (BitConverter.IsLittleEndian) { + dst [0] = src [startIndex]; + dst [1] = src [startIndex + 1]; + dst [2] = src [startIndex + 2]; + dst [3] = src [startIndex + 3]; + } else { + dst [0] = src [startIndex + 3]; + dst [1] = src [startIndex + 2]; + dst [2] = src [startIndex + 1]; + dst [3] = src [startIndex]; + } + } + + unsafe private static void ULongFromBytes (byte *dst, byte[] src, int startIndex) + { + if (BitConverter.IsLittleEndian) { + for (int i = 0; i < 8; ++i) + dst [i] = src [startIndex + i]; + } else { + for (int i = 0; i < 8; ++i) + dst [i] = src [startIndex + (7 - i)]; + } + } + + unsafe internal static bool ToBoolean (byte[] value, int startIndex) + { + return value [startIndex] != 0; + } + + unsafe internal static char ToChar (byte[] value, int startIndex) + { + char ret; + + UShortFromBytes ((byte *) &ret, value, startIndex); + + return ret; + } + + unsafe internal static short ToInt16 (byte[] value, int startIndex) + { + short ret; + + UShortFromBytes ((byte *) &ret, value, startIndex); + + return ret; + } + + unsafe internal static int ToInt32 (byte[] value, int startIndex) + { + int ret; + + UIntFromBytes ((byte *) &ret, value, startIndex); + + return ret; + } + + unsafe internal static long ToInt64 (byte[] value, int startIndex) + { + long ret; + + ULongFromBytes ((byte *) &ret, value, startIndex); + + return ret; + } + + unsafe internal static ushort ToUInt16 (byte[] value, int startIndex) + { + ushort ret; + + UShortFromBytes ((byte *) &ret, value, startIndex); + + return ret; + } + + unsafe internal static uint ToUInt32 (byte[] value, int startIndex) + { + uint ret; + + UIntFromBytes ((byte *) &ret, value, startIndex); + + return ret; + } + + unsafe internal static ulong ToUInt64 (byte[] value, int startIndex) + { + ulong ret; + + ULongFromBytes ((byte *) &ret, value, startIndex); + + return ret; + } + + unsafe internal static float ToSingle (byte[] value, int startIndex) + { + float ret; + + UIntFromBytes ((byte *) &ret, value, startIndex); + + return ret; + } + + unsafe internal static double ToDouble (byte[] value, int startIndex) + { + double ret; + + ULongFromBytes ((byte *) &ret, value, startIndex); + + return ret; + } + } +} diff --git a/MediaBrowser.Server.Startup.Common/Security/CertificateGenerator.cs b/MediaBrowser.Server.Startup.Common/Security/CertificateGenerator.cs new file mode 100644 index 000000000..f36b14cae --- /dev/null +++ b/MediaBrowser.Server.Startup.Common/Security/CertificateGenerator.cs @@ -0,0 +1,67 @@ +using MediaBrowser.Model.Logging; +using System; +using System.Collections; +using System.Security.Cryptography; + +namespace Emby.Common.Implementations.Security +{ + internal class CertificateGenerator + { + private const string MonoTestRootAgency = "v/4nALBxCE+9JgEC0LnDUvKh6e96PwTpN4Rj+vWnqKT7IAp1iK/JjuqvAg6DQ2vTfv0dTlqffmHH51OyioprcT5nzxcSTsZb/9jcHScG0s3/FRIWnXeLk/fgm7mSYhjUaHNI0m1/NTTktipicjKxo71hGIg9qucCWnDum+Krh/k=AQAB

9jbKxMXEruW2CfZrzhxtull4O8P47+mNsEL+9gf9QsRO1jJ77C+jmzfU6zbzjf8+ViK+q62tCMdC1ZzulwdpXQ==

x5+p198l1PkK0Ga2mRh0SIYSykENpY2aLXoyZD/iUpKYAvATm0/wvKNrE4dKJyPCA+y3hfTdgVag+SP9avvDTQ==ISSjCvXsUfbOGG05eddN1gXxL2pj+jegQRfjpk7RAsnWKvNExzhqd5x+ZuNQyc6QH5wxun54inP4RTUI0P/IaQ==R815VQmR3RIbPqzDXzv5j6CSH6fYlcTiQRtkBsUnzhWmkd/y3XmamO+a8zJFjOCCx9CcjpVuGziivBqi65lVPQ==iYiu0KwMWI/dyqN3RJYUzuuLj02/oTD1pYpwo2rvNCXU1Q5VscOeu2DpNg1gWqI+1RrRCsEoaTNzXB1xtKNlSw==nIfh1LYF8fjRBgMdAH/zt9UKHWiaCnc+jXzq5tkR8HVSKTVdzitD8bl1JgAfFQD8VjSXiCJqluexy/B5SGrCXQ49c78NIQj0hD+J13Y8/E0fUbW1QYbhj6Ff7oHyhaYe1WOQfkp2t/h+llHOdt1HRf7bt7dUknYp7m8bQKGxoYE=
"; + + internal static void CreateSelfSignCertificatePfx( + string fileName, + string hostname, + ILogger logger) + { + if (string.IsNullOrWhiteSpace(fileName)) + { + throw new ArgumentNullException("fileName"); + } + + byte[] sn = Guid.NewGuid().ToByteArray(); + string subject = string.Format("CN={0}", hostname); + string issuer = subject; + DateTime notBefore = DateTime.Now.AddDays(-2); + DateTime notAfter = DateTime.Now.AddYears(10); + + RSA issuerKey = RSA.Create(); + issuerKey.FromXmlString(MonoTestRootAgency); + RSA subjectKey = RSA.Create(); + + // serial number MUST be positive + if ((sn[0] & 0x80) == 0x80) + sn[0] -= 0x80; + + issuer = subject; + issuerKey = subjectKey; + + X509CertificateBuilder cb = new X509CertificateBuilder(3); + cb.SerialNumber = sn; + cb.IssuerName = issuer; + cb.NotBefore = notBefore; + cb.NotAfter = notAfter; + cb.SubjectName = subject; + cb.SubjectPublicKey = subjectKey; + + // signature + cb.Hash = "SHA256"; + byte[] rawcert = cb.Sign(issuerKey); + + PKCS12 p12 = new PKCS12(); + + + ArrayList list = new ArrayList(); + // we use a fixed array to avoid endianess issues + // (in case some tools requires the ID to be 1). + list.Add(new byte[4] { 1, 0, 0, 0 }); + Hashtable attributes = new Hashtable(1); + attributes.Add(PKCS9.localKeyId, list); + + p12.AddCertificate(new X509Certificate(rawcert), attributes); + + p12.AddPkcs8ShroudedKeyBag(subjectKey, attributes); + p12.SaveToFile(fileName); + } + } +} diff --git a/MediaBrowser.Server.Startup.Common/Security/CryptoConvert.cs b/MediaBrowser.Server.Startup.Common/Security/CryptoConvert.cs new file mode 100644 index 000000000..c6e466534 --- /dev/null +++ b/MediaBrowser.Server.Startup.Common/Security/CryptoConvert.cs @@ -0,0 +1,745 @@ +// +// CryptoConvert.cs - Crypto Convertion Routines +// +// Author: +// Sebastien Pouliot +// +// (C) 2003 Motus Technologies Inc. (http://www.motus.com) +// Copyright (C) 2004-2006 Novell Inc. (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Globalization; +using System.Security.Cryptography; +using System.Text; + +namespace Emby.Common.Implementations.Security +{ + + public sealed class CryptoConvert { + + private CryptoConvert () + { + } + + static private int ToInt32LE (byte [] bytes, int offset) + { + return (bytes [offset+3] << 24) | (bytes [offset+2] << 16) | (bytes [offset+1] << 8) | bytes [offset]; + } + + static private uint ToUInt32LE (byte [] bytes, int offset) + { + return (uint)((bytes [offset+3] << 24) | (bytes [offset+2] << 16) | (bytes [offset+1] << 8) | bytes [offset]); + } + + static private byte [] GetBytesLE (int val) + { + return new byte [] { + (byte) (val & 0xff), + (byte) ((val >> 8) & 0xff), + (byte) ((val >> 16) & 0xff), + (byte) ((val >> 24) & 0xff) + }; + } + + static private byte[] Trim (byte[] array) + { + for (int i=0; i < array.Length; i++) { + if (array [i] != 0x00) { + byte[] result = new byte [array.Length - i]; + Buffer.BlockCopy (array, i, result, 0, result.Length); + return result; + } + } + return null; + } + + // convert the key from PRIVATEKEYBLOB to RSA + // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/security/Security/private_key_blobs.asp + // e.g. SNK files, PVK files + static public RSA FromCapiPrivateKeyBlob (byte[] blob) + { + return FromCapiPrivateKeyBlob (blob, 0); + } + + static public RSA FromCapiPrivateKeyBlob (byte[] blob, int offset) + { + if (blob == null) + throw new ArgumentNullException ("blob"); + if (offset >= blob.Length) + throw new ArgumentException ("blob is too small."); + + RSAParameters rsap = new RSAParameters (); + try { + if ((blob [offset] != 0x07) || // PRIVATEKEYBLOB (0x07) + (blob [offset+1] != 0x02) || // Version (0x02) + (blob [offset+2] != 0x00) || // Reserved (word) + (blob [offset+3] != 0x00) || + (ToUInt32LE (blob, offset+8) != 0x32415352)) // DWORD magic = RSA2 + throw new CryptographicException ("Invalid blob header"); + + // ALGID (CALG_RSA_SIGN, CALG_RSA_KEYX, ...) + // int algId = ToInt32LE (blob, offset+4); + + // DWORD bitlen + int bitLen = ToInt32LE (blob, offset+12); + + // DWORD public exponent + byte[] exp = new byte [4]; + Buffer.BlockCopy (blob, offset+16, exp, 0, 4); + Array.Reverse (exp); + rsap.Exponent = Trim (exp); + + int pos = offset+20; + // BYTE modulus[rsapubkey.bitlen/8]; + int byteLen = (bitLen >> 3); + rsap.Modulus = new byte [byteLen]; + Buffer.BlockCopy (blob, pos, rsap.Modulus, 0, byteLen); + Array.Reverse (rsap.Modulus); + pos += byteLen; + + // BYTE prime1[rsapubkey.bitlen/16]; + int byteHalfLen = (byteLen >> 1); + rsap.P = new byte [byteHalfLen]; + Buffer.BlockCopy (blob, pos, rsap.P, 0, byteHalfLen); + Array.Reverse (rsap.P); + pos += byteHalfLen; + + // BYTE prime2[rsapubkey.bitlen/16]; + rsap.Q = new byte [byteHalfLen]; + Buffer.BlockCopy (blob, pos, rsap.Q, 0, byteHalfLen); + Array.Reverse (rsap.Q); + pos += byteHalfLen; + + // BYTE exponent1[rsapubkey.bitlen/16]; + rsap.DP = new byte [byteHalfLen]; + Buffer.BlockCopy (blob, pos, rsap.DP, 0, byteHalfLen); + Array.Reverse (rsap.DP); + pos += byteHalfLen; + + // BYTE exponent2[rsapubkey.bitlen/16]; + rsap.DQ = new byte [byteHalfLen]; + Buffer.BlockCopy (blob, pos, rsap.DQ, 0, byteHalfLen); + Array.Reverse (rsap.DQ); + pos += byteHalfLen; + + // BYTE coefficient[rsapubkey.bitlen/16]; + rsap.InverseQ = new byte [byteHalfLen]; + Buffer.BlockCopy (blob, pos, rsap.InverseQ, 0, byteHalfLen); + Array.Reverse (rsap.InverseQ); + pos += byteHalfLen; + + // ok, this is hackish but CryptoAPI support it so... + // note: only works because CRT is used by default + // http://bugzilla.ximian.com/show_bug.cgi?id=57941 + rsap.D = new byte [byteLen]; // must be allocated + if (pos + byteLen + offset <= blob.Length) { + // BYTE privateExponent[rsapubkey.bitlen/8]; + Buffer.BlockCopy (blob, pos, rsap.D, 0, byteLen); + Array.Reverse (rsap.D); + } + } + catch (Exception e) { + throw new CryptographicException ("Invalid blob.", e); + } + + RSA rsa = null; + try + { + rsa = RSA.Create(); + rsa.ImportParameters(rsap); + } + catch (CryptographicException ce) + { + // this may cause problem when this code is run under + // the SYSTEM identity on Windows (e.g. ASP.NET). See + // http://bugzilla.ximian.com/show_bug.cgi?id=77559 + try + { + CspParameters csp = new CspParameters(); + csp.Flags = CspProviderFlags.UseMachineKeyStore; + rsa = new RSACryptoServiceProvider(csp); + rsa.ImportParameters(rsap); + } + catch + { + // rethrow original, not the later, exception if this fails + throw ce; + } + } + return rsa; + } + + static public DSA FromCapiPrivateKeyBlobDSA (byte[] blob) + { + return FromCapiPrivateKeyBlobDSA (blob, 0); + } + + static public DSA FromCapiPrivateKeyBlobDSA (byte[] blob, int offset) + { + if (blob == null) + throw new ArgumentNullException ("blob"); + if (offset >= blob.Length) + throw new ArgumentException ("blob is too small."); + + DSAParameters dsap = new DSAParameters (); + try { + if ((blob [offset] != 0x07) || // PRIVATEKEYBLOB (0x07) + (blob [offset + 1] != 0x02) || // Version (0x02) + (blob [offset + 2] != 0x00) || // Reserved (word) + (blob [offset + 3] != 0x00) || + (ToUInt32LE (blob, offset + 8) != 0x32535344)) // DWORD magic + throw new CryptographicException ("Invalid blob header"); + + int bitlen = ToInt32LE (blob, offset + 12); + int bytelen = bitlen >> 3; + int pos = offset + 16; + + dsap.P = new byte [bytelen]; + Buffer.BlockCopy (blob, pos, dsap.P, 0, bytelen); + Array.Reverse (dsap.P); + pos += bytelen; + + dsap.Q = new byte [20]; + Buffer.BlockCopy (blob, pos, dsap.Q, 0, 20); + Array.Reverse (dsap.Q); + pos += 20; + + dsap.G = new byte [bytelen]; + Buffer.BlockCopy (blob, pos, dsap.G, 0, bytelen); + Array.Reverse (dsap.G); + pos += bytelen; + + dsap.X = new byte [20]; + Buffer.BlockCopy (blob, pos, dsap.X, 0, 20); + Array.Reverse (dsap.X); + pos += 20; + + dsap.Counter = ToInt32LE (blob, pos); + pos += 4; + + dsap.Seed = new byte [20]; + Buffer.BlockCopy (blob, pos, dsap.Seed, 0, 20); + Array.Reverse (dsap.Seed); + pos += 20; + } + catch (Exception e) { + throw new CryptographicException ("Invalid blob.", e); + } + + DSA dsa = null; + try + { + dsa = (DSA)DSA.Create(); + dsa.ImportParameters(dsap); + } + catch (CryptographicException ce) + { + // this may cause problem when this code is run under + // the SYSTEM identity on Windows (e.g. ASP.NET). See + // http://bugzilla.ximian.com/show_bug.cgi?id=77559 + try + { + CspParameters csp = new CspParameters(); + csp.Flags = CspProviderFlags.UseMachineKeyStore; + dsa = new DSACryptoServiceProvider(csp); + dsa.ImportParameters(dsap); + } + catch + { + // rethrow original, not the later, exception if this fails + throw ce; + } + } + return dsa; + } + + static public byte[] ToCapiPrivateKeyBlob (RSA rsa) + { + RSAParameters p = rsa.ExportParameters (true); + int keyLength = p.Modulus.Length; // in bytes + byte[] blob = new byte [20 + (keyLength << 2) + (keyLength >> 1)]; + + blob [0] = 0x07; // Type - PRIVATEKEYBLOB (0x07) + blob [1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02) + // [2], [3] // RESERVED - Always 0 + blob [5] = 0x24; // ALGID - Always 00 24 00 00 (for CALG_RSA_SIGN) + blob [8] = 0x52; // Magic - RSA2 (ASCII in hex) + blob [9] = 0x53; + blob [10] = 0x41; + blob [11] = 0x32; + + byte[] bitlen = GetBytesLE (keyLength << 3); + blob [12] = bitlen [0]; // bitlen + blob [13] = bitlen [1]; + blob [14] = bitlen [2]; + blob [15] = bitlen [3]; + + // public exponent (DWORD) + int pos = 16; + int n = p.Exponent.Length; + while (n > 0) + blob [pos++] = p.Exponent [--n]; + // modulus + pos = 20; + byte[] part = p.Modulus; + int len = part.Length; + Array.Reverse (part, 0, len); + Buffer.BlockCopy (part, 0, blob, pos, len); + pos += len; + // private key + part = p.P; + len = part.Length; + Array.Reverse (part, 0, len); + Buffer.BlockCopy (part, 0, blob, pos, len); + pos += len; + + part = p.Q; + len = part.Length; + Array.Reverse (part, 0, len); + Buffer.BlockCopy (part, 0, blob, pos, len); + pos += len; + + part = p.DP; + len = part.Length; + Array.Reverse (part, 0, len); + Buffer.BlockCopy (part, 0, blob, pos, len); + pos += len; + + part = p.DQ; + len = part.Length; + Array.Reverse (part, 0, len); + Buffer.BlockCopy (part, 0, blob, pos, len); + pos += len; + + part = p.InverseQ; + len = part.Length; + Array.Reverse (part, 0, len); + Buffer.BlockCopy (part, 0, blob, pos, len); + pos += len; + + part = p.D; + len = part.Length; + Array.Reverse (part, 0, len); + Buffer.BlockCopy (part, 0, blob, pos, len); + + return blob; + } + + static public byte[] ToCapiPrivateKeyBlob (DSA dsa) + { + DSAParameters p = dsa.ExportParameters (true); + int keyLength = p.P.Length; // in bytes + + // header + P + Q + G + X + count + seed + byte[] blob = new byte [16 + keyLength + 20 + keyLength + 20 + 4 + 20]; + + blob [0] = 0x07; // Type - PRIVATEKEYBLOB (0x07) + blob [1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02) + // [2], [3] // RESERVED - Always 0 + blob [5] = 0x22; // ALGID + blob [8] = 0x44; // Magic + blob [9] = 0x53; + blob [10] = 0x53; + blob [11] = 0x32; + + byte[] bitlen = GetBytesLE (keyLength << 3); + blob [12] = bitlen [0]; + blob [13] = bitlen [1]; + blob [14] = bitlen [2]; + blob [15] = bitlen [3]; + + int pos = 16; + byte[] part = p.P; + Array.Reverse (part); + Buffer.BlockCopy (part, 0, blob, pos, keyLength); + pos += keyLength; + + part = p.Q; + Array.Reverse (part); + Buffer.BlockCopy (part, 0, blob, pos, 20); + pos += 20; + + part = p.G; + Array.Reverse (part); + Buffer.BlockCopy (part, 0, blob, pos, keyLength); + pos += keyLength; + + part = p.X; + Array.Reverse (part); + Buffer.BlockCopy (part, 0, blob, pos, 20); + pos += 20; + + Buffer.BlockCopy (GetBytesLE (p.Counter), 0, blob, pos, 4); + pos += 4; + + part = p.Seed; + Array.Reverse (part); + Buffer.BlockCopy (part, 0, blob, pos, 20); + + return blob; + } + + static public RSA FromCapiPublicKeyBlob (byte[] blob) + { + return FromCapiPublicKeyBlob (blob, 0); + } + + static public RSA FromCapiPublicKeyBlob (byte[] blob, int offset) + { + if (blob == null) + throw new ArgumentNullException ("blob"); + if (offset >= blob.Length) + throw new ArgumentException ("blob is too small."); + + try { + if ((blob [offset] != 0x06) || // PUBLICKEYBLOB (0x06) + (blob [offset+1] != 0x02) || // Version (0x02) + (blob [offset+2] != 0x00) || // Reserved (word) + (blob [offset+3] != 0x00) || + (ToUInt32LE (blob, offset+8) != 0x31415352)) // DWORD magic = RSA1 + throw new CryptographicException ("Invalid blob header"); + + // ALGID (CALG_RSA_SIGN, CALG_RSA_KEYX, ...) + // int algId = ToInt32LE (blob, offset+4); + + // DWORD bitlen + int bitLen = ToInt32LE (blob, offset+12); + + // DWORD public exponent + RSAParameters rsap = new RSAParameters (); + rsap.Exponent = new byte [3]; + rsap.Exponent [0] = blob [offset+18]; + rsap.Exponent [1] = blob [offset+17]; + rsap.Exponent [2] = blob [offset+16]; + + int pos = offset+20; + // BYTE modulus[rsapubkey.bitlen/8]; + int byteLen = (bitLen >> 3); + rsap.Modulus = new byte [byteLen]; + Buffer.BlockCopy (blob, pos, rsap.Modulus, 0, byteLen); + Array.Reverse (rsap.Modulus); + RSA rsa = null; + try + { + rsa = RSA.Create(); + rsa.ImportParameters(rsap); + } + catch (CryptographicException) + { + // this may cause problem when this code is run under + // the SYSTEM identity on Windows (e.g. ASP.NET). See + // http://bugzilla.ximian.com/show_bug.cgi?id=77559 + CspParameters csp = new CspParameters(); + csp.Flags = CspProviderFlags.UseMachineKeyStore; + rsa = new RSACryptoServiceProvider(csp); + rsa.ImportParameters(rsap); + } + return rsa; + } + catch (Exception e) { + throw new CryptographicException ("Invalid blob.", e); + } + } + + static public DSA FromCapiPublicKeyBlobDSA (byte[] blob) + { + return FromCapiPublicKeyBlobDSA (blob, 0); + } + + static public DSA FromCapiPublicKeyBlobDSA (byte[] blob, int offset) + { + if (blob == null) + throw new ArgumentNullException ("blob"); + if (offset >= blob.Length) + throw new ArgumentException ("blob is too small."); + + try { + if ((blob [offset] != 0x06) || // PUBLICKEYBLOB (0x06) + (blob [offset + 1] != 0x02) || // Version (0x02) + (blob [offset + 2] != 0x00) || // Reserved (word) + (blob [offset + 3] != 0x00) || + (ToUInt32LE (blob, offset + 8) != 0x31535344)) // DWORD magic + throw new CryptographicException ("Invalid blob header"); + + int bitlen = ToInt32LE (blob, offset + 12); + DSAParameters dsap = new DSAParameters (); + int bytelen = bitlen >> 3; + int pos = offset + 16; + + dsap.P = new byte [bytelen]; + Buffer.BlockCopy (blob, pos, dsap.P, 0, bytelen); + Array.Reverse (dsap.P); + pos += bytelen; + + dsap.Q = new byte [20]; + Buffer.BlockCopy (blob, pos, dsap.Q, 0, 20); + Array.Reverse (dsap.Q); + pos += 20; + + dsap.G = new byte [bytelen]; + Buffer.BlockCopy (blob, pos, dsap.G, 0, bytelen); + Array.Reverse (dsap.G); + pos += bytelen; + + dsap.Y = new byte [bytelen]; + Buffer.BlockCopy (blob, pos, dsap.Y, 0, bytelen); + Array.Reverse (dsap.Y); + pos += bytelen; + + dsap.Counter = ToInt32LE (blob, pos); + pos += 4; + + dsap.Seed = new byte [20]; + Buffer.BlockCopy (blob, pos, dsap.Seed, 0, 20); + Array.Reverse (dsap.Seed); + pos += 20; + + DSA dsa = (DSA)DSA.Create (); + dsa.ImportParameters (dsap); + return dsa; + } + catch (Exception e) { + throw new CryptographicException ("Invalid blob.", e); + } + } + + static public byte[] ToCapiPublicKeyBlob (RSA rsa) + { + RSAParameters p = rsa.ExportParameters (false); + int keyLength = p.Modulus.Length; // in bytes + byte[] blob = new byte [20 + keyLength]; + + blob [0] = 0x06; // Type - PUBLICKEYBLOB (0x06) + blob [1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02) + // [2], [3] // RESERVED - Always 0 + blob [5] = 0x24; // ALGID - Always 00 24 00 00 (for CALG_RSA_SIGN) + blob [8] = 0x52; // Magic - RSA1 (ASCII in hex) + blob [9] = 0x53; + blob [10] = 0x41; + blob [11] = 0x31; + + byte[] bitlen = GetBytesLE (keyLength << 3); + blob [12] = bitlen [0]; // bitlen + blob [13] = bitlen [1]; + blob [14] = bitlen [2]; + blob [15] = bitlen [3]; + + // public exponent (DWORD) + int pos = 16; + int n = p.Exponent.Length; + while (n > 0) + blob [pos++] = p.Exponent [--n]; + // modulus + pos = 20; + byte[] part = p.Modulus; + int len = part.Length; + Array.Reverse (part, 0, len); + Buffer.BlockCopy (part, 0, blob, pos, len); + pos += len; + return blob; + } + + static public byte[] ToCapiPublicKeyBlob (DSA dsa) + { + DSAParameters p = dsa.ExportParameters (false); + int keyLength = p.P.Length; // in bytes + + // header + P + Q + G + Y + count + seed + byte[] blob = new byte [16 + keyLength + 20 + keyLength + keyLength + 4 + 20]; + + blob [0] = 0x06; // Type - PUBLICKEYBLOB (0x06) + blob [1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02) + // [2], [3] // RESERVED - Always 0 + blob [5] = 0x22; // ALGID + blob [8] = 0x44; // Magic + blob [9] = 0x53; + blob [10] = 0x53; + blob [11] = 0x31; + + byte[] bitlen = GetBytesLE (keyLength << 3); + blob [12] = bitlen [0]; + blob [13] = bitlen [1]; + blob [14] = bitlen [2]; + blob [15] = bitlen [3]; + + int pos = 16; + byte[] part; + + part = p.P; + Array.Reverse (part); + Buffer.BlockCopy (part, 0, blob, pos, keyLength); + pos += keyLength; + + part = p.Q; + Array.Reverse (part); + Buffer.BlockCopy (part, 0, blob, pos, 20); + pos += 20; + + part = p.G; + Array.Reverse (part); + Buffer.BlockCopy (part, 0, blob, pos, keyLength); + pos += keyLength; + + part = p.Y; + Array.Reverse (part); + Buffer.BlockCopy (part, 0, blob, pos, keyLength); + pos += keyLength; + + Buffer.BlockCopy (GetBytesLE (p.Counter), 0, blob, pos, 4); + pos += 4; + + part = p.Seed; + Array.Reverse (part); + Buffer.BlockCopy (part, 0, blob, pos, 20); + + return blob; + } + + // PRIVATEKEYBLOB + // PUBLICKEYBLOB + static public RSA FromCapiKeyBlob (byte[] blob) + { + return FromCapiKeyBlob (blob, 0); + } + + static public RSA FromCapiKeyBlob (byte[] blob, int offset) + { + if (blob == null) + throw new ArgumentNullException ("blob"); + if (offset >= blob.Length) + throw new ArgumentException ("blob is too small."); + + switch (blob [offset]) { + case 0x00: + // this could be a public key inside an header + // like "sn -e" would produce + if (blob [offset + 12] == 0x06) { + return FromCapiPublicKeyBlob (blob, offset + 12); + } + break; + case 0x06: + return FromCapiPublicKeyBlob (blob, offset); + case 0x07: + return FromCapiPrivateKeyBlob (blob, offset); + } + throw new CryptographicException ("Unknown blob format."); + } + + static public DSA FromCapiKeyBlobDSA (byte[] blob) + { + return FromCapiKeyBlobDSA (blob, 0); + } + + static public DSA FromCapiKeyBlobDSA (byte[] blob, int offset) + { + if (blob == null) + throw new ArgumentNullException ("blob"); + if (offset >= blob.Length) + throw new ArgumentException ("blob is too small."); + + switch (blob [offset]) { + case 0x06: + return FromCapiPublicKeyBlobDSA (blob, offset); + case 0x07: + return FromCapiPrivateKeyBlobDSA (blob, offset); + } + throw new CryptographicException ("Unknown blob format."); + } + + static public byte[] ToCapiKeyBlob (AsymmetricAlgorithm keypair, bool includePrivateKey) + { + if (keypair == null) + throw new ArgumentNullException ("keypair"); + + // check between RSA and DSA (and potentially others like DH) + if (keypair is RSA) + return ToCapiKeyBlob ((RSA)keypair, includePrivateKey); + else if (keypair is DSA) + return ToCapiKeyBlob ((DSA)keypair, includePrivateKey); + else + return null; // TODO + } + + static public byte[] ToCapiKeyBlob (RSA rsa, bool includePrivateKey) + { + if (rsa == null) + throw new ArgumentNullException ("rsa"); + + if (includePrivateKey) + return ToCapiPrivateKeyBlob (rsa); + else + return ToCapiPublicKeyBlob (rsa); + } + + static public byte[] ToCapiKeyBlob (DSA dsa, bool includePrivateKey) + { + if (dsa == null) + throw new ArgumentNullException ("dsa"); + + if (includePrivateKey) + return ToCapiPrivateKeyBlob (dsa); + else + return ToCapiPublicKeyBlob (dsa); + } + + static public string ToHex (byte[] input) + { + if (input == null) + return null; + + StringBuilder sb = new StringBuilder (input.Length * 2); + foreach (byte b in input) { + sb.Append (b.ToString ("X2", CultureInfo.InvariantCulture)); + } + return sb.ToString (); + } + + static private byte FromHexChar (char c) + { + if ((c >= 'a') && (c <= 'f')) + return (byte) (c - 'a' + 10); + if ((c >= 'A') && (c <= 'F')) + return (byte) (c - 'A' + 10); + if ((c >= '0') && (c <= '9')) + return (byte) (c - '0'); + throw new ArgumentException ("invalid hex char"); + } + + static public byte[] FromHex (string hex) + { + if (hex == null) + return null; + if ((hex.Length & 0x1) == 0x1) + throw new ArgumentException ("Length must be a multiple of 2"); + + byte[] result = new byte [hex.Length >> 1]; + int n = 0; + int i = 0; + while (n < result.Length) { + result [n] = (byte) (FromHexChar (hex [i++]) << 4); + result [n++] += FromHexChar (hex [i++]); + } + return result; + } + } +} diff --git a/MediaBrowser.Server.Startup.Common/Security/PKCS1.cs b/MediaBrowser.Server.Startup.Common/Security/PKCS1.cs new file mode 100644 index 000000000..85bf9db38 --- /dev/null +++ b/MediaBrowser.Server.Startup.Common/Security/PKCS1.cs @@ -0,0 +1,491 @@ +// +// PKCS1.cs - Implements PKCS#1 primitives. +// +// Author: +// Sebastien Pouliot +// +// (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com) +// Copyright (C) 2004 Novell, Inc (http://www.novell.com) +// Copyright 2013 Xamarin Inc. (http://www.xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Security.Cryptography; + +namespace Emby.Common.Implementations.Security +{ + + // References: + // a. PKCS#1: RSA Cryptography Standard + // http://www.rsasecurity.com/rsalabs/pkcs/pkcs-1/index.html + + public sealed class PKCS1 { + + private PKCS1 () + { + } + + private static bool Compare (byte[] array1, byte[] array2) + { + bool result = (array1.Length == array2.Length); + if (result) { + for (int i=0; i < array1.Length; i++) + if (array1[i] != array2[i]) + return false; + } + return result; + } + + private static byte[] xor (byte[] array1, byte[] array2) + { + byte[] result = new byte [array1.Length]; + for (int i=0; i < result.Length; i++) + result[i] = (byte) (array1[i] ^ array2[i]); + return result; + } + + private static byte[] emptySHA1 = { 0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09 }; + private static byte[] emptySHA256 = { 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55 }; + private static byte[] emptySHA384 = { 0x38, 0xb0, 0x60, 0xa7, 0x51, 0xac, 0x96, 0x38, 0x4c, 0xd9, 0x32, 0x7e, 0xb1, 0xb1, 0xe3, 0x6a, 0x21, 0xfd, 0xb7, 0x11, 0x14, 0xbe, 0x07, 0x43, 0x4c, 0x0c, 0xc7, 0xbf, 0x63, 0xf6, 0xe1, 0xda, 0x27, 0x4e, 0xde, 0xbf, 0xe7, 0x6f, 0x65, 0xfb, 0xd5, 0x1a, 0xd2, 0xf1, 0x48, 0x98, 0xb9, 0x5b }; + private static byte[] emptySHA512 = { 0xcf, 0x83, 0xe1, 0x35, 0x7e, 0xef, 0xb8, 0xbd, 0xf1, 0x54, 0x28, 0x50, 0xd6, 0x6d, 0x80, 0x07, 0xd6, 0x20, 0xe4, 0x05, 0x0b, 0x57, 0x15, 0xdc, 0x83, 0xf4, 0xa9, 0x21, 0xd3, 0x6c, 0xe9, 0xce, 0x47, 0xd0, 0xd1, 0x3c, 0x5d, 0x85, 0xf2, 0xb0, 0xff, 0x83, 0x18, 0xd2, 0x87, 0x7e, 0xec, 0x2f, 0x63, 0xb9, 0x31, 0xbd, 0x47, 0x41, 0x7a, 0x81, 0xa5, 0x38, 0x32, 0x7a, 0xf9, 0x27, 0xda, 0x3e }; + + private static byte[] GetEmptyHash (HashAlgorithm hash) + { + if (hash is SHA1) + return emptySHA1; + else if (hash is SHA256) + return emptySHA256; + else if (hash is SHA384) + return emptySHA384; + else if (hash is SHA512) + return emptySHA512; + else + return hash.ComputeHash ((byte[])null); + } + + // PKCS #1 v.2.1, Section 4.1 + // I2OSP converts a non-negative integer to an octet string of a specified length. + public static byte[] I2OSP (int x, int size) + { + byte[] array = BitConverterLE.GetBytes (x); + Array.Reverse (array, 0, array.Length); + return I2OSP (array, size); + } + + public static byte[] I2OSP (byte[] x, int size) + { + byte[] result = new byte [size]; + Buffer.BlockCopy (x, 0, result, (result.Length - x.Length), x.Length); + return result; + } + + // PKCS #1 v.2.1, Section 4.2 + // OS2IP converts an octet string to a nonnegative integer. + public static byte[] OS2IP (byte[] x) + { + int i = 0; + while ((x [i++] == 0x00) && (i < x.Length)) { + // confuse compiler into reporting a warning with {} + } + i--; + if (i > 0) { + byte[] result = new byte [x.Length - i]; + Buffer.BlockCopy (x, i, result, 0, result.Length); + return result; + } + else + return x; + } + + // PKCS #1 v.2.1, Section 5.1.1 + public static byte[] RSAEP (RSA rsa, byte[] m) + { + // c = m^e mod n + return rsa.EncryptValue (m); + } + + // PKCS #1 v.2.1, Section 5.1.2 + public static byte[] RSADP (RSA rsa, byte[] c) + { + // m = c^d mod n + // Decrypt value may apply CRT optimizations + return rsa.DecryptValue (c); + } + + // PKCS #1 v.2.1, Section 5.2.1 + public static byte[] RSASP1 (RSA rsa, byte[] m) + { + // first form: s = m^d mod n + // Decrypt value may apply CRT optimizations + return rsa.DecryptValue (m); + } + + // PKCS #1 v.2.1, Section 5.2.2 + public static byte[] RSAVP1 (RSA rsa, byte[] s) + { + // m = s^e mod n + return rsa.EncryptValue (s); + } + + // PKCS #1 v.2.1, Section 7.1.1 + // RSAES-OAEP-ENCRYPT ((n, e), M, L) + public static byte[] Encrypt_OAEP (RSA rsa, HashAlgorithm hash, RandomNumberGenerator rng, byte[] M) + { + int size = rsa.KeySize / 8; + int hLen = hash.HashSize / 8; + if (M.Length > size - 2 * hLen - 2) + throw new CryptographicException ("message too long"); + // empty label L SHA1 hash + byte[] lHash = GetEmptyHash (hash); + int PSLength = (size - M.Length - 2 * hLen - 2); + // DB = lHash || PS || 0x01 || M + byte[] DB = new byte [lHash.Length + PSLength + 1 + M.Length]; + Buffer.BlockCopy (lHash, 0, DB, 0, lHash.Length); + DB [(lHash.Length + PSLength)] = 0x01; + Buffer.BlockCopy (M, 0, DB, (DB.Length - M.Length), M.Length); + + byte[] seed = new byte [hLen]; + rng.GetBytes (seed); + + byte[] dbMask = MGF1 (hash, seed, size - hLen - 1); + byte[] maskedDB = xor (DB, dbMask); + byte[] seedMask = MGF1 (hash, maskedDB, hLen); + byte[] maskedSeed = xor (seed, seedMask); + // EM = 0x00 || maskedSeed || maskedDB + byte[] EM = new byte [maskedSeed.Length + maskedDB.Length + 1]; + Buffer.BlockCopy (maskedSeed, 0, EM, 1, maskedSeed.Length); + Buffer.BlockCopy (maskedDB, 0, EM, maskedSeed.Length + 1, maskedDB.Length); + + byte[] m = OS2IP (EM); + byte[] c = RSAEP (rsa, m); + return I2OSP (c, size); + } + + // PKCS #1 v.2.1, Section 7.1.2 + // RSAES-OAEP-DECRYPT (K, C, L) + public static byte[] Decrypt_OAEP (RSA rsa, HashAlgorithm hash, byte[] C) + { + int size = rsa.KeySize / 8; + int hLen = hash.HashSize / 8; + if ((size < (2 * hLen + 2)) || (C.Length != size)) + throw new CryptographicException ("decryption error"); + + byte[] c = OS2IP (C); + byte[] m = RSADP (rsa, c); + byte[] EM = I2OSP (m, size); + + // split EM = Y || maskedSeed || maskedDB + byte[] maskedSeed = new byte [hLen]; + Buffer.BlockCopy (EM, 1, maskedSeed, 0, maskedSeed.Length); + byte[] maskedDB = new byte [size - hLen - 1]; + Buffer.BlockCopy (EM, (EM.Length - maskedDB.Length), maskedDB, 0, maskedDB.Length); + + byte[] seedMask = MGF1 (hash, maskedDB, hLen); + byte[] seed = xor (maskedSeed, seedMask); + byte[] dbMask = MGF1 (hash, seed, size - hLen - 1); + byte[] DB = xor (maskedDB, dbMask); + + byte[] lHash = GetEmptyHash (hash); + // split DB = lHash' || PS || 0x01 || M + byte[] dbHash = new byte [lHash.Length]; + Buffer.BlockCopy (DB, 0, dbHash, 0, dbHash.Length); + bool h = Compare (lHash, dbHash); + + // find separator 0x01 + int nPos = lHash.Length; + while (DB[nPos] == 0) + nPos++; + + int Msize = DB.Length - nPos - 1; + byte[] M = new byte [Msize]; + Buffer.BlockCopy (DB, (nPos + 1), M, 0, Msize); + + // we could have returned EM[0] sooner but would be helping a timing attack + if ((EM[0] != 0) || (!h) || (DB[nPos] != 0x01)) + return null; + return M; + } + + // PKCS #1 v.2.1, Section 7.2.1 + // RSAES-PKCS1-V1_5-ENCRYPT ((n, e), M) + public static byte[] Encrypt_v15 (RSA rsa, RandomNumberGenerator rng, byte[] M) + { + int size = rsa.KeySize / 8; + if (M.Length > size - 11) + throw new CryptographicException ("message too long"); + int PSLength = System.Math.Max (8, (size - M.Length - 3)); + byte[] PS = new byte [PSLength]; + rng.GetNonZeroBytes (PS); + byte[] EM = new byte [size]; + EM [1] = 0x02; + Buffer.BlockCopy (PS, 0, EM, 2, PSLength); + Buffer.BlockCopy (M, 0, EM, (size - M.Length), M.Length); + + byte[] m = OS2IP (EM); + byte[] c = RSAEP (rsa, m); + byte[] C = I2OSP (c, size); + return C; + } + + // PKCS #1 v.2.1, Section 7.2.2 + // RSAES-PKCS1-V1_5-DECRYPT (K, C) + public static byte[] Decrypt_v15 (RSA rsa, byte[] C) + { + int size = rsa.KeySize >> 3; // div by 8 + if ((size < 11) || (C.Length > size)) + throw new CryptographicException ("decryption error"); + byte[] c = OS2IP (C); + byte[] m = RSADP (rsa, c); + byte[] EM = I2OSP (m, size); + + if ((EM [0] != 0x00) || (EM [1] != 0x02)) + return null; + + int mPos = 10; + // PS is a minimum of 8 bytes + 2 bytes for header + while ((EM [mPos] != 0x00) && (mPos < EM.Length)) + mPos++; + if (EM [mPos] != 0x00) + return null; + mPos++; + byte[] M = new byte [EM.Length - mPos]; + Buffer.BlockCopy (EM, mPos, M, 0, M.Length); + return M; + } + + // PKCS #1 v.2.1, Section 8.2.1 + // RSASSA-PKCS1-V1_5-SIGN (K, M) + public static byte[] Sign_v15 (RSA rsa, HashAlgorithm hash, byte[] hashValue) + { + int size = (rsa.KeySize >> 3); // div 8 + byte[] EM = Encode_v15 (hash, hashValue, size); + byte[] m = OS2IP (EM); + byte[] s = RSASP1 (rsa, m); + byte[] S = I2OSP (s, size); + return S; + } + + internal static byte[] Sign_v15 (RSA rsa, string hashName, byte[] hashValue) + { + using (var hash = CreateFromName (hashName)) + return Sign_v15 (rsa, hash, hashValue); + } + + // PKCS #1 v.2.1, Section 8.2.2 + // RSASSA-PKCS1-V1_5-VERIFY ((n, e), M, S) + public static bool Verify_v15 (RSA rsa, HashAlgorithm hash, byte[] hashValue, byte[] signature) + { + return Verify_v15 (rsa, hash, hashValue, signature, false); + } + + internal static bool Verify_v15 (RSA rsa, string hashName, byte[] hashValue, byte[] signature) + { + using (var hash = CreateFromName (hashName)) + return Verify_v15 (rsa, hash, hashValue, signature, false); + } + + // DO NOT USE WITHOUT A VERY GOOD REASON + public static bool Verify_v15 (RSA rsa, HashAlgorithm hash, byte [] hashValue, byte [] signature, bool tryNonStandardEncoding) + { + int size = (rsa.KeySize >> 3); // div 8 + byte[] s = OS2IP (signature); + byte[] m = RSAVP1 (rsa, s); + byte[] EM2 = I2OSP (m, size); + byte[] EM = Encode_v15 (hash, hashValue, size); + bool result = Compare (EM, EM2); + if (result || !tryNonStandardEncoding) + return result; + + // NOTE: some signatures don't include the hash OID (pretty lame but real) + // and compatible with MS implementation. E.g. Verisign Authenticode Timestamps + + // we're making this "as safe as possible" + if ((EM2 [0] != 0x00) || (EM2 [1] != 0x01)) + return false; + int i; + for (i = 2; i < EM2.Length - hashValue.Length - 1; i++) { + if (EM2 [i] != 0xFF) + return false; + } + if (EM2 [i++] != 0x00) + return false; + + byte [] decryptedHash = new byte [hashValue.Length]; + Buffer.BlockCopy (EM2, i, decryptedHash, 0, decryptedHash.Length); + return Compare (decryptedHash, hashValue); + } + + // PKCS #1 v.2.1, Section 9.2 + // EMSA-PKCS1-v1_5-Encode + public static byte[] Encode_v15 (HashAlgorithm hash, byte[] hashValue, int emLength) + { + if (hashValue.Length != (hash.HashSize >> 3)) + throw new CryptographicException ("bad hash length for " + hash.ToString ()); + + // DigestInfo ::= SEQUENCE { + // digestAlgorithm AlgorithmIdentifier, + // digest OCTET STRING + // } + + byte[] t = null; + + string oid = CryptoConfig.MapNameToOID (hash.ToString ()); + if (oid != null) + { + ASN1 digestAlgorithm = new ASN1 (0x30); + digestAlgorithm.Add (new ASN1 (CryptoConfig.EncodeOID (oid))); + digestAlgorithm.Add (new ASN1 (0x05)); // NULL + ASN1 digest = new ASN1 (0x04, hashValue); + ASN1 digestInfo = new ASN1 (0x30); + digestInfo.Add (digestAlgorithm); + digestInfo.Add (digest); + + t = digestInfo.GetBytes (); + } + else + { + // There are no valid OID, in this case t = hashValue + // This is the case of the MD5SHA hash algorithm + t = hashValue; + } + + Buffer.BlockCopy (hashValue, 0, t, t.Length - hashValue.Length, hashValue.Length); + + int PSLength = System.Math.Max (8, emLength - t.Length - 3); + // PS = PSLength of 0xff + + // EM = 0x00 | 0x01 | PS | 0x00 | T + byte[] EM = new byte [PSLength + t.Length + 3]; + EM [1] = 0x01; + for (int i=2; i < PSLength + 2; i++) + EM[i] = 0xff; + Buffer.BlockCopy (t, 0, EM, PSLength + 3, t.Length); + + return EM; + } + + // PKCS #1 v.2.1, Section B.2.1 + public static byte[] MGF1 (HashAlgorithm hash, byte[] mgfSeed, int maskLen) + { + // 1. If maskLen > 2^32 hLen, output "mask too long" and stop. + // easy - this is impossible by using a int (31bits) as parameter ;-) + // BUT with a signed int we do have to check for negative values! + if (maskLen < 0) + throw new OverflowException(); + + int mgfSeedLength = mgfSeed.Length; + int hLen = (hash.HashSize >> 3); // from bits to bytes + int iterations = (maskLen / hLen); + if (maskLen % hLen != 0) + iterations++; + // 2. Let T be the empty octet string. + byte[] T = new byte [iterations * hLen]; + + byte[] toBeHashed = new byte [mgfSeedLength + 4]; + int pos = 0; + // 3. For counter from 0 to \ceil (maskLen / hLen) - 1, do the following: + for (int counter = 0; counter < iterations; counter++) { + // a. Convert counter to an octet string C of length 4 octets + byte[] C = I2OSP (counter, 4); + + // b. Concatenate the hash of the seed mgfSeed and C to the octet string T: + // T = T || Hash (mgfSeed || C) + Buffer.BlockCopy (mgfSeed, 0, toBeHashed, 0, mgfSeedLength); + Buffer.BlockCopy (C, 0, toBeHashed, mgfSeedLength, 4); + byte[] output = hash.ComputeHash (toBeHashed); + Buffer.BlockCopy (output, 0, T, pos, hLen); + pos += hLen; + } + + // 4. Output the leading maskLen octets of T as the octet string mask. + byte[] mask = new byte [maskLen]; + Buffer.BlockCopy (T, 0, mask, 0, maskLen); + return mask; + } + + static internal string HashNameFromOid (string oid, bool throwOnError = true) + { + switch (oid) { + case "1.2.840.113549.1.1.2": // MD2 with RSA encryption + return "MD2"; + case "1.2.840.113549.1.1.3": // MD4 with RSA encryption + return "MD4"; + case "1.2.840.113549.1.1.4": // MD5 with RSA encryption + return "MD5"; + case "1.2.840.113549.1.1.5": // SHA-1 with RSA Encryption + case "1.3.14.3.2.29": // SHA1 with RSA signature + case "1.2.840.10040.4.3": // SHA1-1 with DSA + return "SHA1"; + case "1.2.840.113549.1.1.11": // SHA-256 with RSA Encryption + return "SHA256"; + case "1.2.840.113549.1.1.12": // SHA-384 with RSA Encryption + return "SHA384"; + case "1.2.840.113549.1.1.13": // SHA-512 with RSA Encryption + return "SHA512"; + case "1.3.36.3.3.1.2": + return "RIPEMD160"; + default: + if (throwOnError) + throw new CryptographicException ("Unsupported hash algorithm: " + oid); + return null; + } + } + + static internal HashAlgorithm CreateFromOid (string oid) + { + return CreateFromName (HashNameFromOid (oid)); + } + + static internal HashAlgorithm CreateFromName (string name) + { +#if FULL_AOT_RUNTIME + switch (name) { + case "MD2": + return MD2.Create (); + case "MD4": + return MD4.Create (); + case "MD5": + return MD5.Create (); + case "SHA1": + return SHA1.Create (); + case "SHA256": + return SHA256.Create (); + case "SHA384": + return SHA384.Create (); + case "SHA512": + return SHA512.Create (); + case "RIPEMD160": + return RIPEMD160.Create (); + default: + try { + return (HashAlgorithm) Activator.CreateInstance (Type.GetType (name)); + } + catch { + throw new CryptographicException ("Unsupported hash algorithm: " + name); + } + } +#else + return HashAlgorithm.Create (name); +#endif + } + } +} diff --git a/MediaBrowser.Server.Startup.Common/Security/PKCS12.cs b/MediaBrowser.Server.Startup.Common/Security/PKCS12.cs new file mode 100644 index 000000000..2205a7160 --- /dev/null +++ b/MediaBrowser.Server.Startup.Common/Security/PKCS12.cs @@ -0,0 +1,1934 @@ +// +// PKCS12.cs: PKCS 12 - Personal Information Exchange Syntax +// +// Author: +// Sebastien Pouliot +// +// (C) 2003 Motus Technologies Inc. (http://www.motus.com) +// Copyright (C) 2004,2005,2006 Novell Inc. (http://www.novell.com) +// Copyright 2013 Xamarin Inc. (http://www.xamarin.com) +// +// Key derivation translated from Bouncy Castle JCE (http://www.bouncycastle.org/) +// See bouncycastle.txt for license. +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +namespace Emby.Common.Implementations.Security +{ + + public class PKCS5 { + + public const string pbeWithMD2AndDESCBC = "1.2.840.113549.1.5.1"; + public const string pbeWithMD5AndDESCBC = "1.2.840.113549.1.5.3"; + public const string pbeWithMD2AndRC2CBC = "1.2.840.113549.1.5.4"; + public const string pbeWithMD5AndRC2CBC = "1.2.840.113549.1.5.6"; + public const string pbeWithSHA1AndDESCBC = "1.2.840.113549.1.5.10"; + public const string pbeWithSHA1AndRC2CBC = "1.2.840.113549.1.5.11"; + + public PKCS5 () {} + } + + public class PKCS9 { + + public const string friendlyName = "1.2.840.113549.1.9.20"; + public const string localKeyId = "1.2.840.113549.1.9.21"; + + public PKCS9 () {} + } + + + internal class SafeBag { + private string _bagOID; + private ASN1 _asn1; + + public SafeBag(string bagOID, ASN1 asn1) { + _bagOID = bagOID; + _asn1 = asn1; + } + + public string BagOID { + get { return _bagOID; } + } + + public ASN1 ASN1 { + get { return _asn1; } + } + } + + + public class PKCS12 : ICloneable { + + public const string pbeWithSHAAnd128BitRC4 = "1.2.840.113549.1.12.1.1"; + public const string pbeWithSHAAnd40BitRC4 = "1.2.840.113549.1.12.1.2"; + public const string pbeWithSHAAnd3KeyTripleDESCBC = "1.2.840.113549.1.12.1.3"; + public const string pbeWithSHAAnd2KeyTripleDESCBC = "1.2.840.113549.1.12.1.4"; + public const string pbeWithSHAAnd128BitRC2CBC = "1.2.840.113549.1.12.1.5"; + public const string pbeWithSHAAnd40BitRC2CBC = "1.2.840.113549.1.12.1.6"; + + // bags + public const string keyBag = "1.2.840.113549.1.12.10.1.1"; + public const string pkcs8ShroudedKeyBag = "1.2.840.113549.1.12.10.1.2"; + public const string certBag = "1.2.840.113549.1.12.10.1.3"; + public const string crlBag = "1.2.840.113549.1.12.10.1.4"; + public const string secretBag = "1.2.840.113549.1.12.10.1.5"; + public const string safeContentsBag = "1.2.840.113549.1.12.10.1.6"; + + // types + public const string x509Certificate = "1.2.840.113549.1.9.22.1"; + public const string sdsiCertificate = "1.2.840.113549.1.9.22.2"; + public const string x509Crl = "1.2.840.113549.1.9.23.1"; + + // Adapted from BouncyCastle PKCS12ParametersGenerator.java + public class DeriveBytes { + + public enum Purpose { + Key, + IV, + MAC + } + + static private byte[] keyDiversifier = { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 }; + static private byte[] ivDiversifier = { 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 }; + static private byte[] macDiversifier = { 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 }; + + private string _hashName; + private int _iterations; + private byte[] _password; + private byte[] _salt; + + public DeriveBytes () {} + + public string HashName { + get { return _hashName; } + set { _hashName = value; } + } + + public int IterationCount { + get { return _iterations; } + set { _iterations = value; } + } + + public byte[] Password { + get { return (byte[]) _password.Clone (); } + set { + if (value == null) + _password = new byte [0]; + else + _password = (byte[]) value.Clone (); + } + } + + public byte[] Salt { + get { return (byte[]) _salt.Clone (); } + set { + if (value != null) + _salt = (byte[]) value.Clone (); + else + _salt = null; + } + } + + private void Adjust (byte[] a, int aOff, byte[] b) + { + int x = (b[b.Length - 1] & 0xff) + (a [aOff + b.Length - 1] & 0xff) + 1; + + a [aOff + b.Length - 1] = (byte) x; + x >>= 8; + + for (int i = b.Length - 2; i >= 0; i--) { + x += (b [i] & 0xff) + (a [aOff + i] & 0xff); + a [aOff + i] = (byte) x; + x >>= 8; + } + } + + private byte[] Derive (byte[] diversifier, int n) + { + HashAlgorithm digest = PKCS1.CreateFromName (_hashName); + int u = (digest.HashSize >> 3); // div 8 + int v = 64; + byte[] dKey = new byte [n]; + + byte[] S; + if ((_salt != null) && (_salt.Length != 0)) { + S = new byte[v * ((_salt.Length + v - 1) / v)]; + + for (int i = 0; i != S.Length; i++) { + S[i] = _salt[i % _salt.Length]; + } + } + else { + S = new byte[0]; + } + + byte[] P; + if ((_password != null) && (_password.Length != 0)) { + P = new byte[v * ((_password.Length + v - 1) / v)]; + + for (int i = 0; i != P.Length; i++) { + P[i] = _password[i % _password.Length]; + } + } + else { + P = new byte[0]; + } + + byte[] I = new byte [S.Length + P.Length]; + + Buffer.BlockCopy (S, 0, I, 0, S.Length); + Buffer.BlockCopy (P, 0, I, S.Length, P.Length); + + byte[] B = new byte[v]; + int c = (n + u - 1) / u; + + for (int i = 1; i <= c; i++) { + digest.TransformBlock (diversifier, 0, diversifier.Length, diversifier, 0); + digest.TransformFinalBlock (I, 0, I.Length); + byte[] A = digest.Hash; + digest.Initialize (); + for (int j = 1; j != _iterations; j++) { + A = digest.ComputeHash (A, 0, A.Length); + } + + for (int j = 0; j != B.Length; j++) { + B [j] = A [j % A.Length]; + } + + for (int j = 0; j != I.Length / v; j++) { + Adjust (I, j * v, B); + } + + if (i == c) { + Buffer.BlockCopy(A, 0, dKey, (i - 1) * u, dKey.Length - ((i - 1) * u)); + } + else { + Buffer.BlockCopy(A, 0, dKey, (i - 1) * u, A.Length); + } + } + + return dKey; + } + + public byte[] DeriveKey (int size) + { + return Derive (keyDiversifier, size); + } + + public byte[] DeriveIV (int size) + { + return Derive (ivDiversifier, size); + } + + public byte[] DeriveMAC (int size) + { + return Derive (macDiversifier, size); + } + } + + const int recommendedIterationCount = 2000; + + //private int _version; + private byte[] _password; + private ArrayList _keyBags; + private ArrayList _secretBags; + private X509CertificateCollection _certs; + private bool _keyBagsChanged; + private bool _secretBagsChanged; + private bool _certsChanged; + private int _iterations; + private ArrayList _safeBags; + private RandomNumberGenerator _rng; + + // constructors + + public PKCS12 () + { + _iterations = recommendedIterationCount; + _keyBags = new ArrayList (); + _secretBags = new ArrayList (); + _certs = new X509CertificateCollection (); + _keyBagsChanged = false; + _secretBagsChanged = false; + _certsChanged = false; + _safeBags = new ArrayList (); + } + + public PKCS12 (byte[] data) + : this () + { + Password = null; + Decode (data); + } + + /* + * PFX ::= SEQUENCE { + * version INTEGER {v3(3)}(v3,...), + * authSafe ContentInfo, + * macData MacData OPTIONAL + * } + * + * MacData ::= SEQUENCE { + * mac DigestInfo, + * macSalt OCTET STRING, + * iterations INTEGER DEFAULT 1 + * -- Note: The default is for historical reasons and its use is deprecated. A higher + * -- value, like 1024 is recommended. + * } + * + * SafeContents ::= SEQUENCE OF SafeBag + * + * SafeBag ::= SEQUENCE { + * bagId BAG-TYPE.&id ({PKCS12BagSet}), + * bagValue [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}), + * bagAttributes SET OF PKCS12Attribute OPTIONAL + * } + */ + public PKCS12 (byte[] data, string password) + : this () + { + Password = password; + Decode (data); + } + + public PKCS12 (byte[] data, byte[] password) + : this () + { + _password = password; + Decode (data); + } + + private void Decode (byte[] data) + { + ASN1 pfx = new ASN1 (data); + if (pfx.Tag != 0x30) + throw new ArgumentException ("invalid data"); + + ASN1 version = pfx [0]; + if (version.Tag != 0x02) + throw new ArgumentException ("invalid PFX version"); + //_version = version.Value [0]; + + PKCS7.ContentInfo authSafe = new PKCS7.ContentInfo (pfx [1]); + if (authSafe.ContentType != PKCS7.Oid.data) + throw new ArgumentException ("invalid authenticated safe"); + + // now that we know it's a PKCS#12 file, check the (optional) MAC + // before decoding anything else in the file + if (pfx.Count > 2) { + ASN1 macData = pfx [2]; + if (macData.Tag != 0x30) + throw new ArgumentException ("invalid MAC"); + + ASN1 mac = macData [0]; + if (mac.Tag != 0x30) + throw new ArgumentException ("invalid MAC"); + ASN1 macAlgorithm = mac [0]; + string macOid = ASN1Convert.ToOid (macAlgorithm [0]); + if (macOid != "1.3.14.3.2.26") + throw new ArgumentException ("unsupported HMAC"); + byte[] macValue = mac [1].Value; + + ASN1 macSalt = macData [1]; + if (macSalt.Tag != 0x04) + throw new ArgumentException ("missing MAC salt"); + + _iterations = 1; // default value + if (macData.Count > 2) { + ASN1 iters = macData [2]; + if (iters.Tag != 0x02) + throw new ArgumentException ("invalid MAC iteration"); + _iterations = ASN1Convert.ToInt32 (iters); + } + + byte[] authSafeData = authSafe.Content [0].Value; + byte[] calculatedMac = MAC (_password, macSalt.Value, _iterations, authSafeData); + if (!Compare (macValue, calculatedMac)) { + byte[] nullPassword = {0, 0}; + calculatedMac = MAC(nullPassword, macSalt.Value, _iterations, authSafeData); + if (!Compare (macValue, calculatedMac)) + throw new CryptographicException ("Invalid MAC - file may have been tampe red!"); + _password = nullPassword; + } + } + + // we now returns to our original presentation - PFX + ASN1 authenticatedSafe = new ASN1 (authSafe.Content [0].Value); + for (int i=0; i < authenticatedSafe.Count; i++) { + PKCS7.ContentInfo ci = new PKCS7.ContentInfo (authenticatedSafe [i]); + switch (ci.ContentType) { + case PKCS7.Oid.data: + // unencrypted (by PKCS#12) + ASN1 safeContents = new ASN1 (ci.Content [0].Value); + for (int j=0; j < safeContents.Count; j++) { + ASN1 safeBag = safeContents [j]; + ReadSafeBag (safeBag); + } + break; + case PKCS7.Oid.encryptedData: + // password encrypted + PKCS7.EncryptedData ed = new PKCS7.EncryptedData (ci.Content [0]); + ASN1 decrypted = new ASN1 (Decrypt (ed)); + for (int j=0; j < decrypted.Count; j++) { + ASN1 safeBag = decrypted [j]; + ReadSafeBag (safeBag); + } + break; + case PKCS7.Oid.envelopedData: + // public key encrypted + throw new NotImplementedException ("public key encrypted"); + default: + throw new ArgumentException ("unknown authenticatedSafe"); + } + } + } + + ~PKCS12 () + { + if (_password != null) { + Array.Clear (_password, 0, _password.Length); + } + _password = null; + } + + // properties + + public string Password { + set { + // Clear old password. + if (_password != null) + Array.Clear (_password, 0, _password.Length); + _password = null; + if (value != null) { + if (value.Length > 0) { + int size = value.Length; + int nul = 0; + if (size < MaximumPasswordLength) { + // if not present, add space for a NULL (0x00) character + if (value[size - 1] != 0x00) + nul = 1; + } else { + size = MaximumPasswordLength; + } + _password = new byte[(size + nul) << 1]; // double for unicode + Encoding.BigEndianUnicode.GetBytes (value, 0, size, _password, 0); + } else { + // double-byte (Unicode) NULL (0x00) - see bug #79617 + _password = new byte[2]; + } + } + } + } + + public int IterationCount { + get { return _iterations; } + set { _iterations = value; } + } + + public ArrayList Keys { + get { + if (_keyBagsChanged) { + _keyBags.Clear (); + foreach (SafeBag sb in _safeBags) { + if (sb.BagOID.Equals (keyBag)) { + ASN1 safeBag = sb.ASN1; + ASN1 bagValue = safeBag [1]; + PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value); + byte[] privateKey = pki.PrivateKey; + switch (privateKey [0]) { + case 0x02: + DSAParameters p = new DSAParameters (); // FIXME + _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p)); + break; + case 0x30: + _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeRSA (privateKey)); + break; + default: + break; + } + Array.Clear (privateKey, 0, privateKey.Length); + + } else if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) { + ASN1 safeBag = sb.ASN1; + ASN1 bagValue = safeBag [1]; + PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value); + byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData); + PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted); + byte[] privateKey = pki.PrivateKey; + switch (privateKey [0]) { + case 0x02: + DSAParameters p = new DSAParameters (); // FIXME + _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p)); + break; + case 0x30: + _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeRSA (privateKey)); + break; + default: + break; + } + Array.Clear (privateKey, 0, privateKey.Length); + Array.Clear (decrypted, 0, decrypted.Length); + } + } + _keyBagsChanged = false; + } + return ArrayList.ReadOnly(_keyBags); + } + } + + public ArrayList Secrets { + get { + if (_secretBagsChanged) { + _secretBags.Clear (); + foreach (SafeBag sb in _safeBags) { + if (sb.BagOID.Equals (secretBag)) { + ASN1 safeBag = sb.ASN1; + ASN1 bagValue = safeBag [1]; + byte[] secret = bagValue.Value; + _secretBags.Add(secret); + } + } + _secretBagsChanged = false; + } + return ArrayList.ReadOnly(_secretBags); + } + } + + public X509CertificateCollection Certificates { + get { + if (_certsChanged) { + _certs.Clear (); + foreach (SafeBag sb in _safeBags) { + if (sb.BagOID.Equals (certBag)) { + ASN1 safeBag = sb.ASN1; + ASN1 bagValue = safeBag [1]; + PKCS7.ContentInfo cert = new PKCS7.ContentInfo (bagValue.Value); + _certs.Add (new X509Certificate (cert.Content [0].Value)); + } + } + _certsChanged = false; + } + return _certs; + } + } + + internal RandomNumberGenerator RNG { + get { + if (_rng == null) + _rng = RandomNumberGenerator.Create (); + return _rng; + } + } + + // private methods + + private bool Compare (byte[] expected, byte[] actual) + { + bool compare = false; + if (expected.Length == actual.Length) { + for (int i=0; i < expected.Length; i++) { + if (expected [i] != actual [i]) + return false; + } + compare = true; + } + return compare; + } + + private SymmetricAlgorithm GetSymmetricAlgorithm (string algorithmOid, byte[] salt, int iterationCount) + { + string algorithm = null; + int keyLength = 8; // 64 bits (default) + int ivLength = 8; // 64 bits (default) + + PKCS12.DeriveBytes pd = new PKCS12.DeriveBytes (); + pd.Password = _password; + pd.Salt = salt; + pd.IterationCount = iterationCount; + + switch (algorithmOid) { + case PKCS5.pbeWithMD2AndDESCBC: // no unit test available + pd.HashName = "MD2"; + algorithm = "DES"; + break; + case PKCS5.pbeWithMD5AndDESCBC: // no unit test available + pd.HashName = "MD5"; + algorithm = "DES"; + break; + case PKCS5.pbeWithMD2AndRC2CBC: // no unit test available + // TODO - RC2-CBC-Parameter (PKCS5) + // if missing default to 32 bits !!! + pd.HashName = "MD2"; + algorithm = "RC2"; + keyLength = 4; // default + break; + case PKCS5.pbeWithMD5AndRC2CBC: // no unit test available + // TODO - RC2-CBC-Parameter (PKCS5) + // if missing default to 32 bits !!! + pd.HashName = "MD5"; + algorithm = "RC2"; + keyLength = 4; // default + break; + case PKCS5.pbeWithSHA1AndDESCBC: // no unit test available + pd.HashName = "SHA1"; + algorithm = "DES"; + break; + case PKCS5.pbeWithSHA1AndRC2CBC: // no unit test available + // TODO - RC2-CBC-Parameter (PKCS5) + // if missing default to 32 bits !!! + pd.HashName = "SHA1"; + algorithm = "RC2"; + keyLength = 4; // default + break; + case PKCS12.pbeWithSHAAnd128BitRC4: // no unit test available + pd.HashName = "SHA1"; + algorithm = "RC4"; + keyLength = 16; + ivLength = 0; // N/A + break; + case PKCS12.pbeWithSHAAnd40BitRC4: // no unit test available + pd.HashName = "SHA1"; + algorithm = "RC4"; + keyLength = 5; + ivLength = 0; // N/A + break; + case PKCS12.pbeWithSHAAnd3KeyTripleDESCBC: + pd.HashName = "SHA1"; + algorithm = "TripleDES"; + keyLength = 24; + break; + case PKCS12.pbeWithSHAAnd2KeyTripleDESCBC: // no unit test available + pd.HashName = "SHA1"; + algorithm = "TripleDES"; + keyLength = 16; + break; + case PKCS12.pbeWithSHAAnd128BitRC2CBC: // no unit test available + pd.HashName = "SHA1"; + algorithm = "RC2"; + keyLength = 16; + break; + case PKCS12.pbeWithSHAAnd40BitRC2CBC: + pd.HashName = "SHA1"; + algorithm = "RC2"; + keyLength = 5; + break; + default: + throw new NotSupportedException ("unknown oid " + algorithm); + } + + SymmetricAlgorithm sa = null; + sa = SymmetricAlgorithm.Create(algorithm); + sa.Key = pd.DeriveKey (keyLength); + // IV required only for block ciphers (not stream ciphers) + if (ivLength > 0) { + sa.IV = pd.DeriveIV (ivLength); + sa.Mode = CipherMode.CBC; + } + return sa; + } + + public byte[] Decrypt (string algorithmOid, byte[] salt, int iterationCount, byte[] encryptedData) + { + SymmetricAlgorithm sa = null; + byte[] result = null; + try { + sa = GetSymmetricAlgorithm (algorithmOid, salt, iterationCount); + ICryptoTransform ct = sa.CreateDecryptor (); + result = ct.TransformFinalBlock (encryptedData, 0, encryptedData.Length); + } + finally { + if (sa != null) + sa.Clear (); + } + return result; + } + + public byte[] Decrypt (PKCS7.EncryptedData ed) + { + return Decrypt (ed.EncryptionAlgorithm.ContentType, + ed.EncryptionAlgorithm.Content [0].Value, + ASN1Convert.ToInt32 (ed.EncryptionAlgorithm.Content [1]), + ed.EncryptedContent); + } + + public byte[] Encrypt (string algorithmOid, byte[] salt, int iterationCount, byte[] data) + { + byte[] result = null; + using (SymmetricAlgorithm sa = GetSymmetricAlgorithm (algorithmOid, salt, iterationCount)) { + ICryptoTransform ct = sa.CreateEncryptor (); + result = ct.TransformFinalBlock (data, 0, data.Length); + } + return result; + } + + private DSAParameters GetExistingParameters (out bool found) + { + foreach (X509Certificate cert in Certificates) { + // FIXME: that won't work if parts of the parameters are missing + if (cert.KeyAlgorithmParameters != null) { + DSA dsa = cert.DSA; + if (dsa != null) { + found = true; + return dsa.ExportParameters (false); + } + } + } + found = false; + return new DSAParameters (); + } + + private void AddPrivateKey (PKCS8.PrivateKeyInfo pki) + { + byte[] privateKey = pki.PrivateKey; + switch (privateKey [0]) { + case 0x02: + bool found; + DSAParameters p = GetExistingParameters (out found); + if (found) { + _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p)); + } + break; + case 0x30: + _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeRSA (privateKey)); + break; + default: + Array.Clear (privateKey, 0, privateKey.Length); + throw new CryptographicException ("Unknown private key format"); + } + Array.Clear (privateKey, 0, privateKey.Length); + } + + private void ReadSafeBag (ASN1 safeBag) + { + if (safeBag.Tag != 0x30) + throw new ArgumentException ("invalid safeBag"); + + ASN1 bagId = safeBag [0]; + if (bagId.Tag != 0x06) + throw new ArgumentException ("invalid safeBag id"); + + ASN1 bagValue = safeBag [1]; + string oid = ASN1Convert.ToOid (bagId); + switch (oid) { + case keyBag: + // NEED UNIT TEST + AddPrivateKey (new PKCS8.PrivateKeyInfo (bagValue.Value)); + break; + case pkcs8ShroudedKeyBag: + PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value); + byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData); + AddPrivateKey (new PKCS8.PrivateKeyInfo (decrypted)); + Array.Clear (decrypted, 0, decrypted.Length); + break; + case certBag: + PKCS7.ContentInfo cert = new PKCS7.ContentInfo (bagValue.Value); + if (cert.ContentType != x509Certificate) + throw new NotSupportedException ("unsupport certificate type"); + X509Certificate x509 = new X509Certificate (cert.Content [0].Value); + _certs.Add (x509); + break; + case crlBag: + // TODO + break; + case secretBag: + byte[] secret = bagValue.Value; + _secretBags.Add(secret); + break; + case safeContentsBag: + // TODO - ? recurse ? + break; + default: + throw new ArgumentException ("unknown safeBag oid"); + } + + if (safeBag.Count > 2) { + ASN1 bagAttributes = safeBag [2]; + if (bagAttributes.Tag != 0x31) + throw new ArgumentException ("invalid safeBag attributes id"); + + for (int i = 0; i < bagAttributes.Count; i++) { + ASN1 pkcs12Attribute = bagAttributes[i]; + + if (pkcs12Attribute.Tag != 0x30) + throw new ArgumentException ("invalid PKCS12 attributes id"); + + ASN1 attrId = pkcs12Attribute [0]; + if (attrId.Tag != 0x06) + throw new ArgumentException ("invalid attribute id"); + + string attrOid = ASN1Convert.ToOid (attrId); + + ASN1 attrValues = pkcs12Attribute[1]; + for (int j = 0; j < attrValues.Count; j++) { + ASN1 attrValue = attrValues[j]; + + switch (attrOid) { + case PKCS9.friendlyName: + if (attrValue.Tag != 0x1e) + throw new ArgumentException ("invalid attribute value id"); + break; + case PKCS9.localKeyId: + if (attrValue.Tag != 0x04) + throw new ArgumentException ("invalid attribute value id"); + break; + default: + // Unknown OID -- don't check Tag + break; + } + } + } + } + + _safeBags.Add (new SafeBag(oid, safeBag)); + } + + private ASN1 Pkcs8ShroudedKeyBagSafeBag (AsymmetricAlgorithm aa, IDictionary attributes) + { + PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (); + if (aa is RSA) { + pki.Algorithm = "1.2.840.113549.1.1.1"; + pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((RSA)aa); + } + else if (aa is DSA) { + pki.Algorithm = null; + pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((DSA)aa); + } + else + throw new CryptographicException ("Unknown asymmetric algorithm {0}", aa.ToString ()); + + PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (); + epki.Algorithm = pbeWithSHAAnd3KeyTripleDESCBC; + epki.IterationCount = _iterations; + epki.EncryptedData = Encrypt (pbeWithSHAAnd3KeyTripleDESCBC, epki.Salt, _iterations, pki.GetBytes ()); + + ASN1 safeBag = new ASN1 (0x30); + safeBag.Add (ASN1Convert.FromOid (pkcs8ShroudedKeyBag)); + ASN1 bagValue = new ASN1 (0xA0); + bagValue.Add (new ASN1 (epki.GetBytes ())); + safeBag.Add (bagValue); + + if (attributes != null) { + ASN1 bagAttributes = new ASN1 (0x31); + IDictionaryEnumerator de = attributes.GetEnumerator (); + + while (de.MoveNext ()) { + string oid = (string)de.Key; + switch (oid) { + case PKCS9.friendlyName: + ArrayList names = (ArrayList)de.Value; + if (names.Count > 0) { + ASN1 pkcs12Attribute = new ASN1 (0x30); + pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName)); + ASN1 attrValues = new ASN1 (0x31); + foreach (byte[] name in names) { + ASN1 attrValue = new ASN1 (0x1e); + attrValue.Value = name; + attrValues.Add (attrValue); + } + pkcs12Attribute.Add (attrValues); + bagAttributes.Add (pkcs12Attribute); + } + break; + case PKCS9.localKeyId: + ArrayList keys = (ArrayList)de.Value; + if (keys.Count > 0) { + ASN1 pkcs12Attribute = new ASN1 (0x30); + pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId)); + ASN1 attrValues = new ASN1 (0x31); + foreach (byte[] key in keys) { + ASN1 attrValue = new ASN1 (0x04); + attrValue.Value = key; + attrValues.Add (attrValue); + } + pkcs12Attribute.Add (attrValues); + bagAttributes.Add (pkcs12Attribute); + } + break; + default: + break; + } + } + + if (bagAttributes.Count > 0) { + safeBag.Add (bagAttributes); + } + } + + return safeBag; + } + + private ASN1 KeyBagSafeBag (AsymmetricAlgorithm aa, IDictionary attributes) + { + PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (); + if (aa is RSA) { + pki.Algorithm = "1.2.840.113549.1.1.1"; + pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((RSA)aa); + } + else if (aa is DSA) { + pki.Algorithm = null; + pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((DSA)aa); + } + else + throw new CryptographicException ("Unknown asymmetric algorithm {0}", aa.ToString ()); + + ASN1 safeBag = new ASN1 (0x30); + safeBag.Add (ASN1Convert.FromOid (keyBag)); + ASN1 bagValue = new ASN1 (0xA0); + bagValue.Add (new ASN1 (pki.GetBytes ())); + safeBag.Add (bagValue); + + if (attributes != null) { + ASN1 bagAttributes = new ASN1 (0x31); + IDictionaryEnumerator de = attributes.GetEnumerator (); + + while (de.MoveNext ()) { + string oid = (string)de.Key; + switch (oid) { + case PKCS9.friendlyName: + ArrayList names = (ArrayList)de.Value; + if (names.Count > 0) { + ASN1 pkcs12Attribute = new ASN1 (0x30); + pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName)); + ASN1 attrValues = new ASN1 (0x31); + foreach (byte[] name in names) { + ASN1 attrValue = new ASN1 (0x1e); + attrValue.Value = name; + attrValues.Add (attrValue); + } + pkcs12Attribute.Add (attrValues); + bagAttributes.Add (pkcs12Attribute); + } + break; + case PKCS9.localKeyId: + ArrayList keys = (ArrayList)de.Value; + if (keys.Count > 0) { + ASN1 pkcs12Attribute = new ASN1 (0x30); + pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId)); + ASN1 attrValues = new ASN1 (0x31); + foreach (byte[] key in keys) { + ASN1 attrValue = new ASN1 (0x04); + attrValue.Value = key; + attrValues.Add (attrValue); + } + pkcs12Attribute.Add (attrValues); + bagAttributes.Add (pkcs12Attribute); + } + break; + default: + break; + } + } + + if (bagAttributes.Count > 0) { + safeBag.Add (bagAttributes); + } + } + + return safeBag; + } + + private ASN1 SecretBagSafeBag (byte[] secret, IDictionary attributes) + { + ASN1 safeBag = new ASN1 (0x30); + safeBag.Add (ASN1Convert.FromOid (secretBag)); + ASN1 bagValue = new ASN1 (0x80, secret); + safeBag.Add (bagValue); + + if (attributes != null) { + ASN1 bagAttributes = new ASN1 (0x31); + IDictionaryEnumerator de = attributes.GetEnumerator (); + + while (de.MoveNext ()) { + string oid = (string)de.Key; + switch (oid) { + case PKCS9.friendlyName: + ArrayList names = (ArrayList)de.Value; + if (names.Count > 0) { + ASN1 pkcs12Attribute = new ASN1 (0x30); + pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName)); + ASN1 attrValues = new ASN1 (0x31); + foreach (byte[] name in names) { + ASN1 attrValue = new ASN1 (0x1e); + attrValue.Value = name; + attrValues.Add (attrValue); + } + pkcs12Attribute.Add (attrValues); + bagAttributes.Add (pkcs12Attribute); + } + break; + case PKCS9.localKeyId: + ArrayList keys = (ArrayList)de.Value; + if (keys.Count > 0) { + ASN1 pkcs12Attribute = new ASN1 (0x30); + pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId)); + ASN1 attrValues = new ASN1 (0x31); + foreach (byte[] key in keys) { + ASN1 attrValue = new ASN1 (0x04); + attrValue.Value = key; + attrValues.Add (attrValue); + } + pkcs12Attribute.Add (attrValues); + bagAttributes.Add (pkcs12Attribute); + } + break; + default: + break; + } + } + + if (bagAttributes.Count > 0) { + safeBag.Add (bagAttributes); + } + } + + return safeBag; + } + + private ASN1 CertificateSafeBag (X509Certificate x509, IDictionary attributes) + { + ASN1 encapsulatedCertificate = new ASN1 (0x04, x509.RawData); + + PKCS7.ContentInfo ci = new PKCS7.ContentInfo (); + ci.ContentType = x509Certificate; + ci.Content.Add (encapsulatedCertificate); + + ASN1 bagValue = new ASN1 (0xA0); + bagValue.Add (ci.ASN1); + + ASN1 safeBag = new ASN1 (0x30); + safeBag.Add (ASN1Convert.FromOid (certBag)); + safeBag.Add (bagValue); + + if (attributes != null) { + ASN1 bagAttributes = new ASN1 (0x31); + IDictionaryEnumerator de = attributes.GetEnumerator (); + + while (de.MoveNext ()) { + string oid = (string)de.Key; + switch (oid) { + case PKCS9.friendlyName: + ArrayList names = (ArrayList)de.Value; + if (names.Count > 0) { + ASN1 pkcs12Attribute = new ASN1 (0x30); + pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName)); + ASN1 attrValues = new ASN1 (0x31); + foreach (byte[] name in names) { + ASN1 attrValue = new ASN1 (0x1e); + attrValue.Value = name; + attrValues.Add (attrValue); + } + pkcs12Attribute.Add (attrValues); + bagAttributes.Add (pkcs12Attribute); + } + break; + case PKCS9.localKeyId: + ArrayList keys = (ArrayList)de.Value; + if (keys.Count > 0) { + ASN1 pkcs12Attribute = new ASN1 (0x30); + pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId)); + ASN1 attrValues = new ASN1 (0x31); + foreach (byte[] key in keys) { + ASN1 attrValue = new ASN1 (0x04); + attrValue.Value = key; + attrValues.Add (attrValue); + } + pkcs12Attribute.Add (attrValues); + bagAttributes.Add (pkcs12Attribute); + } + break; + default: + break; + } + } + + if (bagAttributes.Count > 0) { + safeBag.Add (bagAttributes); + } + } + + return safeBag; + } + + private byte[] MAC (byte[] password, byte[] salt, int iterations, byte[] data) + { + PKCS12.DeriveBytes pd = new PKCS12.DeriveBytes (); + pd.HashName = "SHA1"; + pd.Password = password; + pd.Salt = salt; + pd.IterationCount = iterations; + + HMACSHA1 hmac = (HMACSHA1) HMACSHA1.Create (); + hmac.Key = pd.DeriveMAC (20); + return hmac.ComputeHash (data, 0, data.Length); + } + + /* + * SafeContents ::= SEQUENCE OF SafeBag + * + * SafeBag ::= SEQUENCE { + * bagId BAG-TYPE.&id ({PKCS12BagSet}), + * bagValue [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}), + * bagAttributes SET OF PKCS12Attribute OPTIONAL + * } + */ + public byte[] GetBytes () + { + // TODO (incomplete) + ASN1 safeBagSequence = new ASN1 (0x30); + + // Sync Safe Bag list since X509CertificateCollection may be updated + ArrayList scs = new ArrayList (); + foreach (SafeBag sb in _safeBags) { + if (sb.BagOID.Equals (certBag)) { + ASN1 safeBag = sb.ASN1; + ASN1 bagValue = safeBag [1]; + PKCS7.ContentInfo cert = new PKCS7.ContentInfo (bagValue.Value); + scs.Add (new X509Certificate (cert.Content [0].Value)); + } + } + + ArrayList addcerts = new ArrayList (); + ArrayList removecerts = new ArrayList (); + + foreach (X509Certificate c in Certificates) { + bool found = false; + + foreach (X509Certificate lc in scs) { + if (Compare (c.RawData, lc.RawData)) { + found = true; + } + } + + if (!found) { + addcerts.Add (c); + } + } + foreach (X509Certificate c in scs) { + bool found = false; + + foreach (X509Certificate lc in Certificates) { + if (Compare (c.RawData, lc.RawData)) { + found = true; + } + } + + if (!found) { + removecerts.Add (c); + } + } + + foreach (X509Certificate c in removecerts) { + RemoveCertificate (c); + } + + foreach (X509Certificate c in addcerts) { + AddCertificate (c); + } + // Sync done + + if (_safeBags.Count > 0) { + ASN1 certsSafeBag = new ASN1 (0x30); + foreach (SafeBag sb in _safeBags) { + if (sb.BagOID.Equals (certBag)) { + certsSafeBag.Add (sb.ASN1); + } + } + + if (certsSafeBag.Count > 0) { + PKCS7.ContentInfo contentInfo = EncryptedContentInfo (certsSafeBag, pbeWithSHAAnd3KeyTripleDESCBC); + safeBagSequence.Add (contentInfo.ASN1); + } + } + + if (_safeBags.Count > 0) { + ASN1 safeContents = new ASN1 (0x30); + foreach (SafeBag sb in _safeBags) { + if (sb.BagOID.Equals (keyBag) || + sb.BagOID.Equals (pkcs8ShroudedKeyBag)) { + safeContents.Add (sb.ASN1); + } + } + if (safeContents.Count > 0) { + ASN1 content = new ASN1 (0xA0); + content.Add (new ASN1 (0x04, safeContents.GetBytes ())); + + PKCS7.ContentInfo keyBag = new PKCS7.ContentInfo (PKCS7.Oid.data); + keyBag.Content = content; + safeBagSequence.Add (keyBag.ASN1); + } + } + + // Doing SecretBags separately in case we want to change their encryption independently. + if (_safeBags.Count > 0) { + ASN1 secretsSafeBag = new ASN1 (0x30); + foreach (SafeBag sb in _safeBags) { + if (sb.BagOID.Equals (secretBag)) { + secretsSafeBag.Add (sb.ASN1); + } + } + + if (secretsSafeBag.Count > 0) { + PKCS7.ContentInfo contentInfo = EncryptedContentInfo (secretsSafeBag, pbeWithSHAAnd3KeyTripleDESCBC); + safeBagSequence.Add (contentInfo.ASN1); + } + } + + + ASN1 encapsulates = new ASN1 (0x04, safeBagSequence.GetBytes ()); + ASN1 ci = new ASN1 (0xA0); + ci.Add (encapsulates); + PKCS7.ContentInfo authSafe = new PKCS7.ContentInfo (PKCS7.Oid.data); + authSafe.Content = ci; + + ASN1 macData = new ASN1 (0x30); + if (_password != null) { + // only for password based encryption + byte[] salt = new byte [20]; + RNG.GetBytes (salt); + byte[] macValue = MAC (_password, salt, _iterations, authSafe.Content [0].Value); + ASN1 oidSeq = new ASN1 (0x30); + oidSeq.Add (ASN1Convert.FromOid ("1.3.14.3.2.26")); // SHA1 + oidSeq.Add (new ASN1 (0x05)); + + ASN1 mac = new ASN1 (0x30); + mac.Add (oidSeq); + mac.Add (new ASN1 (0x04, macValue)); + + macData.Add (mac); + macData.Add (new ASN1 (0x04, salt)); + macData.Add (ASN1Convert.FromInt32 (_iterations)); + } + + ASN1 version = new ASN1 (0x02, new byte [1] { 0x03 }); + + ASN1 pfx = new ASN1 (0x30); + pfx.Add (version); + pfx.Add (authSafe.ASN1); + if (macData.Count > 0) { + // only for password based encryption + pfx.Add (macData); + } + + return pfx.GetBytes (); + } + + // Creates an encrypted PKCS#7 ContentInfo with safeBags as its SafeContents. Used in GetBytes(), above. + private PKCS7.ContentInfo EncryptedContentInfo(ASN1 safeBags, string algorithmOid) + { + byte[] salt = new byte [8]; + RNG.GetBytes (salt); + + ASN1 seqParams = new ASN1 (0x30); + seqParams.Add (new ASN1 (0x04, salt)); + seqParams.Add (ASN1Convert.FromInt32 (_iterations)); + + ASN1 seqPbe = new ASN1 (0x30); + seqPbe.Add (ASN1Convert.FromOid (algorithmOid)); + seqPbe.Add (seqParams); + + byte[] encrypted = Encrypt (algorithmOid, salt, _iterations, safeBags.GetBytes ()); + ASN1 encryptedContent = new ASN1 (0x80, encrypted); + + ASN1 seq = new ASN1 (0x30); + seq.Add (ASN1Convert.FromOid (PKCS7.Oid.data)); + seq.Add (seqPbe); + seq.Add (encryptedContent); + + ASN1 version = new ASN1 (0x02, new byte [1] { 0x00 }); + ASN1 encData = new ASN1 (0x30); + encData.Add (version); + encData.Add (seq); + + ASN1 finalContent = new ASN1 (0xA0); + finalContent.Add (encData); + + PKCS7.ContentInfo bag = new PKCS7.ContentInfo (PKCS7.Oid.encryptedData); + bag.Content = finalContent; + return bag; + } + + public void AddCertificate (X509Certificate cert) + { + AddCertificate (cert, null); + } + + public void AddCertificate (X509Certificate cert, IDictionary attributes) + { + bool found = false; + + for (int i = 0; !found && i < _safeBags.Count; i++) { + SafeBag sb = (SafeBag)_safeBags [i]; + + if (sb.BagOID.Equals (certBag)) { + ASN1 safeBag = sb.ASN1; + ASN1 bagValue = safeBag [1]; + PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value); + X509Certificate c = new X509Certificate (crt.Content [0].Value); + if (Compare (cert.RawData, c.RawData)) { + found = true; + } + } + } + + if (!found) { + _safeBags.Add (new SafeBag (certBag, CertificateSafeBag (cert, attributes))); + _certsChanged = true; + } + } + + public void RemoveCertificate (X509Certificate cert) + { + RemoveCertificate (cert, null); + } + + public void RemoveCertificate (X509Certificate cert, IDictionary attrs) + { + int certIndex = -1; + + for (int i = 0; certIndex == -1 && i < _safeBags.Count; i++) { + SafeBag sb = (SafeBag)_safeBags [i]; + + if (sb.BagOID.Equals (certBag)) { + ASN1 safeBag = sb.ASN1; + ASN1 bagValue = safeBag [1]; + PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value); + X509Certificate c = new X509Certificate (crt.Content [0].Value); + if (Compare (cert.RawData, c.RawData)) { + if (attrs != null) { + if (safeBag.Count == 3) { + ASN1 bagAttributes = safeBag [2]; + int bagAttributesFound = 0; + for (int j = 0; j < bagAttributes.Count; j++) { + ASN1 pkcs12Attribute = bagAttributes [j]; + ASN1 attrId = pkcs12Attribute [0]; + string ao = ASN1Convert.ToOid (attrId); + ArrayList dattrValues = (ArrayList)attrs [ao]; + + if (dattrValues != null) { + ASN1 attrValues = pkcs12Attribute [1]; + + if (dattrValues.Count == attrValues.Count) { + int attrValuesFound = 0; + for (int k = 0; k < attrValues.Count; k++) { + ASN1 attrValue = attrValues [k]; + byte[] value = (byte[])dattrValues [k]; + + if (Compare (value, attrValue.Value)) { + attrValuesFound += 1; + } + } + if (attrValuesFound == attrValues.Count) { + bagAttributesFound += 1; + } + } + } + } + if (bagAttributesFound == bagAttributes.Count) { + certIndex = i; + } + } + } else { + certIndex = i; + } + } + } + } + + if (certIndex != -1) { + _safeBags.RemoveAt (certIndex); + _certsChanged = true; + } + } + + private bool CompareAsymmetricAlgorithm (AsymmetricAlgorithm a1, AsymmetricAlgorithm a2) + { + // fast path + if (a1.KeySize != a2.KeySize) + return false; + // compare public keys - if they match we can assume the private match too + return (a1.ToXmlString (false) == a2.ToXmlString (false)); + } + + public void AddPkcs8ShroudedKeyBag (AsymmetricAlgorithm aa) + { + AddPkcs8ShroudedKeyBag (aa, null); + } + + public void AddPkcs8ShroudedKeyBag (AsymmetricAlgorithm aa, IDictionary attributes) + { + bool found = false; + + for (int i = 0; !found && i < _safeBags.Count; i++) { + SafeBag sb = (SafeBag)_safeBags [i]; + + if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) { + ASN1 bagValue = sb.ASN1 [1]; + PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value); + byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData); + PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted); + byte[] privateKey = pki.PrivateKey; + + AsymmetricAlgorithm saa = null; + switch (privateKey [0]) { + case 0x02: + DSAParameters p = new DSAParameters (); // FIXME + saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p); + break; + case 0x30: + saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey); + break; + default: + Array.Clear (decrypted, 0, decrypted.Length); + Array.Clear (privateKey, 0, privateKey.Length); + throw new CryptographicException ("Unknown private key format"); + } + + Array.Clear (decrypted, 0, decrypted.Length); + Array.Clear (privateKey, 0, privateKey.Length); + + if (CompareAsymmetricAlgorithm (aa , saa)) { + found = true; + } + } + } + + if (!found) { + _safeBags.Add (new SafeBag (pkcs8ShroudedKeyBag, Pkcs8ShroudedKeyBagSafeBag (aa, attributes))); + _keyBagsChanged = true; + } + } + + public void RemovePkcs8ShroudedKeyBag (AsymmetricAlgorithm aa) + { + int aaIndex = -1; + + for (int i = 0; aaIndex == -1 && i < _safeBags.Count; i++) { + SafeBag sb = (SafeBag)_safeBags [i]; + + if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) { + ASN1 bagValue = sb.ASN1 [1]; + PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value); + byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData); + PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted); + byte[] privateKey = pki.PrivateKey; + + AsymmetricAlgorithm saa = null; + switch (privateKey [0]) { + case 0x02: + DSAParameters p = new DSAParameters (); // FIXME + saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p); + break; + case 0x30: + saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey); + break; + default: + Array.Clear (decrypted, 0, decrypted.Length); + Array.Clear (privateKey, 0, privateKey.Length); + throw new CryptographicException ("Unknown private key format"); + } + + Array.Clear (decrypted, 0, decrypted.Length); + Array.Clear (privateKey, 0, privateKey.Length); + + if (CompareAsymmetricAlgorithm (aa, saa)) { + aaIndex = i; + } + } + } + + if (aaIndex != -1) { + _safeBags.RemoveAt (aaIndex); + _keyBagsChanged = true; + } + } + + public void AddKeyBag (AsymmetricAlgorithm aa) + { + AddKeyBag (aa, null); + } + + public void AddKeyBag (AsymmetricAlgorithm aa, IDictionary attributes) + { + bool found = false; + + for (int i = 0; !found && i < _safeBags.Count; i++) { + SafeBag sb = (SafeBag)_safeBags [i]; + + if (sb.BagOID.Equals (keyBag)) { + ASN1 bagValue = sb.ASN1 [1]; + PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value); + byte[] privateKey = pki.PrivateKey; + + AsymmetricAlgorithm saa = null; + switch (privateKey [0]) { + case 0x02: + DSAParameters p = new DSAParameters (); // FIXME + saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p); + break; + case 0x30: + saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey); + break; + default: + Array.Clear (privateKey, 0, privateKey.Length); + throw new CryptographicException ("Unknown private key format"); + } + + Array.Clear (privateKey, 0, privateKey.Length); + + if (CompareAsymmetricAlgorithm (aa, saa)) { + found = true; + } + } + } + + if (!found) { + _safeBags.Add (new SafeBag (keyBag, KeyBagSafeBag (aa, attributes))); + _keyBagsChanged = true; + } + } + + public void RemoveKeyBag (AsymmetricAlgorithm aa) + { + int aaIndex = -1; + + for (int i = 0; aaIndex == -1 && i < _safeBags.Count; i++) { + SafeBag sb = (SafeBag)_safeBags [i]; + + if (sb.BagOID.Equals (keyBag)) { + ASN1 bagValue = sb.ASN1 [1]; + PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value); + byte[] privateKey = pki.PrivateKey; + + AsymmetricAlgorithm saa = null; + switch (privateKey [0]) { + case 0x02: + DSAParameters p = new DSAParameters (); // FIXME + saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p); + break; + case 0x30: + saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey); + break; + default: + Array.Clear (privateKey, 0, privateKey.Length); + throw new CryptographicException ("Unknown private key format"); + } + + Array.Clear (privateKey, 0, privateKey.Length); + + if (CompareAsymmetricAlgorithm (aa, saa)) { + aaIndex = i; + } + } + } + + if (aaIndex != -1) { + _safeBags.RemoveAt (aaIndex); + _keyBagsChanged = true; + } + } + + public void AddSecretBag (byte[] secret) + { + AddSecretBag (secret, null); + } + + public void AddSecretBag (byte[] secret, IDictionary attributes) + { + bool found = false; + + for (int i = 0; !found && i < _safeBags.Count; i++) { + SafeBag sb = (SafeBag)_safeBags [i]; + + if (sb.BagOID.Equals (secretBag)) { + ASN1 bagValue = sb.ASN1 [1]; + byte[] ssecret = bagValue.Value; + + if (Compare (secret, ssecret)) { + found = true; + } + } + } + + if (!found) { + _safeBags.Add (new SafeBag (secretBag, SecretBagSafeBag (secret, attributes))); + _secretBagsChanged = true; + } + } + + public void RemoveSecretBag (byte[] secret) + { + int sIndex = -1; + + for (int i = 0; sIndex == -1 && i < _safeBags.Count; i++) { + SafeBag sb = (SafeBag)_safeBags [i]; + + if (sb.BagOID.Equals (secretBag)) { + ASN1 bagValue = sb.ASN1 [1]; + byte[] ssecret = bagValue.Value; + + if (Compare (secret, ssecret)) { + sIndex = i; + } + } + } + + if (sIndex != -1) { + _safeBags.RemoveAt (sIndex); + _secretBagsChanged = true; + } + } + + public AsymmetricAlgorithm GetAsymmetricAlgorithm (IDictionary attrs) + { + foreach (SafeBag sb in _safeBags) { + if (sb.BagOID.Equals (keyBag) || sb.BagOID.Equals (pkcs8ShroudedKeyBag)) { + ASN1 safeBag = sb.ASN1; + + if (safeBag.Count == 3) { + ASN1 bagAttributes = safeBag [2]; + + int bagAttributesFound = 0; + for (int i = 0; i < bagAttributes.Count; i++) { + ASN1 pkcs12Attribute = bagAttributes [i]; + ASN1 attrId = pkcs12Attribute [0]; + string ao = ASN1Convert.ToOid (attrId); + ArrayList dattrValues = (ArrayList)attrs [ao]; + + if (dattrValues != null) { + ASN1 attrValues = pkcs12Attribute [1]; + + if (dattrValues.Count == attrValues.Count) { + int attrValuesFound = 0; + for (int j = 0; j < attrValues.Count; j++) { + ASN1 attrValue = attrValues [j]; + byte[] value = (byte[])dattrValues [j]; + + if (Compare (value, attrValue.Value)) { + attrValuesFound += 1; + } + } + if (attrValuesFound == attrValues.Count) { + bagAttributesFound += 1; + } + } + } + } + if (bagAttributesFound == bagAttributes.Count) { + ASN1 bagValue = safeBag [1]; + AsymmetricAlgorithm aa = null; + if (sb.BagOID.Equals (keyBag)) { + PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value); + byte[] privateKey = pki.PrivateKey; + switch (privateKey [0]) { + case 0x02: + DSAParameters p = new DSAParameters (); // FIXME + aa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p); + break; + case 0x30: + aa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey); + break; + default: + break; + } + Array.Clear (privateKey, 0, privateKey.Length); + } else if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) { + PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value); + byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData); + PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted); + byte[] privateKey = pki.PrivateKey; + switch (privateKey [0]) { + case 0x02: + DSAParameters p = new DSAParameters (); // FIXME + aa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p); + break; + case 0x30: + aa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey); + break; + default: + break; + } + Array.Clear (privateKey, 0, privateKey.Length); + Array.Clear (decrypted, 0, decrypted.Length); + } + return aa; + } + } + } + } + + return null; + } + + public byte[] GetSecret (IDictionary attrs) + { + foreach (SafeBag sb in _safeBags) { + if (sb.BagOID.Equals (secretBag)) { + ASN1 safeBag = sb.ASN1; + + if (safeBag.Count == 3) { + ASN1 bagAttributes = safeBag [2]; + + int bagAttributesFound = 0; + for (int i = 0; i < bagAttributes.Count; i++) { + ASN1 pkcs12Attribute = bagAttributes [i]; + ASN1 attrId = pkcs12Attribute [0]; + string ao = ASN1Convert.ToOid (attrId); + ArrayList dattrValues = (ArrayList)attrs [ao]; + + if (dattrValues != null) { + ASN1 attrValues = pkcs12Attribute [1]; + + if (dattrValues.Count == attrValues.Count) { + int attrValuesFound = 0; + for (int j = 0; j < attrValues.Count; j++) { + ASN1 attrValue = attrValues [j]; + byte[] value = (byte[])dattrValues [j]; + + if (Compare (value, attrValue.Value)) { + attrValuesFound += 1; + } + } + if (attrValuesFound == attrValues.Count) { + bagAttributesFound += 1; + } + } + } + } + if (bagAttributesFound == bagAttributes.Count) { + ASN1 bagValue = safeBag [1]; + return bagValue.Value; + } + } + } + } + + return null; + } + + public X509Certificate GetCertificate (IDictionary attrs) + { + foreach (SafeBag sb in _safeBags) { + if (sb.BagOID.Equals (certBag)) { + ASN1 safeBag = sb.ASN1; + + if (safeBag.Count == 3) { + ASN1 bagAttributes = safeBag [2]; + + int bagAttributesFound = 0; + for (int i = 0; i < bagAttributes.Count; i++) { + ASN1 pkcs12Attribute = bagAttributes [i]; + ASN1 attrId = pkcs12Attribute [0]; + string ao = ASN1Convert.ToOid (attrId); + ArrayList dattrValues = (ArrayList)attrs [ao]; + + if (dattrValues != null) { + ASN1 attrValues = pkcs12Attribute [1]; + + if (dattrValues.Count == attrValues.Count) { + int attrValuesFound = 0; + for (int j = 0; j < attrValues.Count; j++) { + ASN1 attrValue = attrValues [j]; + byte[] value = (byte[])dattrValues [j]; + + if (Compare (value, attrValue.Value)) { + attrValuesFound += 1; + } + } + if (attrValuesFound == attrValues.Count) { + bagAttributesFound += 1; + } + } + } + } + if (bagAttributesFound == bagAttributes.Count) { + ASN1 bagValue = safeBag [1]; + PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value); + return new X509Certificate (crt.Content [0].Value); + } + } + } + } + + return null; + } + + public IDictionary GetAttributes (AsymmetricAlgorithm aa) + { + IDictionary result = new Hashtable (); + + foreach (SafeBag sb in _safeBags) { + if (sb.BagOID.Equals (keyBag) || sb.BagOID.Equals (pkcs8ShroudedKeyBag)) { + ASN1 safeBag = sb.ASN1; + + ASN1 bagValue = safeBag [1]; + AsymmetricAlgorithm saa = null; + if (sb.BagOID.Equals (keyBag)) { + PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value); + byte[] privateKey = pki.PrivateKey; + switch (privateKey [0]) { + case 0x02: + DSAParameters p = new DSAParameters (); // FIXME + saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p); + break; + case 0x30: + saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey); + break; + default: + break; + } + Array.Clear (privateKey, 0, privateKey.Length); + } else if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) { + PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value); + byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData); + PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted); + byte[] privateKey = pki.PrivateKey; + switch (privateKey [0]) { + case 0x02: + DSAParameters p = new DSAParameters (); // FIXME + saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p); + break; + case 0x30: + saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey); + break; + default: + break; + } + Array.Clear (privateKey, 0, privateKey.Length); + Array.Clear (decrypted, 0, decrypted.Length); + } + + if (saa != null && CompareAsymmetricAlgorithm (saa, aa)) { + if (safeBag.Count == 3) { + ASN1 bagAttributes = safeBag [2]; + + for (int i = 0; i < bagAttributes.Count; i++) { + ASN1 pkcs12Attribute = bagAttributes [i]; + ASN1 attrId = pkcs12Attribute [0]; + string aOid = ASN1Convert.ToOid (attrId); + ArrayList aValues = new ArrayList (); + + ASN1 attrValues = pkcs12Attribute [1]; + + for (int j = 0; j < attrValues.Count; j++) { + ASN1 attrValue = attrValues [j]; + aValues.Add (attrValue.Value); + } + result.Add (aOid, aValues); + } + } + } + } + } + + return result; + } + + public IDictionary GetAttributes (X509Certificate cert) + { + IDictionary result = new Hashtable (); + + foreach (SafeBag sb in _safeBags) { + if (sb.BagOID.Equals (certBag)) { + ASN1 safeBag = sb.ASN1; + ASN1 bagValue = safeBag [1]; + PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value); + X509Certificate xc = new X509Certificate (crt.Content [0].Value); + + if (Compare (cert.RawData, xc.RawData)) { + if (safeBag.Count == 3) { + ASN1 bagAttributes = safeBag [2]; + + for (int i = 0; i < bagAttributes.Count; i++) { + ASN1 pkcs12Attribute = bagAttributes [i]; + ASN1 attrId = pkcs12Attribute [0]; + + string aOid = ASN1Convert.ToOid (attrId); + ArrayList aValues = new ArrayList (); + + ASN1 attrValues = pkcs12Attribute [1]; + + for (int j = 0; j < attrValues.Count; j++) { + ASN1 attrValue = attrValues [j]; + aValues.Add (attrValue.Value); + } + result.Add (aOid, aValues); + } + } + } + } + } + + return result; + } + + public void SaveToFile (string filename) + { + if (filename == null) + throw new ArgumentNullException ("filename"); + + using (FileStream fs = File.Create (filename)) { + byte[] data = GetBytes (); + fs.Write (data, 0, data.Length); + } + } + + public object Clone () + { + PKCS12 clone = null; + if (_password != null) { + clone = new PKCS12 (GetBytes (), Encoding.BigEndianUnicode.GetString (_password)); + } else { + clone = new PKCS12 (GetBytes ()); + } + clone.IterationCount = this.IterationCount; + + return clone; + } + + // static + + public const int CryptoApiPasswordLimit = 32; + + static private int password_max_length = Int32.MaxValue; + + // static properties + + // MS CryptoAPI limits the password to a maximum of 31 characters + // other implementations, like OpenSSL, have no such limitation. + // Setting a maximum value will truncate the password length to + // ensure compatibility with MS's PFXImportCertStore API. + static public int MaximumPasswordLength { + get { return password_max_length; } + set { + if (value < CryptoApiPasswordLimit) { + string msg = string.Format ("Maximum password length cannot be less than {0}.", CryptoApiPasswordLimit); + throw new ArgumentOutOfRangeException (msg); + } + password_max_length = value; + } + } + } +} diff --git a/MediaBrowser.Server.Startup.Common/Security/PKCS7.cs b/MediaBrowser.Server.Startup.Common/Security/PKCS7.cs new file mode 100644 index 000000000..1b1a3295b --- /dev/null +++ b/MediaBrowser.Server.Startup.Common/Security/PKCS7.cs @@ -0,0 +1,1012 @@ +// +// PKCS7.cs: PKCS #7 - Cryptographic Message Syntax Standard +// http://www.rsasecurity.com/rsalabs/pkcs/pkcs-7/index.html +// +// Authors: +// Sebastien Pouliot +// Daniel Granath +// +// (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com) +// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections; +using System.Security.Cryptography; + +namespace Emby.Common.Implementations.Security +{ + + public sealed class PKCS7 { + + public class Oid { + // pkcs 1 + public const string rsaEncryption = "1.2.840.113549.1.1.1"; + // pkcs 7 + public const string data = "1.2.840.113549.1.7.1"; + public const string signedData = "1.2.840.113549.1.7.2"; + public const string envelopedData = "1.2.840.113549.1.7.3"; + public const string signedAndEnvelopedData = "1.2.840.113549.1.7.4"; + public const string digestedData = "1.2.840.113549.1.7.5"; + public const string encryptedData = "1.2.840.113549.1.7.6"; + // pkcs 9 + public const string contentType = "1.2.840.113549.1.9.3"; + public const string messageDigest = "1.2.840.113549.1.9.4"; + public const string signingTime = "1.2.840.113549.1.9.5"; + public const string countersignature = "1.2.840.113549.1.9.6"; + + public Oid () + { + } + } + + private PKCS7 () + { + } + + static public ASN1 Attribute (string oid, ASN1 value) + { + ASN1 attr = new ASN1 (0x30); + attr.Add (ASN1Convert.FromOid (oid)); + ASN1 aset = attr.Add (new ASN1 (0x31)); + aset.Add (value); + return attr; + } + + static public ASN1 AlgorithmIdentifier (string oid) + { + ASN1 ai = new ASN1 (0x30); + ai.Add (ASN1Convert.FromOid (oid)); + ai.Add (new ASN1 (0x05)); // NULL + return ai; + } + + static public ASN1 AlgorithmIdentifier (string oid, ASN1 parameters) + { + ASN1 ai = new ASN1 (0x30); + ai.Add (ASN1Convert.FromOid (oid)); + ai.Add (parameters); + return ai; + } + + /* + * IssuerAndSerialNumber ::= SEQUENCE { + * issuer Name, + * serialNumber CertificateSerialNumber + * } + */ + static public ASN1 IssuerAndSerialNumber (X509Certificate x509) + { + ASN1 issuer = null; + ASN1 serial = null; + ASN1 cert = new ASN1 (x509.RawData); + int tbs = 0; + bool flag = false; + while (tbs < cert[0].Count) { + ASN1 e = cert[0][tbs++]; + if (e.Tag == 0x02) + serial = e; + else if (e.Tag == 0x30) { + if (flag) { + issuer = e; + break; + } + flag = true; + } + } + ASN1 iasn = new ASN1 (0x30); + iasn.Add (issuer); + iasn.Add (serial); + return iasn; + } + + /* + * ContentInfo ::= SEQUENCE { + * contentType ContentType, + * content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL + * } + * ContentType ::= OBJECT IDENTIFIER + */ + public class ContentInfo { + + private string contentType; + private ASN1 content; + + public ContentInfo () + { + content = new ASN1 (0xA0); + } + + public ContentInfo (string oid) : this () + { + contentType = oid; + } + + public ContentInfo (byte[] data) + : this (new ASN1 (data)) {} + + public ContentInfo (ASN1 asn1) + { + // SEQUENCE with 1 or 2 elements + if ((asn1.Tag != 0x30) || ((asn1.Count < 1) && (asn1.Count > 2))) + throw new ArgumentException ("Invalid ASN1"); + if (asn1[0].Tag != 0x06) + throw new ArgumentException ("Invalid contentType"); + contentType = ASN1Convert.ToOid (asn1[0]); + if (asn1.Count > 1) { + if (asn1[1].Tag != 0xA0) + throw new ArgumentException ("Invalid content"); + content = asn1[1]; + } + } + + public ASN1 ASN1 { + get { return GetASN1(); } + } + + public ASN1 Content { + get { return content; } + set { content = value; } + } + + public string ContentType { + get { return contentType; } + set { contentType = value; } + } + + internal ASN1 GetASN1 () + { + // ContentInfo ::= SEQUENCE { + ASN1 contentInfo = new ASN1 (0x30); + // contentType ContentType, -> ContentType ::= OBJECT IDENTIFIER + contentInfo.Add (ASN1Convert.FromOid (contentType)); + // content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL + if ((content != null) && (content.Count > 0)) + contentInfo.Add (content); + return contentInfo; + } + + public byte[] GetBytes () + { + return GetASN1 ().GetBytes (); + } + } + + /* + * EncryptedData ::= SEQUENCE { + * version INTEGER {edVer0(0)} (edVer0), + * encryptedContentInfo EncryptedContentInfo + * } + */ + public class EncryptedData { + private byte _version; + private ContentInfo _content; + private ContentInfo _encryptionAlgorithm; + private byte[] _encrypted; + + public EncryptedData () + { + _version = 0; + } + + public EncryptedData (byte[] data) + : this (new ASN1 (data)) + { + } + + public EncryptedData (ASN1 asn1) : this () + { + if ((asn1.Tag != 0x30) || (asn1.Count < 2)) + throw new ArgumentException ("Invalid EncryptedData"); + + if (asn1 [0].Tag != 0x02) + throw new ArgumentException ("Invalid version"); + _version = asn1 [0].Value [0]; + + ASN1 encryptedContentInfo = asn1 [1]; + if (encryptedContentInfo.Tag != 0x30) + throw new ArgumentException ("missing EncryptedContentInfo"); + + ASN1 contentType = encryptedContentInfo [0]; + if (contentType.Tag != 0x06) + throw new ArgumentException ("missing EncryptedContentInfo.ContentType"); + _content = new ContentInfo (ASN1Convert.ToOid (contentType)); + + ASN1 contentEncryptionAlgorithm = encryptedContentInfo [1]; + if (contentEncryptionAlgorithm.Tag != 0x30) + throw new ArgumentException ("missing EncryptedContentInfo.ContentEncryptionAlgorithmIdentifier"); + _encryptionAlgorithm = new ContentInfo (ASN1Convert.ToOid (contentEncryptionAlgorithm [0])); + _encryptionAlgorithm.Content = contentEncryptionAlgorithm [1]; + + ASN1 encryptedContent = encryptedContentInfo [2]; + if (encryptedContent.Tag != 0x80) + throw new ArgumentException ("missing EncryptedContentInfo.EncryptedContent"); + _encrypted = encryptedContent.Value; + } + + public ASN1 ASN1 { + get { return GetASN1(); } + } + + public ContentInfo ContentInfo { + get { return _content; } + } + + public ContentInfo EncryptionAlgorithm { + get { return _encryptionAlgorithm; } + } + + public byte[] EncryptedContent { + get { + if (_encrypted == null) + return null; + return (byte[]) _encrypted.Clone (); + } + } + + public byte Version { + get { return _version; } + set { _version = value; } + } + + // methods + + internal ASN1 GetASN1 () + { + return null; + } + + public byte[] GetBytes () + { + return GetASN1 ().GetBytes (); + } + } + + /* + * EnvelopedData ::= SEQUENCE { + * version Version, + * recipientInfos RecipientInfos, + * encryptedContentInfo EncryptedContentInfo + * } + * + * RecipientInfos ::= SET OF RecipientInfo + * + * EncryptedContentInfo ::= SEQUENCE { + * contentType ContentType, + * contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier, + * encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL + * } + * + * EncryptedContent ::= OCTET STRING + * + */ + public class EnvelopedData { + private byte _version; + private ContentInfo _content; + private ContentInfo _encryptionAlgorithm; + private ArrayList _recipientInfos; + private byte[] _encrypted; + + public EnvelopedData () + { + _version = 0; + _content = new ContentInfo (); + _encryptionAlgorithm = new ContentInfo (); + _recipientInfos = new ArrayList (); + } + + public EnvelopedData (byte[] data) + : this (new ASN1 (data)) + { + } + + public EnvelopedData (ASN1 asn1) : this () + { + if ((asn1[0].Tag != 0x30) || (asn1[0].Count < 3)) + throw new ArgumentException ("Invalid EnvelopedData"); + + if (asn1[0][0].Tag != 0x02) + throw new ArgumentException ("Invalid version"); + _version = asn1[0][0].Value[0]; + + // recipientInfos + + ASN1 recipientInfos = asn1 [0][1]; + if (recipientInfos.Tag != 0x31) + throw new ArgumentException ("missing RecipientInfos"); + for (int i=0; i < recipientInfos.Count; i++) { + ASN1 recipientInfo = recipientInfos [i]; + _recipientInfos.Add (new RecipientInfo (recipientInfo)); + } + + ASN1 encryptedContentInfo = asn1[0][2]; + if (encryptedContentInfo.Tag != 0x30) + throw new ArgumentException ("missing EncryptedContentInfo"); + + ASN1 contentType = encryptedContentInfo [0]; + if (contentType.Tag != 0x06) + throw new ArgumentException ("missing EncryptedContentInfo.ContentType"); + _content = new ContentInfo (ASN1Convert.ToOid (contentType)); + + ASN1 contentEncryptionAlgorithm = encryptedContentInfo [1]; + if (contentEncryptionAlgorithm.Tag != 0x30) + throw new ArgumentException ("missing EncryptedContentInfo.ContentEncryptionAlgorithmIdentifier"); + _encryptionAlgorithm = new ContentInfo (ASN1Convert.ToOid (contentEncryptionAlgorithm [0])); + _encryptionAlgorithm.Content = contentEncryptionAlgorithm [1]; + + ASN1 encryptedContent = encryptedContentInfo [2]; + if (encryptedContent.Tag != 0x80) + throw new ArgumentException ("missing EncryptedContentInfo.EncryptedContent"); + _encrypted = encryptedContent.Value; + } + + public ArrayList RecipientInfos { + get { return _recipientInfos; } + } + + public ASN1 ASN1 { + get { return GetASN1(); } + } + + public ContentInfo ContentInfo { + get { return _content; } + } + + public ContentInfo EncryptionAlgorithm { + get { return _encryptionAlgorithm; } + } + + public byte[] EncryptedContent { + get { + if (_encrypted == null) + return null; + return (byte[]) _encrypted.Clone (); + } + } + + public byte Version { + get { return _version; } + set { _version = value; } + } + + internal ASN1 GetASN1 () + { + // SignedData ::= SEQUENCE { + ASN1 signedData = new ASN1 (0x30); + // version Version -> Version ::= INTEGER +/* byte[] ver = { _version }; + signedData.Add (new ASN1 (0x02, ver)); + // digestAlgorithms DigestAlgorithmIdentifiers -> DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier + ASN1 digestAlgorithms = signedData.Add (new ASN1 (0x31)); + if (hashAlgorithm != null) { + string hashOid = CryptoConfig.MapNameToOid (hashAlgorithm); + digestAlgorithms.Add (AlgorithmIdentifier (hashOid)); + } + + // contentInfo ContentInfo, + ASN1 ci = contentInfo.ASN1; + signedData.Add (ci); + if ((mda == null) && (hashAlgorithm != null)) { + // automatically add the messageDigest authenticated attribute + HashAlgorithm ha = HashAlgorithm.Create (hashAlgorithm); + byte[] idcHash = ha.ComputeHash (ci[1][0].Value); + ASN1 md = new ASN1 (0x30); + mda = Attribute (messageDigest, md.Add (new ASN1 (0x04, idcHash))); + signerInfo.AuthenticatedAttributes.Add (mda); + } + + // certificates [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL, + if (certs.Count > 0) { + ASN1 a0 = signedData.Add (new ASN1 (0xA0)); + foreach (X509Certificate x in certs) + a0.Add (new ASN1 (x.RawData)); + } + // crls [1] IMPLICIT CertificateRevocationLists OPTIONAL, + if (crls.Count > 0) { + ASN1 a1 = signedData.Add (new ASN1 (0xA1)); + foreach (byte[] crl in crls) + a1.Add (new ASN1 (crl)); + } + // signerInfos SignerInfos -> SignerInfos ::= SET OF SignerInfo + ASN1 signerInfos = signedData.Add (new ASN1 (0x31)); + if (signerInfo.Key != null) + signerInfos.Add (signerInfo.ASN1);*/ + return signedData; + } + + public byte[] GetBytes () { + return GetASN1 ().GetBytes (); + } + } + + /* RecipientInfo ::= SEQUENCE { + * version Version, + * issuerAndSerialNumber IssuerAndSerialNumber, + * keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier, + * encryptedKey EncryptedKey + * } + * + * KeyEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier + * + * EncryptedKey ::= OCTET STRING + */ + public class RecipientInfo { + + private int _version; + private string _oid; + private byte[] _key; + private byte[] _ski; + private string _issuer; + private byte[] _serial; + + public RecipientInfo () {} + + public RecipientInfo (ASN1 data) + { + if (data.Tag != 0x30) + throw new ArgumentException ("Invalid RecipientInfo"); + + ASN1 version = data [0]; + if (version.Tag != 0x02) + throw new ArgumentException ("missing Version"); + _version = version.Value [0]; + + // issuerAndSerialNumber IssuerAndSerialNumber + ASN1 subjectIdentifierType = data [1]; + if ((subjectIdentifierType.Tag == 0x80) && (_version == 3)) { + _ski = subjectIdentifierType.Value; + } + else { + _issuer = X501.ToString (subjectIdentifierType [0]); + _serial = subjectIdentifierType [1].Value; + } + + ASN1 keyEncryptionAlgorithm = data [2]; + _oid = ASN1Convert.ToOid (keyEncryptionAlgorithm [0]); + + ASN1 encryptedKey = data [3]; + _key = encryptedKey.Value; + } + + public string Oid { + get { return _oid; } + } + + public byte[] Key { + get { + if (_key == null) + return null; + return (byte[]) _key.Clone (); + } + } + + public byte[] SubjectKeyIdentifier { + get { + if (_ski == null) + return null; + return (byte[]) _ski.Clone (); + } + } + + public string Issuer { + get { return _issuer; } + } + + public byte[] Serial { + get { + if (_serial == null) + return null; + return (byte[]) _serial.Clone (); + } + } + + public int Version { + get { return _version; } + } + } + + /* + * SignedData ::= SEQUENCE { + * version Version, + * digestAlgorithms DigestAlgorithmIdentifiers, + * contentInfo ContentInfo, + * certificates [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL, + * crls [1] IMPLICIT CertificateRevocationLists OPTIONAL, + * signerInfos SignerInfos + * } + */ + public class SignedData { + private byte version; + private string hashAlgorithm; + private ContentInfo contentInfo; + private X509CertificateCollection certs; + private ArrayList crls; + private SignerInfo signerInfo; + private bool mda; + private bool signed; + + public SignedData () + { + version = 1; + contentInfo = new ContentInfo (); + certs = new X509CertificateCollection (); + crls = new ArrayList (); + signerInfo = new SignerInfo (); + mda = true; + signed = false; + } + + public SignedData (byte[] data) + : this (new ASN1 (data)) + { + } + + public SignedData (ASN1 asn1) + { + if ((asn1[0].Tag != 0x30) || (asn1[0].Count < 4)) + throw new ArgumentException ("Invalid SignedData"); + + if (asn1[0][0].Tag != 0x02) + throw new ArgumentException ("Invalid version"); + version = asn1[0][0].Value[0]; + + contentInfo = new ContentInfo (asn1[0][2]); + + int n = 3; + certs = new X509CertificateCollection (); + if (asn1[0][n].Tag == 0xA0) { + for (int i=0; i < asn1[0][n].Count; i++) + certs.Add (new X509Certificate (asn1[0][n][i].GetBytes ())); + n++; + } + + crls = new ArrayList (); + if (asn1[0][n].Tag == 0xA1) { + for (int i=0; i < asn1[0][n].Count; i++) + crls.Add (asn1[0][n][i].GetBytes ()); + n++; + } + + if (asn1[0][n].Count > 0) + signerInfo = new SignerInfo (asn1[0][n]); + else + signerInfo = new SignerInfo (); + + // Exchange hash algorithm Oid from SignerInfo + if (signerInfo.HashName != null) { + HashName = OidToName(signerInfo.HashName); + } + + // Check if SignerInfo has authenticated attributes + mda = (signerInfo.AuthenticatedAttributes.Count > 0); + } + + public ASN1 ASN1 { + get { return GetASN1(); } + } + + public X509CertificateCollection Certificates { + get { return certs; } + } + + public ContentInfo ContentInfo { + get { return contentInfo; } + } + + public ArrayList Crls { + get { return crls; } + } + + public string HashName { + get { return hashAlgorithm; } + // todo add validation + set { + hashAlgorithm = value; + signerInfo.HashName = value; + } + } + + public SignerInfo SignerInfo { + get { return signerInfo; } + } + + public byte Version { + get { return version; } + set { version = value; } + } + + public bool UseAuthenticatedAttributes { + get { return mda; } + set { mda = value; } + } + + public bool VerifySignature (AsymmetricAlgorithm aa) + { + if (aa == null) { + return false; + } + + RSAPKCS1SignatureDeformatter r = new RSAPKCS1SignatureDeformatter (aa); + r.SetHashAlgorithm (hashAlgorithm); + HashAlgorithm ha = HashAlgorithm.Create (hashAlgorithm); + + byte[] signature = signerInfo.Signature; + byte[] hash = null; + + if (mda) { + ASN1 asn = new ASN1 (0x31); + foreach (ASN1 attr in signerInfo.AuthenticatedAttributes) + asn.Add (attr); + + hash = ha.ComputeHash (asn.GetBytes ()); + } else { + hash = ha.ComputeHash (contentInfo.Content[0].Value); + } + + if (hash != null && signature != null) { + return r.VerifySignature (hash, signature); + } + return false; + } + + internal string OidToName (string oid) + { + switch (oid) { + case "1.3.14.3.2.26" : + return "SHA1"; + case "1.2.840.113549.2.2" : + return "MD2"; + case "1.2.840.113549.2.5" : + return "MD5"; + case "2.16.840.1.101.3.4.1" : + return "SHA256"; + case "2.16.840.1.101.3.4.2" : + return "SHA384"; + case "2.16.840.1.101.3.4.3" : + return "SHA512"; + default : + break; + } + // Unknown Oid + return oid; + } + + internal ASN1 GetASN1 () + { + // SignedData ::= SEQUENCE { + ASN1 signedData = new ASN1 (0x30); + // version Version -> Version ::= INTEGER + byte[] ver = { version }; + signedData.Add (new ASN1 (0x02, ver)); + // digestAlgorithms DigestAlgorithmIdentifiers -> DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier + ASN1 digestAlgorithms = signedData.Add (new ASN1 (0x31)); + if (hashAlgorithm != null) { + string hashOid = CryptoConfig.MapNameToOID (hashAlgorithm); + digestAlgorithms.Add (AlgorithmIdentifier (hashOid)); + } + + // contentInfo ContentInfo, + ASN1 ci = contentInfo.ASN1; + signedData.Add (ci); + if (!signed && (hashAlgorithm != null)) { + if (mda) { + // Use authenticated attributes for signature + + // Automatically add the contentType authenticated attribute + ASN1 ctattr = Attribute (Oid.contentType, ci[0]); + signerInfo.AuthenticatedAttributes.Add (ctattr); + + // Automatically add the messageDigest authenticated attribute + HashAlgorithm ha = HashAlgorithm.Create (hashAlgorithm); + byte[] idcHash = ha.ComputeHash (ci[1][0].Value); + ASN1 md = new ASN1 (0x30); + ASN1 mdattr = Attribute (Oid.messageDigest, md.Add (new ASN1 (0x04, idcHash))); + signerInfo.AuthenticatedAttributes.Add (mdattr); + } else { + // Don't use authenticated attributes for signature -- signature is content + RSAPKCS1SignatureFormatter r = new RSAPKCS1SignatureFormatter (signerInfo.Key); + r.SetHashAlgorithm (hashAlgorithm); + HashAlgorithm ha = HashAlgorithm.Create (hashAlgorithm); + byte[] sig = ha.ComputeHash (ci[1][0].Value); + signerInfo.Signature = r.CreateSignature (sig); + } + signed = true; + } + + // certificates [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL, + if (certs.Count > 0) { + ASN1 a0 = signedData.Add (new ASN1 (0xA0)); + foreach (X509Certificate x in certs) + a0.Add (new ASN1 (x.RawData)); + } + // crls [1] IMPLICIT CertificateRevocationLists OPTIONAL, + if (crls.Count > 0) { + ASN1 a1 = signedData.Add (new ASN1 (0xA1)); + foreach (byte[] crl in crls) + a1.Add (new ASN1 (crl)); + } + // signerInfos SignerInfos -> SignerInfos ::= SET OF SignerInfo + ASN1 signerInfos = signedData.Add (new ASN1 (0x31)); + if (signerInfo.Key != null) + signerInfos.Add (signerInfo.ASN1); + return signedData; + } + + public byte[] GetBytes () + { + return GetASN1 ().GetBytes (); + } + } + + /* + * SignerInfo ::= SEQUENCE { + * version Version, + * issuerAndSerialNumber IssuerAndSerialNumber, + * digestAlgorithm DigestAlgorithmIdentifier, + * authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL, + * digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier, + * encryptedDigest EncryptedDigest, + * unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL + * } + * + * For version == 3 issuerAndSerialNumber may be replaced by ... + */ + public class SignerInfo { + + private byte version; + private X509Certificate x509; + private string hashAlgorithm; + private AsymmetricAlgorithm key; + private ArrayList authenticatedAttributes; + private ArrayList unauthenticatedAttributes; + private byte[] signature; + private string issuer; + private byte[] serial; + private byte[] ski; + + public SignerInfo () + { + version = 1; + authenticatedAttributes = new ArrayList (); + unauthenticatedAttributes = new ArrayList (); + } + + public SignerInfo (byte[] data) + : this (new ASN1 (data)) {} + + // TODO: INCOMPLETE + public SignerInfo (ASN1 asn1) : this () + { + if ((asn1[0].Tag != 0x30) || (asn1[0].Count < 5)) + throw new ArgumentException ("Invalid SignedData"); + + // version Version + if (asn1[0][0].Tag != 0x02) + throw new ArgumentException ("Invalid version"); + version = asn1[0][0].Value[0]; + + // issuerAndSerialNumber IssuerAndSerialNumber + ASN1 subjectIdentifierType = asn1 [0][1]; + if ((subjectIdentifierType.Tag == 0x80) && (version == 3)) { + ski = subjectIdentifierType.Value; + } + else { + issuer = X501.ToString (subjectIdentifierType [0]); + serial = subjectIdentifierType [1].Value; + } + + // digestAlgorithm DigestAlgorithmIdentifier + ASN1 digestAlgorithm = asn1 [0][2]; + hashAlgorithm = ASN1Convert.ToOid (digestAlgorithm [0]); + + // authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL + int n = 3; + ASN1 authAttributes = asn1 [0][n]; + if (authAttributes.Tag == 0xA0) { + n++; + for (int i=0; i < authAttributes.Count; i++) + authenticatedAttributes.Add (authAttributes [i]); + } + + // digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier + n++; + // ASN1 digestEncryptionAlgorithm = asn1 [0][n++]; + // string digestEncryptionAlgorithmOid = ASN1Convert.ToOid (digestEncryptionAlgorithm [0]); + + // encryptedDigest EncryptedDigest + ASN1 encryptedDigest = asn1 [0][n++]; + if (encryptedDigest.Tag == 0x04) + signature = encryptedDigest.Value; + + // unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL + ASN1 unauthAttributes = asn1 [0][n]; + if ((unauthAttributes != null) && (unauthAttributes.Tag == 0xA1)) { + for (int i=0; i < unauthAttributes.Count; i++) + unauthenticatedAttributes.Add (unauthAttributes [i]); + } + } + + public string IssuerName { + get { return issuer; } + } + + public byte[] SerialNumber { + get { + if (serial == null) + return null; + return (byte[]) serial.Clone (); + } + } + + public byte[] SubjectKeyIdentifier { + get { + if (ski == null) + return null; + return (byte[]) ski.Clone (); + } + } + + public ASN1 ASN1 { + get { return GetASN1(); } + } + + public ArrayList AuthenticatedAttributes { + get { return authenticatedAttributes; } + } + + public X509Certificate Certificate { + get { return x509; } + set { x509 = value; } + } + + public string HashName { + get { return hashAlgorithm; } + set { hashAlgorithm = value; } + } + + public AsymmetricAlgorithm Key { + get { return key; } + set { key = value; } + } + + public byte[] Signature { + get { + if (signature == null) + return null; + return (byte[]) signature.Clone (); + } + + set { + if (value != null) { + signature = (byte[]) value.Clone (); + } + } + } + + public ArrayList UnauthenticatedAttributes { + get { return unauthenticatedAttributes; } + } + + public byte Version { + get { return version; } + set { version = value; } + } + + internal ASN1 GetASN1 () + { + if ((key == null) || (hashAlgorithm == null)) + return null; + byte[] ver = { version }; + ASN1 signerInfo = new ASN1 (0x30); + // version Version -> Version ::= INTEGER + signerInfo.Add (new ASN1 (0x02, ver)); + // issuerAndSerialNumber IssuerAndSerialNumber, + signerInfo.Add (PKCS7.IssuerAndSerialNumber (x509)); + // digestAlgorithm DigestAlgorithmIdentifier, + string hashOid = CryptoConfig.MapNameToOID (hashAlgorithm); + signerInfo.Add (AlgorithmIdentifier (hashOid)); + // authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL, + ASN1 aa = null; + if (authenticatedAttributes.Count > 0) { + aa = signerInfo.Add (new ASN1 (0xA0)); + authenticatedAttributes.Sort(new SortedSet ()); + foreach (ASN1 attr in authenticatedAttributes) + aa.Add (attr); + } + // digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier, + if (key is RSA) { + signerInfo.Add (AlgorithmIdentifier (PKCS7.Oid.rsaEncryption)); + + if (aa != null) { + // Calculate the signature here; otherwise it must be set from SignedData + RSAPKCS1SignatureFormatter r = new RSAPKCS1SignatureFormatter (key); + r.SetHashAlgorithm (hashAlgorithm); + byte[] tbs = aa.GetBytes (); + tbs [0] = 0x31; // not 0xA0 for signature + HashAlgorithm ha = HashAlgorithm.Create (hashAlgorithm); + byte[] tbsHash = ha.ComputeHash (tbs); + signature = r.CreateSignature (tbsHash); + } + } + else if (key is DSA) { + throw new NotImplementedException ("not yet"); + } + else + throw new CryptographicException ("Unknown assymetric algorithm"); + // encryptedDigest EncryptedDigest, + signerInfo.Add (new ASN1 (0x04, signature)); + // unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL + if (unauthenticatedAttributes.Count > 0) { + ASN1 ua = signerInfo.Add (new ASN1 (0xA1)); + unauthenticatedAttributes.Sort(new SortedSet ()); + foreach (ASN1 attr in unauthenticatedAttributes) + ua.Add (attr); + } + return signerInfo; + } + + public byte[] GetBytes () + { + return GetASN1 ().GetBytes (); + } + } + + internal class SortedSet : IComparer { + + public int Compare (object x, object y) + { + if (x == null) + return (y == null) ? 0 : -1; + else if (y == null) + return 1; + + ASN1 xx = x as ASN1; + ASN1 yy = y as ASN1; + + if ((xx == null) || (yy == null)) { + throw new ArgumentException (("Invalid objects.")); + } + + byte[] xb = xx.GetBytes (); + byte[] yb = yy.GetBytes (); + + for (int i = 0; i < xb.Length; i++) { + if (i == yb.Length) + break; + + if (xb[i] == yb[i]) + continue; + + return (xb[i] < yb[i]) ? -1 : 1; + } + + // The arrays are equal up to the shortest of them. + if (xb.Length > yb.Length) + return 1; + else if (xb.Length < yb.Length) + return -1; + + return 0; + } + } + } +} diff --git a/MediaBrowser.Server.Startup.Common/Security/PKCS8.cs b/MediaBrowser.Server.Startup.Common/Security/PKCS8.cs new file mode 100644 index 000000000..b58ebdaf9 --- /dev/null +++ b/MediaBrowser.Server.Startup.Common/Security/PKCS8.cs @@ -0,0 +1,495 @@ +// +// PKCS8.cs: PKCS #8 - Private-Key Information Syntax Standard +// ftp://ftp.rsasecurity.com/pub/pkcs/doc/pkcs-8.doc +// +// Author: +// Sebastien Pouliot +// +// (C) 2003 Motus Technologies Inc. (http://www.motus.com) +// Copyright (C) 2004-2006 Novell Inc. (http://www.novell.com) +// Copyright 2013 Xamarin Inc. (http://www.xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections; +using System.Security.Cryptography; + +namespace Emby.Common.Implementations.Security +{ + + public sealed class PKCS8 { + + public enum KeyInfo { + PrivateKey, + EncryptedPrivateKey, + Unknown + } + + private PKCS8 () + { + } + + static public KeyInfo GetType (byte[] data) + { + if (data == null) + throw new ArgumentNullException ("data"); + + KeyInfo ki = KeyInfo.Unknown; + try { + ASN1 top = new ASN1 (data); + if ((top.Tag == 0x30) && (top.Count > 0)) { + ASN1 firstLevel = top [0]; + switch (firstLevel.Tag) { + case 0x02: + ki = KeyInfo.PrivateKey; + break; + case 0x30: + ki = KeyInfo.EncryptedPrivateKey; + break; + } + } + } + catch { + throw new CryptographicException ("invalid ASN.1 data"); + } + return ki; + } + + /* + * PrivateKeyInfo ::= SEQUENCE { + * version Version, + * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, + * privateKey PrivateKey, + * attributes [0] IMPLICIT Attributes OPTIONAL + * } + * + * Version ::= INTEGER + * + * PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier + * + * PrivateKey ::= OCTET STRING + * + * Attributes ::= SET OF Attribute + */ + public class PrivateKeyInfo { + + private int _version; + private string _algorithm; + private byte[] _key; + private ArrayList _list; + + public PrivateKeyInfo () + { + _version = 0; + _list = new ArrayList (); + } + + public PrivateKeyInfo (byte[] data) : this () + { + Decode (data); + } + + // properties + + public string Algorithm { + get { return _algorithm; } + set { _algorithm = value; } + } + + public ArrayList Attributes { + get { return _list; } + } + + public byte[] PrivateKey { + get { + if (_key == null) + return null; + return (byte[]) _key.Clone (); + } + set { + if (value == null) + throw new ArgumentNullException ("PrivateKey"); + _key = (byte[]) value.Clone (); + } + } + + public int Version { + get { return _version; } + set { + if (value < 0) + throw new ArgumentOutOfRangeException ("negative version"); + _version = value; + } + } + + // methods + + private void Decode (byte[] data) + { + ASN1 privateKeyInfo = new ASN1 (data); + if (privateKeyInfo.Tag != 0x30) + throw new CryptographicException ("invalid PrivateKeyInfo"); + + ASN1 version = privateKeyInfo [0]; + if (version.Tag != 0x02) + throw new CryptographicException ("invalid version"); + _version = version.Value [0]; + + ASN1 privateKeyAlgorithm = privateKeyInfo [1]; + if (privateKeyAlgorithm.Tag != 0x30) + throw new CryptographicException ("invalid algorithm"); + + ASN1 algorithm = privateKeyAlgorithm [0]; + if (algorithm.Tag != 0x06) + throw new CryptographicException ("missing algorithm OID"); + _algorithm = ASN1Convert.ToOid (algorithm); + + ASN1 privateKey = privateKeyInfo [2]; + _key = privateKey.Value; + + // attributes [0] IMPLICIT Attributes OPTIONAL + if (privateKeyInfo.Count > 3) { + ASN1 attributes = privateKeyInfo [3]; + for (int i=0; i < attributes.Count; i++) { + _list.Add (attributes [i]); + } + } + } + + public byte[] GetBytes () + { + ASN1 privateKeyAlgorithm = new ASN1 (0x30); + privateKeyAlgorithm.Add (ASN1Convert.FromOid (_algorithm)); + privateKeyAlgorithm.Add (new ASN1 (0x05)); // ASN.1 NULL + + ASN1 pki = new ASN1 (0x30); + pki.Add (new ASN1 (0x02, new byte [1] { (byte) _version })); + pki.Add (privateKeyAlgorithm); + pki.Add (new ASN1 (0x04, _key)); + + if (_list.Count > 0) { + ASN1 attributes = new ASN1 (0xA0); + foreach (ASN1 attribute in _list) { + attributes.Add (attribute); + } + pki.Add (attributes); + } + + return pki.GetBytes (); + } + + // static methods + + static private byte[] RemoveLeadingZero (byte[] bigInt) + { + int start = 0; + int length = bigInt.Length; + if (bigInt [0] == 0x00) { + start = 1; + length--; + } + byte[] bi = new byte [length]; + Buffer.BlockCopy (bigInt, start, bi, 0, length); + return bi; + } + + static private byte[] Normalize (byte[] bigInt, int length) + { + if (bigInt.Length == length) + return bigInt; + else if (bigInt.Length > length) + return RemoveLeadingZero (bigInt); + else { + // pad with 0 + byte[] bi = new byte [length]; + Buffer.BlockCopy (bigInt, 0, bi, (length - bigInt.Length), bigInt.Length); + return bi; + } + } + + /* + * RSAPrivateKey ::= SEQUENCE { + * version Version, + * modulus INTEGER, -- n + * publicExponent INTEGER, -- e + * privateExponent INTEGER, -- d + * prime1 INTEGER, -- p + * prime2 INTEGER, -- q + * exponent1 INTEGER, -- d mod (p-1) + * exponent2 INTEGER, -- d mod (q-1) + * coefficient INTEGER, -- (inverse of q) mod p + * otherPrimeInfos OtherPrimeInfos OPTIONAL + * } + */ + static public RSA DecodeRSA (byte[] keypair) + { + ASN1 privateKey = new ASN1 (keypair); + if (privateKey.Tag != 0x30) + throw new CryptographicException ("invalid private key format"); + + ASN1 version = privateKey [0]; + if (version.Tag != 0x02) + throw new CryptographicException ("missing version"); + + if (privateKey.Count < 9) + throw new CryptographicException ("not enough key parameters"); + + RSAParameters param = new RSAParameters (); + // note: MUST remove leading 0 - else MS wont import the key + param.Modulus = RemoveLeadingZero (privateKey [1].Value); + int keysize = param.Modulus.Length; + int keysize2 = (keysize >> 1); // half-size + // size must be normalized - else MS wont import the key + param.D = Normalize (privateKey [3].Value, keysize); + param.DP = Normalize (privateKey [6].Value, keysize2); + param.DQ = Normalize (privateKey [7].Value, keysize2); + param.Exponent = RemoveLeadingZero (privateKey [2].Value); + param.InverseQ = Normalize (privateKey [8].Value, keysize2); + param.P = Normalize (privateKey [4].Value, keysize2); + param.Q = Normalize (privateKey [5].Value, keysize2); + + RSA rsa = null; + try { + rsa = RSA.Create (); + rsa.ImportParameters (param); + } + catch (CryptographicException) { + // this may cause problem when this code is run under + // the SYSTEM identity on Windows (e.g. ASP.NET). See + // http://bugzilla.ximian.com/show_bug.cgi?id=77559 + CspParameters csp = new CspParameters(); + csp.Flags = CspProviderFlags.UseMachineKeyStore; + rsa = new RSACryptoServiceProvider(csp); + rsa.ImportParameters(param); + } + return rsa; + } + + /* + * RSAPrivateKey ::= SEQUENCE { + * version Version, + * modulus INTEGER, -- n + * publicExponent INTEGER, -- e + * privateExponent INTEGER, -- d + * prime1 INTEGER, -- p + * prime2 INTEGER, -- q + * exponent1 INTEGER, -- d mod (p-1) + * exponent2 INTEGER, -- d mod (q-1) + * coefficient INTEGER, -- (inverse of q) mod p + * otherPrimeInfos OtherPrimeInfos OPTIONAL + * } + */ + static public byte[] Encode (RSA rsa) + { + RSAParameters param = rsa.ExportParameters (true); + + ASN1 rsaPrivateKey = new ASN1 (0x30); + rsaPrivateKey.Add (new ASN1 (0x02, new byte [1] { 0x00 })); + rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.Modulus)); + rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.Exponent)); + rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.D)); + rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.P)); + rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.Q)); + rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.DP)); + rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.DQ)); + rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.InverseQ)); + + return rsaPrivateKey.GetBytes (); + } + + // DSA only encode it's X private key inside an ASN.1 INTEGER (Hint: Tag == 0x02) + // which isn't enough for rebuilding the keypair. The other parameters + // can be found (98% of the time) in the X.509 certificate associated + // with the private key or (2% of the time) the parameters are in it's + // issuer X.509 certificate (not supported in the .NET framework). + static public DSA DecodeDSA (byte[] privateKey, DSAParameters dsaParameters) + { + ASN1 pvk = new ASN1 (privateKey); + if (pvk.Tag != 0x02) + throw new CryptographicException ("invalid private key format"); + + // X is ALWAYS 20 bytes (no matter if the key length is 512 or 1024 bits) + dsaParameters.X = Normalize (pvk.Value, 20); + DSA dsa = DSA.Create (); + dsa.ImportParameters (dsaParameters); + return dsa; + } + + static public byte[] Encode (DSA dsa) + { + DSAParameters param = dsa.ExportParameters (true); + return ASN1Convert.FromUnsignedBigInteger (param.X).GetBytes (); + } + + static public byte[] Encode (AsymmetricAlgorithm aa) + { + if (aa is RSA) + return Encode ((RSA)aa); + else if (aa is DSA) + return Encode ((DSA)aa); + else + throw new CryptographicException ("Unknown asymmetric algorithm {0}", aa.ToString ()); + } + } + + /* + * EncryptedPrivateKeyInfo ::= SEQUENCE { + * encryptionAlgorithm EncryptionAlgorithmIdentifier, + * encryptedData EncryptedData + * } + * + * EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier + * + * EncryptedData ::= OCTET STRING + * + * -- + * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * parameters ANY DEFINED BY algorithm OPTIONAL + * } + * + * -- from PKCS#5 + * PBEParameter ::= SEQUENCE { + * salt OCTET STRING SIZE(8), + * iterationCount INTEGER + * } + */ + public class EncryptedPrivateKeyInfo { + + private string _algorithm; + private byte[] _salt; + private int _iterations; + private byte[] _data; + + public EncryptedPrivateKeyInfo () {} + + public EncryptedPrivateKeyInfo (byte[] data) : this () + { + Decode (data); + } + + // properties + + public string Algorithm { + get { return _algorithm; } + set { _algorithm = value; } + } + + public byte[] EncryptedData { + get { return (_data == null) ? null : (byte[]) _data.Clone (); } + set { _data = (value == null) ? null : (byte[]) value.Clone (); } + } + + public byte[] Salt { + get { + if (_salt == null) { + RandomNumberGenerator rng = RandomNumberGenerator.Create (); + _salt = new byte [8]; + rng.GetBytes (_salt); + } + return (byte[]) _salt.Clone (); + } + set { _salt = (byte[]) value.Clone (); } + } + + public int IterationCount { + get { return _iterations; } + set { + if (value < 0) + throw new ArgumentOutOfRangeException ("IterationCount", "Negative"); + _iterations = value; + } + } + + // methods + + private void Decode (byte[] data) + { + ASN1 encryptedPrivateKeyInfo = new ASN1 (data); + if (encryptedPrivateKeyInfo.Tag != 0x30) + throw new CryptographicException ("invalid EncryptedPrivateKeyInfo"); + + ASN1 encryptionAlgorithm = encryptedPrivateKeyInfo [0]; + if (encryptionAlgorithm.Tag != 0x30) + throw new CryptographicException ("invalid encryptionAlgorithm"); + ASN1 algorithm = encryptionAlgorithm [0]; + if (algorithm.Tag != 0x06) + throw new CryptographicException ("invalid algorithm"); + _algorithm = ASN1Convert.ToOid (algorithm); + // parameters ANY DEFINED BY algorithm OPTIONAL + if (encryptionAlgorithm.Count > 1) { + ASN1 parameters = encryptionAlgorithm [1]; + if (parameters.Tag != 0x30) + throw new CryptographicException ("invalid parameters"); + + ASN1 salt = parameters [0]; + if (salt.Tag != 0x04) + throw new CryptographicException ("invalid salt"); + _salt = salt.Value; + + ASN1 iterationCount = parameters [1]; + if (iterationCount.Tag != 0x02) + throw new CryptographicException ("invalid iterationCount"); + _iterations = ASN1Convert.ToInt32 (iterationCount); + } + + ASN1 encryptedData = encryptedPrivateKeyInfo [1]; + if (encryptedData.Tag != 0x04) + throw new CryptographicException ("invalid EncryptedData"); + _data = encryptedData.Value; + } + + // Note: PKCS#8 doesn't define how to generate the key required for encryption + // so you're on your own. Just don't try to copy the big guys too much ;) + // Netscape: http://www.cs.auckland.ac.nz/~pgut001/pubs/netscape.txt + // Microsoft: http://www.cs.auckland.ac.nz/~pgut001/pubs/breakms.txt + public byte[] GetBytes () + { + if (_algorithm == null) + throw new CryptographicException ("No algorithm OID specified"); + + ASN1 encryptionAlgorithm = new ASN1 (0x30); + encryptionAlgorithm.Add (ASN1Convert.FromOid (_algorithm)); + + // parameters ANY DEFINED BY algorithm OPTIONAL + if ((_iterations > 0) || (_salt != null)) { + ASN1 salt = new ASN1 (0x04, _salt); + ASN1 iterations = ASN1Convert.FromInt32 (_iterations); + + ASN1 parameters = new ASN1 (0x30); + parameters.Add (salt); + parameters.Add (iterations); + encryptionAlgorithm.Add (parameters); + } + + // encapsulates EncryptedData into an OCTET STRING + ASN1 encryptedData = new ASN1 (0x04, _data); + + ASN1 encryptedPrivateKeyInfo = new ASN1 (0x30); + encryptedPrivateKeyInfo.Add (encryptionAlgorithm); + encryptedPrivateKeyInfo.Add (encryptedData); + + return encryptedPrivateKeyInfo.GetBytes (); + } + } + } +} diff --git a/MediaBrowser.Server.Startup.Common/Security/PfxGenerator.cs b/MediaBrowser.Server.Startup.Common/Security/PfxGenerator.cs new file mode 100644 index 000000000..3f9b90ac1 --- /dev/null +++ b/MediaBrowser.Server.Startup.Common/Security/PfxGenerator.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections; +using System.Security.Cryptography; + +namespace Emby.Common.Implementations.Security +{ + public class PFXGenerator + { + // http://www.freekpaans.nl/2015/04/creating-self-signed-x-509-certificates-using-mono-security/ + public static byte[] GeneratePfx(string certificateName, string password) + { + byte[] sn = GenerateSerialNumber(); + string subject = string.Format("CN={0}", certificateName); + + DateTime notBefore = DateTime.Now; + DateTime notAfter = DateTime.Now.AddYears(20); + + RSA subjectKey = new RSACryptoServiceProvider(2048); + + + string hashName = "SHA256"; + + X509CertificateBuilder cb = new X509CertificateBuilder(3); + cb.SerialNumber = sn; + cb.IssuerName = subject; + cb.NotBefore = notBefore; + cb.NotAfter = notAfter; + cb.SubjectName = subject; + cb.SubjectPublicKey = subjectKey; + cb.Hash = hashName; + + byte[] rawcert = cb.Sign(subjectKey); + + + PKCS12 p12 = new PKCS12(); + p12.Password = password; + + Hashtable attributes = GetAttributes(); + + p12.AddCertificate(new X509Certificate(rawcert), attributes); + p12.AddPkcs8ShroudedKeyBag(subjectKey, attributes); + + return p12.GetBytes(); + } + + private static Hashtable GetAttributes() + { + ArrayList list = new ArrayList(); + // we use a fixed array to avoid endianess issues + // (in case some tools requires the ID to be 1). + list.Add(new byte[4] { 1, 0, 0, 0 }); + Hashtable attributes = new Hashtable(1); + attributes.Add(PKCS9.localKeyId, list); + return attributes; + } + + private static byte[] GenerateSerialNumber() + { + byte[] sn = Guid.NewGuid().ToByteArray(); + + //must be positive + if ((sn[0] & 0x80) == 0x80) + sn[0] -= 0x80; + return sn; + } + + public static byte[] GetCertificateForBytes(byte[] pfx, string password) + { + var pkcs = new PKCS12(pfx, password); + var cert = pkcs.GetCertificate(GetAttributes()); + + return cert.RawData; + } + } +} diff --git a/MediaBrowser.Server.Startup.Common/Security/X501Name.cs b/MediaBrowser.Server.Startup.Common/Security/X501Name.cs new file mode 100644 index 000000000..93e1a6bef --- /dev/null +++ b/MediaBrowser.Server.Startup.Common/Security/X501Name.cs @@ -0,0 +1,393 @@ +// +// X501Name.cs: X.501 Distinguished Names stuff +// +// Author: +// Sebastien Pouliot +// +// (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com) +// Copyright (C) 2004-2006 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Globalization; +using System.Text; + +namespace Emby.Common.Implementations.Security +{ + + // References: + // 1. Information technology - Open Systems Interconnection - The Directory: Models + // http://www.itu.int/rec/recommendation.asp?type=items&lang=e&parent=T-REC-X.501-200102-I + // 2. RFC2253: Lightweight Directory Access Protocol (v3): UTF-8 String Representation of Distinguished Names + // http://www.ietf.org/rfc/rfc2253.txt + + /* + * Name ::= CHOICE { RDNSequence } + * + * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName + * + * RelativeDistinguishedName ::= SET OF AttributeTypeAndValue + */ + public sealed class X501 { + + static byte[] countryName = { 0x55, 0x04, 0x06 }; + static byte[] organizationName = { 0x55, 0x04, 0x0A }; + static byte[] organizationalUnitName = { 0x55, 0x04, 0x0B }; + static byte[] commonName = { 0x55, 0x04, 0x03 }; + static byte[] localityName = { 0x55, 0x04, 0x07 }; + static byte[] stateOrProvinceName = { 0x55, 0x04, 0x08 }; + static byte[] streetAddress = { 0x55, 0x04, 0x09 }; + //static byte[] serialNumber = { 0x55, 0x04, 0x05 }; + static byte[] domainComponent = { 0x09, 0x92, 0x26, 0x89, 0x93, 0xF2, 0x2C, 0x64, 0x01, 0x19 }; + static byte[] userid = { 0x09, 0x92, 0x26, 0x89, 0x93, 0xF2, 0x2C, 0x64, 0x01, 0x01 }; + static byte[] email = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01 }; + static byte[] dnQualifier = { 0x55, 0x04, 0x2E }; + static byte[] title = { 0x55, 0x04, 0x0C }; + static byte[] surname = { 0x55, 0x04, 0x04 }; + static byte[] givenName = { 0x55, 0x04, 0x2A }; + static byte[] initial = { 0x55, 0x04, 0x2B }; + + private X501 () + { + } + + static public string ToString (ASN1 seq) + { + StringBuilder sb = new StringBuilder (); + for (int i = 0; i < seq.Count; i++) { + ASN1 entry = seq [i]; + AppendEntry (sb, entry, true); + + // separator (not on last iteration) + if (i < seq.Count - 1) + sb.Append (", "); + } + return sb.ToString (); + } + + static public string ToString (ASN1 seq, bool reversed, string separator, bool quotes) + { + StringBuilder sb = new StringBuilder (); + + if (reversed) { + for (int i = seq.Count - 1; i >= 0; i--) { + ASN1 entry = seq [i]; + AppendEntry (sb, entry, quotes); + + // separator (not on last iteration) + if (i > 0) + sb.Append (separator); + } + } else { + for (int i = 0; i < seq.Count; i++) { + ASN1 entry = seq [i]; + AppendEntry (sb, entry, quotes); + + // separator (not on last iteration) + if (i < seq.Count - 1) + sb.Append (separator); + } + } + return sb.ToString (); + } + + static private void AppendEntry (StringBuilder sb, ASN1 entry, bool quotes) + { + // multiple entries are valid + for (int k = 0; k < entry.Count; k++) { + ASN1 pair = entry [k]; + ASN1 s = pair [1]; + if (s == null) + continue; + + ASN1 poid = pair [0]; + if (poid == null) + continue; + + if (poid.CompareValue (countryName)) + sb.Append ("C="); + else if (poid.CompareValue (organizationName)) + sb.Append ("O="); + else if (poid.CompareValue (organizationalUnitName)) + sb.Append ("OU="); + else if (poid.CompareValue (commonName)) + sb.Append ("CN="); + else if (poid.CompareValue (localityName)) + sb.Append ("L="); + else if (poid.CompareValue (stateOrProvinceName)) + sb.Append ("S="); // NOTE: RFC2253 uses ST= + else if (poid.CompareValue (streetAddress)) + sb.Append ("STREET="); + else if (poid.CompareValue (domainComponent)) + sb.Append ("DC="); + else if (poid.CompareValue (userid)) + sb.Append ("UID="); + else if (poid.CompareValue (email)) + sb.Append ("E="); // NOTE: Not part of RFC2253 + else if (poid.CompareValue (dnQualifier)) + sb.Append ("dnQualifier="); + else if (poid.CompareValue (title)) + sb.Append ("T="); + else if (poid.CompareValue (surname)) + sb.Append ("SN="); + else if (poid.CompareValue (givenName)) + sb.Append ("G="); + else if (poid.CompareValue (initial)) + sb.Append ("I="); + else { + // unknown OID + sb.Append ("OID."); // NOTE: Not present as RFC2253 + sb.Append (ASN1Convert.ToOid (poid)); + sb.Append ("="); + } + + string sValue = null; + // 16bits or 8bits string ? TODO not complete (+special chars!) + if (s.Tag == 0x1E) { + // BMPSTRING + StringBuilder sb2 = new StringBuilder (); + for (int j = 1; j < s.Value.Length; j += 2) + sb2.Append ((char)s.Value[j]); + sValue = sb2.ToString (); + } else { + if (s.Tag == 0x14) + sValue = Encoding.UTF7.GetString (s.Value); + else + sValue = Encoding.UTF8.GetString (s.Value); + // in some cases we must quote (") the value + // Note: this doesn't seems to conform to RFC2253 + char[] specials = { ',', '+', '"', '\\', '<', '>', ';' }; + if (quotes) { + if ((sValue.IndexOfAny (specials, 0, sValue.Length) > 0) || + sValue.StartsWith (" ") || (sValue.EndsWith (" "))) + sValue = "\"" + sValue + "\""; + } + } + + sb.Append (sValue); + + // separator (not on last iteration) + if (k < entry.Count - 1) + sb.Append (", "); + } + } + + static private X520.AttributeTypeAndValue GetAttributeFromOid (string attributeType) + { + string s = attributeType.ToUpper (CultureInfo.InvariantCulture).Trim (); + switch (s) { + case "C": + return new X520.CountryName (); + case "O": + return new X520.OrganizationName (); + case "OU": + return new X520.OrganizationalUnitName (); + case "CN": + return new X520.CommonName (); + case "L": + return new X520.LocalityName (); + case "S": // Microsoft + case "ST": // RFC2253 + return new X520.StateOrProvinceName (); + case "E": // NOTE: Not part of RFC2253 + return new X520.EmailAddress (); + case "DC": // RFC2247 + return new X520.DomainComponent (); + case "UID": // RFC1274 + return new X520.UserId (); + case "DNQUALIFIER": + return new X520.DnQualifier (); + case "T": + return new X520.Title (); + case "SN": + return new X520.Surname (); + case "G": + return new X520.GivenName (); + case "I": + return new X520.Initial (); + default: + if (s.StartsWith ("OID.")) { + // MUST support it but it OID may be without it + return new X520.Oid (s.Substring (4)); + } else { + if (IsOid (s)) + return new X520.Oid (s); + else + return null; + } + } + } + + static private bool IsOid (string oid) + { + try { + ASN1 asn = ASN1Convert.FromOid (oid); + return (asn.Tag == 0x06); + } + catch { + return false; + } + } + + // no quote processing + static private X520.AttributeTypeAndValue ReadAttribute (string value, ref int pos) + { + while ((value[pos] == ' ') && (pos < value.Length)) + pos++; + + // get '=' position in substring + int equal = value.IndexOf ('=', pos); + if (equal == -1) { + string msg = ("No attribute found."); + throw new FormatException (msg); + } + + string s = value.Substring (pos, equal - pos); + X520.AttributeTypeAndValue atv = GetAttributeFromOid (s); + if (atv == null) { + string msg = ("Unknown attribute '{0}'."); + throw new FormatException (String.Format (msg, s)); + } + pos = equal + 1; // skip the '=' + return atv; + } + + static private bool IsHex (char c) + { + if (Char.IsDigit (c)) + return true; + char up = Char.ToUpper (c, CultureInfo.InvariantCulture); + return ((up >= 'A') && (up <= 'F')); + } + + static string ReadHex (string value, ref int pos) + { + StringBuilder sb = new StringBuilder (); + // it is (at least an) 8 bits char + sb.Append (value[pos++]); + sb.Append (value[pos]); + // look ahead for a 16 bits char + if ((pos < value.Length - 4) && (value[pos+1] == '\\') && IsHex (value[pos+2])) { + pos += 2; // pass last char and skip \ + sb.Append (value[pos++]); + sb.Append (value[pos]); + } + byte[] data = CryptoConvert.FromHex (sb.ToString ()); + return Encoding.UTF8.GetString (data); + } + + static private int ReadEscaped (StringBuilder sb, string value, int pos) + { + switch (value[pos]) { + case '\\': + case '"': + case '=': + case ';': + case '<': + case '>': + case '+': + case '#': + case ',': + sb.Append (value[pos]); + return pos; + default: + if (pos >= value.Length - 2) { + string msg = ("Malformed escaped value '{0}'."); + throw new FormatException (string.Format (msg, value.Substring (pos))); + } + // it's either a 8 bits or 16 bits char + sb.Append (ReadHex (value, ref pos)); + return pos; + } + } + + static private int ReadQuoted (StringBuilder sb, string value, int pos) + { + int original = pos; + while (pos <= value.Length) { + switch (value[pos]) { + case '"': + return pos; + case '\\': + return ReadEscaped (sb, value, pos); + default: + sb.Append (value[pos]); + pos++; + break; + } + } + string msg = ("Malformed quoted value '{0}'."); + throw new FormatException (string.Format (msg, value.Substring (original))); + } + + static private string ReadValue (string value, ref int pos) + { + int original = pos; + StringBuilder sb = new StringBuilder (); + while (pos < value.Length) { + switch (value [pos]) { + case '\\': + pos = ReadEscaped (sb, value, ++pos); + break; + case '"': + pos = ReadQuoted (sb, value, ++pos); + break; + case '=': + case ';': + case '<': + case '>': + string msg =("Malformed value '{0}' contains '{1}' outside quotes."); + throw new FormatException (string.Format (msg, value.Substring (original), value[pos])); + case '+': + case '#': + throw new NotImplementedException (); + case ',': + pos++; + return sb.ToString (); + default: + sb.Append (value[pos]); + break; + } + pos++; + } + return sb.ToString (); + } + + static public ASN1 FromString (string rdn) + { + if (rdn == null) + throw new ArgumentNullException ("rdn"); + + int pos = 0; + ASN1 asn1 = new ASN1 (0x30); + while (pos < rdn.Length) { + X520.AttributeTypeAndValue atv = ReadAttribute (rdn, ref pos); + atv.Value = ReadValue (rdn, ref pos); + + ASN1 sequence = new ASN1 (0x31); + sequence.Add (atv.GetASN1 ()); + asn1.Add (sequence); + } + return asn1; + } + } +} diff --git a/MediaBrowser.Server.Startup.Common/Security/X509Builder.cs b/MediaBrowser.Server.Startup.Common/Security/X509Builder.cs new file mode 100644 index 000000000..e16855959 --- /dev/null +++ b/MediaBrowser.Server.Startup.Common/Security/X509Builder.cs @@ -0,0 +1,153 @@ +// +// X509Builder.cs: Abstract builder class for X509 objects +// +// Author: +// Sebastien Pouliot +// +// (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com) +// (C) 2004 Novell (http://www.novell.com) +// + +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Globalization; +using System.Security.Cryptography; + +namespace Emby.Common.Implementations.Security +{ + + public abstract class X509Builder { + + private const string defaultHash = "SHA1"; + private string hashName; + + protected X509Builder () + { + hashName = defaultHash; + } + + protected abstract ASN1 ToBeSigned (string hashName); + + // move to PKCS1 + protected string GetOid (string hashName) + { + switch (hashName.ToLower (CultureInfo.InvariantCulture)) { + case "md2": + // md2withRSAEncryption (1 2 840 113549 1 1 2) + return "1.2.840.113549.1.1.2"; + case "md4": + // md4withRSAEncryption (1 2 840 113549 1 1 3) + return "1.2.840.113549.1.1.3"; + case "md5": + // md5withRSAEncryption (1 2 840 113549 1 1 4) + return "1.2.840.113549.1.1.4"; + case "sha1": + // sha1withRSAEncryption (1 2 840 113549 1 1 5) + return "1.2.840.113549.1.1.5"; + case "sha256": + // sha256WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 11 } + return "1.2.840.113549.1.1.11"; + case "sha384": + // sha384WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 12 } + return "1.2.840.113549.1.1.12"; + case "sha512": + // sha512WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 13 } + return "1.2.840.113549.1.1.13"; + default: + throw new NotSupportedException ("Unknown hash algorithm " + hashName); + } + } + + public string Hash { + get { return hashName; } + set { + if (hashName == null) + hashName = defaultHash; + else + hashName = value; + } + } + + public virtual byte[] Sign (AsymmetricAlgorithm aa) + { + if (aa is RSA) + return Sign (aa as RSA); + else if (aa is DSA) + return Sign (aa as DSA); + else + throw new NotSupportedException ("Unknown Asymmetric Algorithm " + aa.ToString()); + } + + private byte[] Build (ASN1 tbs, string hashoid, byte[] signature) + { + ASN1 builder = new ASN1 (0x30); + builder.Add (tbs); + builder.Add (PKCS7.AlgorithmIdentifier (hashoid)); + // first byte of BITSTRING is the number of unused bits in the first byte + byte[] bitstring = new byte [signature.Length + 1]; + Buffer.BlockCopy (signature, 0, bitstring, 1, signature.Length); + builder.Add (new ASN1 (0x03, bitstring)); + return builder.GetBytes (); + } + + public virtual byte[] Sign (RSA key) + { + string oid = GetOid (hashName); + ASN1 tbs = ToBeSigned (oid); + HashAlgorithm ha = HashAlgorithm.Create (hashName); + byte[] hash = ha.ComputeHash (tbs.GetBytes ()); + + RSAPKCS1SignatureFormatter pkcs1 = new RSAPKCS1SignatureFormatter (key); + pkcs1.SetHashAlgorithm (hashName); + byte[] signature = pkcs1.CreateSignature (hash); + + return Build (tbs, oid, signature); + } + + public virtual byte[] Sign (DSA key) + { + string oid = "1.2.840.10040.4.3"; + ASN1 tbs = ToBeSigned (oid); + HashAlgorithm ha = HashAlgorithm.Create (hashName); + if (!(ha is SHA1)) + throw new NotSupportedException ("Only SHA-1 is supported for DSA"); + byte[] hash = ha.ComputeHash (tbs.GetBytes ()); + + DSASignatureFormatter dsa = new DSASignatureFormatter (key); + dsa.SetHashAlgorithm (hashName); + byte[] rs = dsa.CreateSignature (hash); + + // split R and S + byte[] r = new byte [20]; + Buffer.BlockCopy (rs, 0, r, 0, 20); + byte[] s = new byte [20]; + Buffer.BlockCopy (rs, 20, s, 0, 20); + ASN1 signature = new ASN1 (0x30); + signature.Add (new ASN1 (0x02, r)); + signature.Add (new ASN1 (0x02, s)); + + // dsaWithSha1 (1 2 840 10040 4 3) + return Build (tbs, oid, signature.GetBytes ()); + } + } +} diff --git a/MediaBrowser.Server.Startup.Common/Security/X509Certificate.cs b/MediaBrowser.Server.Startup.Common/Security/X509Certificate.cs new file mode 100644 index 000000000..f49445f0e --- /dev/null +++ b/MediaBrowser.Server.Startup.Common/Security/X509Certificate.cs @@ -0,0 +1,563 @@ +// +// X509Certificates.cs: Handles X.509 certificates. +// +// Author: +// Sebastien Pouliot +// +// (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com) +// Copyright (C) 2004-2006 Novell, Inc (http://www.novell.com) +// Copyright 2013 Xamarin Inc. (http://www.xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Runtime.Serialization; +using System.Security.Cryptography; +using System.Security.Permissions; +using System.Text; + +namespace Emby.Common.Implementations.Security +{ + + // References: + // a. Internet X.509 Public Key Infrastructure Certificate and CRL Profile + // http://www.ietf.org/rfc/rfc3280.txt + // b. ITU ASN.1 standards (free download) + // http://www.itu.int/ITU-T/studygroups/com17/languages/ + + public class X509Certificate : ISerializable + { + + private ASN1 decoder; + + private byte[] m_encodedcert; + private DateTime m_from; + private DateTime m_until; + private ASN1 issuer; + private string m_issuername; + private string m_keyalgo; + private byte[] m_keyalgoparams; + private ASN1 subject; + private string m_subject; + private byte[] m_publickey; + private byte[] signature; + private string m_signaturealgo; + private byte[] m_signaturealgoparams; + private byte[] certhash; + private RSA _rsa; + private DSA _dsa; + + // from http://msdn.microsoft.com/en-gb/library/ff635835.aspx + private const string OID_DSA = "1.2.840.10040.4.1"; + private const string OID_RSA = "1.2.840.113549.1.1.1"; + + // from http://www.ietf.org/rfc/rfc2459.txt + // + //Certificate ::= SEQUENCE { + // tbsCertificate TBSCertificate, + // signatureAlgorithm AlgorithmIdentifier, + // signature BIT STRING } + // + //TBSCertificate ::= SEQUENCE { + // version [0] Version DEFAULT v1, + // serialNumber CertificateSerialNumber, + // signature AlgorithmIdentifier, + // issuer Name, + // validity Validity, + // subject Name, + // subjectPublicKeyInfo SubjectPublicKeyInfo, + // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, + // -- If present, version shall be v2 or v3 + // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, + // -- If present, version shall be v2 or v3 + // extensions [3] Extensions OPTIONAL + // -- If present, version shall be v3 -- } + private int version; + private byte[] serialnumber; + + private byte[] issuerUniqueID; + private byte[] subjectUniqueID; + private X509ExtensionCollection extensions; + + private static string encoding_error = ("Input data cannot be coded as a valid certificate."); + + + // that's were the real job is! + private void Parse (byte[] data) + { + try { + decoder = new ASN1 (data); + // Certificate + if (decoder.Tag != 0x30) + throw new CryptographicException (encoding_error); + // Certificate / TBSCertificate + if (decoder [0].Tag != 0x30) + throw new CryptographicException (encoding_error); + + ASN1 tbsCertificate = decoder [0]; + + int tbs = 0; + // Certificate / TBSCertificate / Version + ASN1 v = decoder [0][tbs]; + version = 1; // DEFAULT v1 + if ((v.Tag == 0xA0) && (v.Count > 0)) { + // version (optional) is present only in v2+ certs + version += v [0].Value [0]; // zero based + tbs++; + } + + // Certificate / TBSCertificate / CertificateSerialNumber + ASN1 sn = decoder [0][tbs++]; + if (sn.Tag != 0x02) + throw new CryptographicException (encoding_error); + serialnumber = sn.Value; + Array.Reverse (serialnumber, 0, serialnumber.Length); + + // Certificate / TBSCertificate / AlgorithmIdentifier + tbs++; + // ASN1 signatureAlgo = tbsCertificate.Element (tbs++, 0x30); + + issuer = tbsCertificate.Element (tbs++, 0x30); + m_issuername = X501.ToString (issuer); + + ASN1 validity = tbsCertificate.Element (tbs++, 0x30); + ASN1 notBefore = validity [0]; + m_from = ASN1Convert.ToDateTime (notBefore); + ASN1 notAfter = validity [1]; + m_until = ASN1Convert.ToDateTime (notAfter); + + subject = tbsCertificate.Element (tbs++, 0x30); + m_subject = X501.ToString (subject); + + ASN1 subjectPublicKeyInfo = tbsCertificate.Element (tbs++, 0x30); + + ASN1 algorithm = subjectPublicKeyInfo.Element (0, 0x30); + ASN1 algo = algorithm.Element (0, 0x06); + m_keyalgo = ASN1Convert.ToOid (algo); + // parameters ANY DEFINED BY algorithm OPTIONAL + // so we dont ask for a specific (Element) type and return DER + ASN1 parameters = algorithm [1]; + m_keyalgoparams = ((algorithm.Count > 1) ? parameters.GetBytes () : null); + + ASN1 subjectPublicKey = subjectPublicKeyInfo.Element (1, 0x03); + // we must drop th first byte (which is the number of unused bits + // in the BITSTRING) + int n = subjectPublicKey.Length - 1; + m_publickey = new byte [n]; + Buffer.BlockCopy (subjectPublicKey.Value, 1, m_publickey, 0, n); + + // signature processing + byte[] bitstring = decoder [2].Value; + // first byte contains unused bits in first byte + signature = new byte [bitstring.Length - 1]; + Buffer.BlockCopy (bitstring, 1, signature, 0, signature.Length); + + algorithm = decoder [1]; + algo = algorithm.Element (0, 0x06); + m_signaturealgo = ASN1Convert.ToOid (algo); + parameters = algorithm [1]; + if (parameters != null) + m_signaturealgoparams = parameters.GetBytes (); + else + m_signaturealgoparams = null; + + // Certificate / TBSCertificate / issuerUniqueID + ASN1 issuerUID = tbsCertificate.Element (tbs, 0x81); + if (issuerUID != null) { + tbs++; + issuerUniqueID = issuerUID.Value; + } + + // Certificate / TBSCertificate / subjectUniqueID + ASN1 subjectUID = tbsCertificate.Element (tbs, 0x82); + if (subjectUID != null) { + tbs++; + subjectUniqueID = subjectUID.Value; + } + + // Certificate / TBSCertificate / Extensions + ASN1 extns = tbsCertificate.Element (tbs, 0xA3); + if ((extns != null) && (extns.Count == 1)) + extensions = new X509ExtensionCollection (extns [0]); + else + extensions = new X509ExtensionCollection (null); + + // keep a copy of the original data + m_encodedcert = (byte[]) data.Clone (); + } + catch (Exception ex) { + throw new CryptographicException (encoding_error, ex); + } + } + + // constructors + + public X509Certificate (byte[] data) + { + if (data != null) { + // does it looks like PEM ? + if ((data.Length > 0) && (data [0] != 0x30)) { + try { + data = PEM ("CERTIFICATE", data); + } + catch (Exception ex) { + throw new CryptographicException (encoding_error, ex); + } + } + Parse (data); + } + } + + private byte[] GetUnsignedBigInteger (byte[] integer) + { + if (integer [0] == 0x00) { + // this first byte is added so we're sure it's an unsigned integer + // however we can't feed it into RSAParameters or DSAParameters + int length = integer.Length - 1; + byte[] uinteger = new byte [length]; + Buffer.BlockCopy (integer, 1, uinteger, 0, length); + return uinteger; + } + else + return integer; + } + + // public methods + + public DSA DSA { + get { + if (m_keyalgoparams == null) + throw new CryptographicException ("Missing key algorithm parameters."); + + if (_dsa == null && m_keyalgo == OID_DSA) { + DSAParameters dsaParams = new DSAParameters (); + // for DSA m_publickey contains 1 ASN.1 integer - Y + ASN1 pubkey = new ASN1 (m_publickey); + if ((pubkey == null) || (pubkey.Tag != 0x02)) + return null; + dsaParams.Y = GetUnsignedBigInteger (pubkey.Value); + + ASN1 param = new ASN1 (m_keyalgoparams); + if ((param == null) || (param.Tag != 0x30) || (param.Count < 3)) + return null; + if ((param [0].Tag != 0x02) || (param [1].Tag != 0x02) || (param [2].Tag != 0x02)) + return null; + dsaParams.P = GetUnsignedBigInteger (param [0].Value); + dsaParams.Q = GetUnsignedBigInteger (param [1].Value); + dsaParams.G = GetUnsignedBigInteger (param [2].Value); + + // BUG: MS BCL 1.0 can't import a key which + // isn't the same size as the one present in + // the container. + _dsa = (DSA) new DSACryptoServiceProvider (dsaParams.Y.Length << 3); + _dsa.ImportParameters (dsaParams); + } + return _dsa; + } + + set { + _dsa = value; + if (value != null) + _rsa = null; + } + } + + public X509ExtensionCollection Extensions { + get { return extensions; } + } + + public byte[] Hash { + get { + if (certhash == null) { + if ((decoder == null) || (decoder.Count < 1)) + return null; + string algo = PKCS1.HashNameFromOid (m_signaturealgo, false); + if (algo == null) + return null; + byte[] toBeSigned = decoder [0].GetBytes (); + using (var hash = PKCS1.CreateFromName (algo)) + certhash = hash.ComputeHash (toBeSigned, 0, toBeSigned.Length); + } + return (byte[]) certhash.Clone (); + } + } + + public virtual string IssuerName { + get { return m_issuername; } + } + + public virtual string KeyAlgorithm { + get { return m_keyalgo; } + } + + public virtual byte[] KeyAlgorithmParameters { + get { + if (m_keyalgoparams == null) + return null; + return (byte[]) m_keyalgoparams.Clone (); + } + set { m_keyalgoparams = value; } + } + + public virtual byte[] PublicKey { + get { + if (m_publickey == null) + return null; + return (byte[]) m_publickey.Clone (); + } + } + + public virtual RSA RSA { + get { + if (_rsa == null && m_keyalgo == OID_RSA) { + RSAParameters rsaParams = new RSAParameters (); + // for RSA m_publickey contains 2 ASN.1 integers + // the modulus and the public exponent + ASN1 pubkey = new ASN1 (m_publickey); + ASN1 modulus = pubkey [0]; + if ((modulus == null) || (modulus.Tag != 0x02)) + return null; + ASN1 exponent = pubkey [1]; + if (exponent.Tag != 0x02) + return null; + + rsaParams.Modulus = GetUnsignedBigInteger (modulus.Value); + rsaParams.Exponent = exponent.Value; + + // BUG: MS BCL 1.0 can't import a key which + // isn't the same size as the one present in + // the container. + int keySize = (rsaParams.Modulus.Length << 3); + _rsa = (RSA) new RSACryptoServiceProvider (keySize); + _rsa.ImportParameters (rsaParams); + } + return _rsa; + } + + set { + if (value != null) + _dsa = null; + _rsa = value; + } + } + + public virtual byte[] RawData { + get { + if (m_encodedcert == null) + return null; + return (byte[]) m_encodedcert.Clone (); + } + } + + public virtual byte[] SerialNumber { + get { + if (serialnumber == null) + return null; + return (byte[]) serialnumber.Clone (); + } + } + + public virtual byte[] Signature { + get { + if (signature == null) + return null; + + switch (m_signaturealgo) { + case "1.2.840.113549.1.1.2": // MD2 with RSA encryption + case "1.2.840.113549.1.1.3": // MD4 with RSA encryption + case "1.2.840.113549.1.1.4": // MD5 with RSA encryption + case "1.2.840.113549.1.1.5": // SHA-1 with RSA Encryption + case "1.3.14.3.2.29": // SHA1 with RSA signature + case "1.2.840.113549.1.1.11": // SHA-256 with RSA Encryption + case "1.2.840.113549.1.1.12": // SHA-384 with RSA Encryption + case "1.2.840.113549.1.1.13": // SHA-512 with RSA Encryption + case "1.3.36.3.3.1.2": // RIPEMD160 with RSA Encryption + return (byte[]) signature.Clone (); + + case "1.2.840.10040.4.3": // SHA-1 with DSA + ASN1 sign = new ASN1 (signature); + if ((sign == null) || (sign.Count != 2)) + return null; + byte[] part1 = sign [0].Value; + byte[] part2 = sign [1].Value; + byte[] sig = new byte [40]; + // parts may be less than 20 bytes (i.e. first bytes were 0x00) + // parts may be more than 20 bytes (i.e. first byte > 0x80, negative) + int s1 = System.Math.Max (0, part1.Length - 20); + int e1 = System.Math.Max (0, 20 - part1.Length); + Buffer.BlockCopy (part1, s1, sig, e1, part1.Length - s1); + int s2 = System.Math.Max (0, part2.Length - 20); + int e2 = System.Math.Max (20, 40 - part2.Length); + Buffer.BlockCopy (part2, s2, sig, e2, part2.Length - s2); + return sig; + + default: + throw new CryptographicException ("Unsupported hash algorithm: " + m_signaturealgo); + } + } + } + + public virtual string SignatureAlgorithm { + get { return m_signaturealgo; } + } + + public virtual byte[] SignatureAlgorithmParameters { + get { + if (m_signaturealgoparams == null) + return m_signaturealgoparams; + return (byte[]) m_signaturealgoparams.Clone (); + } + } + + public virtual string SubjectName { + get { return m_subject; } + } + + public virtual DateTime ValidFrom { + get { return m_from; } + } + + public virtual DateTime ValidUntil { + get { return m_until; } + } + + public int Version { + get { return version; } + } + + public bool IsCurrent { + get { return WasCurrent (DateTime.UtcNow); } + } + + public bool WasCurrent (DateTime instant) + { + return ((instant > ValidFrom) && (instant <= ValidUntil)); + } + + // uncommon v2 "extension" + public byte[] IssuerUniqueIdentifier { + get { + if (issuerUniqueID == null) + return null; + return (byte[]) issuerUniqueID.Clone (); + } + } + + // uncommon v2 "extension" + public byte[] SubjectUniqueIdentifier { + get { + if (subjectUniqueID == null) + return null; + return (byte[]) subjectUniqueID.Clone (); + } + } + + internal bool VerifySignature (DSA dsa) + { + // signatureOID is check by both this.Hash and this.Signature + DSASignatureDeformatter v = new DSASignatureDeformatter (dsa); + // only SHA-1 is supported + v.SetHashAlgorithm ("SHA1"); + return v.VerifySignature (this.Hash, this.Signature); + } + + internal bool VerifySignature (RSA rsa) + { + // SHA1-1 with DSA + if (m_signaturealgo == "1.2.840.10040.4.3") + return false; + RSAPKCS1SignatureDeformatter v = new RSAPKCS1SignatureDeformatter (rsa); + v.SetHashAlgorithm (PKCS1.HashNameFromOid (m_signaturealgo)); + return v.VerifySignature (this.Hash, this.Signature); + } + + public bool VerifySignature (AsymmetricAlgorithm aa) + { + if (aa == null) + throw new ArgumentNullException ("aa"); + + if (aa is RSA) + return VerifySignature (aa as RSA); + else if (aa is DSA) + return VerifySignature (aa as DSA); + else + throw new NotSupportedException ("Unknown Asymmetric Algorithm " + aa.ToString ()); + } + + public bool CheckSignature (byte[] hash, string hashAlgorithm, byte[] signature) + { + RSACryptoServiceProvider r = (RSACryptoServiceProvider) RSA; + return r.VerifyHash (hash, hashAlgorithm, signature); + } + + public bool IsSelfSigned { + get { + if (m_issuername != m_subject) + return false; + + try { + if (RSA != null) + return VerifySignature (RSA); + else if (DSA != null) + return VerifySignature (DSA); + else + return false; // e.g. a certificate with only DSA parameters + } + catch (CryptographicException) { + return false; + } + } + } + + public ASN1 GetIssuerName () + { + return issuer; + } + + public ASN1 GetSubjectName () + { + return subject; + } + + protected X509Certificate (SerializationInfo info, StreamingContext context) + { + Parse ((byte[]) info.GetValue ("raw", typeof (byte[]))); + } + + [SecurityPermission (SecurityAction.Demand, SerializationFormatter = true)] + public virtual void GetObjectData (SerializationInfo info, StreamingContext context) + { + info.AddValue ("raw", m_encodedcert); + // note: we NEVER serialize the private key + } + + static byte[] PEM (string type, byte[] data) + { + string pem = Encoding.ASCII.GetString (data); + string header = String.Format ("-----BEGIN {0}-----", type); + string footer = String.Format ("-----END {0}-----", type); + int start = pem.IndexOf (header) + header.Length; + int end = pem.IndexOf (footer, start); + string base64 = pem.Substring (start, (end - start)); + return Convert.FromBase64String (base64); + } + } +} diff --git a/MediaBrowser.Server.Startup.Common/Security/X509CertificateBuilder.cs b/MediaBrowser.Server.Startup.Common/Security/X509CertificateBuilder.cs new file mode 100644 index 000000000..fb6f8ec72 --- /dev/null +++ b/MediaBrowser.Server.Startup.Common/Security/X509CertificateBuilder.cs @@ -0,0 +1,245 @@ +// +// X509CertificateBuilder.cs: Handles building of X.509 certificates. +// +// Author: +// Sebastien Pouliot +// +// (C) 2003 Motus Technologies Inc. (http://www.motus.com) +// (C) 2004 Novell (http://www.novell.com) +// + +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Security.Cryptography; + +namespace Emby.Common.Implementations.Security +{ + // From RFC3280 + /* + * Certificate ::= SEQUENCE { + * tbsCertificate TBSCertificate, + * signatureAlgorithm AlgorithmIdentifier, + * signature BIT STRING + * } + * TBSCertificate ::= SEQUENCE { + * version [0] Version DEFAULT v1, + * serialNumber CertificateSerialNumber, + * signature AlgorithmIdentifier, + * issuer Name, + * validity Validity, + * subject Name, + * subjectPublicKeyInfo SubjectPublicKeyInfo, + * issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, + * -- If present, version MUST be v2 or v3 + * subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, + * -- If present, version MUST be v2 or v3 + * extensions [3] Extensions OPTIONAL + * -- If present, version MUST be v3 -- + * } + * Version ::= INTEGER { v1(0), v2(1), v3(2) } + * CertificateSerialNumber ::= INTEGER + * Validity ::= SEQUENCE { + * notBefore Time, + * notAfter Time + * } + * Time ::= CHOICE { + * utcTime UTCTime, + * generalTime GeneralizedTime + * } + */ + public class X509CertificateBuilder : X509Builder { + + private byte version; + private byte[] sn; + private string issuer; + private DateTime notBefore; + private DateTime notAfter; + private string subject; + private AsymmetricAlgorithm aa; + private byte[] issuerUniqueID; + private byte[] subjectUniqueID; + private X509ExtensionCollection extensions; + + public X509CertificateBuilder () : this (3) {} + + public X509CertificateBuilder (byte version) + { + if (version > 3) + throw new ArgumentException ("Invalid certificate version"); + this.version = version; + extensions = new X509ExtensionCollection (); + } + + public byte Version { + get { return version; } + set { version = value; } + } + + public byte[] SerialNumber { + get { return sn; } + set { sn = value; } + } + + public string IssuerName { + get { return issuer; } + set { issuer = value; } + } + + public DateTime NotBefore { + get { return notBefore; } + set { notBefore = value; } + } + + public DateTime NotAfter { + get { return notAfter; } + set { notAfter = value; } + } + + public string SubjectName { + get { return subject; } + set { subject = value; } + } + + public AsymmetricAlgorithm SubjectPublicKey { + get { return aa; } + set { aa = value; } + } + + public byte[] IssuerUniqueId { + get { return issuerUniqueID; } + set { issuerUniqueID = value; } + } + + public byte[] SubjectUniqueId { + get { return subjectUniqueID; } + set { subjectUniqueID = value; } + } + + public X509ExtensionCollection Extensions { + get { return extensions; } + } + + + /* SubjectPublicKeyInfo ::= SEQUENCE { + * algorithm AlgorithmIdentifier, + * subjectPublicKey BIT STRING } + */ + private ASN1 SubjectPublicKeyInfo () + { + ASN1 keyInfo = new ASN1 (0x30); + if (aa is RSA) { + keyInfo.Add (PKCS7.AlgorithmIdentifier ("1.2.840.113549.1.1.1")); + RSAParameters p = (aa as RSA).ExportParameters (false); + /* RSAPublicKey ::= SEQUENCE { + * modulus INTEGER, -- n + * publicExponent INTEGER } -- e + */ + ASN1 key = new ASN1 (0x30); + key.Add (ASN1Convert.FromUnsignedBigInteger (p.Modulus)); + key.Add (ASN1Convert.FromUnsignedBigInteger (p.Exponent)); + keyInfo.Add (new ASN1 (UniqueIdentifier (key.GetBytes ()))); + } + else if (aa is DSA) { + DSAParameters p = (aa as DSA).ExportParameters (false); + /* Dss-Parms ::= SEQUENCE { + * p INTEGER, + * q INTEGER, + * g INTEGER } + */ + ASN1 param = new ASN1 (0x30); + param.Add (ASN1Convert.FromUnsignedBigInteger (p.P)); + param.Add (ASN1Convert.FromUnsignedBigInteger (p.Q)); + param.Add (ASN1Convert.FromUnsignedBigInteger (p.G)); + keyInfo.Add (PKCS7.AlgorithmIdentifier ("1.2.840.10040.4.1", param)); + ASN1 key = keyInfo.Add (new ASN1 (0x03)); + // DSAPublicKey ::= INTEGER -- public key, y + key.Add (ASN1Convert.FromUnsignedBigInteger (p.Y)); + } + else + throw new NotSupportedException ("Unknown Asymmetric Algorithm " + aa.ToString ()); + return keyInfo; + } + + private byte[] UniqueIdentifier (byte[] id) + { + // UniqueIdentifier ::= BIT STRING + ASN1 uid = new ASN1 (0x03); + // first byte in a BITSTRING is the number of unused bits in the first byte + byte[] v = new byte [id.Length + 1]; + Buffer.BlockCopy (id, 0, v, 1, id.Length); + uid.Value = v; + return uid.GetBytes (); + } + + protected override ASN1 ToBeSigned (string oid) + { + // TBSCertificate + ASN1 tbsCert = new ASN1 (0x30); + + if (version > 1) { + // TBSCertificate / [0] Version DEFAULT v1, + byte[] ver = { (byte)(version - 1) }; + ASN1 v = tbsCert.Add (new ASN1 (0xA0)); + v.Add (new ASN1 (0x02, ver)); + } + + // TBSCertificate / CertificateSerialNumber, + tbsCert.Add (new ASN1 (0x02, sn)); + + // TBSCertificate / AlgorithmIdentifier, + tbsCert.Add (PKCS7.AlgorithmIdentifier (oid)); + + // TBSCertificate / Name + tbsCert.Add (X501.FromString (issuer)); + + // TBSCertificate / Validity + ASN1 validity = tbsCert.Add (new ASN1 (0x30)); + // TBSCertificate / Validity / Time + validity.Add (ASN1Convert.FromDateTime (notBefore)); + // TBSCertificate / Validity / Time + validity.Add (ASN1Convert.FromDateTime (notAfter)); + + // TBSCertificate / Name + tbsCert.Add (X501.FromString (subject)); + + // TBSCertificate / SubjectPublicKeyInfo + tbsCert.Add (SubjectPublicKeyInfo ()); + + if (version > 1) { + // TBSCertificate / [1] IMPLICIT UniqueIdentifier OPTIONAL + if (issuerUniqueID != null) + tbsCert.Add (new ASN1 (0xA1, UniqueIdentifier (issuerUniqueID))); + + // TBSCertificate / [2] IMPLICIT UniqueIdentifier OPTIONAL + if (subjectUniqueID != null) + tbsCert.Add (new ASN1 (0xA1, UniqueIdentifier (subjectUniqueID))); + + // TBSCertificate / [3] Extensions OPTIONAL + if ((version > 2) && (extensions.Count > 0)) + tbsCert.Add (new ASN1 (0xA3, extensions.GetBytes ())); + } + + return tbsCert; + } + } +} diff --git a/MediaBrowser.Server.Startup.Common/Security/X509CertificateCollection.cs b/MediaBrowser.Server.Startup.Common/Security/X509CertificateCollection.cs new file mode 100644 index 000000000..5d353f9cf --- /dev/null +++ b/MediaBrowser.Server.Startup.Common/Security/X509CertificateCollection.cs @@ -0,0 +1,201 @@ +// +// Based on System.Security.Cryptography.X509Certificates.X509CertificateCollection +// in System assembly +// +// Authors: +// Lawrence Pit (loz@cable.a2000.nl) +// Sebastien Pouliot +// + +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections; + +namespace Emby.Common.Implementations.Security +{ + + [Serializable] + public class X509CertificateCollection : CollectionBase, IEnumerable { + + public X509CertificateCollection () + { + } + + public X509CertificateCollection (X509Certificate [] value) + { + AddRange (value); + } + + public X509CertificateCollection (X509CertificateCollection value) + { + AddRange (value); + } + + // Properties + + public X509Certificate this [int index] { + get { return (X509Certificate) InnerList [index]; } + set { InnerList [index] = value; } + } + + // Methods + + public int Add (X509Certificate value) + { + if (value == null) + throw new ArgumentNullException ("value"); + + return InnerList.Add (value); + } + + public void AddRange (X509Certificate [] value) + { + if (value == null) + throw new ArgumentNullException ("value"); + + for (int i = 0; i < value.Length; i++) + InnerList.Add (value [i]); + } + + public void AddRange (X509CertificateCollection value) + { + if (value == null) + throw new ArgumentNullException ("value"); + + for (int i = 0; i < value.InnerList.Count; i++) + InnerList.Add (value [i]); + } + + public bool Contains (X509Certificate value) + { + return (IndexOf (value) != -1); + } + + public void CopyTo (X509Certificate[] array, int index) + { + InnerList.CopyTo (array, index); + } + + public new X509CertificateEnumerator GetEnumerator () + { + return new X509CertificateEnumerator (this); + } + + IEnumerator IEnumerable.GetEnumerator () + { + return InnerList.GetEnumerator (); + } + + public override int GetHashCode () + { + return InnerList.GetHashCode (); + } + + public int IndexOf (X509Certificate value) + { + if (value == null) + throw new ArgumentNullException ("value"); + + byte[] hash = value.Hash; + for (int i=0; i < InnerList.Count; i++) { + X509Certificate x509 = (X509Certificate) InnerList [i]; + if (Compare (x509.Hash, hash)) + return i; + } + return -1; + } + + public void Insert (int index, X509Certificate value) + { + InnerList.Insert (index, value); + } + + public void Remove (X509Certificate value) + { + InnerList.Remove (value); + } + + // private stuff + + private bool Compare (byte[] array1, byte[] array2) + { + if ((array1 == null) && (array2 == null)) + return true; + if ((array1 == null) || (array2 == null)) + return false; + if (array1.Length != array2.Length) + return false; + for (int i=0; i < array1.Length; i++) { + if (array1 [i] != array2 [i]) + return false; + } + return true; + } + + // Inner Class + + public class X509CertificateEnumerator : IEnumerator { + + private IEnumerator enumerator; + + // Constructors + + public X509CertificateEnumerator (X509CertificateCollection mappings) + { + enumerator = ((IEnumerable) mappings).GetEnumerator (); + } + + // Properties + + public X509Certificate Current { + get { return (X509Certificate) enumerator.Current; } + } + + object IEnumerator.Current { + get { return enumerator.Current; } + } + + // Methods + + bool IEnumerator.MoveNext () + { + return enumerator.MoveNext (); + } + + void IEnumerator.Reset () + { + enumerator.Reset (); + } + + public bool MoveNext () + { + return enumerator.MoveNext (); + } + + public void Reset () + { + enumerator.Reset (); + } + } + } +} diff --git a/MediaBrowser.Server.Startup.Common/Security/X509Extension.cs b/MediaBrowser.Server.Startup.Common/Security/X509Extension.cs new file mode 100644 index 000000000..e82f52bac --- /dev/null +++ b/MediaBrowser.Server.Startup.Common/Security/X509Extension.cs @@ -0,0 +1,208 @@ +// +// X509Extension.cs: Base class for all X.509 extensions. +// +// Author: +// Sebastien Pouliot +// +// (C) 2003 Motus Technologies Inc. (http://www.motus.com) +// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Globalization; +using System.Text; + +namespace Emby.Common.Implementations.Security +{ + /* + * Extension ::= SEQUENCE { + * extnID OBJECT IDENTIFIER, + * critical BOOLEAN DEFAULT FALSE, + * extnValue OCTET STRING + * } + */ + public class X509Extension { + + protected string extnOid; + protected bool extnCritical; + protected ASN1 extnValue; + + protected X509Extension () + { + extnCritical = false; + } + + public X509Extension (ASN1 asn1) + { + if ((asn1.Tag != 0x30) || (asn1.Count < 2)) + throw new ArgumentException (("Invalid X.509 extension.")); + if (asn1[0].Tag != 0x06) + throw new ArgumentException (("Invalid X.509 extension.")); + + extnOid = ASN1Convert.ToOid (asn1[0]); + extnCritical = ((asn1[1].Tag == 0x01) && (asn1[1].Value[0] == 0xFF)); + // last element is an octet string which may need to be decoded + extnValue = asn1 [asn1.Count - 1]; + if ((extnValue.Tag == 0x04) && (extnValue.Length > 0) && (extnValue.Count == 0)) { + try { + ASN1 encapsulated = new ASN1 (extnValue.Value); + extnValue.Value = null; + extnValue.Add (encapsulated); + } + catch { + // data isn't ASN.1 + } + } + Decode (); + } + + public X509Extension (X509Extension extension) + { + if (extension == null) + throw new ArgumentNullException ("extension"); + if ((extension.Value == null) || (extension.Value.Tag != 0x04) || (extension.Value.Count != 1)) + throw new ArgumentException (("Invalid X.509 extension.")); + + extnOid = extension.Oid; + extnCritical = extension.Critical; + extnValue = extension.Value; + Decode (); + } + + // encode the extension *into* an OCTET STRING + protected virtual void Decode () + { + } + + // decode the extension from *inside* an OCTET STRING + protected virtual void Encode () + { + } + + public ASN1 ASN1 { + get { + ASN1 extension = new ASN1 (0x30); + extension.Add (ASN1Convert.FromOid (extnOid)); + if (extnCritical) + extension.Add (new ASN1 (0x01, new byte [1] { 0xFF })); + Encode (); + extension.Add (extnValue); + return extension; + } + } + + public string Oid { + get { return extnOid; } + } + + public bool Critical { + get { return extnCritical; } + set { extnCritical = value; } + } + + // this gets overrided with more meaningful names + public virtual string Name { + get { return extnOid; } + } + + public ASN1 Value { + get { + if (extnValue == null) { + Encode (); + } + return extnValue; + } + } + + public override bool Equals (object obj) + { + if (obj == null) + return false; + + X509Extension ex = (obj as X509Extension); + if (ex == null) + return false; + + if (extnCritical != ex.extnCritical) + return false; + if (extnOid != ex.extnOid) + return false; + if (extnValue.Length != ex.extnValue.Length) + return false; + + for (int i=0; i < extnValue.Length; i++) { + if (extnValue [i] != ex.extnValue [i]) + return false; + } + return true; + } + + public byte[] GetBytes () + { + return ASN1.GetBytes (); + } + + public override int GetHashCode () + { + // OID should be unique in a collection of extensions + return extnOid.GetHashCode (); + } + + private void WriteLine (StringBuilder sb, int n, int pos) + { + byte[] value = extnValue.Value; + int p = pos; + for (int j=0; j < 8; j++) { + if (j < n) { + sb.Append (value [p++].ToString ("X2", CultureInfo.InvariantCulture)); + sb.Append (" "); + } + else + sb.Append (" "); + } + sb.Append (" "); + p = pos; + for (int j=0; j < n; j++) { + byte b = value [p++]; + if (b < 0x20) + sb.Append ("."); + else + sb.Append (Convert.ToChar (b)); + } + sb.Append (Environment.NewLine); + } + + public override string ToString () + { + StringBuilder sb = new StringBuilder (); + int div = (extnValue.Length >> 3); + int rem = (extnValue.Length - (div << 3)); + int x = 0; + for (int i=0; i < div; i++) { + WriteLine (sb, 8, x); + x += 8; + } + WriteLine (sb, rem, x); + return sb.ToString (); + } + } +} diff --git a/MediaBrowser.Server.Startup.Common/Security/X509Extensions.cs b/MediaBrowser.Server.Startup.Common/Security/X509Extensions.cs new file mode 100644 index 000000000..c7d5f0046 --- /dev/null +++ b/MediaBrowser.Server.Startup.Common/Security/X509Extensions.cs @@ -0,0 +1,195 @@ +// +// X509Extensions.cs: Handles X.509 extensions. +// +// Author: +// Sebastien Pouliot +// +// (C) 2003 Motus Technologies Inc. (http://www.motus.com) +// (C) 2004 Novell (http://www.novell.com) +// + +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections; + +namespace Emby.Common.Implementations.Security +{ + /* + * Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension + * + * Note: 1..MAX -> There shouldn't be 0 Extensions in the ASN1 structure + */ + public sealed class X509ExtensionCollection : CollectionBase, IEnumerable { + + private bool readOnly; + + public X509ExtensionCollection () : base () + { + } + + public X509ExtensionCollection (ASN1 asn1) : this () + { + readOnly = true; + if (asn1 == null) + return; + if (asn1.Tag != 0x30) + throw new Exception ("Invalid extensions format"); + for (int i=0; i < asn1.Count; i++) { + X509Extension extension = new X509Extension (asn1 [i]); + InnerList.Add (extension); + } + } + + public int Add (X509Extension extension) + { + if (extension == null) + throw new ArgumentNullException ("extension"); + if (readOnly) + throw new NotSupportedException ("Extensions are read only"); + + return InnerList.Add (extension); + } + + public void AddRange (X509Extension[] extension) + { + if (extension == null) + throw new ArgumentNullException ("extension"); + if (readOnly) + throw new NotSupportedException ("Extensions are read only"); + + for (int i = 0; i < extension.Length; i++) + InnerList.Add (extension [i]); + } + + public void AddRange (X509ExtensionCollection collection) + { + if (collection == null) + throw new ArgumentNullException ("collection"); + if (readOnly) + throw new NotSupportedException ("Extensions are read only"); + + for (int i = 0; i < collection.InnerList.Count; i++) + InnerList.Add (collection [i]); + } + + public bool Contains (X509Extension extension) + { + return (IndexOf (extension) != -1); + } + + public bool Contains (string oid) + { + return (IndexOf (oid) != -1); + } + + public void CopyTo (X509Extension[] extensions, int index) + { + if (extensions == null) + throw new ArgumentNullException ("extensions"); + + InnerList.CopyTo (extensions, index); + } + + public int IndexOf (X509Extension extension) + { + if (extension == null) + throw new ArgumentNullException ("extension"); + + for (int i=0; i < InnerList.Count; i++) { + X509Extension ex = (X509Extension) InnerList [i]; + if (ex.Equals (extension)) + return i; + } + return -1; + } + + public int IndexOf (string oid) + { + if (oid == null) + throw new ArgumentNullException ("oid"); + + for (int i=0; i < InnerList.Count; i++) { + X509Extension ex = (X509Extension) InnerList [i]; + if (ex.Oid == oid) + return i; + } + return -1; + } + + public void Insert (int index, X509Extension extension) + { + if (extension == null) + throw new ArgumentNullException ("extension"); + + InnerList.Insert (index, extension); + } + + public void Remove (X509Extension extension) + { + if (extension == null) + throw new ArgumentNullException ("extension"); + + InnerList.Remove (extension); + } + + public void Remove (string oid) + { + if (oid == null) + throw new ArgumentNullException ("oid"); + + int index = IndexOf (oid); + if (index != -1) + InnerList.RemoveAt (index); + } + + IEnumerator IEnumerable.GetEnumerator () + { + return InnerList.GetEnumerator (); + } + + public X509Extension this [int index] { + get { return (X509Extension) InnerList [index]; } + } + + public X509Extension this [string oid] { + get { + int index = IndexOf (oid); + if (index == -1) + return null; + return (X509Extension) InnerList [index]; + } + } + + public byte[] GetBytes () + { + if (InnerList.Count < 1) + return null; + ASN1 sequence = new ASN1 (0x30); + for (int i=0; i < InnerList.Count; i++) { + X509Extension x = (X509Extension) InnerList [i]; + sequence.Add (x.ASN1); + } + return sequence.GetBytes (); + } + } +} diff --git a/MediaBrowser.Server.Startup.Common/Security/X520Attributes.cs b/MediaBrowser.Server.Startup.Common/Security/X520Attributes.cs new file mode 100644 index 000000000..5062bf80a --- /dev/null +++ b/MediaBrowser.Server.Startup.Common/Security/X520Attributes.cs @@ -0,0 +1,346 @@ +// +// X520.cs: X.520 related stuff (attributes, RDN) +// +// Author: +// Sebastien Pouliot +// +// (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com) +// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Text; + +namespace Emby.Common.Implementations.Security +{ + + // References: + // 1. Information technology - Open Systems Interconnection - The Directory: Selected attribute types + // http://www.itu.int/rec/recommendation.asp?type=folders&lang=e&parent=T-REC-X.520 + // 2. Internet X.509 Public Key Infrastructure Certificate and CRL Profile + // http://www.ietf.org/rfc/rfc3280.txt + // 3. A Summary of the X.500(96) User Schema for use with LDAPv3 + // http://www.faqs.org/rfcs/rfc2256.html + // 4. RFC 2247 - Using Domains in LDAP/X.500 Distinguished Names + // http://www.faqs.org/rfcs/rfc2247.html + + /* + * AttributeTypeAndValue ::= SEQUENCE { + * type AttributeType, + * value AttributeValue + * } + * + * AttributeType ::= OBJECT IDENTIFIER + * + * AttributeValue ::= ANY DEFINED BY AttributeType + */ + public class X520 { + + public abstract class AttributeTypeAndValue { + private string oid; + private string attrValue; + private int upperBound; + private byte encoding; + + protected AttributeTypeAndValue (string oid, int upperBound) + { + this.oid = oid; + this.upperBound = upperBound; + this.encoding = 0xFF; + } + + protected AttributeTypeAndValue (string oid, int upperBound, byte encoding) + { + this.oid = oid; + this.upperBound = upperBound; + this.encoding = encoding; + } + + public string Value { + get { return attrValue; } + set { + if ((attrValue != null) && (attrValue.Length > upperBound)) { + string msg = ("Value length bigger than upperbound ({0})."); + throw new FormatException (String.Format (msg, upperBound)); + } + attrValue = value; + } + } + + public ASN1 ASN1 { + get { return GetASN1 (); } + } + + internal ASN1 GetASN1 (byte encoding) + { + byte encode = encoding; + if (encode == 0xFF) + encode = SelectBestEncoding (); + + ASN1 asn1 = new ASN1 (0x30); + asn1.Add (ASN1Convert.FromOid (oid)); + switch (encode) { + case 0x13: + // PRINTABLESTRING + asn1.Add (new ASN1 (0x13, Encoding.ASCII.GetBytes (attrValue))); + break; + case 0x16: + // IA5STRING + asn1.Add (new ASN1 (0x16, Encoding.ASCII.GetBytes (attrValue))); + break; + case 0x1E: + // BMPSTRING + asn1.Add (new ASN1 (0x1E, Encoding.BigEndianUnicode.GetBytes (attrValue))); + break; + } + return asn1; + } + + internal ASN1 GetASN1 () + { + return GetASN1 (encoding); + } + + public byte[] GetBytes (byte encoding) + { + return GetASN1 (encoding) .GetBytes (); + } + + public byte[] GetBytes () + { + return GetASN1 () .GetBytes (); + } + + private byte SelectBestEncoding () + { + foreach (char c in attrValue) { + switch (c) { + case '@': + case '_': + return 0x1E; // BMPSTRING + default: + if (c > 127) + return 0x1E; // BMPSTRING + break; + } + } + return 0x13; // PRINTABLESTRING + } + } + + public class Name : AttributeTypeAndValue { + + public Name () : base ("2.5.4.41", 32768) + { + } + } + + public class CommonName : AttributeTypeAndValue { + + public CommonName () : base ("2.5.4.3", 64) + { + } + } + + // RFC2256, Section 5.6 + public class SerialNumber : AttributeTypeAndValue { + + // max length 64 bytes, Printable String only + public SerialNumber () + : base ("2.5.4.5", 64, 0x13) + { + } + } + + public class LocalityName : AttributeTypeAndValue { + + public LocalityName () : base ("2.5.4.7", 128) + { + } + } + + public class StateOrProvinceName : AttributeTypeAndValue { + + public StateOrProvinceName () : base ("2.5.4.8", 128) + { + } + } + + public class OrganizationName : AttributeTypeAndValue { + + public OrganizationName () : base ("2.5.4.10", 64) + { + } + } + + public class OrganizationalUnitName : AttributeTypeAndValue { + + public OrganizationalUnitName () : base ("2.5.4.11", 64) + { + } + } + + // NOTE: Not part of RFC2253 + public class EmailAddress : AttributeTypeAndValue { + + public EmailAddress () : base ("1.2.840.113549.1.9.1", 128, 0x16) + { + } + } + + // RFC2247, Section 4 + public class DomainComponent : AttributeTypeAndValue { + + // no maximum length defined + public DomainComponent () + : base ("0.9.2342.19200300.100.1.25", Int32.MaxValue, 0x16) + { + } + } + + // RFC1274, Section 9.3.1 + public class UserId : AttributeTypeAndValue { + + public UserId () + : base ("0.9.2342.19200300.100.1.1", 256) + { + } + } + + public class Oid : AttributeTypeAndValue { + + public Oid (string oid) + : base (oid, Int32.MaxValue) + { + } + } + + /* -- Naming attributes of type X520Title + * id-at-title AttributeType ::= { id-at 12 } + * + * X520Title ::= CHOICE { + * teletexString TeletexString (SIZE (1..ub-title)), + * printableString PrintableString (SIZE (1..ub-title)), + * universalString UniversalString (SIZE (1..ub-title)), + * utf8String UTF8String (SIZE (1..ub-title)), + * bmpString BMPString (SIZE (1..ub-title)) + * } + */ + public class Title : AttributeTypeAndValue { + + public Title () : base ("2.5.4.12", 64) + { + } + } + + public class CountryName : AttributeTypeAndValue { + + // (0x13) PRINTABLESTRING + public CountryName () : base ("2.5.4.6", 2, 0x13) + { + } + } + + public class DnQualifier : AttributeTypeAndValue { + + // (0x13) PRINTABLESTRING + public DnQualifier () : base ("2.5.4.46", 2, 0x13) + { + } + } + + public class Surname : AttributeTypeAndValue { + + public Surname () : base ("2.5.4.4", 32768) + { + } + } + + public class GivenName : AttributeTypeAndValue { + + public GivenName () : base ("2.5.4.42", 16) + { + } + } + + public class Initial : AttributeTypeAndValue { + + public Initial () : base ("2.5.4.43", 5) + { + } + } + + } + + /* From RFC3280 + * -- specifications of Upper Bounds MUST be regarded as mandatory + * -- from Annex B of ITU-T X.411 Reference Definition of MTS Parameter + * + * -- Upper Bounds + * + * ub-name INTEGER ::= 32768 + * ub-common-name INTEGER ::= 64 + * ub-locality-name INTEGER ::= 128 + * ub-state-name INTEGER ::= 128 + * ub-organization-name INTEGER ::= 64 + * ub-organizational-unit-name INTEGER ::= 64 + * ub-title INTEGER ::= 64 + * ub-serial-number INTEGER ::= 64 + * ub-match INTEGER ::= 128 + * ub-emailaddress-length INTEGER ::= 128 + * ub-common-name-length INTEGER ::= 64 + * ub-country-name-alpha-length INTEGER ::= 2 + * ub-country-name-numeric-length INTEGER ::= 3 + * ub-domain-defined-attributes INTEGER ::= 4 + * ub-domain-defined-attribute-type-length INTEGER ::= 8 + * ub-domain-defined-attribute-value-length INTEGER ::= 128 + * ub-domain-name-length INTEGER ::= 16 + * ub-extension-attributes INTEGER ::= 256 + * ub-e163-4-number-length INTEGER ::= 15 + * ub-e163-4-sub-address-length INTEGER ::= 40 + * ub-generation-qualifier-length INTEGER ::= 3 + * ub-given-name-length INTEGER ::= 16 + * ub-initials-length INTEGER ::= 5 + * ub-integer-options INTEGER ::= 256 + * ub-numeric-user-id-length INTEGER ::= 32 + * ub-organization-name-length INTEGER ::= 64 + * ub-organizational-unit-name-length INTEGER ::= 32 + * ub-organizational-units INTEGER ::= 4 + * ub-pds-name-length INTEGER ::= 16 + * ub-pds-parameter-length INTEGER ::= 30 + * ub-pds-physical-address-lines INTEGER ::= 6 + * ub-postal-code-length INTEGER ::= 16 + * ub-pseudonym INTEGER ::= 128 + * ub-surname-length INTEGER ::= 40 + * ub-terminal-id-length INTEGER ::= 24 + * ub-unformatted-address-length INTEGER ::= 180 + * ub-x121-address-length INTEGER ::= 16 + * + * -- Note - upper bounds on string types, such as TeletexString, are + * -- measured in characters. Excepting PrintableString or IA5String, a + * -- significantly greater number of octets will be required to hold + * -- such a value. As a minimum, 16 octets, or twice the specified + * -- upper bound, whichever is the larger, should be allowed for + * -- TeletexString. For UTF8String or UniversalString at least four + * -- times the upper bound should be allowed. + */ +} diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj index 666ba640e..eaf4f3148 100644 --- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj +++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj @@ -131,7 +131,6 @@ - diff --git a/MediaBrowser.ServerApplication/Networking/CertificateGenerator.cs b/MediaBrowser.ServerApplication/Networking/CertificateGenerator.cs deleted file mode 100644 index a7b0d6c32..000000000 --- a/MediaBrowser.ServerApplication/Networking/CertificateGenerator.cs +++ /dev/null @@ -1,244 +0,0 @@ -using MediaBrowser.Model.Logging; -using System; -using System.IO; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace MediaBrowser.ServerApplication.Networking -{ - // Copied from: http://blogs.msdn.com/b/dcook/archive/2014/05/16/9143036.aspx - // In case anybody is interested, source code is attached and is free for use by anybody as long as you don't hold me or Microsoft liable for it -- - // I have no idea whether this is actually the right or best way to do this. Give it the X500 distinguished name, validity start and end dates, - // and an optional password for encrypting the key data, and it will give you the PFX file data. Let me know if you find any bugs or have any suggestions. - internal class CertificateGenerator - { - internal static void CreateSelfSignCertificatePfx( - string fileName, - string hostname, - ILogger logger) - { - if (string.IsNullOrWhiteSpace(fileName)) - { - throw new ArgumentNullException("fileName"); - } - - string x500 = string.Format("CN={0}", hostname); - - DateTime startTime = DateTime.Now.AddDays(-2); - DateTime endTime = DateTime.Now.AddYears(10); - - byte[] pfxData = CreateSelfSignCertificatePfx( - x500, - startTime, - endTime); - - File.WriteAllBytes(fileName, pfxData); - } - - private static byte[] CreateSelfSignCertificatePfx( - string x500, - DateTime startTime, - DateTime endTime) - { - byte[] pfxData; - - if (x500 == null) - { - x500 = ""; - } - - SystemTime startSystemTime = ToSystemTime(startTime); - SystemTime endSystemTime = ToSystemTime(endTime); - string containerName = Guid.NewGuid().ToString(); - - GCHandle dataHandle = new GCHandle(); - IntPtr providerContext = IntPtr.Zero; - IntPtr cryptKey = IntPtr.Zero; - IntPtr certContext = IntPtr.Zero; - IntPtr certStore = IntPtr.Zero; - IntPtr storeCertContext = IntPtr.Zero; - IntPtr passwordPtr = IntPtr.Zero; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - Check(NativeMethods.CryptAcquireContextW( - out providerContext, - containerName, - null, - 1, // PROV_RSA_FULL - 8)); // CRYPT_NEWKEYSET - - Check(NativeMethods.CryptGenKey( - providerContext, - 1, // AT_KEYEXCHANGE - 1 | 2048 << 16, // CRYPT_EXPORTABLE 2048 bit key - out cryptKey)); - - IntPtr errorStringPtr; - int nameDataLength = 0; - byte[] nameData; - - // errorStringPtr gets a pointer into the middle of the x500 string, - // so x500 needs to be pinned until after we've copied the value - // of errorStringPtr. - dataHandle = GCHandle.Alloc(x500, GCHandleType.Pinned); - - if (!NativeMethods.CertStrToNameW( - 0x00010001, // X509_ASN_ENCODING | PKCS_7_ASN_ENCODING - dataHandle.AddrOfPinnedObject(), - 3, // CERT_X500_NAME_STR = 3 - IntPtr.Zero, - null, - ref nameDataLength, - out errorStringPtr)) - { - string error = Marshal.PtrToStringUni(errorStringPtr); - throw new ArgumentException(error); - } - - nameData = new byte[nameDataLength]; - - if (!NativeMethods.CertStrToNameW( - 0x00010001, // X509_ASN_ENCODING | PKCS_7_ASN_ENCODING - dataHandle.AddrOfPinnedObject(), - 3, // CERT_X500_NAME_STR = 3 - IntPtr.Zero, - nameData, - ref nameDataLength, - out errorStringPtr)) - { - string error = Marshal.PtrToStringUni(errorStringPtr); - throw new ArgumentException(error); - } - - dataHandle.Free(); - - dataHandle = GCHandle.Alloc(nameData, GCHandleType.Pinned); - CryptoApiBlob nameBlob = new CryptoApiBlob( - nameData.Length, - dataHandle.AddrOfPinnedObject()); - - CryptKeyProviderInformation kpi = new CryptKeyProviderInformation(); - kpi.ContainerName = containerName; - kpi.ProviderType = 1; // PROV_RSA_FULL - kpi.KeySpec = 1; // AT_KEYEXCHANGE - - CryptAlgorithmIdentifier sha256Identifier = new CryptAlgorithmIdentifier(); - sha256Identifier.pszObjId = "1.2.840.113549.1.1.11"; - - certContext = NativeMethods.CertCreateSelfSignCertificate( - providerContext, - ref nameBlob, - 0, - ref kpi, - ref sha256Identifier, - ref startSystemTime, - ref endSystemTime, - IntPtr.Zero); - Check(certContext != IntPtr.Zero); - dataHandle.Free(); - - certStore = NativeMethods.CertOpenStore( - "Memory", // sz_CERT_STORE_PROV_MEMORY - 0, - IntPtr.Zero, - 0x2000, // CERT_STORE_CREATE_NEW_FLAG - IntPtr.Zero); - Check(certStore != IntPtr.Zero); - - Check(NativeMethods.CertAddCertificateContextToStore( - certStore, - certContext, - 1, // CERT_STORE_ADD_NEW - out storeCertContext)); - - NativeMethods.CertSetCertificateContextProperty( - storeCertContext, - 2, // CERT_KEY_PROV_INFO_PROP_ID - 0, - ref kpi); - - CryptoApiBlob pfxBlob = new CryptoApiBlob(); - Check(NativeMethods.PFXExportCertStoreEx( - certStore, - ref pfxBlob, - passwordPtr, - IntPtr.Zero, - 7)); // EXPORT_PRIVATE_KEYS | REPORT_NO_PRIVATE_KEY | REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY - - pfxData = new byte[pfxBlob.DataLength]; - dataHandle = GCHandle.Alloc(pfxData, GCHandleType.Pinned); - pfxBlob.Data = dataHandle.AddrOfPinnedObject(); - Check(NativeMethods.PFXExportCertStoreEx( - certStore, - ref pfxBlob, - passwordPtr, - IntPtr.Zero, - 7)); // EXPORT_PRIVATE_KEYS | REPORT_NO_PRIVATE_KEY | REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY - dataHandle.Free(); - } - finally - { - if (passwordPtr != IntPtr.Zero) - { - Marshal.ZeroFreeCoTaskMemUnicode(passwordPtr); - } - - if (dataHandle.IsAllocated) - { - dataHandle.Free(); - } - - if (certContext != IntPtr.Zero) - { - NativeMethods.CertFreeCertificateContext(certContext); - } - - if (storeCertContext != IntPtr.Zero) - { - NativeMethods.CertFreeCertificateContext(storeCertContext); - } - - if (certStore != IntPtr.Zero) - { - NativeMethods.CertCloseStore(certStore, 0); - } - - if (cryptKey != IntPtr.Zero) - { - NativeMethods.CryptDestroyKey(cryptKey); - } - - if (providerContext != IntPtr.Zero) - { - NativeMethods.CryptReleaseContext(providerContext, 0); - NativeMethods.CryptAcquireContextW( - out providerContext, - containerName, - null, - 1, // PROV_RSA_FULL - 0x10); // CRYPT_DELETEKEYSET - } - } - - return pfxData; - } - - private static SystemTime ToSystemTime(DateTime dateTime) - { - long fileTime = dateTime.ToFileTime(); - SystemTime systemTime; - Check(NativeMethods.FileTimeToSystemTime(ref fileTime, out systemTime)); - return systemTime; - } - - private static void Check(bool nativeCallSucceeded) - { - if (!nativeCallSucceeded) - { - int error = Marshal.GetHRForLastWin32Error(); - Marshal.ThrowExceptionForHR(error); - } - } - } -} diff --git a/MediaBrowser.ServerApplication/Networking/NetworkManager.cs b/MediaBrowser.ServerApplication/Networking/NetworkManager.cs index 62e1b5228..7b4379abf 100644 --- a/MediaBrowser.ServerApplication/Networking/NetworkManager.cs +++ b/MediaBrowser.ServerApplication/Networking/NetworkManager.cs @@ -1,5 +1,4 @@ -using MediaBrowser.Common.Net; -using MediaBrowser.Model.IO; +using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Net; using System; @@ -8,14 +7,13 @@ using System.Globalization; using System.IO; using System.Linq; using System.Runtime.InteropServices; -using Emby.Common.Implementations.Networking; namespace MediaBrowser.ServerApplication.Networking { /// /// Class NetUtils /// - public class NetworkManager : BaseNetworkManager, INetworkManager + public class NetworkManager : Server.Startup.Common.Networking.NetworkManager { public NetworkManager(ILogger logger) : base(logger) @@ -160,16 +158,6 @@ namespace MediaBrowser.ServerApplication.Networking }); } - /// - /// Generates a self signed certificate at the locatation specified by . - /// - /// The path to generate the certificate. - /// The common name for the certificate. - public void GenerateSelfSignedSslCertificate(string certificatePath, string hostname) - { - CertificateGenerator.CreateSelfSignCertificatePfx(certificatePath, hostname, Logger); - } - /// /// Gets the network prefix. /// diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index 6803ad306..ebd11ca9a 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -79,19 +79,19 @@ namespace MediaBrowser.WebDashboard.Api /// /// Class DashboardService /// - public class DashboardService : IService, IHasResultFactory + public class DashboardService : IService, IRequiresRequest { /// /// Gets or sets the logger. /// /// The logger. - public ILogger Logger { get; set; } + private readonly ILogger _logger; /// /// Gets or sets the HTTP result factory. /// /// The HTTP result factory. - public IHttpResultFactory ResultFactory { get; set; } + private readonly IHttpResultFactory _resultFactory; /// /// Gets or sets the request context. @@ -120,7 +120,7 @@ namespace MediaBrowser.WebDashboard.Api /// The app host. /// The server configuration manager. /// The file system. - public DashboardService(IServerApplicationHost appHost, IServerConfigurationManager serverConfigurationManager, IFileSystem fileSystem, ILocalizationManager localization, IJsonSerializer jsonSerializer, IAssemblyInfo assemblyInfo) + public DashboardService(IServerApplicationHost appHost, IServerConfigurationManager serverConfigurationManager, IFileSystem fileSystem, ILocalizationManager localization, IJsonSerializer jsonSerializer, IAssemblyInfo assemblyInfo, ILogger logger, IHttpResultFactory resultFactory) { _appHost = appHost; _serverConfigurationManager = serverConfigurationManager; @@ -128,6 +128,8 @@ namespace MediaBrowser.WebDashboard.Api _localization = localization; _jsonSerializer = jsonSerializer; _assemblyInfo = assemblyInfo; + _logger = logger; + _resultFactory = resultFactory; } /// @@ -159,7 +161,7 @@ namespace MediaBrowser.WebDashboard.Api if (plugin != null && stream != null) { - return ResultFactory.GetStaticResult(Request, plugin.Version.ToString().GetMD5(), null, null, MimeTypes.GetMimeType("page.html"), () => GetPackageCreator().ModifyHtml("dummy.html", stream, null, _appHost.ApplicationVersion.ToString(), null, false)); + return _resultFactory.GetStaticResult(Request, plugin.Version.ToString().GetMD5(), null, null, MimeTypes.GetMimeType("page.html"), () => GetPackageCreator().ModifyHtml("dummy.html", stream, null, _appHost.ApplicationVersion.ToString(), null, false)); } throw new ResourceNotFoundException(); @@ -203,7 +205,7 @@ namespace MediaBrowser.WebDashboard.Api } catch (Exception ex) { - Logger.ErrorException("Error getting plugin information from {0}", ex, p.GetType().Name); + _logger.ErrorException("Error getting plugin information from {0}", ex, p.GetType().Name); return null; } }) @@ -212,7 +214,7 @@ namespace MediaBrowser.WebDashboard.Api configPages.AddRange(_appHost.Plugins.SelectMany(GetConfigPages)); - return ResultFactory.GetOptimizedResult(Request, configPages); + return _resultFactory.GetOptimizedResult(Request, configPages); } private IEnumerable> GetPluginPages() @@ -280,7 +282,7 @@ namespace MediaBrowser.WebDashboard.Api !contentType.StartsWith("font/", StringComparison.OrdinalIgnoreCase)) { var stream = await GetResourceStream(path, localizationCulture).ConfigureAwait(false); - return ResultFactory.GetResult(stream, contentType); + return _resultFactory.GetResult(stream, contentType); } TimeSpan? cacheDuration = null; @@ -294,7 +296,7 @@ namespace MediaBrowser.WebDashboard.Api var cacheKey = (_appHost.ApplicationVersion.ToString() + (localizationCulture ?? string.Empty) + path).GetMD5(); - return await ResultFactory.GetStaticResult(Request, cacheKey, null, cacheDuration, contentType, () => GetResourceStream(path, localizationCulture)).ConfigureAwait(false); + return await _resultFactory.GetStaticResult(Request, cacheKey, null, cacheDuration, contentType, () => GetResourceStream(path, localizationCulture)).ConfigureAwait(false); } private string GetLocalizationCulture() @@ -318,7 +320,7 @@ namespace MediaBrowser.WebDashboard.Api private PackageCreator GetPackageCreator() { - return new PackageCreator(_fileSystem, _localization, Logger, _serverConfigurationManager, _jsonSerializer); + return new PackageCreator(_fileSystem, _localization, _logger, _serverConfigurationManager, _jsonSerializer); } private List GetDeployIgnoreExtensions() -- cgit v1.2.3 From 8492225deef59b4548976e162f6fa147abf923be Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 10 Nov 2016 22:29:51 -0500 Subject: update portable projects --- .../Emby.Server.Implementations.csproj | 2 + .../HttpServer/HttpListenerHost.cs | 710 +++++++++++++++++++ .../Playlists/ManualPlaylistsFolder.cs | 74 ++ .../ServerManager/ServerManager.cs | 8 +- MediaBrowser.Controller/Net/IHttpServer.cs | 10 +- MediaBrowser.Controller/Net/IServerManager.cs | 4 +- .../Configuration/ServerConfigurationManager.cs | 259 ------- .../Devices/CameraUploadsFolder.cs | 1 + .../HttpServer/HttpListenerHost.cs | 764 --------------------- .../HttpServer/ServerFactory.cs | 39 -- .../MediaBrowser.Server.Implementations.csproj | 44 +- .../Persistence/SqliteItemRepository.cs | 2 +- .../Playlists/ManualPlaylistsFolder.cs | 76 -- .../ServerApplicationPaths.cs | 233 ------- .../packages.config | 5 - .../ApplicationHost.cs | 43 +- .../Configuration/ServerConfigurationManager.cs | 257 +++++++ .../HttpServerFactory.cs | 107 +++ .../MediaBrowser.Server.Startup.Common.csproj | 15 +- .../ServerApplicationPaths.cs | 233 +++++++ MediaBrowser.Server.Startup.Common/packages.config | 1 + 21 files changed, 1442 insertions(+), 1445 deletions(-) create mode 100644 Emby.Server.Implementations/HttpServer/HttpListenerHost.cs create mode 100644 Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs delete mode 100644 MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs delete mode 100644 MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs delete mode 100644 MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs delete mode 100644 MediaBrowser.Server.Implementations/Playlists/ManualPlaylistsFolder.cs delete mode 100644 MediaBrowser.Server.Implementations/ServerApplicationPaths.cs create mode 100644 MediaBrowser.Server.Startup.Common/Configuration/ServerConfigurationManager.cs create mode 100644 MediaBrowser.Server.Startup.Common/HttpServerFactory.cs create mode 100644 MediaBrowser.Server.Startup.Common/ServerApplicationPaths.cs (limited to 'MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs') diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 07f3bd020..ccdecbf7b 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -69,6 +69,7 @@ + @@ -172,6 +173,7 @@ + diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs new file mode 100644 index 000000000..d7897f165 --- /dev/null +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -0,0 +1,710 @@ +using MediaBrowser.Common.Extensions; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Net; +using MediaBrowser.Model.Logging; +using ServiceStack; +using ServiceStack.Host; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using Emby.Server.Implementations.HttpServer; +using Emby.Server.Implementations.HttpServer.SocketSharp; +using MediaBrowser.Common.Net; +using MediaBrowser.Common.Security; +using MediaBrowser.Controller; +using MediaBrowser.Model.Cryptography; +using MediaBrowser.Model.Extensions; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Net; +using MediaBrowser.Model.Serialization; +using MediaBrowser.Model.Services; +using MediaBrowser.Model.System; +using MediaBrowser.Model.Text; +using SocketHttpListener.Net; +using SocketHttpListener.Primitives; + +namespace Emby.Server.Implementations.HttpServer +{ + public class HttpListenerHost : ServiceStackHost, IHttpServer + { + private string DefaultRedirectPath { get; set; } + + private readonly ILogger _logger; + public IEnumerable UrlPrefixes { get; private set; } + + private readonly List _restServices = new List(); + + private IHttpListener _listener; + + public event EventHandler WebSocketConnected; + public event EventHandler WebSocketConnecting; + + private readonly IServerConfigurationManager _config; + private readonly INetworkManager _networkManager; + private readonly IMemoryStreamFactory _memoryStreamProvider; + + private readonly IServerApplicationHost _appHost; + + private readonly ITextEncoding _textEncoding; + private readonly ISocketFactory _socketFactory; + private readonly ICryptoProvider _cryptoProvider; + + private readonly IJsonSerializer _jsonSerializer; + private readonly IXmlSerializer _xmlSerializer; + private readonly ICertificate _certificate; + private readonly IEnvironmentInfo _environment; + private readonly IStreamFactory _streamFactory; + private readonly Func> _funcParseFn; + + public HttpListenerHost(IServerApplicationHost applicationHost, + ILogger logger, + IServerConfigurationManager config, + string serviceName, + string defaultRedirectPath, INetworkManager networkManager, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding, ISocketFactory socketFactory, ICryptoProvider cryptoProvider, IJsonSerializer jsonSerializer, IXmlSerializer xmlSerializer, IEnvironmentInfo environment, ICertificate certificate, IStreamFactory streamFactory, Func> funcParseFn) + : base(serviceName, new Assembly[] { }) + { + _appHost = applicationHost; + DefaultRedirectPath = defaultRedirectPath; + _networkManager = networkManager; + _memoryStreamProvider = memoryStreamProvider; + _textEncoding = textEncoding; + _socketFactory = socketFactory; + _cryptoProvider = cryptoProvider; + _jsonSerializer = jsonSerializer; + _xmlSerializer = xmlSerializer; + _environment = environment; + _certificate = certificate; + _streamFactory = streamFactory; + _funcParseFn = funcParseFn; + _config = config; + + _logger = logger; + } + + public string GlobalResponse { get; set; } + + public override void Configure() + { + var mapExceptionToStatusCode = new Dictionary + { + {typeof (InvalidOperationException), 500}, + {typeof (NotImplementedException), 500}, + {typeof (ResourceNotFoundException), 404}, + {typeof (FileNotFoundException), 404}, + //{typeof (DirectoryNotFoundException), 404}, + {typeof (SecurityException), 401}, + {typeof (PaymentRequiredException), 402}, + {typeof (UnauthorizedAccessException), 500}, + {typeof (PlatformNotSupportedException), 500}, + {typeof (NotSupportedException), 500} + }; + + var requestFilters = _appHost.GetExports().ToList(); + foreach (var filter in requestFilters) + { + GlobalRequestFilters.Add(filter.Filter); + } + + GlobalResponseFilters.Add(new ResponseFilter(_logger).FilterResponse); + } + + protected override ILogger Logger + { + get + { + return _logger; + } + } + + public override T Resolve() + { + return _appHost.Resolve(); + } + + public override T TryResolve() + { + return _appHost.TryResolve(); + } + + public override object CreateInstance(Type type) + { + return _appHost.CreateInstance(type); + } + + protected override ServiceController CreateServiceController(params Assembly[] assembliesWithServices) + { + var types = _restServices.Select(r => r.GetType()).ToArray(); + + return new ServiceController(this, () => types); + } + + public override ServiceStackHost Start(string listeningAtUrlBase) + { + StartListener(); + return this; + } + + /// + /// Starts the Web Service + /// + private void StartListener() + { + WebSocketSharpRequest.HandlerFactoryPath = GetHandlerPathIfAny(UrlPrefixes.First()); + + _listener = GetListener(); + + _listener.WebSocketConnected = OnWebSocketConnected; + _listener.WebSocketConnecting = OnWebSocketConnecting; + _listener.ErrorHandler = ErrorHandler; + _listener.RequestHandler = RequestHandler; + + _listener.Start(UrlPrefixes); + } + + public static string GetHandlerPathIfAny(string listenerUrl) + { + if (listenerUrl == null) return null; + var pos = listenerUrl.IndexOf("://", StringComparison.OrdinalIgnoreCase); + if (pos == -1) return null; + var startHostUrl = listenerUrl.Substring(pos + "://".Length); + var endPos = startHostUrl.IndexOf('/'); + if (endPos == -1) return null; + var endHostUrl = startHostUrl.Substring(endPos + 1); + return string.IsNullOrEmpty(endHostUrl) ? null : endHostUrl.TrimEnd('/'); + } + + private IHttpListener GetListener() + { + var enableDualMode = _environment.OperatingSystem == OperatingSystem.Windows; + + return new WebSocketSharpListener(_logger, + _certificate, + _memoryStreamProvider, + _textEncoding, + _networkManager, + _socketFactory, + _cryptoProvider, + _streamFactory, + enableDualMode, + GetRequest); + } + + private IHttpRequest GetRequest(HttpListenerContext httpContext) + { + var operationName = httpContext.Request.GetOperationName(); + + var req = new WebSocketSharpRequest(httpContext, operationName, _logger, _memoryStreamProvider); + + return req; + } + + private void OnWebSocketConnecting(WebSocketConnectingEventArgs args) + { + if (_disposed) + { + return; + } + + if (WebSocketConnecting != null) + { + WebSocketConnecting(this, args); + } + } + + private void OnWebSocketConnected(WebSocketConnectEventArgs args) + { + if (_disposed) + { + return; + } + + if (WebSocketConnected != null) + { + WebSocketConnected(this, args); + } + } + + private void ErrorHandler(Exception ex, IRequest httpReq) + { + try + { + _logger.ErrorException("Error processing request", ex); + + var httpRes = httpReq.Response; + + if (httpRes.IsClosed) + { + return; + } + + httpRes.StatusCode = 500; + + httpRes.ContentType = "text/html"; + httpRes.Write(ex.Message); + + httpRes.Close(); + } + catch + { + //_logger.ErrorException("Error this.ProcessRequest(context)(Exception while writing error to the response)", errorEx); + } + } + + /// + /// Shut down the Web Service + /// + public void Stop() + { + if (_listener != null) + { + _listener.Stop(); + } + } + + private readonly Dictionary _skipLogExtensions = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + {".js", 0}, + {".css", 0}, + {".woff", 0}, + {".woff2", 0}, + {".ttf", 0}, + {".html", 0} + }; + + private bool EnableLogging(string url, string localPath) + { + var extension = GetExtension(url); + + if (string.IsNullOrWhiteSpace(extension) || !_skipLogExtensions.ContainsKey(extension)) + { + if (string.IsNullOrWhiteSpace(localPath) || localPath.IndexOf("system/ping", StringComparison.OrdinalIgnoreCase) == -1) + { + return true; + } + } + + return false; + } + + private string GetExtension(string url) + { + var parts = url.Split(new[] { '?' }, 2); + + return Path.GetExtension(parts[0]); + } + + public static string RemoveQueryStringByKey(string url, string key) + { + var uri = new Uri(url); + + // this gets all the query string key value pairs as a collection + var newQueryString = MyHttpUtility.ParseQueryString(uri.Query); + + var originalCount = newQueryString.Count; + + if (originalCount == 0) + { + return url; + } + + // this removes the key if exists + newQueryString.Remove(key); + + if (originalCount == newQueryString.Count) + { + return url; + } + + // this gets the page path from root without QueryString + string pagePathWithoutQueryString = url.Split(new[] { '?' }, StringSplitOptions.RemoveEmptyEntries)[0]; + + return newQueryString.Count > 0 + ? String.Format("{0}?{1}", pagePathWithoutQueryString, newQueryString) + : pagePathWithoutQueryString; + } + + private string GetUrlToLog(string url) + { + url = RemoveQueryStringByKey(url, "api_key"); + + return url; + } + + private string NormalizeConfiguredLocalAddress(string address) + { + var index = address.Trim('/').IndexOf('/'); + + if (index != -1) + { + address = address.Substring(index + 1); + } + + return address.Trim('/'); + } + + private bool ValidateHost(Uri url) + { + var hosts = _config + .Configuration + .LocalNetworkAddresses + .Select(NormalizeConfiguredLocalAddress) + .ToList(); + + if (hosts.Count == 0) + { + return true; + } + + var host = url.Host ?? string.Empty; + + _logger.Debug("Validating host {0}", host); + + if (_networkManager.IsInPrivateAddressSpace(host)) + { + hosts.Add("localhost"); + hosts.Add("127.0.0.1"); + + return hosts.Any(i => host.IndexOf(i, StringComparison.OrdinalIgnoreCase) != -1); + } + + return true; + } + + /// + /// Overridable method that can be used to implement a custom hnandler + /// + /// The HTTP req. + /// The URL. + /// Task. + protected async Task RequestHandler(IHttpRequest httpReq, Uri url) + { + var date = DateTime.Now; + var httpRes = httpReq.Response; + bool enableLog = false; + string urlToLog = null; + string remoteIp = null; + + try + { + if (_disposed) + { + httpRes.StatusCode = 503; + return; + } + + if (!ValidateHost(url)) + { + httpRes.StatusCode = 400; + httpRes.ContentType = "text/plain"; + httpRes.Write("Invalid host"); + return; + } + + if (string.Equals(httpReq.Verb, "OPTIONS", StringComparison.OrdinalIgnoreCase)) + { + 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.ContentType = "text/html"; + return; + } + + var operationName = httpReq.OperationName; + var localPath = url.LocalPath; + + var urlString = url.OriginalString; + enableLog = EnableLogging(urlString, localPath); + urlToLog = urlString; + + if (enableLog) + { + urlToLog = GetUrlToLog(urlString); + remoteIp = httpReq.RemoteIp; + + LoggerUtils.LogRequest(_logger, urlToLog, httpReq.HttpMethod, httpReq.UserAgent); + } + + if (string.Equals(localPath, "/emby/", StringComparison.OrdinalIgnoreCase) || + string.Equals(localPath, "/mediabrowser/", StringComparison.OrdinalIgnoreCase)) + { + RedirectToUrl(httpRes, DefaultRedirectPath); + return; + } + if (string.Equals(localPath, "/emby", StringComparison.OrdinalIgnoreCase) || + string.Equals(localPath, "/mediabrowser", StringComparison.OrdinalIgnoreCase)) + { + RedirectToUrl(httpRes, "emby/" + DefaultRedirectPath); + return; + } + + if (string.Equals(localPath, "/mediabrowser/", StringComparison.OrdinalIgnoreCase) || + string.Equals(localPath, "/mediabrowser", StringComparison.OrdinalIgnoreCase) || + localPath.IndexOf("mediabrowser/web", StringComparison.OrdinalIgnoreCase) != -1) + { + httpRes.StatusCode = 200; + httpRes.ContentType = "text/html"; + var newUrl = urlString.Replace("mediabrowser", "emby", StringComparison.OrdinalIgnoreCase) + .Replace("/dashboard/", "/web/", StringComparison.OrdinalIgnoreCase); + + if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase)) + { + httpRes.Write( + "EmbyPlease update your Emby bookmark to " + newUrl + ""); + return; + } + } + + if (localPath.IndexOf("dashboard/", StringComparison.OrdinalIgnoreCase) != -1 && + localPath.IndexOf("web/dashboard", StringComparison.OrdinalIgnoreCase) == -1) + { + httpRes.StatusCode = 200; + httpRes.ContentType = "text/html"; + var newUrl = urlString.Replace("mediabrowser", "emby", StringComparison.OrdinalIgnoreCase) + .Replace("/dashboard/", "/web/", StringComparison.OrdinalIgnoreCase); + + if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase)) + { + httpRes.Write( + "EmbyPlease update your Emby bookmark to " + newUrl + ""); + return; + } + } + + if (string.Equals(localPath, "/web", StringComparison.OrdinalIgnoreCase)) + { + RedirectToUrl(httpRes, DefaultRedirectPath); + return; + } + if (string.Equals(localPath, "/web/", StringComparison.OrdinalIgnoreCase)) + { + RedirectToUrl(httpRes, "../" + DefaultRedirectPath); + return; + } + if (string.Equals(localPath, "/", StringComparison.OrdinalIgnoreCase)) + { + RedirectToUrl(httpRes, DefaultRedirectPath); + return; + } + if (string.IsNullOrEmpty(localPath)) + { + RedirectToUrl(httpRes, "/" + DefaultRedirectPath); + return; + } + + if (string.Equals(localPath, "/emby/pin", StringComparison.OrdinalIgnoreCase)) + { + RedirectToUrl(httpRes, "web/pin.html"); + return; + } + + if (!string.IsNullOrWhiteSpace(GlobalResponse)) + { + httpRes.StatusCode = 503; + httpRes.ContentType = "text/html"; + httpRes.Write(GlobalResponse); + return; + } + + var handler = HttpHandlerFactory.GetHandler(httpReq); + + if (handler != null) + { + await handler.ProcessRequestAsync(httpReq, httpRes, operationName).ConfigureAwait(false); + } + } + catch (Exception ex) + { + ErrorHandler(ex, httpReq); + } + finally + { + httpRes.Close(); + + if (enableLog) + { + var statusCode = httpRes.StatusCode; + + var duration = DateTime.Now - date; + + LoggerUtils.LogResponse(_logger, statusCode, urlToLog, remoteIp, duration); + } + } + } + + public static void RedirectToUrl(IResponse httpRes, string url) + { + httpRes.StatusCode = 302; + httpRes.AddHeader("Location", url); + } + + + /// + /// Adds the rest handlers. + /// + /// The services. + public void Init(IEnumerable services) + { + _restServices.AddRange(services); + + ServiceController = CreateServiceController(); + + _logger.Info("Calling ServiceStack AppHost.Init"); + + base.Init(); + } + + public override RouteAttribute[] GetRouteAttributes(Type requestType) + { + var routes = base.GetRouteAttributes(requestType).ToList(); + var clone = routes.ToList(); + + foreach (var route in clone) + { + routes.Add(new RouteAttribute(NormalizeEmbyRoutePath(route.Path), route.Verbs) + { + Notes = route.Notes, + Priority = route.Priority, + Summary = route.Summary + }); + + routes.Add(new RouteAttribute(NormalizeRoutePath(route.Path), route.Verbs) + { + Notes = route.Notes, + Priority = route.Priority, + Summary = route.Summary + }); + + routes.Add(new RouteAttribute(DoubleNormalizeEmbyRoutePath(route.Path), route.Verbs) + { + Notes = route.Notes, + Priority = route.Priority, + Summary = route.Summary + }); + } + + return routes.ToArray(); + } + + public override object GetTaskResult(Task task, string requestName) + { + try + { + var taskObject = task as Task; + if (taskObject != null) + { + return taskObject.Result; + } + + task.Wait(); + + var type = task.GetType().GetTypeInfo(); + if (!type.IsGenericType) + { + return null; + } + + Logger.Warn("Getting task result from " + requestName + " using reflection. For better performance have your api return Task"); + return type.GetDeclaredProperty("Result").GetValue(task); + } + catch (TypeAccessException) + { + return null; //return null for void Task's + } + } + + public override Func GetParseFn(Type propertyType) + { + return _funcParseFn(propertyType); + } + + public override void SerializeToJson(object o, Stream stream) + { + _jsonSerializer.SerializeToStream(o, stream); + } + + public override void SerializeToXml(object o, Stream stream) + { + _xmlSerializer.SerializeToStream(o, stream); + } + + public override object DeserializeXml(Type type, Stream stream) + { + return _xmlSerializer.DeserializeFromStream(type, stream); + } + + public override object DeserializeJson(Type type, Stream stream) + { + return _jsonSerializer.DeserializeFromStream(stream, type); + } + + private string NormalizeEmbyRoutePath(string path) + { + if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase)) + { + return "/emby" + path; + } + + return "emby/" + path; + } + + private string DoubleNormalizeEmbyRoutePath(string path) + { + if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase)) + { + return "/emby/emby" + path; + } + + return "emby/emby/" + path; + } + + private string NormalizeRoutePath(string path) + { + if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase)) + { + return "/mediabrowser" + path; + } + + return "mediabrowser/" + path; + } + + private bool _disposed; + private readonly object _disposeLock = new object(); + protected virtual void Dispose(bool disposing) + { + if (_disposed) return; + base.Dispose(); + + lock (_disposeLock) + { + if (_disposed) return; + + if (disposing) + { + Stop(); + } + + //release unmanaged resources here... + _disposed = true; + } + } + + public override void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + public void StartServer(IEnumerable urlPrefixes) + { + UrlPrefixes = urlPrefixes.ToList(); + Start(UrlPrefixes.First()); + } + } +} \ No newline at end of file diff --git a/Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs b/Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs new file mode 100644 index 000000000..8e65ccaf7 --- /dev/null +++ b/Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs @@ -0,0 +1,74 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Playlists; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Querying; + +namespace Emby.Server.Implementations.Playlists +{ + public class PlaylistsFolder : BasePluginFolder + { + public PlaylistsFolder() + { + Name = "Playlists"; + } + + public override bool IsVisible(User user) + { + return base.IsVisible(user) && GetChildren(user, true).Any(); + } + + protected override IEnumerable GetEligibleChildrenForRecursiveChildren(User user) + { + return base.GetEligibleChildrenForRecursiveChildren(user).OfType(); + } + + public override bool IsHidden + { + get + { + return true; + } + } + + public override string CollectionType + { + get { return MediaBrowser.Model.Entities.CollectionType.Playlists; } + } + + protected override Task> GetItemsInternal(InternalItemsQuery query) + { + query.Recursive = false; + return base.GetItemsInternal(query); + } + } + + public class PlaylistsDynamicFolder : IVirtualFolderCreator + { + private readonly IApplicationPaths _appPaths; + private readonly IFileSystem _fileSystem; + + public PlaylistsDynamicFolder(IApplicationPaths appPaths, IFileSystem fileSystem) + { + _appPaths = appPaths; + _fileSystem = fileSystem; + } + + public BasePluginFolder GetFolder() + { + var path = Path.Combine(_appPaths.DataPath, "playlists"); + + _fileSystem.CreateDirectory(path); + + return new PlaylistsFolder + { + Path = path + }; + } + } +} + diff --git a/Emby.Server.Implementations/ServerManager/ServerManager.cs b/Emby.Server.Implementations/ServerManager/ServerManager.cs index f660d01ec..f7e4c0ce2 100644 --- a/Emby.Server.Implementations/ServerManager/ServerManager.cs +++ b/Emby.Server.Implementations/ServerManager/ServerManager.cs @@ -112,22 +112,22 @@ namespace Emby.Server.Implementations.ServerManager /// /// Starts this instance. /// - public void Start(IEnumerable urlPrefixes, string certificatePath) + public void Start(IEnumerable urlPrefixes) { - ReloadHttpServer(urlPrefixes, certificatePath); + ReloadHttpServer(urlPrefixes); } /// /// Restarts the Http Server, or starts it if not currently running /// - private void ReloadHttpServer(IEnumerable urlPrefixes, string certificatePath) + private void ReloadHttpServer(IEnumerable urlPrefixes) { _logger.Info("Loading Http Server"); try { HttpServer = _applicationHost.Resolve(); - HttpServer.StartServer(urlPrefixes, certificatePath); + HttpServer.StartServer(urlPrefixes); } catch (Exception ex) { diff --git a/MediaBrowser.Controller/Net/IHttpServer.cs b/MediaBrowser.Controller/Net/IHttpServer.cs index 8c503c199..f319244da 100644 --- a/MediaBrowser.Controller/Net/IHttpServer.cs +++ b/MediaBrowser.Controller/Net/IHttpServer.cs @@ -15,19 +15,11 @@ namespace MediaBrowser.Controller.Net /// The URL prefix. IEnumerable UrlPrefixes { get; } - /// - /// Gets the certificate path. - /// - /// The certificate path. - string CertificatePath { get; } - /// /// Starts the specified server name. /// /// The URL prefixes. - /// If an https prefix is specified, - /// the ssl certificate localtion on the file system. - void StartServer(IEnumerable urlPrefixes, string certificatePath); + void StartServer(IEnumerable urlPrefixes); /// /// Stops this instance. diff --git a/MediaBrowser.Controller/Net/IServerManager.cs b/MediaBrowser.Controller/Net/IServerManager.cs index 5191a62e3..202df2982 100644 --- a/MediaBrowser.Controller/Net/IServerManager.cs +++ b/MediaBrowser.Controller/Net/IServerManager.cs @@ -15,9 +15,7 @@ namespace MediaBrowser.Controller.Net /// Starts this instance. /// /// The URL prefixes. - /// If an https prefix is specified, - /// the ssl certificate localtion on the file system. - void Start(IEnumerable urlPrefixes, string certificatePath); + void Start(IEnumerable urlPrefixes); /// /// Sends a message to all clients currently connected via a web socket diff --git a/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs b/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs deleted file mode 100644 index 4b6195192..000000000 --- a/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs +++ /dev/null @@ -1,259 +0,0 @@ -using System.Collections.Generic; -using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Events; -using Emby.Common.Implementations.Configuration; -using MediaBrowser.Controller; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Entities.Movies; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Model.Configuration; -using MediaBrowser.Model.Events; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Serialization; -using System; -using System.IO; -using System.Linq; -using MediaBrowser.Common.IO; -using MediaBrowser.Controller.IO; -using MediaBrowser.Model.IO; - -namespace MediaBrowser.Server.Implementations.Configuration -{ - /// - /// Class ServerConfigurationManager - /// - public class ServerConfigurationManager : BaseConfigurationManager, IServerConfigurationManager - { - - /// - /// Initializes a new instance of the class. - /// - /// The application paths. - /// The log manager. - /// The XML serializer. - /// The file system. - public ServerConfigurationManager(IApplicationPaths applicationPaths, ILogManager logManager, IXmlSerializer xmlSerializer, IFileSystem fileSystem) - : base(applicationPaths, logManager, xmlSerializer, fileSystem) - { - UpdateMetadataPath(); - } - - public event EventHandler> ConfigurationUpdating; - - /// - /// Gets the type of the configuration. - /// - /// The type of the configuration. - protected override Type ConfigurationType - { - get { return typeof(ServerConfiguration); } - } - - /// - /// Gets the application paths. - /// - /// The application paths. - public IServerApplicationPaths ApplicationPaths - { - get { return (IServerApplicationPaths)CommonApplicationPaths; } - } - - /// - /// Gets the configuration. - /// - /// The configuration. - public ServerConfiguration Configuration - { - get { return (ServerConfiguration)CommonConfiguration; } - } - - /// - /// Called when [configuration updated]. - /// - protected override void OnConfigurationUpdated() - { - UpdateMetadataPath(); - - base.OnConfigurationUpdated(); - } - - public override void AddParts(IEnumerable factories) - { - base.AddParts(factories); - - UpdateTranscodingTempPath(); - } - - /// - /// Updates the metadata path. - /// - private void UpdateMetadataPath() - { - string metadataPath; - - if (string.IsNullOrWhiteSpace(Configuration.MetadataPath)) - { - metadataPath = GetInternalMetadataPath(); - } - else - { - metadataPath = Path.Combine(Configuration.MetadataPath, "metadata"); - } - - ((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath = metadataPath; - - ((ServerApplicationPaths)ApplicationPaths).ItemsByNamePath = ((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath; - } - - private string GetInternalMetadataPath() - { - return Path.Combine(ApplicationPaths.ProgramDataPath, "metadata"); - } - - /// - /// Updates the transcoding temporary path. - /// - private void UpdateTranscodingTempPath() - { - var encodingConfig = this.GetConfiguration("encoding"); - - ((ServerApplicationPaths)ApplicationPaths).TranscodingTempPath = string.IsNullOrEmpty(encodingConfig.TranscodingTempPath) ? - null : - Path.Combine(encodingConfig.TranscodingTempPath, "transcoding-temp"); - } - - protected override void OnNamedConfigurationUpdated(string key, object configuration) - { - base.OnNamedConfigurationUpdated(key, configuration); - - if (string.Equals(key, "encoding", StringComparison.OrdinalIgnoreCase)) - { - UpdateTranscodingTempPath(); - } - } - - /// - /// Replaces the configuration. - /// - /// The new configuration. - /// - public override void ReplaceConfiguration(BaseApplicationConfiguration newConfiguration) - { - var newConfig = (ServerConfiguration)newConfiguration; - - ValidatePathSubstitutions(newConfig); - ValidateMetadataPath(newConfig); - ValidateSslCertificate(newConfig); - - EventHelper.FireEventIfNotNull(ConfigurationUpdating, this, new GenericEventArgs { Argument = newConfig }, Logger); - - base.ReplaceConfiguration(newConfiguration); - } - - - /// - /// Validates the SSL certificate. - /// - /// The new configuration. - /// - private void ValidateSslCertificate(BaseApplicationConfiguration newConfig) - { - var serverConfig = (ServerConfiguration)newConfig; - - var newPath = serverConfig.CertificatePath; - - if (!string.IsNullOrWhiteSpace(newPath) - && !string.Equals(Configuration.CertificatePath ?? string.Empty, newPath)) - { - // Validate - if (!FileSystem.FileExists(newPath)) - { - throw new FileNotFoundException(string.Format("Certificate file '{0}' does not exist.", newPath)); - } - } - } - - private void ValidatePathSubstitutions(ServerConfiguration newConfig) - { - foreach (var map in newConfig.PathSubstitutions) - { - if (string.IsNullOrWhiteSpace(map.From) || string.IsNullOrWhiteSpace(map.To)) - { - throw new ArgumentException("Invalid path substitution"); - } - } - } - - /// - /// Validates the metadata path. - /// - /// The new configuration. - /// - private void ValidateMetadataPath(ServerConfiguration newConfig) - { - var newPath = newConfig.MetadataPath; - - if (!string.IsNullOrWhiteSpace(newPath) - && !string.Equals(Configuration.MetadataPath ?? string.Empty, newPath)) - { - // Validate - if (!FileSystem.DirectoryExists(newPath)) - { - throw new DirectoryNotFoundException(string.Format("{0} does not exist.", newPath)); - } - - EnsureWriteAccess(newPath); - } - } - - public void DisableMetadataService(string service) - { - DisableMetadataService(typeof(Movie), Configuration, service); - DisableMetadataService(typeof(Episode), Configuration, service); - DisableMetadataService(typeof(Series), Configuration, service); - DisableMetadataService(typeof(Season), Configuration, service); - DisableMetadataService(typeof(MusicArtist), Configuration, service); - DisableMetadataService(typeof(MusicAlbum), Configuration, service); - DisableMetadataService(typeof(MusicVideo), Configuration, service); - DisableMetadataService(typeof(Video), Configuration, service); - } - - private void DisableMetadataService(Type type, ServerConfiguration config, string service) - { - var options = GetMetadataOptions(type, config); - - if (!options.DisabledMetadataSavers.Contains(service, StringComparer.OrdinalIgnoreCase)) - { - var list = options.DisabledMetadataSavers.ToList(); - - list.Add(service); - - options.DisabledMetadataSavers = list.ToArray(); - } - } - - private MetadataOptions GetMetadataOptions(Type type, ServerConfiguration config) - { - var options = config.MetadataOptions - .FirstOrDefault(i => string.Equals(i.ItemType, type.Name, StringComparison.OrdinalIgnoreCase)); - - if (options == null) - { - var list = config.MetadataOptions.ToList(); - - options = new MetadataOptions - { - ItemType = type.Name - }; - - list.Add(options); - - config.MetadataOptions = list.ToArray(); - } - - return options; - } - } -} diff --git a/MediaBrowser.Server.Implementations/Devices/CameraUploadsFolder.cs b/MediaBrowser.Server.Implementations/Devices/CameraUploadsFolder.cs index 19a0593e3..882701885 100644 --- a/MediaBrowser.Server.Implementations/Devices/CameraUploadsFolder.cs +++ b/MediaBrowser.Server.Implementations/Devices/CameraUploadsFolder.cs @@ -10,6 +10,7 @@ using MediaBrowser.Common.IO; using MediaBrowser.Controller.IO; using MediaBrowser.Model.IO; using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Serialization; namespace MediaBrowser.Server.Implementations.Devices { diff --git a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs deleted file mode 100644 index f0a9c5ca3..000000000 --- a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs +++ /dev/null @@ -1,764 +0,0 @@ -using MediaBrowser.Common.Extensions; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Logging; -using ServiceStack; -using ServiceStack.Host; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net.Security; -using System.Net.Sockets; -using System.Reflection; -using System.Security.Cryptography.X509Certificates; -using System.Threading.Tasks; -using Emby.Common.Implementations.Net; -using Emby.Server.Implementations.HttpServer; -using Emby.Server.Implementations.HttpServer.SocketSharp; -using MediaBrowser.Common.Net; -using MediaBrowser.Common.Security; -using MediaBrowser.Controller; -using MediaBrowser.Model.Cryptography; -using MediaBrowser.Model.Extensions; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Net; -using MediaBrowser.Model.Serialization; -using MediaBrowser.Model.Services; -using MediaBrowser.Model.Text; -using ServiceStack.Text.Jsv; -using SocketHttpListener.Net; -using SocketHttpListener.Primitives; - -namespace MediaBrowser.Server.Implementations.HttpServer -{ - public class HttpListenerHost : ServiceStackHost, IHttpServer - { - private string DefaultRedirectPath { get; set; } - - private readonly ILogger _logger; - public IEnumerable UrlPrefixes { get; private set; } - - private readonly List _restServices = new List(); - - private IHttpListener _listener; - - public event EventHandler WebSocketConnected; - public event EventHandler WebSocketConnecting; - - public string CertificatePath { get; private set; } - - private readonly IServerConfigurationManager _config; - private readonly INetworkManager _networkManager; - private readonly IMemoryStreamFactory _memoryStreamProvider; - - private readonly IServerApplicationHost _appHost; - - private readonly ITextEncoding _textEncoding; - private readonly ISocketFactory _socketFactory; - private readonly ICryptoProvider _cryptoProvider; - - private readonly IJsonSerializer _jsonSerializer; - private readonly IXmlSerializer _xmlSerializer; - - public HttpListenerHost(IServerApplicationHost applicationHost, - ILogManager logManager, - IServerConfigurationManager config, - string serviceName, - string defaultRedirectPath, INetworkManager networkManager, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding, ISocketFactory socketFactory, ICryptoProvider cryptoProvider, IJsonSerializer jsonSerializer, IXmlSerializer xmlSerializer) - : base(serviceName, new Assembly[] { }) - { - _appHost = applicationHost; - DefaultRedirectPath = defaultRedirectPath; - _networkManager = networkManager; - _memoryStreamProvider = memoryStreamProvider; - _textEncoding = textEncoding; - _socketFactory = socketFactory; - _cryptoProvider = cryptoProvider; - _jsonSerializer = jsonSerializer; - _xmlSerializer = xmlSerializer; - _config = config; - - _logger = logManager.GetLogger("HttpServer"); - } - - public string GlobalResponse { get; set; } - - public override void Configure() - { - var mapExceptionToStatusCode = new Dictionary - { - {typeof (InvalidOperationException), 500}, - {typeof (NotImplementedException), 500}, - {typeof (ResourceNotFoundException), 404}, - {typeof (FileNotFoundException), 404}, - {typeof (DirectoryNotFoundException), 404}, - {typeof (SecurityException), 401}, - {typeof (PaymentRequiredException), 402}, - {typeof (UnauthorizedAccessException), 500}, - {typeof (ApplicationException), 500}, - {typeof (PlatformNotSupportedException), 500}, - {typeof (NotSupportedException), 500} - }; - - var requestFilters = _appHost.GetExports().ToList(); - foreach (var filter in requestFilters) - { - GlobalRequestFilters.Add(filter.Filter); - } - - GlobalResponseFilters.Add(new ResponseFilter(_logger).FilterResponse); - } - - protected override ILogger Logger - { - get - { - return _logger; - } - } - - public override T Resolve() - { - return _appHost.Resolve(); - } - - public override Type[] GetGenericArguments(Type type) - { - return type.GetGenericArguments(); - } - - public override bool IsAssignableFrom(Type type1, Type type2) - { - return type1.IsAssignableFrom(type2); - } - - public override T TryResolve() - { - return _appHost.TryResolve(); - } - - public override object CreateInstance(Type type) - { - return _appHost.CreateInstance(type); - } - - protected override ServiceController CreateServiceController(params Assembly[] assembliesWithServices) - { - var types = _restServices.Select(r => r.GetType()).ToArray(); - - return new ServiceController(this, () => types); - } - - public override ServiceStackHost Start(string listeningAtUrlBase) - { - StartListener(); - return this; - } - - /// - /// Starts the Web Service - /// - private void StartListener() - { - WebSocketSharpRequest.HandlerFactoryPath = GetHandlerPathIfAny(UrlPrefixes.First()); - - _listener = GetListener(); - - _listener.WebSocketConnected = OnWebSocketConnected; - _listener.WebSocketConnecting = OnWebSocketConnecting; - _listener.ErrorHandler = ErrorHandler; - _listener.RequestHandler = RequestHandler; - - _listener.Start(UrlPrefixes); - } - - public static string GetHandlerPathIfAny(string listenerUrl) - { - if (listenerUrl == null) return null; - var pos = listenerUrl.IndexOf("://", StringComparison.OrdinalIgnoreCase); - if (pos == -1) return null; - var startHostUrl = listenerUrl.Substring(pos + "://".Length); - var endPos = startHostUrl.IndexOf('/'); - if (endPos == -1) return null; - var endHostUrl = startHostUrl.Substring(endPos + 1); - return string.IsNullOrEmpty(endHostUrl) ? null : endHostUrl.TrimEnd('/'); - } - - private IHttpListener GetListener() - { - var cert = !string.IsNullOrWhiteSpace(CertificatePath) && File.Exists(CertificatePath) - ? GetCert(CertificatePath) : - null; - - var enableDualMode = Environment.OSVersion.Platform == PlatformID.Win32NT; - - return new WebSocketSharpListener(_logger, cert, _memoryStreamProvider, _textEncoding, _networkManager, _socketFactory, _cryptoProvider, new StreamFactory(), enableDualMode, GetRequest); - } - - public ICertificate GetCert(string certificateLocation) - { - try - { - X509Certificate2 localCert = new X509Certificate2(certificateLocation); - //localCert.PrivateKey = PrivateKey.CreateFromFile(pvk_file).RSA; - if (localCert.PrivateKey == null) - { - //throw new FileNotFoundException("Secure requested, no private key included", certificateLocation); - return null; - } - - return new Certificate(localCert); - } - catch (Exception ex) - { - Logger.ErrorException("Error loading cert from {0}", ex, certificateLocation); - return null; - } - } - - private IHttpRequest GetRequest(HttpListenerContext httpContext) - { - var operationName = httpContext.Request.GetOperationName(); - - var req = new WebSocketSharpRequest(httpContext, operationName, _logger, _memoryStreamProvider); - - return req; - } - - private void OnWebSocketConnecting(WebSocketConnectingEventArgs args) - { - if (_disposed) - { - return; - } - - if (WebSocketConnecting != null) - { - WebSocketConnecting(this, args); - } - } - - private void OnWebSocketConnected(WebSocketConnectEventArgs args) - { - if (_disposed) - { - return; - } - - if (WebSocketConnected != null) - { - WebSocketConnected(this, args); - } - } - - private void ErrorHandler(Exception ex, IRequest httpReq) - { - try - { - _logger.ErrorException("Error processing request", ex); - - var httpRes = httpReq.Response; - - if (httpRes.IsClosed) - { - return; - } - - httpRes.StatusCode = 500; - - httpRes.ContentType = "text/html"; - httpRes.Write(ex.Message); - - httpRes.Close(); - } - catch - { - //_logger.ErrorException("Error this.ProcessRequest(context)(Exception while writing error to the response)", errorEx); - } - } - - /// - /// Shut down the Web Service - /// - public void Stop() - { - if (_listener != null) - { - _listener.Stop(); - } - } - - private readonly Dictionary _skipLogExtensions = new Dictionary(StringComparer.OrdinalIgnoreCase) - { - {".js", 0}, - {".css", 0}, - {".woff", 0}, - {".woff2", 0}, - {".ttf", 0}, - {".html", 0} - }; - - private bool EnableLogging(string url, string localPath) - { - var extension = GetExtension(url); - - if (string.IsNullOrWhiteSpace(extension) || !_skipLogExtensions.ContainsKey(extension)) - { - if (string.IsNullOrWhiteSpace(localPath) || localPath.IndexOf("system/ping", StringComparison.OrdinalIgnoreCase) == -1) - { - return true; - } - } - - return false; - } - - private string GetExtension(string url) - { - var parts = url.Split(new[] { '?' }, 2); - - return Path.GetExtension(parts[0]); - } - - public static string RemoveQueryStringByKey(string url, string key) - { - var uri = new Uri(url); - - // this gets all the query string key value pairs as a collection - var newQueryString = MyHttpUtility.ParseQueryString(uri.Query); - - if (newQueryString.Count == 0) - { - return url; - } - - // this removes the key if exists - newQueryString.Remove(key); - - // this gets the page path from root without QueryString - string pagePathWithoutQueryString = uri.GetLeftPart(UriPartial.Path); - - return newQueryString.Count > 0 - ? String.Format("{0}?{1}", pagePathWithoutQueryString, newQueryString) - : pagePathWithoutQueryString; - } - - private string GetUrlToLog(string url) - { - url = RemoveQueryStringByKey(url, "api_key"); - - return url; - } - - private string NormalizeConfiguredLocalAddress(string address) - { - var index = address.Trim('/').IndexOf('/'); - - if (index != -1) - { - address = address.Substring(index + 1); - } - - return address.Trim('/'); - } - - private bool ValidateHost(Uri url) - { - var hosts = _config - .Configuration - .LocalNetworkAddresses - .Select(NormalizeConfiguredLocalAddress) - .ToList(); - - if (hosts.Count == 0) - { - return true; - } - - var host = url.Host ?? string.Empty; - - _logger.Debug("Validating host {0}", host); - - if (_networkManager.IsInPrivateAddressSpace(host)) - { - hosts.Add("localhost"); - hosts.Add("127.0.0.1"); - - return hosts.Any(i => host.IndexOf(i, StringComparison.OrdinalIgnoreCase) != -1); - } - - return true; - } - - /// - /// Overridable method that can be used to implement a custom hnandler - /// - /// The HTTP req. - /// The URL. - /// Task. - protected async Task RequestHandler(IHttpRequest httpReq, Uri url) - { - var date = DateTime.Now; - var httpRes = httpReq.Response; - bool enableLog = false; - string urlToLog = null; - string remoteIp = null; - - try - { - if (_disposed) - { - httpRes.StatusCode = 503; - return; - } - - if (!ValidateHost(url)) - { - httpRes.StatusCode = 400; - httpRes.ContentType = "text/plain"; - httpRes.Write("Invalid host"); - return; - } - - if (string.Equals(httpReq.Verb, "OPTIONS", StringComparison.OrdinalIgnoreCase)) - { - 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.ContentType = "text/html"; - return; - } - - var operationName = httpReq.OperationName; - var localPath = url.LocalPath; - - var urlString = url.OriginalString; - enableLog = EnableLogging(urlString, localPath); - urlToLog = urlString; - - if (enableLog) - { - urlToLog = GetUrlToLog(urlString); - remoteIp = httpReq.RemoteIp; - - LoggerUtils.LogRequest(_logger, urlToLog, httpReq.HttpMethod, httpReq.UserAgent); - } - - if (string.Equals(localPath, "/emby/", StringComparison.OrdinalIgnoreCase) || - string.Equals(localPath, "/mediabrowser/", StringComparison.OrdinalIgnoreCase)) - { - RedirectToUrl(httpRes, DefaultRedirectPath); - return; - } - if (string.Equals(localPath, "/emby", StringComparison.OrdinalIgnoreCase) || - string.Equals(localPath, "/mediabrowser", StringComparison.OrdinalIgnoreCase)) - { - RedirectToUrl(httpRes, "emby/" + DefaultRedirectPath); - return; - } - - if (string.Equals(localPath, "/mediabrowser/", StringComparison.OrdinalIgnoreCase) || - string.Equals(localPath, "/mediabrowser", StringComparison.OrdinalIgnoreCase) || - localPath.IndexOf("mediabrowser/web", StringComparison.OrdinalIgnoreCase) != -1) - { - httpRes.StatusCode = 200; - httpRes.ContentType = "text/html"; - var newUrl = urlString.Replace("mediabrowser", "emby", StringComparison.OrdinalIgnoreCase) - .Replace("/dashboard/", "/web/", StringComparison.OrdinalIgnoreCase); - - if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase)) - { - httpRes.Write( - "EmbyPlease update your Emby bookmark to " + newUrl + ""); - return; - } - } - - if (localPath.IndexOf("dashboard/", StringComparison.OrdinalIgnoreCase) != -1 && - localPath.IndexOf("web/dashboard", StringComparison.OrdinalIgnoreCase) == -1) - { - httpRes.StatusCode = 200; - httpRes.ContentType = "text/html"; - var newUrl = urlString.Replace("mediabrowser", "emby", StringComparison.OrdinalIgnoreCase) - .Replace("/dashboard/", "/web/", StringComparison.OrdinalIgnoreCase); - - if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase)) - { - httpRes.Write( - "EmbyPlease update your Emby bookmark to " + newUrl + ""); - return; - } - } - - if (string.Equals(localPath, "/web", StringComparison.OrdinalIgnoreCase)) - { - RedirectToUrl(httpRes, DefaultRedirectPath); - return; - } - if (string.Equals(localPath, "/web/", StringComparison.OrdinalIgnoreCase)) - { - RedirectToUrl(httpRes, "../" + DefaultRedirectPath); - return; - } - if (string.Equals(localPath, "/", StringComparison.OrdinalIgnoreCase)) - { - RedirectToUrl(httpRes, DefaultRedirectPath); - return; - } - if (string.IsNullOrEmpty(localPath)) - { - RedirectToUrl(httpRes, "/" + DefaultRedirectPath); - return; - } - - if (string.Equals(localPath, "/emby/pin", StringComparison.OrdinalIgnoreCase)) - { - RedirectToUrl(httpRes, "web/pin.html"); - return; - } - - if (!string.IsNullOrWhiteSpace(GlobalResponse)) - { - httpRes.StatusCode = 503; - httpRes.ContentType = "text/html"; - httpRes.Write(GlobalResponse); - return; - } - - var handler = HttpHandlerFactory.GetHandler(httpReq); - - if (handler != null) - { - await handler.ProcessRequestAsync(httpReq, httpRes, operationName).ConfigureAwait(false); - } - } - catch (Exception ex) - { - ErrorHandler(ex, httpReq); - } - finally - { - httpRes.Close(); - - if (enableLog) - { - var statusCode = httpRes.StatusCode; - - var duration = DateTime.Now - date; - - LoggerUtils.LogResponse(_logger, statusCode, urlToLog, remoteIp, duration); - } - } - } - - public static void RedirectToUrl(IResponse httpRes, string url) - { - httpRes.StatusCode = 302; - httpRes.AddHeader("Location", url); - } - - - /// - /// Adds the rest handlers. - /// - /// The services. - public void Init(IEnumerable services) - { - _restServices.AddRange(services); - - ServiceController = CreateServiceController(); - - _logger.Info("Calling ServiceStack AppHost.Init"); - - base.Init(); - } - - public override Model.Services.RouteAttribute[] GetRouteAttributes(Type requestType) - { - var routes = base.GetRouteAttributes(requestType).ToList(); - var clone = routes.ToList(); - - foreach (var route in clone) - { - routes.Add(new Model.Services.RouteAttribute(NormalizeEmbyRoutePath(route.Path), route.Verbs) - { - Notes = route.Notes, - Priority = route.Priority, - Summary = route.Summary - }); - - routes.Add(new Model.Services.RouteAttribute(NormalizeRoutePath(route.Path), route.Verbs) - { - Notes = route.Notes, - Priority = route.Priority, - Summary = route.Summary - }); - - routes.Add(new Model.Services.RouteAttribute(DoubleNormalizeEmbyRoutePath(route.Path), route.Verbs) - { - Notes = route.Notes, - Priority = route.Priority, - Summary = route.Summary - }); - } - - return routes.ToArray(); - } - - public override object GetTaskResult(Task task, string requestName) - { - try - { - var taskObject = task as Task; - if (taskObject != null) - { - return taskObject.Result; - } - - task.Wait(); - - var type = task.GetType(); - if (!type.IsGenericType) - { - return null; - } - - Logger.Warn("Getting task result from " + requestName + " using reflection. For better performance have your api return Task"); - return type.GetProperty("Result").GetValue(task); - } - catch (TypeAccessException) - { - return null; //return null for void Task's - } - } - - public override Func GetParseFn(Type propertyType) - { - var fn = JsvReader.GetParseFn(propertyType); - - return s => fn(s); - } - - public override void SerializeToJson(object o, Stream stream) - { - _jsonSerializer.SerializeToStream(o, stream); - } - - public override void SerializeToXml(object o, Stream stream) - { - _xmlSerializer.SerializeToStream(o, stream); - } - - public override object DeserializeXml(Type type, Stream stream) - { - return _xmlSerializer.DeserializeFromStream(type, stream); - } - - public override object DeserializeJson(Type type, Stream stream) - { - return _jsonSerializer.DeserializeFromStream(stream, type); - } - - private string NormalizeEmbyRoutePath(string path) - { - if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase)) - { - return "/emby" + path; - } - - return "emby/" + path; - } - - private string DoubleNormalizeEmbyRoutePath(string path) - { - if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase)) - { - return "/emby/emby" + path; - } - - return "emby/emby/" + path; - } - - private string NormalizeRoutePath(string path) - { - if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase)) - { - return "/mediabrowser" + path; - } - - return "mediabrowser/" + path; - } - - private bool _disposed; - private readonly object _disposeLock = new object(); - protected virtual void Dispose(bool disposing) - { - if (_disposed) return; - base.Dispose(); - - lock (_disposeLock) - { - if (_disposed) return; - - if (disposing) - { - Stop(); - } - - //release unmanaged resources here... - _disposed = true; - } - } - - public override void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - public void StartServer(IEnumerable urlPrefixes, string certificatePath) - { - CertificatePath = certificatePath; - UrlPrefixes = urlPrefixes.ToList(); - Start(UrlPrefixes.First()); - } - } - - public class StreamFactory : IStreamFactory - { - public Stream CreateNetworkStream(ISocket socket, bool ownsSocket) - { - var netSocket = (NetSocket)socket; - - return new NetworkStream(netSocket.Socket, ownsSocket); - } - - public Task AuthenticateSslStreamAsServer(Stream stream, ICertificate certificate) - { - var sslStream = (SslStream)stream; - var cert = (Certificate)certificate; - - return sslStream.AuthenticateAsServerAsync(cert.X509Certificate); - } - - public Stream CreateSslStream(Stream innerStream, bool leaveInnerStreamOpen) - { - return new SslStream(innerStream, leaveInnerStreamOpen); - } - } - - public class Certificate : ICertificate - { - public Certificate(X509Certificate x509Certificate) - { - X509Certificate = x509Certificate; - } - - public X509Certificate X509Certificate { get; private set; } - } -} \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs b/MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs deleted file mode 100644 index abcf84abd..000000000 --- a/MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs +++ /dev/null @@ -1,39 +0,0 @@ -using MediaBrowser.Common.Net; -using MediaBrowser.Controller; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Cryptography; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Net; -using MediaBrowser.Model.Serialization; -using MediaBrowser.Model.Text; - -namespace MediaBrowser.Server.Implementations.HttpServer -{ - /// - /// Class ServerFactory - /// - public static class ServerFactory - { - /// - /// Creates the server. - /// - /// IHttpServer. - public static IHttpServer CreateServer(IServerApplicationHost applicationHost, - ILogManager logManager, - IServerConfigurationManager config, - INetworkManager networkmanager, - IMemoryStreamFactory streamProvider, - string serverName, - string defaultRedirectpath, - ITextEncoding textEncoding, - ISocketFactory socketFactory, - ICryptoProvider cryptoProvider, - IJsonSerializer json, - IXmlSerializer xml) - { - return new HttpListenerHost(applicationHost, logManager, config, serverName, defaultRedirectpath, networkmanager, streamProvider, textEncoding, socketFactory, cryptoProvider, json, xml); - } - } -} diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 6fb4f9e36..8d5697c49 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -42,21 +42,10 @@ 4 - - ..\ThirdParty\emby\Emby.Common.Implementations.dll - - - ..\packages\Emby.XmlTv.1.0.0.63\lib\portable-net45+win8\Emby.XmlTv.dll - True - ..\packages\ini-parser.2.3.0\lib\net20\INIFileParser.dll True - - ..\packages\MediaBrowser.Naming.1.0.0.59\lib\portable-net45+win8\MediaBrowser.Naming.dll - True - ..\packages\Microsoft.IO.RecyclableMemoryStream.1.1.0.0\lib\net45\Microsoft.IO.RecyclableMemoryStream.dll True @@ -65,38 +54,18 @@ False ..\ThirdParty\emby\Mono.Nat.dll - - ..\packages\Patterns.Logging.1.0.0.6\lib\portable-net45+win8\Patterns.Logging.dll - True - - - ..\packages\ServiceStack.Text.4.5.4\lib\net45\ServiceStack.Text.dll - True - False ..\ThirdParty\SharpCompress\SharpCompress.dll - - ..\ThirdParty\emby\SocketHttpListener.Portable.dll - - - - - ..\ThirdParty\ServiceStack\ServiceStack.dll - - - ..\packages\UniversalDetector.1.0.1\lib\portable-net45+sl4+wp71+win8+wpa81\UniversalDetector.dll - True - @@ -105,12 +74,9 @@ - - - @@ -143,10 +109,8 @@ - - @@ -170,10 +134,6 @@ {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B} MediaBrowser.Model - - {442b5058-dcaf-4263-bb6a-f21e31120a1b} - MediaBrowser.Providers - @@ -348,9 +308,7 @@ - - - +