Merge pull request #1584 from Bond-009/checksum
Check checksum for plugin downloads
This commit is contained in:
commit
685e9e4f58
|
@ -73,9 +73,6 @@ namespace Emby.Dlna.PlayTo
|
||||||
UserAgent = USERAGENT,
|
UserAgent = USERAGENT,
|
||||||
LogErrorResponseBody = true,
|
LogErrorResponseBody = true,
|
||||||
BufferContent = false,
|
BufferContent = false,
|
||||||
|
|
||||||
// The periodic requests may keep some devices awake
|
|
||||||
LogRequestAsDebug = true
|
|
||||||
};
|
};
|
||||||
|
|
||||||
options.RequestHeaders["HOST"] = ip + ":" + port.ToString(_usCulture);
|
options.RequestHeaders["HOST"] = ip + ":" + port.ToString(_usCulture);
|
||||||
|
@ -98,9 +95,6 @@ namespace Emby.Dlna.PlayTo
|
||||||
LogErrorResponseBody = true,
|
LogErrorResponseBody = true,
|
||||||
BufferContent = false,
|
BufferContent = false,
|
||||||
|
|
||||||
// The periodic requests may keep some devices awake
|
|
||||||
LogRequestAsDebug = true,
|
|
||||||
|
|
||||||
CancellationToken = cancellationToken
|
CancellationToken = cancellationToken
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -135,13 +129,9 @@ namespace Emby.Dlna.PlayTo
|
||||||
{
|
{
|
||||||
Url = url,
|
Url = url,
|
||||||
UserAgent = USERAGENT,
|
UserAgent = USERAGENT,
|
||||||
LogRequest = logRequest || _config.GetDlnaConfiguration().EnableDebugLog,
|
|
||||||
LogErrorResponseBody = true,
|
LogErrorResponseBody = true,
|
||||||
BufferContent = false,
|
BufferContent = false,
|
||||||
|
|
||||||
// The periodic requests may keep some devices awake
|
|
||||||
LogRequestAsDebug = true,
|
|
||||||
|
|
||||||
CancellationToken = cancellationToken
|
CancellationToken = cancellationToken
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1537,8 +1537,6 @@ namespace Emby.Server.Implementations
|
||||||
{
|
{
|
||||||
Url = Url,
|
Url = Url,
|
||||||
LogErrorResponseBody = false,
|
LogErrorResponseBody = false,
|
||||||
LogErrors = false,
|
|
||||||
LogRequest = false,
|
|
||||||
BufferContent = false,
|
BufferContent = false,
|
||||||
CancellationToken = cancellationToken
|
CancellationToken = cancellationToken
|
||||||
}).ConfigureAwait(false))
|
}).ConfigureAwait(false))
|
||||||
|
@ -1690,8 +1688,8 @@ namespace Emby.Server.Implementations
|
||||||
|
|
||||||
private async Task<bool> IsIpAddressValidAsync(IPAddress address, CancellationToken cancellationToken)
|
private async Task<bool> IsIpAddressValidAsync(IPAddress address, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (address.Equals(IPAddress.Loopback) ||
|
if (address.Equals(IPAddress.Loopback)
|
||||||
address.Equals(IPAddress.IPv6Loopback))
|
|| address.Equals(IPAddress.IPv6Loopback))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1704,12 +1702,6 @@ namespace Emby.Server.Implementations
|
||||||
return cachedResult;
|
return cachedResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
const bool LogPing = true;
|
|
||||||
#else
|
|
||||||
const bool LogPing = false;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (var response = await HttpClient.SendAsync(
|
using (var response = await HttpClient.SendAsync(
|
||||||
|
@ -1717,8 +1709,6 @@ namespace Emby.Server.Implementations
|
||||||
{
|
{
|
||||||
Url = apiUrl,
|
Url = apiUrl,
|
||||||
LogErrorResponseBody = false,
|
LogErrorResponseBody = false,
|
||||||
LogErrors = LogPing,
|
|
||||||
LogRequest = LogPing,
|
|
||||||
BufferContent = false,
|
BufferContent = false,
|
||||||
CancellationToken = cancellationToken
|
CancellationToken = cancellationToken
|
||||||
}, HttpMethod.Post).ConfigureAwait(false))
|
}, HttpMethod.Post).ConfigureAwait(false))
|
||||||
|
|
|
@ -5,9 +5,6 @@ using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Common.Extensions;
|
||||||
|
@ -20,7 +17,7 @@ using Microsoft.Net.Http.Headers;
|
||||||
namespace Emby.Server.Implementations.HttpClientManager
|
namespace Emby.Server.Implementations.HttpClientManager
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class HttpClientManager
|
/// Class HttpClientManager.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class HttpClientManager : IHttpClient
|
public class HttpClientManager : IHttpClient
|
||||||
{
|
{
|
||||||
|
@ -45,19 +42,9 @@ namespace Emby.Server.Implementations.HttpClientManager
|
||||||
IFileSystem fileSystem,
|
IFileSystem fileSystem,
|
||||||
Func<string> defaultUserAgentFn)
|
Func<string> defaultUserAgentFn)
|
||||||
{
|
{
|
||||||
if (appPaths == null)
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(appPaths));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (logger == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(logger));
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger = logger;
|
|
||||||
_fileSystem = fileSystem;
|
_fileSystem = fileSystem;
|
||||||
_appPaths = appPaths;
|
_appPaths = appPaths ?? throw new ArgumentNullException(nameof(appPaths));
|
||||||
_defaultUserAgentFn = defaultUserAgentFn;
|
_defaultUserAgentFn = defaultUserAgentFn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,7 +105,7 @@ namespace Emby.Server.Implementations.HttpClientManager
|
||||||
request.Headers.Add(HeaderNames.Connection, "Keep-Alive");
|
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))
|
if (!string.IsNullOrWhiteSpace(userInfo))
|
||||||
|
@ -196,7 +183,7 @@ namespace Emby.Server.Implementations.HttpClientManager
|
||||||
}
|
}
|
||||||
|
|
||||||
var url = options.Url;
|
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);
|
var responseCachePath = Path.Combine(_appPaths.CachePath, "httpclient", urlHash);
|
||||||
|
|
||||||
|
@ -239,7 +226,13 @@ namespace Emby.Server.Implementations.HttpClientManager
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(responseCachePath));
|
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);
|
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();
|
options.CancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
var response = await client.SendAsync(
|
var response = await client.SendAsync(
|
||||||
httpWebRequest,
|
httpWebRequest,
|
||||||
options.BufferContent ? HttpCompletionOption.ResponseContentRead : HttpCompletionOption.ResponseHeadersRead,
|
options.BufferContent || options.CacheMode == CacheMode.Unconditional ? HttpCompletionOption.ResponseContentRead : HttpCompletionOption.ResponseHeadersRead,
|
||||||
options.CancellationToken).ConfigureAwait(false);
|
options.CancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
await EnsureSuccessStatusCode(response, options).ConfigureAwait(false);
|
await EnsureSuccessStatusCode(response, options).ConfigureAwait(false);
|
||||||
|
@ -308,138 +296,6 @@ namespace Emby.Server.Implementations.HttpClientManager
|
||||||
public Task<HttpResponseInfo> Post(HttpRequestOptions options)
|
public Task<HttpResponseInfo> Post(HttpRequestOptions options)
|
||||||
=> SendAsync(options, HttpMethod.Post);
|
=> 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)
|
private void ValidateParams(HttpRequestOptions options)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(options.Url))
|
if (string.IsNullOrEmpty(options.Url))
|
||||||
|
@ -471,35 +327,6 @@ namespace Emby.Server.Implementations.HttpClientManager
|
||||||
return url;
|
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)
|
private async Task EnsureSuccessStatusCode(HttpResponseMessage response, HttpRequestOptions options)
|
||||||
{
|
{
|
||||||
if (response.IsSuccessStatusCode)
|
if (response.IsSuccessStatusCode)
|
||||||
|
@ -507,8 +334,11 @@ namespace Emby.Server.Implementations.HttpClientManager
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var msg = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
if (options.LogErrorResponseBody)
|
||||||
_logger.LogError("HTTP request failed with message: {Message}", msg);
|
{
|
||||||
|
var msg = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
|
_logger.LogError("HTTP request failed with message: {Message}", msg);
|
||||||
|
}
|
||||||
|
|
||||||
throw new HttpException(response.ReasonPhrase)
|
throw new HttpException(response.ReasonPhrase)
|
||||||
{
|
{
|
||||||
|
|
|
@ -65,7 +65,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _installationManager.InstallPackage(package, new SimpleProgress<double>(), cancellationToken).ConfigureAwait(false);
|
await _installationManager.InstallPackage(package, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
|
@ -87,8 +87,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
|
||||||
// Update progress
|
// Update progress
|
||||||
lock (progress)
|
lock (progress)
|
||||||
{
|
{
|
||||||
numComplete++;
|
progress.Report((90.0 * ++numComplete / packagesToInstall.Count) + 10);
|
||||||
progress.Report(90.0 * numComplete / packagesToInstall.Count + 10);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,13 +4,14 @@ using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Security.Cryptography;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Common;
|
using MediaBrowser.Common;
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Common.Plugins;
|
using MediaBrowser.Common.Plugins;
|
||||||
using MediaBrowser.Common.Progress;
|
|
||||||
using MediaBrowser.Common.Updates;
|
using MediaBrowser.Common.Updates;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Model.Events;
|
using MediaBrowser.Model.Events;
|
||||||
|
@ -126,13 +127,16 @@ namespace Emby.Server.Implementations.Updates
|
||||||
/// <returns>Task{List{PackageInfo}}.</returns>
|
/// <returns>Task{List{PackageInfo}}.</returns>
|
||||||
public async Task<List<PackageInfo>> GetAvailablePackagesWithoutRegistrationInfo(CancellationToken cancellationToken)
|
public async Task<List<PackageInfo>> GetAvailablePackagesWithoutRegistrationInfo(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
using (var response = await _httpClient.SendAsync(new HttpRequestOptions
|
using (var response = await _httpClient.SendAsync(
|
||||||
{
|
new HttpRequestOptions
|
||||||
Url = "https://repo.jellyfin.org/releases/plugin/manifest.json",
|
{
|
||||||
CancellationToken = cancellationToken,
|
Url = "https://repo.jellyfin.org/releases/plugin/manifest.json",
|
||||||
CacheLength = GetCacheLength()
|
CancellationToken = cancellationToken,
|
||||||
}, HttpMethod.Get).ConfigureAwait(false))
|
CacheMode = CacheMode.Unconditional,
|
||||||
using (var stream = response.Content)
|
CacheLength = GetCacheLength()
|
||||||
|
},
|
||||||
|
HttpMethod.Get).ConfigureAwait(false))
|
||||||
|
using (Stream stream = response.Content)
|
||||||
{
|
{
|
||||||
return FilterPackages(await _jsonSerializer.DeserializeFromStreamAsync<PackageInfo[]>(stream).ConfigureAwait(false));
|
return FilterPackages(await _jsonSerializer.DeserializeFromStreamAsync<PackageInfo[]>(stream).ConfigureAwait(false));
|
||||||
}
|
}
|
||||||
|
@ -275,12 +279,7 @@ namespace Emby.Server.Implementations.Updates
|
||||||
var package = availablePackages.FirstOrDefault(p => string.Equals(p.guid, guid ?? "none", StringComparison.OrdinalIgnoreCase))
|
var package = availablePackages.FirstOrDefault(p => string.Equals(p.guid, guid ?? "none", StringComparison.OrdinalIgnoreCase))
|
||||||
?? availablePackages.FirstOrDefault(p => p.name.Equals(name, StringComparison.OrdinalIgnoreCase));
|
?? availablePackages.FirstOrDefault(p => p.name.Equals(name, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
if (package == null)
|
return package?.versions
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return package.versions
|
|
||||||
.OrderByDescending(x => x.Version)
|
.OrderByDescending(x => x.Version)
|
||||||
.FirstOrDefault(v => v.classification <= classification && IsPackageVersionUpToDate(v, currentServerVersion));
|
.FirstOrDefault(v => v.classification <= classification && IsPackageVersionUpToDate(v, currentServerVersion));
|
||||||
}
|
}
|
||||||
|
@ -304,32 +303,18 @@ namespace Emby.Server.Implementations.Updates
|
||||||
var latestPluginInfo = GetLatestCompatibleVersion(catalog, p.Name, p.Id.ToString(), applicationVersion, systemUpdateLevel);
|
var latestPluginInfo = GetLatestCompatibleVersion(catalog, p.Name, p.Id.ToString(), applicationVersion, systemUpdateLevel);
|
||||||
|
|
||||||
return latestPluginInfo != null && latestPluginInfo.Version > p.Version ? latestPluginInfo : null;
|
return latestPluginInfo != null && latestPluginInfo.Version > p.Version ? latestPluginInfo : null;
|
||||||
|
|
||||||
}).Where(i => i != null)
|
}).Where(i => i != null)
|
||||||
.Where(p => !string.IsNullOrEmpty(p.sourceUrl) && !CompletedInstallations.Any(i => string.Equals(i.AssemblyGuid, p.guid, StringComparison.OrdinalIgnoreCase)));
|
.Where(p => !string.IsNullOrEmpty(p.sourceUrl) && !CompletedInstallations.Any(i => string.Equals(i.AssemblyGuid, p.guid, StringComparison.OrdinalIgnoreCase)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Installs the package.
|
public async Task InstallPackage(PackageVersionInfo package, CancellationToken cancellationToken)
|
||||||
/// </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)
|
|
||||||
{
|
{
|
||||||
if (package == null)
|
if (package == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(package));
|
throw new ArgumentNullException(nameof(package));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (progress == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(progress));
|
|
||||||
}
|
|
||||||
|
|
||||||
var installationInfo = new InstallationInfo
|
var installationInfo = new InstallationInfo
|
||||||
{
|
{
|
||||||
Id = Guid.NewGuid(),
|
Id = Guid.NewGuid(),
|
||||||
|
@ -349,16 +334,6 @@ namespace Emby.Server.Implementations.Updates
|
||||||
_currentInstallations.Add(tuple);
|
_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 linkedToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, innerCancellationTokenSource.Token).Token;
|
||||||
|
|
||||||
var installationEventArgs = new InstallationEventArgs
|
var installationEventArgs = new InstallationEventArgs
|
||||||
|
@ -371,7 +346,7 @@ namespace Emby.Server.Implementations.Updates
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await InstallPackageInternal(package, innerProgress, linkedToken).ConfigureAwait(false);
|
await InstallPackageInternal(package, linkedToken).ConfigureAwait(false);
|
||||||
|
|
||||||
lock (_currentInstallations)
|
lock (_currentInstallations)
|
||||||
{
|
{
|
||||||
|
@ -423,20 +398,16 @@ namespace Emby.Server.Implementations.Updates
|
||||||
/// Installs the package internal.
|
/// Installs the package internal.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="package">The package.</param>
|
/// <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>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task.</returns>
|
/// <returns><see cref="Task" />.</returns>
|
||||||
private async Task InstallPackageInternal(PackageVersionInfo package, IProgress<double> progress, CancellationToken cancellationToken)
|
private async Task InstallPackageInternal(PackageVersionInfo package, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// Set last update time if we were installed before
|
// Set last update time if we were installed before
|
||||||
IPlugin plugin = _applicationHost.Plugins.FirstOrDefault(p => string.Equals(p.Id.ToString(), package.guid, StringComparison.OrdinalIgnoreCase))
|
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));
|
?? _applicationHost.Plugins.FirstOrDefault(p => p.Name.Equals(package.name, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
string targetPath = plugin == null ? null : plugin.AssemblyFilePath;
|
|
||||||
|
|
||||||
// Do the install
|
// Do the install
|
||||||
await PerformPackageInstallation(progress, targetPath, package, cancellationToken).ConfigureAwait(false);
|
await PerformPackageInstallation(package, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
// Do plugin-specific processing
|
// Do plugin-specific processing
|
||||||
if (plugin == null)
|
if (plugin == null)
|
||||||
|
@ -455,76 +426,57 @@ namespace Emby.Server.Implementations.Updates
|
||||||
_applicationHost.NotifyPendingRestart();
|
_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 extension = Path.GetExtension(package.targetFilename);
|
||||||
var isArchive = string.Equals(extension, ".zip", StringComparison.OrdinalIgnoreCase);
|
if (!string.Equals(extension, ".zip", StringComparison.OrdinalIgnoreCase))
|
||||||
|
|
||||||
if (!isArchive)
|
|
||||||
{
|
{
|
||||||
_logger.LogError("Only zip packages are supported. {Filename} is not a zip archive.", package.targetFilename);
|
_logger.LogError("Only zip packages are supported. {Filename} is not a zip archive.", package.targetFilename);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always override the passed-in target (which is a file) and figure it out again
|
// Always override the passed-in target (which is a file) and figure it out again
|
||||||
target = Path.Combine(_appPaths.PluginsPath, package.name);
|
string targetDir = 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
|
// CA5351: Do Not Use Broken Cryptographic Algorithms
|
||||||
_logger.LogDebug("Downloading ZIP.");
|
#pragma warning disable CA5351
|
||||||
var tempFile = await _httpClient.GetTempFile(new HttpRequestOptions
|
using (var res = await _httpClient.SendAsync(
|
||||||
{
|
new HttpRequestOptions
|
||||||
Url = package.sourceUrl,
|
|
||||||
CancellationToken = cancellationToken,
|
|
||||||
Progress = progress
|
|
||||||
|
|
||||||
}).ConfigureAwait(false);
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Success - move it to the real target
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_logger.LogDebug("Extracting ZIP {TempFile} to {Filename}.", tempFile, target);
|
|
||||||
using (var stream = File.OpenRead(tempFile))
|
|
||||||
{
|
{
|
||||||
_zipClient.ExtractAllFromZip(stream, target, true);
|
Url = package.sourceUrl,
|
||||||
}
|
CancellationToken = cancellationToken,
|
||||||
}
|
// We need it to be buffered for setting the position
|
||||||
catch (IOException ex)
|
BufferContent = true
|
||||||
|
},
|
||||||
|
HttpMethod.Get).ConfigureAwait(false))
|
||||||
|
using (var stream = res.Content)
|
||||||
|
using (var md5 = MD5.Create())
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Error attempting to extract {TempFile} to {TargetFile}", tempFile, target);
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
throw;
|
|
||||||
|
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}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Directory.Exists(targetDir))
|
||||||
|
{
|
||||||
|
Directory.Delete(targetDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.Position = 0;
|
||||||
|
_zipClient.ExtractAllFromZip(stream, targetDir, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
#pragma warning restore CA5351
|
||||||
{
|
|
||||||
_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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Uninstalls a plugin
|
/// Uninstalls a plugin
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="plugin">The plugin.</param>
|
/// <param name="plugin">The plugin.</param>
|
||||||
/// <exception cref="ArgumentException"></exception>
|
|
||||||
public void UninstallPlugin(IPlugin plugin)
|
public void UninstallPlugin(IPlugin plugin)
|
||||||
{
|
{
|
||||||
plugin.OnUninstalling();
|
plugin.OnUninstalling();
|
||||||
|
|
|
@ -197,7 +197,7 @@ namespace MediaBrowser.Api
|
||||||
throw new ResourceNotFoundException(string.Format("Package not found: {0}", request.Name));
|
throw new ResourceNotFoundException(string.Format("Package not found: {0}", request.Name));
|
||||||
}
|
}
|
||||||
|
|
||||||
await _installationManager.InstallPackage(package, new SimpleProgress<double>(), CancellationToken.None);
|
await _installationManager.InstallPackage(package);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
22
MediaBrowser.Common/Extensions/HexHelper.cs
Normal file
22
MediaBrowser.Common/Extensions/HexHelper.cs
Normal file
|
@ -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("-", "");
|
||||||
|
}
|
||||||
|
}
|
|
@ -64,12 +64,6 @@ namespace MediaBrowser.Common.Net
|
||||||
set => RequestHeaders[HeaderNames.Host] = value;
|
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 Dictionary<string, string> RequestHeaders { get; private set; }
|
||||||
|
|
||||||
public string RequestContentType { get; set; }
|
public string RequestContentType { get; set; }
|
||||||
|
@ -79,10 +73,6 @@ namespace MediaBrowser.Common.Net
|
||||||
|
|
||||||
public bool BufferContent { get; set; }
|
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 LogErrorResponseBody { get; set; }
|
||||||
public bool EnableKeepAlive { get; set; }
|
public bool EnableKeepAlive { get; set; }
|
||||||
|
|
||||||
|
@ -105,8 +95,6 @@ namespace MediaBrowser.Common.Net
|
||||||
{
|
{
|
||||||
RequestHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
RequestHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
LogRequest = true;
|
|
||||||
LogErrors = true;
|
|
||||||
CacheMode = CacheMode.None;
|
CacheMode = CacheMode.None;
|
||||||
DecompressionMethod = CompressionMethod.Deflate;
|
DecompressionMethod = CompressionMethod.Deflate;
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,21 +47,5 @@ namespace MediaBrowser.Common.Net
|
||||||
/// <param name="options">The options.</param>
|
/// <param name="options">The options.</param>
|
||||||
/// <returns>Task{HttpResponseInfo}.</returns>
|
/// <returns>Task{HttpResponseInfo}.</returns>
|
||||||
Task<HttpResponseInfo> Post(HttpRequestOptions options);
|
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,12 +97,9 @@ namespace MediaBrowser.Common.Updates
|
||||||
/// Installs the package.
|
/// Installs the package.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="package">The package.</param>
|
/// <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>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task.</returns>
|
/// <returns><see cref="Task" />.</returns>
|
||||||
/// <exception cref="ArgumentNullException">package</exception>
|
Task InstallPackage(PackageVersionInfo package, CancellationToken cancellationToken = default);
|
||||||
Task InstallPackage(PackageVersionInfo package, IProgress<double> progress, CancellationToken cancellationToken);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Uninstalls a plugin
|
/// Uninstalls a plugin
|
||||||
|
|
|
@ -84,6 +84,7 @@ namespace MediaBrowser.Model.Cryptography
|
||||||
_hash = Array.Empty<Byte>();
|
_hash = Array.Empty<Byte>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: move this class and use the HexHelper class
|
||||||
public static byte[] ConvertFromByteString(string byteString)
|
public static byte[] ConvertFromByteString(string byteString)
|
||||||
{
|
{
|
||||||
byte[] bytes = new byte[byteString.Length / 2];
|
byte[] bytes = new byte[byteString.Length / 2];
|
||||||
|
|
|
@ -36,11 +36,5 @@ namespace MediaBrowser.Model.Updates
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The update class.</value>
|
/// <value>The update class.</value>
|
||||||
public PackageVersionClass UpdateClass { get; set; }
|
public PackageVersionClass UpdateClass { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the percent complete.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The percent complete.</value>
|
|
||||||
public double? PercentComplete { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,10 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Common.Progress;
|
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
|
@ -143,26 +143,20 @@ namespace MediaBrowser.Providers.Studios
|
||||||
|
|
||||||
if (!fileInfo.Exists || (DateTime.UtcNow - fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays > 1)
|
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));
|
Directory.CreateDirectory(Path.GetDirectoryName(file));
|
||||||
|
|
||||||
try
|
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))
|
||||||
{
|
{
|
||||||
File.Copy(temp, file, true);
|
await content.CopyToAsync(fileStream).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return temp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return file;
|
return file;
|
||||||
|
|
|
@ -25,50 +25,37 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
namespace Mono.Nat.Upnp
|
namespace Mono.Nat.Upnp
|
||||||
{
|
{
|
||||||
internal class GetServicesMessage : MessageBase
|
internal class GetServicesMessage : MessageBase
|
||||||
{
|
{
|
||||||
private string servicesDescriptionUrl;
|
private string _servicesDescriptionUrl;
|
||||||
private EndPoint hostAddress;
|
private EndPoint _hostAddress;
|
||||||
private readonly ILogger _logger;
|
|
||||||
|
|
||||||
public GetServicesMessage(string description, EndPoint hostAddress, ILogger logger)
|
public GetServicesMessage(string description, EndPoint hostAddress)
|
||||||
: base(null)
|
: base(null)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(description))
|
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()
|
public override HttpRequestOptions Encode()
|
||||||
{
|
{
|
||||||
var req = new HttpRequestOptions();
|
var req = new HttpRequestOptions()
|
||||||
|
{
|
||||||
|
Url = $"http://{this._hostAddress}{this._servicesDescriptionUrl}"
|
||||||
|
};
|
||||||
|
|
||||||
// The periodic request logging may keep some devices awake
|
|
||||||
req.LogRequestAsDebug = true;
|
|
||||||
req.LogErrors = false;
|
|
||||||
|
|
||||||
req.Url = "http://" + this.hostAddress.ToString() + this.servicesDescriptionUrl;
|
|
||||||
req.RequestHeaders.Add("ACCEPT-LANGUAGE", "en");
|
req.RequestHeaders.Add("ACCEPT-LANGUAGE", "en");
|
||||||
|
|
||||||
return req;
|
return req;
|
||||||
|
|
|
@ -24,13 +24,8 @@
|
||||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using System.Net;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Globalization;
|
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
|
|
||||||
namespace Mono.Nat.Upnp
|
namespace Mono.Nat.Upnp
|
||||||
|
@ -46,38 +41,31 @@ namespace Mono.Nat.Upnp
|
||||||
|
|
||||||
protected HttpRequestOptions CreateRequest(string upnpMethod, string methodParameters)
|
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.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;
|
return req;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract HttpRequestOptions Encode();
|
public abstract HttpRequestOptions Encode();
|
||||||
|
|
||||||
public virtual string Method
|
public virtual string Method => "POST";
|
||||||
{
|
|
||||||
get { return "POST"; }
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void WriteFullElement(XmlWriter writer, string element, string value)
|
protected void WriteFullElement(XmlWriter writer, string element, string value)
|
||||||
{
|
{
|
||||||
|
|
|
@ -27,11 +27,9 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
@ -96,7 +94,7 @@ namespace Mono.Nat.Upnp
|
||||||
public async Task GetServicesList()
|
public async Task GetServicesList()
|
||||||
{
|
{
|
||||||
// Create a HTTPWebRequest to download the list of services the device offers
|
// 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))
|
using (var response = await _httpClient.SendAsync(message.Encode(), message.Method).ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
|
|
|
@ -38,5 +38,7 @@
|
||||||
<Rule Id="CA1054" Action="None" />
|
<Rule Id="CA1054" Action="None" />
|
||||||
<!-- disable warning CA1303: Do not pass literals as localized parameters -->
|
<!-- disable warning CA1303: Do not pass literals as localized parameters -->
|
||||||
<Rule Id="CA1303" Action="None" />
|
<Rule Id="CA1303" Action="None" />
|
||||||
|
<!-- disable warning CA2000: Dispose objects before losing scope -->
|
||||||
|
<Rule Id="CA2000" Action="None" />
|
||||||
</Rules>
|
</Rules>
|
||||||
</RuleSet>
|
</RuleSet>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user