aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations/Updates/InstallationManager.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Server.Implementations/Updates/InstallationManager.cs')
-rw-r--r--Emby.Server.Implementations/Updates/InstallationManager.cs197
1 files changed, 91 insertions, 106 deletions
diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs
index 4f54c06dd..6ead603ae 100644
--- a/Emby.Server.Implementations/Updates/InstallationManager.cs
+++ b/Emby.Server.Implementations/Updates/InstallationManager.cs
@@ -10,17 +10,22 @@ using System.Runtime.Serialization;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Events;
using MediaBrowser.Common;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.Updates;
+using MediaBrowser.Common.System;
using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Events.Updates;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Updates;
using Microsoft.Extensions.Logging;
+using MediaBrowser.Model.System;
namespace Emby.Server.Implementations.Updates
{
@@ -34,7 +39,8 @@ namespace Emby.Server.Implementations.Updates
/// </summary>
private readonly ILogger<InstallationManager> _logger;
private readonly IApplicationPaths _appPaths;
- private readonly IHttpClient _httpClient;
+ private readonly IEventManager _eventManager;
+ private readonly IHttpClientFactory _httpClientFactory;
private readonly IJsonSerializer _jsonSerializer;
private readonly IServerConfigurationManager _config;
private readonly IFileSystem _fileSystem;
@@ -63,24 +69,21 @@ namespace Emby.Server.Implementations.Updates
ILogger<InstallationManager> logger,
IApplicationHost appHost,
IApplicationPaths appPaths,
- IHttpClient httpClient,
+ IEventManager eventManager,
+ IHttpClientFactory httpClientFactory,
IJsonSerializer jsonSerializer,
IServerConfigurationManager config,
IFileSystem fileSystem,
IZipClient zipClient)
{
- if (logger == null)
- {
- throw new ArgumentNullException(nameof(logger));
- }
-
_currentInstallations = new List<(InstallationInfo, CancellationTokenSource)>();
_completedInstallationsInternal = new ConcurrentBag<InstallationInfo>();
_logger = logger;
_applicationHost = appHost;
_appPaths = appPaths;
- _httpClient = httpClient;
+ _eventManager = eventManager;
+ _httpClientFactory = httpClientFactory;
_jsonSerializer = jsonSerializer;
_config = config;
_fileSystem = fileSystem;
@@ -88,27 +91,6 @@ namespace Emby.Server.Implementations.Updates
}
/// <inheritdoc />
- public event EventHandler<InstallationInfo> PackageInstalling;
-
- /// <inheritdoc />
- public event EventHandler<InstallationInfo> PackageInstallationCompleted;
-
- /// <inheritdoc />
- public event EventHandler<InstallationFailedEventArgs> PackageInstallationFailed;
-
- /// <inheritdoc />
- public event EventHandler<InstallationInfo> PackageInstallationCancelled;
-
- /// <inheritdoc />
- public event EventHandler<IPlugin> PluginUninstalled;
-
- /// <inheritdoc />
- public event EventHandler<InstallationInfo> PluginUpdated;
-
- /// <inheritdoc />
- public event EventHandler<InstallationInfo> PluginInstalled;
-
- /// <inheritdoc />
public IEnumerable<InstallationInfo> CompletedInstallations => _completedInstallationsInternal;
/// <inheritdoc />
@@ -116,26 +98,18 @@ namespace Emby.Server.Implementations.Updates
{
try
{
- using (var response = await _httpClient.SendAsync(
- new HttpRequestOptions
- {
- Url = manifest,
- CancellationToken = cancellationToken,
- CacheMode = CacheMode.Unconditional,
- CacheLength = TimeSpan.FromMinutes(3)
- },
- HttpMethod.Get).ConfigureAwait(false))
- using (Stream stream = response.Content)
+ using var response = await _httpClientFactory.CreateClient(NamedClient.Default)
+ .GetAsync(manifest, cancellationToken).ConfigureAwait(false);
+ await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
+
+ try
{
- try
- {
- return await _jsonSerializer.DeserializeFromStreamAsync<IReadOnlyList<PackageInfo>>(stream).ConfigureAwait(false);
- }
- catch (SerializationException ex)
- {
- _logger.LogError(ex, "Failed to deserialize the plugin manifest retrieved from {Manifest}", manifest);
- return Array.Empty<PackageInfo>();
- }
+ return await _jsonSerializer.DeserializeFromStreamAsync<IReadOnlyList<PackageInfo>>(stream).ConfigureAwait(false);
+ }
+ catch (SerializationException ex)
+ {
+ _logger.LogError(ex, "Failed to deserialize the plugin manifest retrieved from {Manifest}", manifest);
+ return Array.Empty<PackageInfo>();
}
}
catch (UriFormatException ex)
@@ -161,7 +135,12 @@ namespace Emby.Server.Implementations.Updates
var result = new List<PackageInfo>();
foreach (RepositoryInfo repository in _config.Configuration.PluginRepositories)
{
- result.AddRange(await GetPackages(repository.Url, cancellationToken).ConfigureAwait(true));
+ foreach (var package in await GetPackages(repository.Url, cancellationToken).ConfigureAwait(true))
+ {
+ package.repositoryName = repository.Name;
+ package.repositoryUrl = repository.Url;
+ result.Add(package);
+ }
}
return result;
@@ -191,7 +170,8 @@ namespace Emby.Server.Implementations.Updates
IEnumerable<PackageInfo> availablePackages,
string name = null,
Guid guid = default,
- Version minVersion = null)
+ Version minVersion = null,
+ Version specificVersion = null)
{
var package = FilterPackages(availablePackages, name, guid).FirstOrDefault();
@@ -205,7 +185,11 @@ namespace Emby.Server.Implementations.Updates
var availableVersions = package.versions
.Where(x => Version.Parse(x.targetAbi) <= appVer);
- if (minVersion != null)
+ if (specificVersion != null)
+ {
+ availableVersions = availableVersions.Where(x => new Version(x.version) == specificVersion);
+ }
+ else if (minVersion != null)
{
availableVersions = availableVersions.Where(x => new Version(x.version) >= minVersion);
}
@@ -235,8 +219,8 @@ namespace Emby.Server.Implementations.Updates
{
foreach (var plugin in _applicationHost.Plugins)
{
- var compatibleversions = GetCompatibleVersions(pluginCatalog, plugin.Name, plugin.Id, plugin.Version);
- var version = compatibleversions.FirstOrDefault(y => y.Version > plugin.Version);
+ var compatibleVersions = GetCompatibleVersions(pluginCatalog, plugin.Name, plugin.Id, minVersion: plugin.Version);
+ var version = compatibleVersions.FirstOrDefault(y => y.Version > plugin.Version);
if (version != null && CompletedInstallations.All(x => x.Guid != version.Guid))
{
yield return version;
@@ -264,11 +248,11 @@ namespace Emby.Server.Implementations.Updates
var linkedToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, innerCancellationTokenSource.Token).Token;
- PackageInstalling?.Invoke(this, package);
+ await _eventManager.PublishAsync(new PluginInstallingEventArgs(package)).ConfigureAwait(false);
try
{
- await InstallPackageInternal(package, linkedToken).ConfigureAwait(false);
+ var isUpdate = await InstallPackageInternal(package, linkedToken).ConfigureAwait(false);
lock (_currentInstallationsLock)
{
@@ -276,8 +260,11 @@ namespace Emby.Server.Implementations.Updates
}
_completedInstallationsInternal.Add(package);
+ await _eventManager.PublishAsync(isUpdate
+ ? (GenericEventArgs<InstallationInfo>)new PluginUpdatedEventArgs(package)
+ : new PluginInstalledEventArgs(package)).ConfigureAwait(false);
- PackageInstallationCompleted?.Invoke(this, package);
+ _applicationHost.NotifyPendingRestart();
}
catch (OperationCanceledException)
{
@@ -288,7 +275,7 @@ namespace Emby.Server.Implementations.Updates
_logger.LogInformation("Package installation cancelled: {0} {1}", package.Name, package.Version);
- PackageInstallationCancelled?.Invoke(this, package);
+ await _eventManager.PublishAsync(new PluginInstallationCancelledEventArgs(package)).ConfigureAwait(false);
throw;
}
@@ -301,11 +288,11 @@ namespace Emby.Server.Implementations.Updates
_currentInstallations.Remove(tuple);
}
- PackageInstallationFailed?.Invoke(this, new InstallationFailedEventArgs
+ await _eventManager.PublishAsync(new InstallationFailedEventArgs
{
InstallationInfo = package,
Exception = ex
- });
+ }).ConfigureAwait(false);
throw;
}
@@ -322,7 +309,7 @@ namespace Emby.Server.Implementations.Updates
/// <param name="package">The package.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns><see cref="Task" />.</returns>
- private async Task InstallPackageInternal(InstallationInfo package, CancellationToken cancellationToken)
+ private async Task<bool> InstallPackageInternal(InstallationInfo package, CancellationToken cancellationToken)
{
// Set last update time if we were installed before
IPlugin plugin = _applicationHost.Plugins.FirstOrDefault(p => p.Id == package.Guid)
@@ -332,20 +319,9 @@ namespace Emby.Server.Implementations.Updates
await PerformPackageInstallation(package, cancellationToken).ConfigureAwait(false);
// Do plugin-specific processing
- if (plugin == null)
- {
- _logger.LogInformation("New plugin installed: {0} {1}", package.Name, package.Version);
-
- PluginInstalled?.Invoke(this, package);
- }
- else
- {
- _logger.LogInformation("Plugin updated: {0} {1}", package.Name, package.Version);
+ _logger.LogInformation(plugin == null ? "New plugin installed: {0} {1}" : "Plugin updated: {0} {1}", package.Name, package.Version);
- PluginUpdated?.Invoke(this, package);
- }
-
- _applicationHost.NotifyPendingRestart();
+ return plugin != null;
}
private async Task PerformPackageInstallation(InstallationInfo package, CancellationToken cancellationToken)
@@ -360,42 +336,44 @@ namespace Emby.Server.Implementations.Updates
// Always override the passed-in target (which is a file) and figure it out again
string targetDir = Path.Combine(_appPaths.PluginsPath, package.Name);
+ using var response = await _httpClientFactory.CreateClient(NamedClient.Default)
+ .GetAsync(package.SourceUrl, cancellationToken).ConfigureAwait(false);
+ await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
+
// CA5351: Do Not Use Broken Cryptographic Algorithms
#pragma warning disable CA5351
- using (var res = await _httpClient.SendAsync(
- new HttpRequestOptions
- {
- Url = package.SourceUrl,
- CancellationToken = cancellationToken,
- // We need it to be buffered for setting the position
- BufferContent = true
- },
- HttpMethod.Get).ConfigureAwait(false))
- using (var stream = res.Content)
- using (var md5 = MD5.Create())
+ using var md5 = MD5.Create();
+ cancellationToken.ThrowIfCancellationRequested();
+
+ var hash = Hex.Encode(md5.ComputeHash(stream));
+ if (!string.Equals(package.Checksum, hash, StringComparison.OrdinalIgnoreCase))
{
- cancellationToken.ThrowIfCancellationRequested();
+ _logger.LogError(
+ "The checksums didn't match while installing {Package}, expected: {Expected}, got: {Received}",
+ package.Name,
+ package.Checksum,
+ hash);
+ throw new InvalidDataException("The checksum of the received data doesn't match.");
+ }
- var hash = Hex.Encode(md5.ComputeHash(stream));
- if (!string.Equals(package.Checksum, hash, StringComparison.OrdinalIgnoreCase))
- {
- _logger.LogError(
- "The checksums didn't match while installing {Package}, expected: {Expected}, got: {Received}",
- package.Name,
- package.Checksum,
- hash);
- throw new InvalidDataException("The checksum of the received data doesn't match.");
- }
+ // Version folder as they cannot be overwritten in Windows.
+ targetDir += "_" + package.Version;
- if (Directory.Exists(targetDir))
+ if (Directory.Exists(targetDir))
+ {
+ try
{
Directory.Delete(targetDir, true);
}
-
- stream.Position = 0;
- _zipClient.ExtractAllFromZip(stream, targetDir, true);
+ catch
+ {
+ // Ignore any exceptions.
+ }
}
+ stream.Position = 0;
+ _zipClient.ExtractAllFromZip(stream, targetDir, true);
+
#pragma warning restore CA5351
}
@@ -434,15 +412,22 @@ namespace Emby.Server.Implementations.Updates
path = file;
}
- if (isDirectory)
+ try
{
- _logger.LogInformation("Deleting plugin directory {0}", path);
- Directory.Delete(path, true);
+ if (isDirectory)
+ {
+ _logger.LogInformation("Deleting plugin directory {0}", path);
+ Directory.Delete(path, true);
+ }
+ else
+ {
+ _logger.LogInformation("Deleting plugin file {0}", path);
+ _fileSystem.DeleteFile(path);
+ }
}
- else
+ catch
{
- _logger.LogInformation("Deleting plugin file {0}", path);
- _fileSystem.DeleteFile(path);
+ // Ignore file errors.
}
var list = _config.Configuration.UninstalledPlugins.ToList();
@@ -454,7 +439,7 @@ namespace Emby.Server.Implementations.Updates
_config.SaveConfiguration();
}
- PluginUninstalled?.Invoke(this, plugin);
+ _eventManager.Publish(new PluginUninstalledEventArgs(plugin));
_applicationHost.NotifyPendingRestart();
}