Răsfoiți Sursa

Check checksum for plugin downloads

* Compare the MD5 checksum when downloading plugins
* Reduced log spam due to http requests
* Removed 'GetTempFileResponse' function from HttpClientManager
* Fixed caching for HttpClientManager
Bond_009 5 ani în urmă
părinte
comite
5eaf5465a5

+ 0 - 10
Emby.Dlna/PlayTo/SsdpHttpClient.cs

@@ -73,9 +73,6 @@ namespace Emby.Dlna.PlayTo
                 UserAgent = USERAGENT,
                 LogErrorResponseBody = true,
                 BufferContent = false,
-
-                // The periodic requests may keep some devices awake
-                LogRequestAsDebug = true
             };
 
             options.RequestHeaders["HOST"] = ip + ":" + port.ToString(_usCulture);
@@ -98,9 +95,6 @@ namespace Emby.Dlna.PlayTo
                 LogErrorResponseBody = true,
                 BufferContent = false,
 
-                // The periodic requests may keep some devices awake
-                LogRequestAsDebug = true,
-
                 CancellationToken = cancellationToken
             };
 
@@ -135,13 +129,9 @@ namespace Emby.Dlna.PlayTo
             {
                 Url = url,
                 UserAgent = USERAGENT,
-                LogRequest = logRequest || _config.GetDlnaConfiguration().EnableDebugLog,
                 LogErrorResponseBody = true,
                 BufferContent = false,
 
-                // The periodic requests may keep some devices awake
-                LogRequestAsDebug = true,
-
                 CancellationToken = cancellationToken
             };
 

+ 2 - 12
Emby.Server.Implementations/ApplicationHost.cs

@@ -1528,8 +1528,6 @@ namespace Emby.Server.Implementations
                 {
                     Url = Url,
                     LogErrorResponseBody = false,
-                    LogErrors = false,
-                    LogRequest = false,
                     BufferContent = false,
                     CancellationToken = cancellationToken
                 }).ConfigureAwait(false))
@@ -1681,8 +1679,8 @@ namespace Emby.Server.Implementations
 
         private async Task<bool> IsIpAddressValidAsync(IPAddress address, CancellationToken cancellationToken)
         {
-            if (address.Equals(IPAddress.Loopback) ||
-                address.Equals(IPAddress.IPv6Loopback))
+            if (address.Equals(IPAddress.Loopback)
+                || address.Equals(IPAddress.IPv6Loopback))
             {
                 return true;
             }
@@ -1695,12 +1693,6 @@ namespace Emby.Server.Implementations
                 return cachedResult;
             }
 
-#if DEBUG
-            const bool LogPing = true;
-#else
-            const bool LogPing = false;
-#endif
-
             try
             {
                 using (var response = await HttpClient.SendAsync(
@@ -1708,8 +1700,6 @@ namespace Emby.Server.Implementations
                     {
                         Url = apiUrl,
                         LogErrorResponseBody = false,
-                        LogErrors = LogPing,
-                        LogRequest = LogPing,
                         BufferContent = false,
                         CancellationToken = cancellationToken
                     }, HttpMethod.Post).ConfigureAwait(false))

+ 18 - 188
Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs

@@ -5,9 +5,6 @@ using System.IO;
 using System.Linq;
 using System.Net;
 using System.Net.Http;
-using System.Net.Http.Headers;
-using System.Text;
-using System.Threading;
 using System.Threading.Tasks;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Extensions;
@@ -20,7 +17,7 @@ using Microsoft.Net.Http.Headers;
 namespace Emby.Server.Implementations.HttpClientManager
 {
     /// <summary>
-    /// Class HttpClientManager
+    /// Class HttpClientManager.
     /// </summary>
     public class HttpClientManager : IHttpClient
     {
@@ -45,19 +42,9 @@ namespace Emby.Server.Implementations.HttpClientManager
             IFileSystem fileSystem,
             Func<string> defaultUserAgentFn)
         {
-            if (appPaths == null)
-            {
-                throw new ArgumentNullException(nameof(appPaths));
-            }
-
-            if (logger == null)
-            {
-                throw new ArgumentNullException(nameof(logger));
-            }
-
-            _logger = logger;
+            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
             _fileSystem = fileSystem;
-            _appPaths = appPaths;
+            _appPaths = appPaths ?? throw new ArgumentNullException(nameof(appPaths));
             _defaultUserAgentFn = defaultUserAgentFn;
         }
 
@@ -118,7 +105,7 @@ namespace Emby.Server.Implementations.HttpClientManager
                 request.Headers.Add(HeaderNames.Connection, "Keep-Alive");
             }
 
-            //request.Headers.Add(HeaderNames.CacheControl, "no-cache");
+            // request.Headers.Add(HeaderNames.CacheControl, "no-cache");
 
             /*
             if (!string.IsNullOrWhiteSpace(userInfo))
@@ -196,7 +183,7 @@ namespace Emby.Server.Implementations.HttpClientManager
             }
 
             var url = options.Url;
-            var urlHash = url.ToLowerInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture);
+            var urlHash = url.ToUpperInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture);
 
             var responseCachePath = Path.Combine(_appPaths.CachePath, "httpclient", urlHash);
 
@@ -239,7 +226,13 @@ namespace Emby.Server.Implementations.HttpClientManager
         {
             Directory.CreateDirectory(Path.GetDirectoryName(responseCachePath));
 
-            using (var fileStream = _fileSystem.GetFileStream(responseCachePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.None, true))
+            using (var fileStream = new FileStream(
+                responseCachePath,
+                FileMode.Create,
+                FileAccess.Write,
+                FileShare.None,
+                StreamDefaults.DefaultFileStreamBufferSize,
+                true))
             {
                 await response.Content.CopyToAsync(fileStream).ConfigureAwait(false);
 
@@ -278,16 +271,11 @@ namespace Emby.Server.Implementations.HttpClientManager
                 }
             }
 
-            if (options.LogRequest)
-            {
-                _logger.LogDebug("HttpClientManager {0}: {1}", httpMethod.ToString(), options.Url);
-            }
-
             options.CancellationToken.ThrowIfCancellationRequested();
 
             var response = await client.SendAsync(
                 httpWebRequest,
-                options.BufferContent ? HttpCompletionOption.ResponseContentRead : HttpCompletionOption.ResponseHeadersRead,
+                options.BufferContent || options.CacheMode == CacheMode.Unconditional ? HttpCompletionOption.ResponseContentRead : HttpCompletionOption.ResponseHeadersRead,
                 options.CancellationToken).ConfigureAwait(false);
 
             await EnsureSuccessStatusCode(response, options).ConfigureAwait(false);
@@ -308,138 +296,6 @@ namespace Emby.Server.Implementations.HttpClientManager
         public Task<HttpResponseInfo> Post(HttpRequestOptions options)
             => SendAsync(options, HttpMethod.Post);
 
-        /// <summary>
-        /// Downloads the contents of a given url into a temporary location
-        /// </summary>
-        /// <param name="options">The options.</param>
-        /// <returns>Task{System.String}.</returns>
-        public async Task<string> GetTempFile(HttpRequestOptions options)
-        {
-            var response = await GetTempFileResponse(options).ConfigureAwait(false);
-            return response.TempFilePath;
-        }
-
-        public async Task<HttpResponseInfo> GetTempFileResponse(HttpRequestOptions options)
-        {
-            ValidateParams(options);
-
-            Directory.CreateDirectory(_appPaths.TempDirectory);
-
-            var tempFile = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid() + ".tmp");
-
-            if (options.Progress == null)
-            {
-                throw new ArgumentException("Options did not have a Progress value.", nameof(options));
-            }
-
-            options.CancellationToken.ThrowIfCancellationRequested();
-
-            var httpWebRequest = GetRequestMessage(options, HttpMethod.Get);
-
-            options.Progress.Report(0);
-
-            if (options.LogRequest)
-            {
-                _logger.LogDebug("HttpClientManager.GetTempFileResponse url: {0}", options.Url);
-            }
-
-            var client = GetHttpClient(options.Url);
-
-            try
-            {
-                options.CancellationToken.ThrowIfCancellationRequested();
-
-                using (var response = (await client.SendAsync(httpWebRequest, options.CancellationToken).ConfigureAwait(false)))
-                {
-                    await EnsureSuccessStatusCode(response, options).ConfigureAwait(false);
-
-                    options.CancellationToken.ThrowIfCancellationRequested();
-
-                    using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
-                    using (var fs = _fileSystem.GetFileStream(tempFile, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
-                    {
-                        await stream.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false);
-                    }
-
-                    options.Progress.Report(100);
-
-                    var responseInfo = new HttpResponseInfo(response.Headers, response.Content.Headers)
-                    {
-                        TempFilePath = tempFile,
-                        StatusCode = response.StatusCode,
-                        ContentType = response.Content.Headers.ContentType?.MediaType,
-                        ContentLength = response.Content.Headers.ContentLength
-                    };
-
-                    return responseInfo;
-                }
-            }
-            catch (Exception ex)
-            {
-                if (File.Exists(tempFile))
-                {
-                    File.Delete(tempFile);
-                }
-
-                throw GetException(ex, options);
-            }
-        }
-
-        private Exception GetException(Exception ex, HttpRequestOptions options)
-        {
-            if (ex is HttpException)
-            {
-                return ex;
-            }
-
-            var webException = ex as WebException
-                               ?? ex.InnerException as WebException;
-
-            if (webException != null)
-            {
-                if (options.LogErrors)
-                {
-                    _logger.LogError(webException, "Error {Status} getting response from {Url}", webException.Status, options.Url);
-                }
-
-                var exception = new HttpException(webException.Message, webException);
-
-                using (var response = webException.Response as HttpWebResponse)
-                {
-                    if (response != null)
-                    {
-                        exception.StatusCode = response.StatusCode;
-                    }
-                }
-
-                if (!exception.StatusCode.HasValue)
-                {
-                    if (webException.Status == WebExceptionStatus.NameResolutionFailure ||
-                        webException.Status == WebExceptionStatus.ConnectFailure)
-                    {
-                        exception.IsTimedOut = true;
-                    }
-                }
-
-                return exception;
-            }
-
-            var operationCanceledException = ex as OperationCanceledException
-                                             ?? ex.InnerException as OperationCanceledException;
-
-            if (operationCanceledException != null)
-            {
-                return GetCancellationException(options, options.CancellationToken, operationCanceledException);
-            }
-
-            if (options.LogErrors)
-            {
-                _logger.LogError(ex, "Error getting response from {Url}", options.Url);
-            }
-
-            return ex;
-        }
-
         private void ValidateParams(HttpRequestOptions options)
         {
             if (string.IsNullOrEmpty(options.Url))
@@ -471,35 +327,6 @@ namespace Emby.Server.Implementations.HttpClientManager
             return url;
         }
 
-        /// <summary>
-        /// Throws the cancellation exception.
-        /// </summary>
-        /// <param name="options">The options.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <param name="exception">The exception.</param>
-        /// <returns>Exception.</returns>
-        private Exception GetCancellationException(HttpRequestOptions options, CancellationToken cancellationToken, OperationCanceledException exception)
-        {
-            // If the HttpClient's timeout is reached, it will cancel the Task internally
-            if (!cancellationToken.IsCancellationRequested)
-            {
-                var msg = string.Format("Connection to {0} timed out", options.Url);
-
-                if (options.LogErrors)
-                {
-                    _logger.LogError(msg);
-                }
-
-                // Throw an HttpException so that the caller doesn't think it was cancelled by user code
-                return new HttpException(msg, exception)
-                {
-                    IsTimedOut = true
-                };
-            }
-
-            return exception;
-        }
-
         private async Task EnsureSuccessStatusCode(HttpResponseMessage response, HttpRequestOptions options)
         {
             if (response.IsSuccessStatusCode)
@@ -507,8 +334,11 @@ namespace Emby.Server.Implementations.HttpClientManager
                 return;
             }
 
-            var msg = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
-            _logger.LogError("HTTP request failed with message: {Message}", msg);
+            if (options.LogErrorResponseBody)
+            {
+                var msg = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
+                _logger.LogError("HTTP request failed with message: {Message}", msg);
+            }
 
             throw new HttpException(response.ReasonPhrase)
             {

+ 2 - 3
Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs

@@ -65,7 +65,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
 
                 try
                 {
-                    await _installationManager.InstallPackage(package, new SimpleProgress<double>(), cancellationToken).ConfigureAwait(false);
+                    await _installationManager.InstallPackage(package, cancellationToken).ConfigureAwait(false);
                 }
                 catch (OperationCanceledException)
                 {
@@ -87,8 +87,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
                 // Update progress
                 lock (progress)
                 {
-                    numComplete++;
-                    progress.Report(90.0 * numComplete / packagesToInstall.Count + 10);
+                    progress.Report((90.0 * ++numComplete / packagesToInstall.Count) + 10);
                 }
             }
 

+ 49 - 91
Emby.Server.Implementations/Updates/InstallationManager.cs

@@ -4,13 +4,14 @@ using System.Collections.Generic;
 using System.IO;
 using System.Linq;
 using System.Net.Http;
+using System.Security.Cryptography;
 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.Plugins;
-using MediaBrowser.Common.Progress;
 using MediaBrowser.Common.Updates;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Model.Events;
@@ -126,13 +127,16 @@ namespace Emby.Server.Implementations.Updates
         /// <returns>Task{List{PackageInfo}}.</returns>
         public async Task<List<PackageInfo>> GetAvailablePackagesWithoutRegistrationInfo(CancellationToken cancellationToken)
         {
-            using (var response = await _httpClient.SendAsync(new HttpRequestOptions
-            {
-                Url = "https://repo.jellyfin.org/releases/plugin/manifest.json",
-                CancellationToken = cancellationToken,
-                CacheLength = GetCacheLength()
-            }, HttpMethod.Get).ConfigureAwait(false))
-            using (var stream = response.Content)
+            using (var response = await _httpClient.SendAsync(
+                new HttpRequestOptions
+                {
+                    Url = "https://repo.jellyfin.org/releases/plugin/manifest.json",
+                    CancellationToken = cancellationToken,
+                    CacheMode = CacheMode.Unconditional,
+                    CacheLength = GetCacheLength()
+                },
+                HttpMethod.Get).ConfigureAwait(false))
+            using (Stream stream = response.Content)
             {
                 return FilterPackages(await _jsonSerializer.DeserializeFromStreamAsync<PackageInfo[]>(stream).ConfigureAwait(false));
             }
@@ -309,27 +313,14 @@ namespace Emby.Server.Implementations.Updates
             .Where(p => !string.IsNullOrEmpty(p.sourceUrl) && !CompletedInstallations.Any(i => string.Equals(i.AssemblyGuid, p.guid, StringComparison.OrdinalIgnoreCase)));
         }
 
-        /// <summary>
-        /// Installs the package.
-        /// </summary>
-        /// <param name="package">The package.</param>
-        /// <param name="isPlugin">if set to <c>true</c> [is plugin].</param>
-        /// <param name="progress">The progress.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task.</returns>
-        /// <exception cref="ArgumentNullException">package</exception>
-        public async Task InstallPackage(PackageVersionInfo package, IProgress<double> progress, CancellationToken cancellationToken)
+        /// <inheritdoc />
+        public async Task InstallPackage(PackageVersionInfo package, CancellationToken cancellationToken)
         {
             if (package == null)
             {
                 throw new ArgumentNullException(nameof(package));
             }
 
-            if (progress == null)
-            {
-                throw new ArgumentNullException(nameof(progress));
-            }
-
             var installationInfo = new InstallationInfo
             {
                 Id = Guid.NewGuid(),
@@ -349,16 +340,6 @@ namespace Emby.Server.Implementations.Updates
                 _currentInstallations.Add(tuple);
             }
 
-            var innerProgress = new ActionableProgress<double>();
-
-            // Whenever the progress updates, update the outer progress object and InstallationInfo
-            innerProgress.RegisterAction(percent =>
-            {
-                progress.Report(percent);
-
-                installationInfo.PercentComplete = percent;
-            });
-
             var linkedToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, innerCancellationTokenSource.Token).Token;
 
             var installationEventArgs = new InstallationEventArgs
@@ -371,7 +352,7 @@ namespace Emby.Server.Implementations.Updates
 
             try
             {
-                await InstallPackageInternal(package, innerProgress, linkedToken).ConfigureAwait(false);
+                await InstallPackageInternal(package, linkedToken).ConfigureAwait(false);
 
                 lock (_currentInstallations)
                 {
@@ -423,20 +404,16 @@ namespace Emby.Server.Implementations.Updates
         /// Installs the package internal.
         /// </summary>
         /// <param name="package">The package.</param>
-        /// <param name="isPlugin">if set to <c>true</c> [is plugin].</param>
-        /// <param name="progress">The progress.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task.</returns>
-        private async Task InstallPackageInternal(PackageVersionInfo package, IProgress<double> progress, CancellationToken cancellationToken)
+        /// <returns><see cref="Task" />.</returns>
+        private async Task InstallPackageInternal(PackageVersionInfo package, CancellationToken cancellationToken)
         {
             // Set last update time if we were installed before
             IPlugin plugin = _applicationHost.Plugins.FirstOrDefault(p => string.Equals(p.Id.ToString(), package.guid, StringComparison.OrdinalIgnoreCase))
                            ?? _applicationHost.Plugins.FirstOrDefault(p => p.Name.Equals(package.name, StringComparison.OrdinalIgnoreCase));
 
-            string targetPath = plugin == null ? null : plugin.AssemblyFilePath;
-
             // Do the install
-            await PerformPackageInstallation(progress, targetPath, package, cancellationToken).ConfigureAwait(false);
+            await PerformPackageInstallation(package, cancellationToken).ConfigureAwait(false);
 
             // Do plugin-specific processing
             if (plugin == null)
@@ -455,76 +432,57 @@ namespace Emby.Server.Implementations.Updates
             _applicationHost.NotifyPendingRestart();
         }
 
-        private async Task PerformPackageInstallation(IProgress<double> progress, string target, PackageVersionInfo package, CancellationToken cancellationToken)
+        private async Task PerformPackageInstallation(PackageVersionInfo package, CancellationToken cancellationToken)
         {
-            // TODO: Remove the `string target` argument as it is not used any longer
-
             var extension = Path.GetExtension(package.targetFilename);
-            var isArchive = string.Equals(extension, ".zip", StringComparison.OrdinalIgnoreCase);
-
-            if (!isArchive)
+            if (!string.Equals(extension, ".zip", StringComparison.OrdinalIgnoreCase))
             {
                 _logger.LogError("Only zip packages are supported. {Filename} is not a zip archive.", package.targetFilename);
                 return;
             }
 
             // Always override the passed-in target (which is a file) and figure it out again
-            target = Path.Combine(_appPaths.PluginsPath, package.name);
-            _logger.LogDebug("Installing plugin to {Filename}.", target);
-
-            // Download to temporary file so that, if interrupted, it won't destroy the existing installation
-            _logger.LogDebug("Downloading ZIP.");
-            var tempFile = await _httpClient.GetTempFile(new HttpRequestOptions
-            {
-                Url = package.sourceUrl,
-                CancellationToken = cancellationToken,
-                Progress = progress
-
-            }).ConfigureAwait(false);
+            string targetDir = Path.Combine(_appPaths.PluginsPath, package.name);
 
-            cancellationToken.ThrowIfCancellationRequested();
-
-            // TODO: Validate with a checksum, *properly*
-
-            // Check if the target directory already exists, and remove it if so
-            if (Directory.Exists(target))
-            {
-                _logger.LogDebug("Deleting existing plugin at {Filename}.", target);
-                Directory.Delete(target, true);
-            }
+// 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())
+            {
+                cancellationToken.ThrowIfCancellationRequested();
+
+                var hash = HexHelper.ToHexString(md5.ComputeHash(stream));
+                if (!string.Equals(package.checksum, hash, StringComparison.OrdinalIgnoreCase))
+                {
+                    _logger.LogDebug("{0}, {1}", package.checksum, hash);
+                    throw new InvalidDataException($"The checksums didn't match while installing {package.name}.");
+                }
 
-            // Success - move it to the real target
-            try
-            {
-                _logger.LogDebug("Extracting ZIP {TempFile} to {Filename}.", tempFile, target);
-                using (var stream = File.OpenRead(tempFile))
+                if (Directory.Exists(targetDir))
                 {
-                    _zipClient.ExtractAllFromZip(stream, target, true);
+                    Directory.Delete(targetDir);
                 }
-            }
-            catch (IOException ex)
-            {
-                _logger.LogError(ex, "Error attempting to extract {TempFile} to {TargetFile}", tempFile, target);
-                throw;
-            }
 
-            try
-            {
-                _logger.LogDebug("Deleting temporary file {Filename}.", tempFile);
-                _fileSystem.DeleteFile(tempFile);
-            }
-            catch (IOException ex)
-            {
-                // Don't fail because of this
-                _logger.LogError(ex, "Error deleting temp file {TempFile}", tempFile);
+                stream.Position = 0;
+                _zipClient.ExtractAllFromZip(stream, targetDir, true);
             }
+
+#pragma warning restore CA5351
         }
 
         /// <summary>
         /// Uninstalls a plugin
         /// </summary>
         /// <param name="plugin">The plugin.</param>
-        /// <exception cref="ArgumentException"></exception>
         public void UninstallPlugin(IPlugin plugin)
         {
             plugin.OnUninstalling();

+ 1 - 1
MediaBrowser.Api/PackageService.cs

@@ -197,7 +197,7 @@ namespace MediaBrowser.Api
                 throw new ResourceNotFoundException(string.Format("Package not found: {0}", request.Name));
             }
 
-            await _installationManager.InstallPackage(package, new SimpleProgress<double>(), CancellationToken.None);
+            await _installationManager.InstallPackage(package, CancellationToken.None);
         }
 
         /// <summary>

+ 22 - 0
MediaBrowser.Common/Extensions/HexHelper.cs

@@ -0,0 +1,22 @@
+using System;
+using System.Globalization;
+
+namespace MediaBrowser.Common.Extensions
+{
+    public static class HexHelper
+    {
+        public static byte[] FromHexString(string str)
+        {
+            byte[] bytes = new byte[str.Length / 2];
+            for (int i = 0; i < str.Length; i += 2)
+            {
+                bytes[i / 2] = byte.Parse(str.Substring(i, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
+            }
+
+            return bytes;
+        }
+
+        public static string ToHexString(byte[] bytes)
+            => BitConverter.ToString(bytes).Replace("-", "");
+    }
+}

+ 0 - 12
MediaBrowser.Common/Net/HttpRequestOptions.cs

@@ -64,12 +64,6 @@ namespace MediaBrowser.Common.Net
             set => RequestHeaders[HeaderNames.Host] = value;
         }
 
-        /// <summary>
-        /// Gets or sets the progress.
-        /// </summary>
-        /// <value>The progress.</value>
-        public IProgress<double> Progress { get; set; }
-
         public Dictionary<string, string> RequestHeaders { get; private set; }
 
         public string RequestContentType { get; set; }
@@ -79,10 +73,6 @@ namespace MediaBrowser.Common.Net
 
         public bool BufferContent { get; set; }
 
-        public bool LogRequest { get; set; }
-        public bool LogRequestAsDebug { get; set; }
-        public bool LogErrors { get; set; }
-
         public bool LogErrorResponseBody { get; set; }
         public bool EnableKeepAlive { get; set; }
 
@@ -105,8 +95,6 @@ namespace MediaBrowser.Common.Net
         {
             RequestHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
 
-            LogRequest = true;
-            LogErrors = true;
             CacheMode = CacheMode.None;
             DecompressionMethod = CompressionMethod.Deflate;
         }

+ 0 - 16
MediaBrowser.Common/Net/IHttpClient.cs

@@ -47,21 +47,5 @@ namespace MediaBrowser.Common.Net
         /// <param name="options">The options.</param>
         /// <returns>Task{HttpResponseInfo}.</returns>
         Task<HttpResponseInfo> Post(HttpRequestOptions options);
-
-        /// <summary>
-        /// Downloads the contents of a given url into a temporary location
-        /// </summary>
-        /// <param name="options">The options.</param>
-        /// <returns>Task{System.String}.</returns>
-        /// <exception cref="System.ArgumentNullException">progress</exception>
-        /// <exception cref="Model.Net.HttpException"></exception>
-        Task<string> GetTempFile(HttpRequestOptions options);
-
-        /// <summary>
-        /// Gets the temporary file response.
-        /// </summary>
-        /// <param name="options">The options.</param>
-        /// <returns>Task{HttpResponseInfo}.</returns>
-        Task<HttpResponseInfo> GetTempFileResponse(HttpRequestOptions options);
     }
 }

+ 2 - 5
MediaBrowser.Common/Updates/IInstallationManager.cs

@@ -97,12 +97,9 @@ namespace MediaBrowser.Common.Updates
         /// Installs the package.
         /// </summary>
         /// <param name="package">The package.</param>
-        /// <param name="isPlugin">if set to <c>true</c> [is plugin].</param>
-        /// <param name="progress">The progress.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task.</returns>
-        /// <exception cref="ArgumentNullException">package</exception>
-        Task InstallPackage(PackageVersionInfo package, IProgress<double> progress, CancellationToken cancellationToken);
+        /// <returns><see cref="Task" />.</returns>
+        Task InstallPackage(PackageVersionInfo package, CancellationToken cancellationToken);
 
         /// <summary>
         /// Uninstalls a plugin

+ 1 - 0
MediaBrowser.Model/Cryptography/PasswordHash.cs

@@ -84,6 +84,7 @@ namespace MediaBrowser.Model.Cryptography
             _hash = Array.Empty<Byte>();
         }
 
+        // TODO: move this class and use the HexHelper class
         public static byte[] ConvertFromByteString(string byteString)
         {
             byte[] bytes = new byte[byteString.Length / 2];

+ 0 - 6
MediaBrowser.Model/Updates/InstallationInfo.cs

@@ -36,11 +36,5 @@ namespace MediaBrowser.Model.Updates
         /// </summary>
         /// <value>The update class.</value>
         public PackageVersionClass UpdateClass { get; set; }
-
-        /// <summary>
-        /// Gets or sets the percent complete.
-        /// </summary>
-        /// <value>The percent complete.</value>
-        public double? PercentComplete { get; set; }
     }
 }

+ 11 - 17
MediaBrowser.Providers/Studios/StudiosImageProvider.cs

@@ -2,10 +2,10 @@ using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
+using System.Net.Http;
 using System.Threading;
 using System.Threading.Tasks;
 using MediaBrowser.Common.Net;
-using MediaBrowser.Common.Progress;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Providers;
@@ -143,26 +143,20 @@ namespace MediaBrowser.Providers.Studios
 
             if (!fileInfo.Exists || (DateTime.UtcNow - fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays > 1)
             {
-                var temp = await httpClient.GetTempFile(new HttpRequestOptions
-                {
-                    CancellationToken = cancellationToken,
-                    Progress = new SimpleProgress<double>(),
-                    Url = url
-
-                }).ConfigureAwait(false);
-
                 Directory.CreateDirectory(Path.GetDirectoryName(file));
 
-                try
-                {
-                    File.Copy(temp, file, true);
-                }
-                catch
+                using (var res = await httpClient.SendAsync(
+                    new HttpRequestOptions
+                    {
+                        CancellationToken = cancellationToken,
+                        Url = url
+                    },
+                    HttpMethod.Get).ConfigureAwait(false))
+                using (var content = res.Content)
+                using (var fileStream = new FileStream(file, FileMode.Create))
                 {
-
+                    await content.CopyToAsync(fileStream).ConfigureAwait(false);
                 }
-
-                return temp;
             }
 
             return file;

+ 13 - 26
Mono.Nat/Upnp/Messages/GetServicesMessage.cs

@@ -25,50 +25,37 @@
 //
 
 using System;
-using System.Diagnostics;
 using System.Net;
 using MediaBrowser.Common.Net;
-using Microsoft.Extensions.Logging;
 
 namespace Mono.Nat.Upnp
 {
     internal class GetServicesMessage : MessageBase
     {
-        private string servicesDescriptionUrl;
-        private EndPoint hostAddress;
-        private readonly ILogger _logger;
+        private string _servicesDescriptionUrl;
+        private EndPoint _hostAddress;
 
-        public GetServicesMessage(string description, EndPoint hostAddress, ILogger logger)
+        public GetServicesMessage(string description, EndPoint hostAddress)
             : base(null)
         {
             if (string.IsNullOrEmpty(description))
-                _logger.LogWarning("Description is null");
-
-            if (hostAddress == null)
-                _logger.LogWarning("hostaddress is null");
-
-            this.servicesDescriptionUrl = description;
-            this.hostAddress = hostAddress;
-            _logger = logger;
-        }
-
-        public override string Method
-        {
-            get
             {
-                return "GET";
+                throw new ArgumentException("Description is null/empty", nameof(description));
             }
+
+            this._servicesDescriptionUrl = description;
+            this._hostAddress = hostAddress ?? throw new ArgumentNullException(nameof(hostAddress));
         }
 
+        public override string Method => "GET";
+
         public override HttpRequestOptions Encode()
         {
-            var req = new HttpRequestOptions();
-
-            // The periodic request logging may keep some devices awake
-            req.LogRequestAsDebug = true;
-            req.LogErrors = false;
+            var req = new HttpRequestOptions()
+            {
+                Url = $"http://{this._hostAddress}{this._servicesDescriptionUrl}"
+            };
 
-            req.Url = "http://" + this.hostAddress.ToString() + this.servicesDescriptionUrl;
             req.RequestHeaders.Add("ACCEPT-LANGUAGE", "en");
 
             return req;

+ 17 - 29
Mono.Nat/Upnp/Messages/UpnpMessage.cs

@@ -24,13 +24,8 @@
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
-using System;
-using System.Diagnostics;
 using System.Xml;
-using System.Net;
-using System.IO;
 using System.Text;
-using System.Globalization;
 using MediaBrowser.Common.Net;
 
 namespace Mono.Nat.Upnp
@@ -46,38 +41,31 @@ namespace Mono.Nat.Upnp
 
         protected HttpRequestOptions CreateRequest(string upnpMethod, string methodParameters)
         {
-            string ss = "http://" + this.device.HostEndPoint.ToString() + this.device.ControlUrl;
+            var req = new HttpRequestOptions()
+            {
+                Url = $"http://{this.device.HostEndPoint}{this.device.ControlUrl}",
+                EnableKeepAlive = false,
+                RequestContentType = "text/xml",
+                RequestContent = "<s:Envelope "
+                + "xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
+                + "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
+                + "<s:Body>"
+                + "<u:" + upnpMethod + " "
+                + "xmlns:u=\"" + device.ServiceType + "\">"
+                + methodParameters
+                + "</u:" + upnpMethod + ">"
+                + "</s:Body>"
+                + "</s:Envelope>\r\n\r\n"
+            };
 
-            var req = new HttpRequestOptions();
-            req.LogErrors = false;
-
-            // The periodic request logging may keep some devices awake
-            req.LogRequestAsDebug = true;
-
-            req.Url = ss;
-            req.EnableKeepAlive = false;
-            req.RequestContentType = "text/xml";
             req.RequestHeaders.Add("SOAPACTION", "\"" + device.ServiceType + "#" + upnpMethod + "\"");
 
-            req.RequestContent = "<s:Envelope "
-               + "xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
-               + "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
-               + "<s:Body>"
-               + "<u:" + upnpMethod + " "
-               + "xmlns:u=\"" + device.ServiceType + "\">"
-               + methodParameters
-               + "</u:" + upnpMethod + ">"
-               + "</s:Body>"
-               + "</s:Envelope>\r\n\r\n";
             return req;
         }
 
         public abstract HttpRequestOptions Encode();
 
-        public virtual string Method
-        {
-            get { return "POST"; }
-        }
+        public virtual string Method => "POST";
 
         protected void WriteFullElement(XmlWriter writer, string element, string value)
         {

+ 1 - 3
Mono.Nat/Upnp/UpnpNatDevice.cs

@@ -27,11 +27,9 @@
 //
 
 using System;
-using System.IO;
 using System.Net;
 using System.Xml;
 using System.Text;
-using System.Diagnostics;
 using System.Threading.Tasks;
 using MediaBrowser.Common.Net;
 using Microsoft.Extensions.Logging;
@@ -96,7 +94,7 @@ namespace Mono.Nat.Upnp
         public async Task GetServicesList()
         {
             // Create a HTTPWebRequest to download the list of services the device offers
-            var message = new GetServicesMessage(this.serviceDescriptionUrl, this.hostEndPoint, _logger);
+            var message = new GetServicesMessage(this.serviceDescriptionUrl, this.hostEndPoint);
 
             using (var response = await _httpClient.SendAsync(message.Encode(), message.Method).ConfigureAwait(false))
             {

+ 2 - 0
jellyfin.ruleset

@@ -38,5 +38,7 @@
     <Rule Id="CA1054" Action="None" />
     <!-- disable warning CA1303: Do not pass literals as localized parameters -->
     <Rule Id="CA1303" Action="None" />
+    <!-- disable warning CA2000: Dispose objects before losing scope -->
+    <Rule Id="CA2000" Action="None" />
   </Rules>
 </RuleSet>