using MediaBrowser.Common; using MediaBrowser.Common.Net; using MediaBrowser.Common.Updates; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Plugins; using MediaBrowser.Model.Serialization; using System; using System.IO; using System.Linq; using System.Threading.Tasks; using MediaBrowser.Model.Services; using MediaBrowser.Common.Plugins; using Microsoft.Extensions.Logging; namespace MediaBrowser.Api { /// /// Class Plugins /// [Route("/Plugins", "GET", Summary = "Gets a list of currently installed plugins")] [Authenticated] public class GetPlugins : IReturn { public bool? IsAppStoreEnabled { get; set; } } /// /// Class UninstallPlugin /// [Route("/Plugins/{Id}", "DELETE", Summary = "Uninstalls a plugin")] [Authenticated(Roles = "Admin")] public class UninstallPlugin : IReturnVoid { /// /// Gets or sets the id. /// /// The id. [ApiMember(Name = "Id", Description = "Plugin Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] public string Id { get; set; } } /// /// Class GetPluginConfiguration /// [Route("/Plugins/{Id}/Configuration", "GET", Summary = "Gets a plugin's configuration")] [Authenticated] public class GetPluginConfiguration { /// /// Gets or sets the id. /// /// The id. [ApiMember(Name = "Id", Description = "Plugin Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] public string Id { get; set; } } /// /// Class UpdatePluginConfiguration /// [Route("/Plugins/{Id}/Configuration", "POST", Summary = "Updates a plugin's configuration")] [Authenticated] public class UpdatePluginConfiguration : IRequiresRequestStream, IReturnVoid { /// /// Gets or sets the id. /// /// The id. [ApiMember(Name = "Id", Description = "Plugin Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] public string Id { get; set; } /// /// The raw Http Request Input Stream /// /// The request stream. public Stream RequestStream { get; set; } } //TODO Once we have proper apps and plugins and decide to break compatibility with paid plugins, // delete all these registration endpoints. They are only kept for compatibility. [Route("/Registrations/{Name}", "GET", Summary = "Gets registration status for a feature", IsHidden = true)] [Authenticated] public class GetRegistration : IReturn { [ApiMember(Name = "Name", Description = "Feature Name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] public string Name { get; set; } } /// /// Class GetPluginSecurityInfo /// [Route("/Plugins/SecurityInfo", "GET", Summary = "Gets plugin registration information", IsHidden = true)] [Authenticated] public class GetPluginSecurityInfo : IReturn { } /// /// Class UpdatePluginSecurityInfo /// [Route("/Plugins/SecurityInfo", "POST", Summary = "Updates plugin registration information", IsHidden = true)] [Authenticated(Roles = "Admin")] public class UpdatePluginSecurityInfo : PluginSecurityInfo, IReturnVoid { } [Route("/Plugins/RegistrationRecords/{Name}", "GET", Summary = "Gets registration status for a feature", IsHidden = true)] [Authenticated] public class GetRegistrationStatus { [ApiMember(Name = "Name", Description = "Feature Name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] public string Name { get; set; } } // TODO these two classes are only kept for compability with paid plugins and should be removed public class RegistrationInfo { public string Name { get; set; } public DateTime ExpirationDate { get; set; } public bool IsTrial { get; set; } public bool IsRegistered { get; set; } } public class MBRegistrationRecord { public DateTime ExpirationDate { get; set; } public bool IsRegistered { get; set; } public bool RegChecked { get; set; } public bool RegError { get; set; } public bool TrialVersion { get; set; } public bool IsValid { get; set; } } public class PluginSecurityInfo { public string SupporterKey { get; set; } public bool IsMBSupporter { get; set; } } /// /// Class PluginsService /// public class PluginService : BaseApiService { /// /// The _json serializer /// private readonly IJsonSerializer _jsonSerializer; /// /// The _app host /// private readonly IApplicationHost _appHost; private readonly IInstallationManager _installationManager; private readonly INetworkManager _network; private readonly IDeviceManager _deviceManager; public PluginService(IJsonSerializer jsonSerializer, IApplicationHost appHost, IInstallationManager installationManager, INetworkManager network, IDeviceManager deviceManager) : base() { if (jsonSerializer == null) { throw new ArgumentNullException(nameof(jsonSerializer)); } _appHost = appHost; _installationManager = installationManager; _network = network; _deviceManager = deviceManager; _jsonSerializer = jsonSerializer; } /// /// Gets the specified request. /// /// The request. /// System.Object. public async Task Get(GetRegistrationStatus request) { var record = new MBRegistrationRecord { IsRegistered = true, RegChecked = true, TrialVersion = false, IsValid = true, RegError = false }; return ToOptimizedResult(record); } //TODO this function is only kept for compatibility and should be removed once paid plugins break public async Task Get(GetRegistration request) { var info = new RegistrationInfo { ExpirationDate = DateTime.Now.AddYears(100), IsRegistered = true, IsTrial = false, Name = request.Name }; return ToOptimizedResult(info); } /// /// Gets the specified request. /// /// The request. /// System.Object. public async Task Get(GetPlugins request) { var result = _appHost.Plugins.OrderBy(p => p.Name).Select(p => p.GetPluginInfo()).ToArray(); return ToOptimizedResult(result); } /// /// Gets the specified request. /// /// The request. /// System.Object. public object Get(GetPluginConfiguration request) { var guid = new Guid(request.Id); var plugin = _appHost.Plugins.First(p => p.Id == guid) as IHasPluginConfiguration; return ToOptimizedResult(plugin.Configuration); } /// /// Gets the specified request. /// /// The request. /// System.Object. public async Task Get(GetPluginSecurityInfo request) { var result = new PluginSecurityInfo { IsMBSupporter = true, SupporterKey = "IAmTotallyLegit" }; return ToOptimizedResult(result); } /// /// Posts the specified request. /// /// The request. public Task Post(UpdatePluginSecurityInfo request) { return Task.CompletedTask; } /// /// Posts the specified request. /// /// The request. public async Task Post(UpdatePluginConfiguration request) { // We need to parse this manually because we told service stack not to with IRequiresRequestStream // https://code.google.com/p/servicestack/source/browse/trunk/Common/ServiceStack.Text/ServiceStack.Text/Controller/PathInfo.cs var id = new Guid(GetPathValue(1)); var plugin = _appHost.Plugins.First(p => p.Id == id) as IHasPluginConfiguration; if (plugin == null) { throw new FileNotFoundException(); } var configuration = (await _jsonSerializer.DeserializeFromStreamAsync(request.RequestStream, plugin.ConfigurationType).ConfigureAwait(false)) as BasePluginConfiguration; plugin.UpdateConfiguration(configuration); } /// /// Deletes the specified request. /// /// The request. public void Delete(UninstallPlugin request) { var guid = new Guid(request.Id); var plugin = _appHost.Plugins.First(p => p.Id == guid); _installationManager.UninstallPlugin(plugin); } } }