using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Common.Security; using MediaBrowser.Controller; using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Security; using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Serialization; namespace Emby.Security; public class PluginSecurityManager : IServerEntryPoint, IDisposable { private const string MBValidateUrl = "https://mb3admin.com/admin/service/registration/validate"; private readonly IHttpClient _httpClient; private readonly IJsonSerializer _jsonSerializer; private readonly IServerApplicationHost _appHost; private readonly ILogger _logger; private readonly IApplicationPaths _appPaths; private readonly IFileSystem _fileSystem; private readonly ICryptoProvider _cryptographyProvider; private readonly ISecurityManager _securityManager; private readonly IEncryptionManager _encryption; public static PluginSecurityManager Current; private readonly Dictionary _regRecordCache = new Dictionary(); private SemaphoreSlim _regLock = new SemaphoreSlim(1, 1); public PluginSecurityManager(IServerApplicationHost appHost, IHttpClient httpClient, IJsonSerializer jsonSerializer, IApplicationPaths appPaths, ILogManager logManager, IFileSystem fileSystem, ICryptoProvider cryptographyProvider, ISecurityManager securityManager, IEncryptionManager encryption) { if (httpClient == null) { throw new ArgumentNullException("httpClient"); } _appHost = appHost; _httpClient = httpClient; _jsonSerializer = jsonSerializer; _appPaths = appPaths; _fileSystem = fileSystem; _cryptographyProvider = cryptographyProvider; _securityManager = securityManager; _encryption = encryption; _logger = logManager.GetLogger("Plugin"); Current = this; } public void Run() { } public void Dispose() { } private string GetCacheFilePath(string feature) { string text = GetType().FullName + GetCacheKey(feature); text = BaseExtensions.GetMD5(text).ToString("N"); return Path.Combine(_appPaths.PluginConfigurationsPath, text); } private RegRecord GetCachedRegRecord(string feature) { lock (_regRecordCache) { if (_regRecordCache.TryGetValue(GetCacheKey(feature), out var value)) { return value; } } string cacheFilePath = GetCacheFilePath(feature); try { string text = _encryption.DecryptString(_fileSystem.ReadAllText(cacheFilePath)); RegRecord value = _jsonSerializer.DeserializeFromString(text); if (value != null) { return value; } } catch (Exception) { } return null; } private string GetCacheKey(string feature) { string text = _securityManager.SupporterKey; if (string.IsNullOrWhiteSpace(text)) { text = "none"; } return text + "-" + feature; } private void DeleteCachedRecord(string feature) { lock (_regRecordCache) { _regRecordCache.Remove(GetCacheKey(feature)); } try { _fileSystem.DeleteFile(GetCacheFilePath(feature)); } catch { } } private void SaveCachedRecord(string feature, RegRecord record) { lock (_regRecordCache) { _regRecordCache[GetCacheKey(feature)] = record; } string cacheFilePath = GetCacheFilePath(feature); string text = _jsonSerializer.SerializeToString((object)record); string text2 = _encryption.EncryptString(text); _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(cacheFilePath)); _fileSystem.WriteAllText(cacheFilePath, text2); } public async Task GetRegistrationStatus(string feature, CancellationToken cancellationToken) { await _regLock.WaitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false); try { return await GetRegistrationStatusInternal(feature, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } finally { _regLock.Release(); } } private bool CheckRegistrationWithServer(DateTime lastChecked, bool registered) { if (registered || string.IsNullOrWhiteSpace(_securityManager.SupporterKey)) { if (DateTime.UtcNow.AddDays(-1.0) > lastChecked) { return true; } } else if (DateTime.UtcNow.AddHours(-1.0) > lastChecked) { return true; } return false; } private async Task GetRegistrationStatusInternal(string feature, CancellationToken cancellationToken) { RegRecord regInfo = GetCachedRegRecord(feature); DateTime lastChecked = regInfo?.lastChecked ?? DateTime.MinValue; DateTime expDate = regInfo?.expDate ?? DateTime.MinValue; bool registered = regInfo?.registered ?? false; string supporterKey = _securityManager.SupporterKey; if (CheckRegistrationWithServer(lastChecked, registered)) { Dictionary postData = new Dictionary { { "feature", feature }, { "key", supporterKey }, { "mac", ((IApplicationHost)_appHost).SystemId }, { "systemid", ((IApplicationHost)_appHost).SystemId }, { "platform", ((IApplicationHost)_appHost).OperatingSystemDisplayName } }; try { HttpRequestOptions val = new HttpRequestOptions { Url = "https://mb3admin.com/admin/service/registration/validate", EnableHttpCompression = false, BufferContent = false, CancellationToken = cancellationToken }; val.SetPostData((IDictionary)postData); HttpResponseInfo val2 = await _httpClient.SendAsync(val, "POST").ConfigureAwait(continueOnCapturedContext: false); try { using Stream stream = val2.Content; regInfo = _jsonSerializer.DeserializeFromStream(stream); regInfo.lastChecked = DateTime.UtcNow; SaveCachedRecord(feature, regInfo); } finally { ((IDisposable)val2)?.Dispose(); } } catch (Exception ex) { _logger.ErrorException("Error checking registration status of {0}", ex, new object[1] { feature }); int num = 14; DateTime dateTime = new DateTime[2] { expDate, lastChecked.AddDays(num) }.Min(); if (dateTime > DateTime.UtcNow.AddDays(num)) { dateTime = DateTime.MinValue; } regInfo = new RegRecord { registered = (regInfo != null && regInfo.registered && dateTime >= DateTime.UtcNow && expDate >= DateTime.UtcNow), expDate = expDate }; } } return regInfo; } }