Added: Platform Detection Improvements

pull/2/head
Qstick 6 years ago
parent ea5ad24944
commit aef89160e2

@ -14,7 +14,7 @@ namespace NzbDrone.Api.Frontend
{ {
public bool IsCacheable(NancyContext context) public bool IsCacheable(NancyContext context)
{ {
if (!RuntimeInfoBase.IsProduction) if (!RuntimeInfo.IsProduction)
{ {
return false; return false;
} }
@ -46,4 +46,4 @@ namespace NzbDrone.Api.Frontend
return true; return true;
} }
} }
} }

@ -79,7 +79,7 @@ namespace NzbDrone.Api.Frontend.Mappers
private string GetIndexText() private string GetIndexText()
{ {
if (RuntimeInfoBase.IsProduction && _generatedContent != null) if (RuntimeInfo.IsProduction && _generatedContent != null)
{ {
return _generatedContent; return _generatedContent;
} }
@ -111,7 +111,7 @@ namespace NzbDrone.Api.Frontend.Mappers
text = text.Replace("APP_BRANCH", _configFileProvider.Branch.ToLower()); text = text.Replace("APP_BRANCH", _configFileProvider.Branch.ToLower());
text = text.Replace("APP_ANALYTICS", _analyticsService.IsEnabled.ToString().ToLowerInvariant()); text = text.Replace("APP_ANALYTICS", _analyticsService.IsEnabled.ToString().ToLowerInvariant());
text = text.Replace("URL_BASE", URL_BASE); text = text.Replace("URL_BASE", URL_BASE);
text = text.Replace("PRODUCTION", RuntimeInfoBase.IsProduction.ToString().ToLowerInvariant()); text = text.Replace("PRODUCTION", RuntimeInfo.IsProduction.ToString().ToLowerInvariant());
_generatedContent = text; _generatedContent = text;

@ -67,7 +67,7 @@ namespace NzbDrone.Api.Frontend.Mappers
private string GetLoginText() private string GetLoginText()
{ {
if (RuntimeInfoBase.IsProduction && _generatedContent != null) if (RuntimeInfo.IsProduction && _generatedContent != null)
{ {
return _generatedContent; return _generatedContent;
} }

@ -21,7 +21,7 @@ namespace NzbDrone.Api.Frontend.Mappers
_diskProvider = diskProvider; _diskProvider = diskProvider;
_logger = logger; _logger = logger;
if (!RuntimeInfoBase.IsProduction) if (!RuntimeInfo.IsProduction)
{ {
_caseSensitive = StringComparison.OrdinalIgnoreCase; _caseSensitive = StringComparison.OrdinalIgnoreCase;
} }

@ -1,4 +1,4 @@
using System.Linq; using System.Linq;
using Nancy.Bootstrapper; using Nancy.Bootstrapper;
using Nancy.Diagnostics; using Nancy.Diagnostics;
using NLog; using NLog;
@ -24,9 +24,9 @@ namespace NzbDrone.Api
protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines) protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
{ {
Logger.Info("Starting NzbDrone API"); Logger.Info("Starting Web Server");
if (RuntimeInfoBase.IsProduction) if (RuntimeInfo.IsProduction)
{ {
DiagnosticsHook.Disable(pipelines); DiagnosticsHook.Disable(pipelines);
} }

@ -1,4 +1,4 @@
using Nancy; using Nancy;
using Nancy.Routing; using Nancy.Routing;
using NzbDrone.Api.Extensions; using NzbDrone.Api.Extensions;
using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.EnvironmentInfo;
@ -13,6 +13,8 @@ namespace NzbDrone.Api.System
{ {
private readonly IAppFolderInfo _appFolderInfo; private readonly IAppFolderInfo _appFolderInfo;
private readonly IRuntimeInfo _runtimeInfo; private readonly IRuntimeInfo _runtimeInfo;
private readonly IPlatformInfo _platformInfo;
private readonly IOsInfo _osInfo;
private readonly IRouteCacheProvider _routeCacheProvider; private readonly IRouteCacheProvider _routeCacheProvider;
private readonly IConfigFileProvider _configFileProvider; private readonly IConfigFileProvider _configFileProvider;
private readonly IMainDatabase _database; private readonly IMainDatabase _database;
@ -20,14 +22,17 @@ namespace NzbDrone.Api.System
public SystemModule(IAppFolderInfo appFolderInfo, public SystemModule(IAppFolderInfo appFolderInfo,
IRuntimeInfo runtimeInfo, IRuntimeInfo runtimeInfo,
IPlatformInfo platformInfo,
IOsInfo osInfo,
IRouteCacheProvider routeCacheProvider, IRouteCacheProvider routeCacheProvider,
IConfigFileProvider configFileProvider, IConfigFileProvider configFileProvider,
IMainDatabase database, IMainDatabase database,
ILifecycleService lifecycleService) ILifecycleService lifecycleService) : base("system")
: base("system")
{ {
_appFolderInfo = appFolderInfo; _appFolderInfo = appFolderInfo;
_runtimeInfo = runtimeInfo; _runtimeInfo = runtimeInfo;
_platformInfo = platformInfo;
_osInfo = osInfo;
_routeCacheProvider = routeCacheProvider; _routeCacheProvider = routeCacheProvider;
_configFileProvider = configFileProvider; _configFileProvider = configFileProvider;
_database = database; _database = database;
@ -41,27 +46,29 @@ namespace NzbDrone.Api.System
private Response GetStatus() private Response GetStatus()
{ {
return new return new
{ {
Version = BuildInfo.Version.ToString(), Version = BuildInfo.Version.ToString(),
BuildTime = BuildInfo.BuildDateTime, BuildTime = BuildInfo.BuildDateTime,
IsDebug = BuildInfo.IsDebug, IsDebug = BuildInfo.IsDebug,
IsProduction = RuntimeInfoBase.IsProduction, IsProduction = RuntimeInfo.IsProduction,
IsAdmin = _runtimeInfo.IsAdmin, IsAdmin = _runtimeInfo.IsAdmin,
IsUserInteractive = RuntimeInfoBase.IsUserInteractive, IsUserInteractive = RuntimeInfo.IsUserInteractive,
StartupPath = _appFolderInfo.StartUpFolder, StartupPath = _appFolderInfo.StartUpFolder,
AppData = _appFolderInfo.GetAppDataPath(), AppData = _appFolderInfo.GetAppDataPath(),
OsVersion = OsInfo.Version.ToString(), OsName = _osInfo.Name,
IsMonoRuntime = OsInfo.IsMonoRuntime, OsVersion = _osInfo.Version,
IsMono = OsInfo.IsNotWindows, IsMonoRuntime = PlatformInfo.IsMono,
IsLinux = OsInfo.IsLinux, IsMono = PlatformInfo.IsMono,
IsOsx = OsInfo.IsOsx, IsLinux = OsInfo.IsLinux,
IsWindows = OsInfo.IsWindows, IsOsx = OsInfo.IsOsx,
Branch = _configFileProvider.Branch, IsWindows = OsInfo.IsWindows,
Authentication = _configFileProvider.AuthenticationMethod, Branch = _configFileProvider.Branch,
SqliteVersion = _database.Version, Authentication = _configFileProvider.AuthenticationMethod,
UrlBase = _configFileProvider.UrlBase, SqliteVersion = _database.Version,
RuntimeVersion = _runtimeInfo.RuntimeVersion UrlBase = _configFileProvider.UrlBase,
}.AsResponse(); RuntimeVersion = _platformInfo.Version,
RuntimeName = PlatformInfo.Platform
}.AsResponse();
} }
private Response GetRoutes() private Response GetRoutes()

@ -4,7 +4,7 @@
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/> <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0"/> <bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0"/>
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="FluentMigrator" publicKeyToken="aacfc7de5acabf05" culture="neutral"/> <assemblyIdentity name="FluentMigrator" publicKeyToken="aacfc7de5acabf05" culture="neutral"/>

@ -4,7 +4,7 @@
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" /> <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" /> <bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="FluentMigrator" publicKeyToken="aacfc7de5acabf05" culture="neutral" /> <assemblyIdentity name="FluentMigrator" publicKeyToken="aacfc7de5acabf05" culture="neutral" />

@ -29,7 +29,7 @@ namespace NzbDrone.Common.Test
[Test] [Test]
public void IsProduction_should_return_false_when_run_within_nunit() public void IsProduction_should_return_false_when_run_within_nunit()
{ {
RuntimeInfoBase.IsProduction.Should().BeFalse("Process name is " + Process.GetCurrentProcess().ProcessName + " Folder is " + Directory.GetCurrentDirectory()); RuntimeInfo.IsProduction.Should().BeFalse("Process name is " + Process.GetCurrentProcess().ProcessName + " Folder is " + Directory.GetCurrentDirectory());
} }
[Test] [Test]

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
@ -9,6 +9,7 @@ using Moq;
using NLog; using NLog;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common.Cache; using NzbDrone.Common.Cache;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
using NzbDrone.Common.Http.Dispatchers; using NzbDrone.Common.Http.Dispatchers;
using NzbDrone.Common.Http.Proxy; using NzbDrone.Common.Http.Proxy;
@ -30,6 +31,13 @@ namespace NzbDrone.Common.Test.Http
[SetUp] [SetUp]
public void SetUp() public void SetUp()
{ {
Mocker.GetMock<IPlatformInfo>().Setup(c => c.Version).Returns(new Version("1.0.0"));
Mocker.GetMock<IOsInfo>().Setup(c => c.Name).Returns("TestOS");
Mocker.GetMock<IOsInfo>().Setup(c => c.Version).Returns("9.0.0");
Mocker.SetConstant<IUserAgentBuilder>(Mocker.Resolve<UserAgentBuilder>());
Mocker.SetConstant<ICacheManager>(Mocker.Resolve<CacheManager>()); Mocker.SetConstant<ICacheManager>(Mocker.Resolve<CacheManager>());
Mocker.SetConstant<ICreateManagedWebProxy>(Mocker.Resolve<ManagedWebProxyFactory>()); Mocker.SetConstant<ICreateManagedWebProxy>(Mocker.Resolve<ManagedWebProxyFactory>());
Mocker.SetConstant<IRateLimitService>(Mocker.Resolve<RateLimitService>()); Mocker.SetConstant<IRateLimitService>(Mocker.Resolve<RateLimitService>());
@ -48,7 +56,7 @@ namespace NzbDrone.Common.Test.Http
[Test] [Test]
public void should_execute_simple_get() public void should_execute_simple_get()
{ {
var request = new HttpRequest(string.Format("http://{0}/get", _httpBinHost)); var request = new HttpRequest($"http://{_httpBinHost}/get");
var response = Subject.Execute(request); var response = Subject.Execute(request);
@ -58,7 +66,7 @@ namespace NzbDrone.Common.Test.Http
[Test] [Test]
public void should_execute_https_get() public void should_execute_https_get()
{ {
var request = new HttpRequest(string.Format("https://{0}/get", _httpBinHost)); var request = new HttpRequest($"https://{_httpBinHost}/get");
var response = Subject.Execute(request); var response = Subject.Execute(request);
@ -68,7 +76,7 @@ namespace NzbDrone.Common.Test.Http
[Test] [Test]
public void should_execute_typed_get() public void should_execute_typed_get()
{ {
var request = new HttpRequest(string.Format("http://{0}/get", _httpBinHost)); var request = new HttpRequest($"http://{_httpBinHost}/get");
var response = Subject.Get<HttpBinResource>(request); var response = Subject.Get<HttpBinResource>(request);
@ -80,7 +88,7 @@ namespace NzbDrone.Common.Test.Http
{ {
var message = "{ my: 1 }"; var message = "{ my: 1 }";
var request = new HttpRequest(string.Format("http://{0}/post", _httpBinHost)); var request = new HttpRequest($"http://{_httpBinHost}/post");
request.SetContent(message); request.SetContent(message);
var response = Subject.Post<HttpBinResource>(request); var response = Subject.Post<HttpBinResource>(request);
@ -91,7 +99,7 @@ namespace NzbDrone.Common.Test.Http
[TestCase("gzip")] [TestCase("gzip")]
public void should_execute_get_using_gzip(string compression) public void should_execute_get_using_gzip(string compression)
{ {
var request = new HttpRequest(string.Format("http://{0}/{1}", _httpBinHost, compression)); var request = new HttpRequest($"http://{_httpBinHost}/{compression}");
var response = Subject.Get<HttpBinResource>(request); var response = Subject.Get<HttpBinResource>(request);
@ -107,7 +115,7 @@ namespace NzbDrone.Common.Test.Http
[TestCase(HttpStatusCode.BadGateway)] [TestCase(HttpStatusCode.BadGateway)]
public void should_throw_on_unsuccessful_status_codes(int statusCode) public void should_throw_on_unsuccessful_status_codes(int statusCode)
{ {
var request = new HttpRequest(string.Format("http://{0}/status/{1}", _httpBinHost, statusCode)); var request = new HttpRequest($"http://{_httpBinHost}/status/{statusCode}");
var exception = Assert.Throws<HttpException>(() => Subject.Get<HttpBinResource>(request)); var exception = Assert.Throws<HttpException>(() => Subject.Get<HttpBinResource>(request));
@ -119,7 +127,7 @@ namespace NzbDrone.Common.Test.Http
[Test] [Test]
public void should_not_follow_redirects_when_not_in_production() public void should_not_follow_redirects_when_not_in_production()
{ {
var request = new HttpRequest(string.Format("http://{0}/redirect/1", _httpBinHost)); var request = new HttpRequest($"http://{_httpBinHost}/redirect/1");
Subject.Get(request); Subject.Get(request);
@ -129,7 +137,7 @@ namespace NzbDrone.Common.Test.Http
[Test] [Test]
public void should_follow_redirects() public void should_follow_redirects()
{ {
var request = new HttpRequest(string.Format("http://{0}/redirect/1", _httpBinHost)); var request = new HttpRequest($"http://{_httpBinHost}/redirect/1");
request.AllowAutoRedirect = true; request.AllowAutoRedirect = true;
var response = Subject.Get(request); var response = Subject.Get(request);
@ -182,7 +190,7 @@ namespace NzbDrone.Common.Test.Http
[Test] [Test]
public void should_send_user_agent() public void should_send_user_agent()
{ {
var request = new HttpRequest(string.Format("http://{0}/get", _httpBinHost)); var request = new HttpRequest($"http://{_httpBinHost}/get");
var response = Subject.Get<HttpBinResource>(request); var response = Subject.Get<HttpBinResource>(request);
@ -196,7 +204,7 @@ namespace NzbDrone.Common.Test.Http
[TestCase("Accept", "text/xml, text/rss+xml, application/rss+xml")] [TestCase("Accept", "text/xml, text/rss+xml, application/rss+xml")]
public void should_send_headers(string header, string value) public void should_send_headers(string header, string value)
{ {
var request = new HttpRequest(string.Format("http://{0}/get", _httpBinHost)); var request = new HttpRequest($"http://{_httpBinHost}/get");
request.Headers.Add(header, value); request.Headers.Add(header, value);
var response = Subject.Get<HttpBinResource>(request); var response = Subject.Get<HttpBinResource>(request);
@ -219,7 +227,7 @@ namespace NzbDrone.Common.Test.Http
[Test] [Test]
public void should_send_cookie() public void should_send_cookie()
{ {
var request = new HttpRequest(string.Format("http://{0}/get", _httpBinHost)); var request = new HttpRequest($"http://{_httpBinHost}/get");
request.Cookies["my"] = "cookie"; request.Cookies["my"] = "cookie";
var response = Subject.Get<HttpBinResource>(request); var response = Subject.Get<HttpBinResource>(request);
@ -236,7 +244,7 @@ namespace NzbDrone.Common.Test.Http
var oldRequest = new HttpRequest("http://eu.httpbin.org/get"); var oldRequest = new HttpRequest("http://eu.httpbin.org/get");
oldRequest.Cookies["my"] = "cookie"; oldRequest.Cookies["my"] = "cookie";
var oldClient = new HttpClient(new IHttpRequestInterceptor[0], Mocker.Resolve<ICacheManager>(), Mocker.Resolve<IRateLimitService>(), Mocker.Resolve<IHttpDispatcher>(), Mocker.Resolve<Logger>()); var oldClient = new HttpClient(new IHttpRequestInterceptor[0], Mocker.Resolve<ICacheManager>(), Mocker.Resolve<IRateLimitService>(), Mocker.Resolve<IHttpDispatcher>(), Mocker.GetMock<IUserAgentBuilder>().Object, Mocker.Resolve<Logger>());
oldClient.Should().NotBeSameAs(Subject); oldClient.Should().NotBeSameAs(Subject);
@ -329,7 +337,7 @@ namespace NzbDrone.Common.Test.Http
[Test] [Test]
public void should_not_store_response_cookie() public void should_not_store_response_cookie()
{ {
var requestSet = new HttpRequest(string.Format("http://{0}/cookies/set?my=cookie", _httpBinHost)); var requestSet = new HttpRequest($"http://{_httpBinHost}/cookies/set?my=cookie");
requestSet.AllowAutoRedirect = false; requestSet.AllowAutoRedirect = false;
requestSet.StoreRequestCookie = false; requestSet.StoreRequestCookie = false;
requestSet.StoreResponseCookie.Should().BeFalse(); requestSet.StoreResponseCookie.Should().BeFalse();
@ -348,7 +356,7 @@ namespace NzbDrone.Common.Test.Http
[Test] [Test]
public void should_store_response_cookie() public void should_store_response_cookie()
{ {
var requestSet = new HttpRequest(string.Format("http://{0}/cookies/set?my=cookie", _httpBinHost)); var requestSet = new HttpRequest($"http://{_httpBinHost}/cookies/set?my=cookie");
requestSet.AllowAutoRedirect = false; requestSet.AllowAutoRedirect = false;
requestSet.StoreRequestCookie = false; requestSet.StoreRequestCookie = false;
requestSet.StoreResponseCookie = true; requestSet.StoreResponseCookie = true;
@ -527,7 +535,7 @@ namespace NzbDrone.Common.Test.Http
[Test] [Test]
public void should_throw_on_http429_too_many_requests() public void should_throw_on_http429_too_many_requests()
{ {
var request = new HttpRequest(string.Format("http://{0}/status/429", _httpBinHost)); var request = new HttpRequest($"http://{_httpBinHost}/status/429");
Assert.Throws<TooManyRequestsException>(() => Subject.Get(request)); Assert.Throws<TooManyRequestsException>(() => Subject.Get(request));
@ -547,7 +555,7 @@ namespace NzbDrone.Common.Test.Http
.Setup(v => v.PostResponse(It.IsAny<HttpResponse>())) .Setup(v => v.PostResponse(It.IsAny<HttpResponse>()))
.Returns<HttpResponse>(r => r); .Returns<HttpResponse>(r => r);
var request = new HttpRequest(string.Format("http://{0}/get", _httpBinHost)); var request = new HttpRequest($"http://{_httpBinHost}/get");
Subject.Get(request); Subject.Get(request);
@ -569,7 +577,7 @@ namespace NzbDrone.Common.Test.Http
{ {
// the date is bad in the below - should be 13-Jul-2026 // the date is bad in the below - should be 13-Jul-2026
string malformedCookie = @"__cfduid=d29e686a9d65800021c66faca0a29b4261436890790; expires=Mon, 13-Jul-26 16:19:50 GMT; path=/; HttpOnly"; string malformedCookie = @"__cfduid=d29e686a9d65800021c66faca0a29b4261436890790; expires=Mon, 13-Jul-26 16:19:50 GMT; path=/; HttpOnly";
var requestSet = new HttpRequestBuilder(string.Format("http://{0}/response-headers", _httpBinHost)) var requestSet = new HttpRequestBuilder($"http://{_httpBinHost}/response-headers")
.AddQueryParam("Set-Cookie", malformedCookie) .AddQueryParam("Set-Cookie", malformedCookie)
.Build(); .Build();
@ -578,7 +586,7 @@ namespace NzbDrone.Common.Test.Http
var responseSet = Subject.Get(requestSet); var responseSet = Subject.Get(requestSet);
var request = new HttpRequest(string.Format("http://{0}/get", _httpBinHost)); var request = new HttpRequest($"http://{_httpBinHost}/get");
var response = Subject.Get<HttpBinResource>(request); var response = Subject.Get<HttpBinResource>(request);
@ -602,7 +610,8 @@ namespace NzbDrone.Common.Test.Http
{ {
try try
{ {
string url = string.Format("http://{0}/response-headers?Set-Cookie={1}", _httpBinHost, Uri.EscapeUriString(malformedCookie)); string url =
$"http://{_httpBinHost}/response-headers?Set-Cookie={Uri.EscapeUriString(malformedCookie)}";
var requestSet = new HttpRequest(url); var requestSet = new HttpRequest(url);
requestSet.AllowAutoRedirect = false; requestSet.AllowAutoRedirect = false;
@ -610,7 +619,7 @@ namespace NzbDrone.Common.Test.Http
var responseSet = Subject.Get(requestSet); var responseSet = Subject.Get(requestSet);
var request = new HttpRequest(string.Format("http://{0}/get", _httpBinHost)); var request = new HttpRequest($"http://{_httpBinHost}/get");
var response = Subject.Get<HttpBinResource>(request); var response = Subject.Get<HttpBinResource>(request);

@ -0,0 +1,30 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Http;
using NzbDrone.Test.Common;
namespace NzbDrone.Common.Test.Http
{
[TestFixture]
public class UserAgentBuilderFixture : TestBase<UserAgentBuilder>
{
[Test]
public void should_get_user_agent_if_os_version_is_null()
{
Mocker.GetMock<IOsInfo>().SetupGet(c => c.Version).Returns((string)null);
Mocker.GetMock<IOsInfo>().SetupGet(c => c.Name).Returns("TestOS");
Subject.GetUserAgent(false).Should().NotBeNullOrWhiteSpace();
}
[Test]
public void should_get_use_os_family_if_name_is_null()
{
Mocker.GetMock<IOsInfo>().SetupGet(c => c.Version).Returns((string)null);
Mocker.GetMock<IOsInfo>().SetupGet(c => c.Name).Returns((string)null);
Subject.GetUserAgent(false).Should().NotBeNullOrWhiteSpace();
}
}
}

@ -92,6 +92,7 @@
<Compile Include="Http\HttpRequestBuilderFixture.cs" /> <Compile Include="Http\HttpRequestBuilderFixture.cs" />
<Compile Include="Http\HttpRequestFixture.cs" /> <Compile Include="Http\HttpRequestFixture.cs" />
<Compile Include="Http\HttpUriFixture.cs" /> <Compile Include="Http\HttpUriFixture.cs" />
<Compile Include="Http\UserAgentBuilderFixture.cs" />
<Compile Include="InstrumentationTests\CleanseLogMessageFixture.cs" /> <Compile Include="InstrumentationTests\CleanseLogMessageFixture.cs" />
<Compile Include="LevenshteinDistanceFixture.cs" /> <Compile Include="LevenshteinDistanceFixture.cs" />
<Compile Include="OsPathFixture.cs" /> <Compile Include="OsPathFixture.cs" />

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
@ -13,12 +13,15 @@ namespace NzbDrone.Common.Composition
{ {
private readonly List<Type> _loadedTypes; private readonly List<Type> _loadedTypes;
public IContainer Container { get; private set; } protected IContainer Container { get; }
protected ContainerBuilderBase(IStartupContext args, params string[] assemblies) protected ContainerBuilderBase(IStartupContext args, List<string> assemblies)
{ {
_loadedTypes = new List<Type>(); _loadedTypes = new List<Type>();
assemblies.Add(OsInfo.IsWindows ? "NzbDrone.Windows" : "NzbDrone.Mono");
assemblies.Add("NzbDrone.Common");
foreach (var assembly in assemblies) foreach (var assembly in assemblies)
{ {
_loadedTypes.AddRange(Assembly.Load(assembly).GetTypes()); _loadedTypes.AddRange(Assembly.Load(assembly).GetTypes());

@ -17,6 +17,19 @@ namespace NzbDrone.Common.Disk
{ {
private static readonly Logger Logger = NzbDroneLogger.GetLogger(typeof(DiskProviderBase)); private static readonly Logger Logger = NzbDroneLogger.GetLogger(typeof(DiskProviderBase));
public static StringComparison PathStringComparison
{
get
{
if (OsInfo.IsWindows)
{
return StringComparison.OrdinalIgnoreCase;
}
return StringComparison.Ordinal;
}
}
public abstract long? GetAvailableSpace(string path); public abstract long? GetAvailableSpace(string path);
public abstract void InheritFolderPermissions(string filename); public abstract void InheritFolderPermissions(string filename);
public abstract void SetPermissions(string path, string mask, string user, string group); public abstract void SetPermissions(string path, string mask, string user, string group);
@ -87,7 +100,7 @@ namespace NzbDrone.Common.Disk
public bool FileExists(string path) public bool FileExists(string path)
{ {
Ensure.That(path, () => path).IsValidPath(); Ensure.That(path, () => path).IsValidPath();
return FileExists(path, OsInfo.PathStringComparison); return FileExists(path, PathStringComparison);
} }
public bool FileExists(string path, StringComparison stringComparison) public bool FileExists(string path, StringComparison stringComparison)

@ -101,12 +101,12 @@ namespace NzbDrone.Common.EnsureThat
if (param.Value.IsPathValid()) return param; if (param.Value.IsPathValid()) return param;
if (OsInfo.IsNotWindows) if (OsInfo.IsWindows)
{ {
throw ExceptionFactory.CreateForParamValidation(param.Name, string.Format("value [{0}] is not a valid *nix path. paths must start with /", param.Value)); throw ExceptionFactory.CreateForParamValidation(param.Name, string.Format("value [{0}] is not a valid Windows path. paths must be a full path eg. C:\\Windows", param.Value));
} }
throw ExceptionFactory.CreateForParamValidation(param.Name, string.Format("value [{0}] is not a valid Windows path. paths must be a full path eg. C:\\Windows", param.Value)); throw ExceptionFactory.CreateForParamValidation(param.Name, string.Format("value [{0}] is not a valid *nix path. paths must start with /", param.Value));
} }
} }
} }

@ -0,0 +1,9 @@
namespace NzbDrone.Common.EnvironmentInfo
{
public interface IOperatingSystemVersionInfo
{
string Version { get; }
string Name { get; }
string FullName { get; }
}
}

@ -0,0 +1,9 @@
namespace NzbDrone.Common.EnvironmentInfo
{
public interface IOsVersionAdapter
{
bool Enabled { get; }
OsVersionModel Read();
}
}

@ -0,0 +1,50 @@
using System;
namespace NzbDrone.Common.EnvironmentInfo
{
public enum PlatformType
{
DotNet = 0,
Mono = 1
}
public interface IPlatformInfo
{
Version Version { get; }
}
public abstract class PlatformInfo : IPlatformInfo
{
static PlatformInfo()
{
if (Type.GetType("Mono.Runtime") != null)
{
Platform = PlatformType.Mono;
}
else
{
Platform = PlatformType.DotNet;
}
}
public static PlatformType Platform { get; }
public static bool IsMono => Platform == PlatformType.Mono;
public static bool IsDotNet => Platform == PlatformType.DotNet;
public static string PlatformName
{
get
{
if (IsDotNet)
{
return ".NET";
}
return "Mono";
}
}
public abstract Version Version { get; }
}
}

@ -1,14 +1,13 @@
namespace NzbDrone.Common.EnvironmentInfo namespace NzbDrone.Common.EnvironmentInfo
{ {
public interface IRuntimeInfo public interface IRuntimeInfo
{ {
bool IsUserInteractive { get; } bool IsUserInteractive { get; }
bool IsAdmin { get; } bool IsAdmin { get; }
bool IsWindowsService { get; } bool IsWindowsService { get; }
bool IsConsole { get; } bool IsWindowsTray { get; }
bool IsRunning { get; set; } bool IsExiting { get; set; }
bool RestartPending { get; set; } bool RestartPending { get; set; }
string ExecutingApplication { get; } string ExecutingApplication { get; }
string RuntimeVersion { get; }
} }
} }

@ -1,93 +1,101 @@
using System; using System;
using System.Diagnostics; using System.Collections.Generic;
using System.Globalization; using System.IO;
using System.Runtime.InteropServices; using System.Linq;
using NLog;
namespace NzbDrone.Common.EnvironmentInfo namespace NzbDrone.Common.EnvironmentInfo
{ {
public static class OsInfo public class OsInfo : IOsInfo
{ {
public static Os Os { get; }
static OsInfo() public static bool IsNotWindows => !IsWindows;
{ public static bool IsLinux => Os == Os.Linux;
var platform = (int)Environment.OSVersion.Platform; public static bool IsOsx => Os == Os.Osx;
public static bool IsWindows => Os == Os.Windows;
Version = Environment.OSVersion.Version; public string Version { get; }
public string Name { get; }
public string FullName { get; }
IsMonoRuntime = Type.GetType("Mono.Runtime") != null; static OsInfo()
IsNotWindows = (platform == 4) || (platform == 6) || (platform == 128); {
IsOsx = IsRunningOnMac(); var platform = Environment.OSVersion.Platform;
IsLinux = IsNotWindows && !IsOsx;
IsWindows = !IsNotWindows;
FirstDayOfWeek = CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek;
if (IsWindows) switch (platform)
{
Os = Os.Windows;
PathStringComparison = StringComparison.OrdinalIgnoreCase;
}
else
{ {
Os = IsOsx ? Os.Osx : Os.Linux; case PlatformID.Win32NT:
{
PathStringComparison = StringComparison.Ordinal; Os = Os.Windows;
break;
}
case PlatformID.MacOSX:
case PlatformID.Unix:
{
// Sometimes Mac OS reports itself as Unix
if (Directory.Exists("/System/Library/CoreServices/") &&
(File.Exists("/System/Library/CoreServices/SystemVersion.plist") ||
File.Exists("/System/Library/CoreServices/ServerVersion.plist"))
)
{
Os = Os.Osx;
}
else
{
Os = Os.Linux;
}
break;
}
} }
} }
public static Version Version { get; private set; } public OsInfo(IEnumerable<IOsVersionAdapter> versionAdapters, Logger logger)
public static bool IsMonoRuntime { get; private set; }
public static bool IsNotWindows { get; private set; }
public static bool IsLinux { get; private set; }
public static bool IsOsx { get; private set; }
public static bool IsWindows { get; private set; }
public static Os Os { get; private set; }
public static DayOfWeek FirstDayOfWeek { get; private set; }
public static StringComparison PathStringComparison { get; private set; }
//Borrowed from: https://github.com/jpobst/Pinta/blob/master/Pinta.Core/Managers/SystemManager.cs
//From Managed.Windows.Forms/XplatUI
[DllImport("libc")]
static extern int uname(IntPtr buf);
[DebuggerStepThrough]
static bool IsRunningOnMac()
{ {
var buf = IntPtr.Zero; OsVersionModel osInfo = null;
try foreach (var osVersionAdapter in versionAdapters.Where(c => c.Enabled))
{ {
buf = Marshal.AllocHGlobal(8192); try
// This is a hacktastic way of getting sysname from uname ()
if (uname(buf) == 0)
{ {
var os = Marshal.PtrToStringAnsi(buf); osInfo = osVersionAdapter.Read();
}
catch (Exception e)
{
logger.Error(e, "Couldn't get OS Version info");
}
if (os == "Darwin") if (osInfo != null)
{ {
return true; break;
}
} }
} }
catch
if (osInfo != null)
{ {
Name = osInfo.Name;
Version = osInfo.Version;
FullName = osInfo.FullName;
} }
finally else
{ {
if (buf != IntPtr.Zero) Name = Os.ToString();
{ FullName = Name;
Marshal.FreeHGlobal(buf);
}
} }
return false;
} }
} }
public interface IOsInfo
{
string Version { get; }
string Name { get; }
string FullName { get; }
}
public enum Os public enum Os
{ {
Windows, Windows,
Linux, Linux,
Osx Osx
} }
} }

@ -0,0 +1,29 @@
namespace NzbDrone.Common.EnvironmentInfo
{
public class OsVersionModel
{
public OsVersionModel(string name, string version, string fullName = null)
{
Name = Trim(name);
Version = Trim(version);
if (string.IsNullOrWhiteSpace(fullName))
{
fullName = $"{Name} {Version}";
}
FullName = Trim(fullName);
}
private static string Trim(string source)
{
return source.Trim().Trim('"', '\'');
}
public string Name { get; }
public string FullName { get; }
public string Version { get; }
}
}

@ -1,4 +1,4 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
@ -9,11 +9,11 @@ using NzbDrone.Common.Processes;
namespace NzbDrone.Common.EnvironmentInfo namespace NzbDrone.Common.EnvironmentInfo
{ {
public abstract class RuntimeInfoBase : IRuntimeInfo public class RuntimeInfo : IRuntimeInfo
{ {
private readonly Logger _logger; private readonly Logger _logger;
public RuntimeInfoBase(IServiceProvider serviceProvider, Logger logger) public RuntimeInfo(IServiceProvider serviceProvider, Logger logger)
{ {
_logger = logger; _logger = logger;
@ -28,10 +28,11 @@ namespace NzbDrone.Common.EnvironmentInfo
if (entry != null) if (entry != null)
{ {
ExecutingApplication = entry.Location; ExecutingApplication = entry.Location;
IsWindowsTray = entry.ManifestModule.Name == $"{ProcessProvider.NZB_DRONE_PROCESS_NAME}.exe";
} }
} }
static RuntimeInfoBase() static RuntimeInfo()
{ {
IsProduction = InternalIsProduction(); IsProduction = InternalIsProduction();
} }
@ -59,31 +60,18 @@ namespace NzbDrone.Common.EnvironmentInfo
public bool IsWindowsService { get; private set; } public bool IsWindowsService { get; private set; }
public bool IsConsole public bool IsExiting { get; set; }
{
get
{
if (OsInfo.IsWindows)
{
return IsUserInteractive && Process.GetCurrentProcess().ProcessName.Equals(ProcessProvider.NZB_DRONE_CONSOLE_PROCESS_NAME, StringComparison.InvariantCultureIgnoreCase);
}
return true;
}
}
public bool IsRunning { get; set; }
public bool RestartPending { get; set; } public bool RestartPending { get; set; }
public string ExecutingApplication { get; private set; } public string ExecutingApplication { get; }
public abstract string RuntimeVersion { get; } public static bool IsProduction { get; }
public static bool IsProduction { get; private set; }
private static bool InternalIsProduction() private static bool InternalIsProduction()
{ {
if (BuildInfo.IsDebug || Debugger.IsAttached) return false; if (BuildInfo.IsDebug || Debugger.IsAttached) return false;
if (BuildInfo.Version.Revision > 10000) return false; //Official builds will never have such a high revision
//Official builds will never have such a high revision
if (BuildInfo.Version.Revision > 10000) return false;
try try
{ {
@ -99,21 +87,23 @@ namespace NzbDrone.Common.EnvironmentInfo
} }
try try
{ {
var currentAssmeblyLocation = typeof(RuntimeInfoBase).Assembly.Location; var currentAssmeblyLocation = typeof(RuntimeInfo).Assembly.Location;
if(currentAssmeblyLocation.ToLower().Contains("_output"))return false; if (currentAssmeblyLocation.ToLower().Contains("_output")) return false;
} }
catch catch
{ {
} }
string lowerCurrentDir = Directory.GetCurrentDirectory().ToLower(); var lowerCurrentDir = Directory.GetCurrentDirectory().ToLower();
if (lowerCurrentDir.Contains("teamcity")) return false; if (lowerCurrentDir.Contains("teamcity")) return false;
if (lowerCurrentDir.Contains("_output")) return false; if (lowerCurrentDir.Contains("_output")) return false;
return true; return true;
} }
public bool IsWindowsTray { get; private set; }
} }
} }

@ -1,31 +0,0 @@
using System;
using NzbDrone.Common.EnvironmentInfo;
namespace NzbDrone.Common.Exceptron
{
public static class ExceptionExtentions
{
private const string IGNORE_FLAG = "exceptron_ignore";
public static Exception ExceptronIgnoreOnMono(this Exception exception)
{
if (OsInfo.IsNotWindows)
{
exception.ExceptronIgnore();
}
return exception;
}
public static Exception ExceptronIgnore(this Exception exception)
{
exception.Data.Add(IGNORE_FLAG, true);
return exception;
}
public static bool ExceptronShouldIgnore(this Exception exception)
{
return exception.Data.Contains(IGNORE_FLAG);
}
}
}

@ -1,7 +1,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using NzbDrone.Common.Disk;
using NzbDrone.Common.EnsureThat; using NzbDrone.Common.EnsureThat;
using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.EnvironmentInfo;
@ -47,7 +48,7 @@ namespace NzbDrone.Common.Extensions
{ {
if (!comparison.HasValue) if (!comparison.HasValue)
{ {
comparison = OsInfo.PathStringComparison; comparison = DiskProviderBase.PathStringComparison;
} }
if (firstPath.Equals(secondPath, comparison.Value)) return true; if (firstPath.Equals(secondPath, comparison.Value)) return true;
@ -93,7 +94,7 @@ namespace NzbDrone.Common.Extensions
while (child.Parent != null) while (child.Parent != null)
{ {
if (child.Parent.FullName.Equals(parent.FullName, OsInfo.PathStringComparison)) if (child.Parent.FullName.Equals(parent.FullName, DiskProviderBase.PathStringComparison))
{ {
return true; return true;
} }
@ -275,4 +276,4 @@ namespace NzbDrone.Common.Extensions
return Path.Combine(appFolderInfo.StartUpFolder, NLOG_CONFIG_FILE); return Path.Combine(appFolderInfo.StartUpFolder, NLOG_CONFIG_FILE);
} }
} }
} }

@ -21,6 +21,7 @@ namespace NzbDrone.Common.Http.Dispatchers
private static readonly Regex ExpiryDate = new Regex(@"(expires=)([^;]+)", RegexOptions.IgnoreCase | RegexOptions.Compiled); private static readonly Regex ExpiryDate = new Regex(@"(expires=)([^;]+)", RegexOptions.IgnoreCase | RegexOptions.Compiled);
private readonly IHttpProxySettingsProvider _proxySettingsProvider; private readonly IHttpProxySettingsProvider _proxySettingsProvider;
private readonly IUserAgentBuilder _userAgentBuilder;
private readonly Logger _logger; private readonly Logger _logger;
private const string _caBundleFileName = "curl-ca-bundle.crt"; private const string _caBundleFileName = "curl-ca-bundle.crt";
@ -38,9 +39,10 @@ namespace NzbDrone.Common.Http.Dispatchers
} }
} }
public CurlHttpDispatcher(IHttpProxySettingsProvider proxySettingsProvider, Logger logger) public CurlHttpDispatcher(IHttpProxySettingsProvider proxySettingsProvider, IUserAgentBuilder userAgentBuilder, Logger logger)
{ {
_proxySettingsProvider = proxySettingsProvider; _proxySettingsProvider = proxySettingsProvider;
_userAgentBuilder = userAgentBuilder;
_logger = logger; _logger = logger;
} }

@ -10,21 +10,23 @@ namespace NzbDrone.Common.Http.Dispatchers
{ {
private readonly ManagedHttpDispatcher _managedDispatcher; private readonly ManagedHttpDispatcher _managedDispatcher;
private readonly CurlHttpDispatcher _curlDispatcher; private readonly CurlHttpDispatcher _curlDispatcher;
private readonly IPlatformInfo _platformInfo;
private readonly Logger _logger; private readonly Logger _logger;
private readonly ICached<bool> _curlTLSFallbackCache; private readonly ICached<bool> _curlTLSFallbackCache;
public FallbackHttpDispatcher(ManagedHttpDispatcher managedDispatcher, CurlHttpDispatcher curlDispatcher, ICacheManager cacheManager, Logger logger) public FallbackHttpDispatcher(ManagedHttpDispatcher managedDispatcher, CurlHttpDispatcher curlDispatcher, ICacheManager cacheManager, IPlatformInfo platformInfo, Logger logger)
{ {
_managedDispatcher = managedDispatcher; _managedDispatcher = managedDispatcher;
_curlDispatcher = curlDispatcher; _curlDispatcher = curlDispatcher;
_platformInfo = platformInfo;
_curlTLSFallbackCache = cacheManager.GetCache<bool>(GetType(), "curlTLSFallback"); _curlTLSFallbackCache = cacheManager.GetCache<bool>(GetType(), "curlTLSFallback");
_logger = logger; _logger = logger;
} }
public HttpResponse GetResponse(HttpRequest request, CookieContainer cookies) public HttpResponse GetResponse(HttpRequest request, CookieContainer cookies)
{ {
if (OsInfo.IsMonoRuntime && request.Url.Scheme == "https") if (PlatformInfo.IsMono && request.Url.Scheme == "https")
{ {
if (!_curlTLSFallbackCache.Find(request.Url.Host)) if (!_curlTLSFallbackCache.Find(request.Url.Host))
{ {

@ -13,22 +13,35 @@ namespace NzbDrone.Common.Http.Dispatchers
{ {
private readonly IHttpProxySettingsProvider _proxySettingsProvider; private readonly IHttpProxySettingsProvider _proxySettingsProvider;
private readonly ICreateManagedWebProxy _createManagedWebProxy; private readonly ICreateManagedWebProxy _createManagedWebProxy;
private readonly IUserAgentBuilder _userAgentBuilder;
public ManagedHttpDispatcher(IHttpProxySettingsProvider proxySettingsProvider, ICreateManagedWebProxy createManagedWebProxy) public ManagedHttpDispatcher(IHttpProxySettingsProvider proxySettingsProvider, ICreateManagedWebProxy createManagedWebProxy, IUserAgentBuilder userAgentBuilder)
{ {
_proxySettingsProvider = proxySettingsProvider; _proxySettingsProvider = proxySettingsProvider;
_createManagedWebProxy = createManagedWebProxy; _createManagedWebProxy = createManagedWebProxy;
_userAgentBuilder = userAgentBuilder;
} }
public HttpResponse GetResponse(HttpRequest request, CookieContainer cookies) public HttpResponse GetResponse(HttpRequest request, CookieContainer cookies)
{ {
HttpWebResponse httpWebResponse = null; var webRequest = (HttpWebRequest)WebRequest.Create((Uri)request.Url);
HttpWebRequest webRequest = null;
try // Deflate is not a standard and could break depending on implementation.
// we should just stick with the more compatible Gzip
//http://stackoverflow.com/questions/8490718/how-to-decompress-stream-deflated-with-java-util-zip-deflater-in-net
webRequest.AutomaticDecompression = DecompressionMethods.GZip;
webRequest.Method = request.Method.ToString();
webRequest.UserAgent = _userAgentBuilder.GetUserAgent(request.UseSimplifiedUserAgent);
webRequest.KeepAlive = request.ConnectionKeepAlive;
webRequest.AllowAutoRedirect = false;
webRequest.CookieContainer = cookies;
if (request.RequestTimeout != TimeSpan.Zero)
{ {
webRequest = (HttpWebRequest) WebRequest.Create((Uri) request.Url); webRequest = (HttpWebRequest) WebRequest.Create((Uri) request.Url);
if (OsInfo.IsMonoRuntime) if (PlatformInfo.IsMonoRuntime)
{ {
// On Mono GZipStream/DeflateStream leaks memory if an exception is thrown, use an intermediate buffer in that case. // On Mono GZipStream/DeflateStream leaks memory if an exception is thrown, use an intermediate buffer in that case.
webRequest.AutomaticDecompression = DecompressionMethods.None; webRequest.AutomaticDecompression = DecompressionMethods.None;
@ -98,7 +111,7 @@ namespace NzbDrone.Common.Http.Dispatchers
{ {
data = responseStream.ToBytes(); data = responseStream.ToBytes();
if (OsInfo.IsMonoRuntime && httpWebResponse.ContentEncoding == "gzip") if (PlatformInfo.IsMonoRuntime && httpWebResponse.ContentEncoding == "gzip")
{ {
using (var compressedStream = new MemoryStream(data)) using (var compressedStream = new MemoryStream(data))
using (var gzip = new GZipStream(compressedStream, CompressionMode.Decompress)) using (var gzip = new GZipStream(compressedStream, CompressionMode.Decompress))

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
@ -31,12 +31,14 @@ namespace NzbDrone.Common.Http
private readonly ICached<CookieContainer> _cookieContainerCache; private readonly ICached<CookieContainer> _cookieContainerCache;
private readonly List<IHttpRequestInterceptor> _requestInterceptors; private readonly List<IHttpRequestInterceptor> _requestInterceptors;
private readonly IHttpDispatcher _httpDispatcher; private readonly IHttpDispatcher _httpDispatcher;
private readonly IUserAgentBuilder _userAgentBuilder;
public HttpClient(IEnumerable<IHttpRequestInterceptor> requestInterceptors, ICacheManager cacheManager, IRateLimitService rateLimitService, IHttpDispatcher httpDispatcher, Logger logger) public HttpClient(IEnumerable<IHttpRequestInterceptor> requestInterceptors, ICacheManager cacheManager, IRateLimitService rateLimitService, IHttpDispatcher httpDispatcher, IUserAgentBuilder userAgentBuilder, Logger logger)
{ {
_requestInterceptors = requestInterceptors.ToList(); _requestInterceptors = requestInterceptors.ToList();
_rateLimitService = rateLimitService; _rateLimitService = rateLimitService;
_httpDispatcher = httpDispatcher; _httpDispatcher = httpDispatcher;
_userAgentBuilder = userAgentBuilder;
_logger = logger; _logger = logger;
ServicePointManager.DefaultConnectionLimit = 12; ServicePointManager.DefaultConnectionLimit = 12;
@ -71,7 +73,7 @@ namespace NzbDrone.Common.Http
while (response.HasHttpRedirect); while (response.HasHttpRedirect);
} }
if (response.HasHttpRedirect && !RuntimeInfoBase.IsProduction) if (response.HasHttpRedirect && !RuntimeInfo.IsProduction)
{ {
_logger.Error("Server requested a redirect to [{0}] while in developer mode. Update the request URL to avoid this redirect.", response.Headers["Location"]); _logger.Error("Server requested a redirect to [{0}] while in developer mode. Update the request URL to avoid this redirect.", response.Headers["Location"]);
} }

@ -17,7 +17,7 @@ namespace NzbDrone.Common.Http
IgnorePersistentCookies = false; IgnorePersistentCookies = false;
Cookies = new Dictionary<string, string>(); Cookies = new Dictionary<string, string>();
if (!RuntimeInfoBase.IsProduction) if (!RuntimeInfo.IsProduction)
{ {
AllowAutoRedirect = false; AllowAutoRedirect = false;
} }

@ -2,19 +2,39 @@ using NzbDrone.Common.EnvironmentInfo;
namespace NzbDrone.Common.Http namespace NzbDrone.Common.Http
{ {
public static class UserAgentBuilder public interface IUserAgentBuilder
{ {
public static string UserAgent { get; private set; } string GetUserAgent(bool simplified = false);
public static string UserAgentSimplified { get; private set; } }
public class UserAgentBuilder : IUserAgentBuilder
{
private readonly string _userAgentSimplified;
private readonly string _userAgent;
static UserAgentBuilder() public string GetUserAgent(bool simplified)
{ {
UserAgent = string.Format("Radarr/{0} ({1} {2})", if (simplified)
BuildInfo.Version, {
OsInfo.Os, OsInfo.Version.ToString(2)); return _userAgentSimplified;
}
return _userAgent;
}
public UserAgentBuilder(IOsInfo osInfo)
{
var osName = OsInfo.Os.ToString();
if (!string.IsNullOrWhiteSpace(osInfo.Name))
{
osName = osInfo.Name.ToLower();
}
var osVersion = osInfo.Version?.ToLower();
UserAgentSimplified = string.Format("Radarr/{0}", _userAgent = $"Radarr/{BuildInfo.Version} ({osName} {osVersion})";
BuildInfo.Version.ToString(2)); _userAgentSimplified = $"Radarr/{BuildInfo.Version.ToString(2)}";
} }
} }
} }

@ -1,89 +0,0 @@
using System;
using NLog;
using NLog.Common;
using NLog.Layouts;
using NLog.Targets;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Exceptron;
using NzbDrone.Common.Exceptron.Configuration;
namespace NzbDrone.Common.Instrumentation
{
/// <summary>
/// <see cref="NLog"/> target for exceptron. Allows you to automatically report all
/// exceptions logged to Nlog/>
/// </summary>
[Target("Exceptron")]
public class ExceptronTarget : Target
{
/// <summary>
/// <see cref="ExceptronClient"/> instance that Nlog Target uses to report the exceptions.
/// </summary>
public IExceptronClient ExceptronClient { get; internal set; }
protected override void InitializeTarget()
{
var config = new ExceptronConfiguration
{
ApiKey = "d64e0a72845d495abc625af3a27cf5f5",
IncludeMachineName = true,
};
if (RuntimeInfoBase.IsProduction)
{
config.ApiKey = "82c0f66dd2d64d1480cc88b551c9bdd8";
}
ExceptronClient = new ExceptronClient(config, BuildInfo.Version);
}
/// <summary>
/// String that identifies the active user
/// </summary>
public Layout UserId { get; set; }
protected override void Write(LogEventInfo logEvent)
{
if (logEvent == null || logEvent.Exception == null || logEvent.Exception.ExceptronShouldIgnore()) return;
try
{
var exceptionData = new ExceptionData
{
Exception = logEvent.Exception,
Component = logEvent.LoggerName,
Message = logEvent.FormattedMessage,
};
if (UserId != null)
{
exceptionData.UserId = UserId.Render(logEvent);
}
if (logEvent.Level <= LogLevel.Info)
{
exceptionData.Severity = ExceptionSeverity.None;
}
else if (logEvent.Level <= LogLevel.Warn)
{
exceptionData.Severity = ExceptionSeverity.Warning;
}
else if (logEvent.Level <= LogLevel.Error)
{
exceptionData.Severity = ExceptionSeverity.Error;
}
else if (logEvent.Level <= LogLevel.Fatal)
{
exceptionData.Severity = ExceptionSeverity.Fatal;
}
ExceptronClient.SubmitException(exceptionData);
}
catch (Exception e)
{
InternalLogger.Warn("Unable to report exception. {0}", e);
}
}
}
}

@ -1,4 +1,4 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using NLog; using NLog;
using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.EnvironmentInfo;
@ -35,7 +35,7 @@ namespace NzbDrone.Common.Instrumentation
return; return;
} }
if (OsInfo.IsMonoRuntime) if (PlatformInfo.IsMono)
{ {
if (exception is TypeInitializationException && exception.InnerException is DllNotFoundException || if (exception is TypeInitializationException && exception.InnerException is DllNotFoundException ||
exception is DllNotFoundException) exception is DllNotFoundException)
@ -51,4 +51,4 @@ namespace NzbDrone.Common.Instrumentation
Logger.Fatal(exception, "EPIC FAIL: " + exception.Message); Logger.Fatal(exception, "EPIC FAIL: " + exception.Message);
} }
} }
} }

@ -1,4 +1,4 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using LogentriesNLog; using LogentriesNLog;
@ -48,7 +48,7 @@ namespace NzbDrone.Common.Instrumentation
} }
else else
{ {
if (inConsole && (OsInfo.IsNotWindows || RuntimeInfoBase.IsUserInteractive)) if (inConsole && (OsInfo.IsNotWindows || RuntimeInfo.IsUserInteractive))
{ {
RegisterConsole(); RegisterConsole();
} }
@ -152,16 +152,6 @@ namespace NzbDrone.Common.Instrumentation
LogManager.Configuration.LoggingRules.Add(loggingRule); LogManager.Configuration.LoggingRules.Add(loggingRule);
} }
private static void RegisterExceptron()
{
var exceptronTarget = new ExceptronTarget();
var rule = new LoggingRule("*", LogLevel.Warn, exceptronTarget);
LogManager.Configuration.AddTarget("ExceptronTarget", exceptronTarget);
LogManager.Configuration.LoggingRules.Add(rule);
}
public static Logger GetLogger(Type obj) public static Logger GetLogger(Type obj)
{ {
return LogManager.GetLogger(obj.Name.Replace("NzbDrone.", "")); return LogManager.GetLogger(obj.Name.Replace("NzbDrone.", ""));

@ -94,6 +94,10 @@
<Compile Include="Disk\RelativeFileSystemModel.cs" /> <Compile Include="Disk\RelativeFileSystemModel.cs" />
<Compile Include="Disk\FileSystemModel.cs" /> <Compile Include="Disk\FileSystemModel.cs" />
<Compile Include="Disk\FileSystemResult.cs" /> <Compile Include="Disk\FileSystemResult.cs" />
<Compile Include="EnvironmentInfo\IOperatingSystemVersionInfo.cs" />
<Compile Include="EnvironmentInfo\IOsVersionAdapter.cs" />
<Compile Include="EnvironmentInfo\IPlatformInfo.cs" />
<Compile Include="EnvironmentInfo\OsVersionModel.cs" />
<Compile Include="Extensions\DictionaryExtensions.cs" /> <Compile Include="Extensions\DictionaryExtensions.cs" />
<Compile Include="Disk\GdiPlusInterop.cs" /> <Compile Include="Disk\GdiPlusInterop.cs" />
<Compile Include="Disk\OsPath.cs" /> <Compile Include="Disk\OsPath.cs" />
@ -125,13 +129,12 @@
<Compile Include="EnvironmentInfo\BuildInfo.cs" /> <Compile Include="EnvironmentInfo\BuildInfo.cs" />
<Compile Include="EnvironmentInfo\OsInfo.cs" /> <Compile Include="EnvironmentInfo\OsInfo.cs" />
<Compile Include="EnvironmentInfo\IRuntimeInfo.cs" /> <Compile Include="EnvironmentInfo\IRuntimeInfo.cs" />
<Compile Include="EnvironmentInfo\RuntimeInfoBase.cs" /> <Compile Include="EnvironmentInfo\RuntimeInfo.cs" />
<Compile Include="EnvironmentInfo\StartupContext.cs" /> <Compile Include="EnvironmentInfo\StartupContext.cs" />
<Compile Include="Exceptions\NotParentException.cs" /> <Compile Include="Exceptions\NotParentException.cs" />
<Compile Include="Exceptions\NzbDroneException.cs" /> <Compile Include="Exceptions\NzbDroneException.cs" />
<Compile Include="Exceptron\Configuration\ExceptronConfiguration.cs" /> <Compile Include="Exceptron\Configuration\ExceptronConfiguration.cs" />
<Compile Include="Exceptron\ExceptionData.cs" /> <Compile Include="Exceptron\ExceptionData.cs" />
<Compile Include="Exceptron\ExceptionExtentions.cs" />
<Compile Include="Exceptron\ExceptionSeverity.cs" /> <Compile Include="Exceptron\ExceptionSeverity.cs" />
<Compile Include="Exceptron\ExceptronApiException.cs" /> <Compile Include="Exceptron\ExceptronApiException.cs" />
<Compile Include="Exceptron\ExceptronClient.cs" /> <Compile Include="Exceptron\ExceptronClient.cs" />
@ -194,7 +197,6 @@
<Compile Include="Extensions\IEnumerableExtensions.cs" /> <Compile Include="Extensions\IEnumerableExtensions.cs" />
<Compile Include="Http\UserAgentBuilder.cs" /> <Compile Include="Http\UserAgentBuilder.cs" />
<Compile Include="Instrumentation\CleanseLogMessage.cs" /> <Compile Include="Instrumentation\CleanseLogMessage.cs" />
<Compile Include="Instrumentation\ExceptronTarget.cs" />
<Compile Include="Instrumentation\Extensions\LoggerProgressExtensions.cs" /> <Compile Include="Instrumentation\Extensions\LoggerProgressExtensions.cs" />
<Compile Include="Instrumentation\GlobalExceptionHandlers.cs" /> <Compile Include="Instrumentation\GlobalExceptionHandlers.cs" />
<Compile Include="Instrumentation\LogEventExtensions.cs" /> <Compile Include="Instrumentation\LogEventExtensions.cs" />

@ -108,7 +108,7 @@ namespace NzbDrone.Common.Processes
public Process Start(string path, string args = null, StringDictionary environmentVariables = null, Action<string> onOutputDataReceived = null, Action<string> onErrorDataReceived = null) public Process Start(string path, string args = null, StringDictionary environmentVariables = null, Action<string> onOutputDataReceived = null, Action<string> onErrorDataReceived = null)
{ {
if (OsInfo.IsMonoRuntime && path.EndsWith(".exe", StringComparison.InvariantCultureIgnoreCase)) if (PlatformInfo.IsMono && path.EndsWith(".exe", StringComparison.InvariantCultureIgnoreCase))
{ {
args = GetMonoArgs(path, args); args = GetMonoArgs(path, args);
path = "mono"; path = "mono";
@ -192,7 +192,7 @@ namespace NzbDrone.Common.Processes
public Process SpawnNewProcess(string path, string args = null, StringDictionary environmentVariables = null) public Process SpawnNewProcess(string path, string args = null, StringDictionary environmentVariables = null)
{ {
if (OsInfo.IsMonoRuntime && path.EndsWith(".exe", StringComparison.InvariantCultureIgnoreCase)) if (PlatformInfo.IsMono && path.EndsWith(".exe", StringComparison.InvariantCultureIgnoreCase))
{ {
args = GetMonoArgs(path, args); args = GetMonoArgs(path, args);
path = "mono"; path = "mono";

@ -4,7 +4,7 @@
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/> <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0"/> <bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0"/>
</dependentAssembly> </dependentAssembly>
</assemblyBinding> </assemblyBinding>
</runtime> </runtime>

@ -1,9 +1,9 @@
using System.Collections.Specialized;
using System.Security.AccessControl;
using Moq; using Moq;
using System;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common.Cache; using NzbDrone.Common.Cache;
using NzbDrone.Common.Cloud; using NzbDrone.Common.Cloud;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
using NzbDrone.Common.Http.Dispatchers; using NzbDrone.Common.Http.Dispatchers;
using NzbDrone.Common.TPL; using NzbDrone.Common.TPL;
@ -24,12 +24,16 @@ namespace NzbDrone.Core.Test.Framework
{ {
protected void UseRealHttp() protected void UseRealHttp()
{ {
Mocker.GetMock<IPlatformInfo>().SetupGet(c => c.Version).Returns(new Version("3.0.0"));
Mocker.GetMock<IOsInfo>().SetupGet(c => c.Version).Returns("1.0.0");
Mocker.GetMock<IOsInfo>().SetupGet(c => c.Name).Returns("TestOS");
Mocker.SetConstant<IHttpProxySettingsProvider>(new HttpProxySettingsProvider(Mocker.Resolve<ConfigService>())); Mocker.SetConstant<IHttpProxySettingsProvider>(new HttpProxySettingsProvider(Mocker.Resolve<ConfigService>()));
Mocker.SetConstant<ICreateManagedWebProxy>(new ManagedWebProxyFactory(Mocker.Resolve<CacheManager>())); Mocker.SetConstant<ICreateManagedWebProxy>(new ManagedWebProxyFactory(Mocker.Resolve<CacheManager>()));
Mocker.SetConstant<ManagedHttpDispatcher>(new ManagedHttpDispatcher(Mocker.Resolve<IHttpProxySettingsProvider>(), Mocker.Resolve<ICreateManagedWebProxy>())); Mocker.SetConstant<ManagedHttpDispatcher>(new ManagedHttpDispatcher(Mocker.Resolve<IHttpProxySettingsProvider>(), Mocker.Resolve<ICreateManagedWebProxy>(), Mocker.Resolve<UserAgentBuilder>()));
Mocker.SetConstant<CurlHttpDispatcher>(new CurlHttpDispatcher(Mocker.Resolve<IHttpProxySettingsProvider>(), Mocker.Resolve<NLog.Logger>())); Mocker.SetConstant<CurlHttpDispatcher>(new CurlHttpDispatcher(Mocker.Resolve<IHttpProxySettingsProvider>(), Mocker.Resolve<UserAgentBuilder>(), Mocker.Resolve<NLog.Logger>()));
Mocker.SetConstant<IHttpProvider>(new HttpProvider(TestLogger)); Mocker.SetConstant<IHttpProvider>(new HttpProvider(TestLogger));
Mocker.SetConstant<IHttpClient>(new HttpClient(new IHttpRequestInterceptor[0], Mocker.Resolve<CacheManager>(), Mocker.Resolve<RateLimitService>(), Mocker.Resolve<FallbackHttpDispatcher>(), TestLogger)); Mocker.SetConstant<IHttpClient>(new HttpClient(new IHttpRequestInterceptor[0], Mocker.Resolve<CacheManager>(), Mocker.Resolve<RateLimitService>(), Mocker.Resolve<FallbackHttpDispatcher>(), Mocker.Resolve<UserAgentBuilder>(), TestLogger));
Mocker.SetConstant<IRadarrCloudRequestBuilder>(new RadarrCloudRequestBuilder()); Mocker.SetConstant<IRadarrCloudRequestBuilder>(new RadarrCloudRequestBuilder());
} }

@ -1,4 +1,5 @@
using NUnit.Framework; using System;
using NUnit.Framework;
using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Core.HealthCheck.Checks; using NzbDrone.Core.HealthCheck.Checks;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
@ -8,17 +9,12 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
[TestFixture] [TestFixture]
public class MonoVersionCheckFixture : CoreTest<MonoVersionCheck> public class MonoVersionCheckFixture : CoreTest<MonoVersionCheck>
{ {
[SetUp]
public void Setup()
{
MonoOnly();
}
private void GivenOutput(string version) private void GivenOutput(string version)
{ {
Mocker.GetMock<IRuntimeInfo>() MonoOnly();
.SetupGet(s => s.RuntimeVersion) Mocker.GetMock<IPlatformInfo>()
.Returns(string.Format("{0} (tarball Wed Sep 25 16:35:44 CDT 2013)", version)); .SetupGet(s => s.Version)
.Returns(new Version(version));
} }
[TestCase("3.10")] [TestCase("3.10")]

@ -2,6 +2,7 @@ using System;
using System.Linq; using System.Linq;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Update; using NzbDrone.Core.Update;
@ -10,6 +11,12 @@ namespace NzbDrone.Core.Test.UpdateTests
{ {
public class UpdatePackageProviderFixture : CoreTest<UpdatePackageProvider> public class UpdatePackageProviderFixture : CoreTest<UpdatePackageProvider>
{ {
[SetUp]
public void Setup()
{
Mocker.GetMock<IPlatformInfo>().SetupGet(c => c.Version).Returns(new Version("9.9.9"));
}
[Test] [Test]
public void no_update_when_version_higher() public void no_update_when_version_higher()
{ {

@ -17,6 +17,6 @@ namespace NzbDrone.Core.Analytics
_configFileProvider = configFileProvider; _configFileProvider = configFileProvider;
} }
public bool IsEnabled => _configFileProvider.AnalyticsEnabled && RuntimeInfoBase.IsProduction; public bool IsEnabled => _configFileProvider.AnalyticsEnabled && RuntimeInfo.IsProduction;
} }
} }

@ -5,7 +5,7 @@
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/> <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0"/> <bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0"/>
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="FluentMigrator" publicKeyToken="aacfc7de5acabf05" culture="neutral"/> <assemblyIdentity name="FluentMigrator" publicKeyToken="aacfc7de5acabf05" culture="neutral"/>

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using NLog; using NLog;
using NzbDrone.Common.EnsureThat; using NzbDrone.Common.EnsureThat;
@ -373,7 +374,7 @@ namespace NzbDrone.Core.Configuration
public int FirstDayOfWeek public int FirstDayOfWeek
{ {
get { return GetValueInt("FirstDayOfWeek", (int)OsInfo.FirstDayOfWeek); } get { return GetValueInt("FirstDayOfWeek", (int)CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek); }
set { SetValue("FirstDayOfWeek", value); } set { SetValue("FirstDayOfWeek", value); }
} }

@ -1,7 +1,6 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text.RegularExpressions;
using NLog; using NLog;
using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.EnvironmentInfo;
@ -9,50 +8,43 @@ namespace NzbDrone.Core.HealthCheck.Checks
{ {
public class MonoVersionCheck : HealthCheckBase public class MonoVersionCheck : HealthCheckBase
{ {
private readonly IRuntimeInfo _runtimeInfo; private readonly IPlatformInfo _platformInfo;
private readonly Logger _logger; private readonly Logger _logger;
private static readonly Regex VersionRegex = new Regex(@"(?<=\W|^)(?<version>\d+\.\d+(\.\d+)?(\.\d+)?)(?=\W)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
public MonoVersionCheck(IRuntimeInfo runtimeInfo, Logger logger) public MonoVersionCheck(IPlatformInfo platformInfo, Logger logger)
{ {
_runtimeInfo = runtimeInfo; _platformInfo = platformInfo;
_logger = logger; _logger = logger;
} }
public override HealthCheck Check() public override HealthCheck Check()
{ {
if (OsInfo.IsWindows) if (!PlatformInfo.IsMono)
{ {
return new HealthCheck(GetType()); return new HealthCheck(GetType());
} }
var versionString = _runtimeInfo.RuntimeVersion; var monoVersion = _platformInfo.Version;
var versionMatch = VersionRegex.Match(versionString);
if (versionMatch.Success) if (monoVersion == new Version("3.4.0") && HasMonoBug18599())
{ {
var version = new Version(versionMatch.Groups["version"].Value); _logger.Debug("Mono version 3.4.0, checking for Mono bug #18599 returned positive.");
return new HealthCheck(GetType(), HealthCheckResult.Error, "You are running an old and unsupported version of Mono with a known bug. You should upgrade to a higher version");
if (version == new Version(3, 4, 0) && HasMonoBug18599())
{
_logger.Debug("mono version 3.4.0, checking for mono bug #18599 returned positive.");
return new HealthCheck(GetType(), HealthCheckResult.Error, "your mono version 3.4.0 has a critical bug, you should upgrade to a higher version");
}
if (version == new Version(4, 4, 0) || version == new Version(4, 4, 1))
{
_logger.Debug("mono version {0}", version);
return new HealthCheck(GetType(), HealthCheckResult.Error, $"your mono version {version} has a bug that causes issues connecting to indexers/download clients");
}
if (version >= new Version(3, 10))
{
_logger.Debug("mono version is 3.10 or better: {0}", version.ToString());
return new HealthCheck(GetType());
}
} }
return new HealthCheck(GetType(), HealthCheckResult.Warning, "mono version is less than 3.10, upgrade for improved stability"); if (monoVersion == new Version("4.4.0") || monoVersion == new Version("4.4.1"))
{
_logger.Debug("Mono version {0}", monoVersion);
return new HealthCheck(GetType(), HealthCheckResult.Error, $"Your Mono version {monoVersion} has a bug that causes issues connecting to indexers/download clients. You should upgrade to a higher version");
}
if (monoVersion >= new Version("3.10"))
{
_logger.Debug("Mono version is 3.10 or better: {0}", monoVersion);
return new HealthCheck(GetType());
}
return new HealthCheck(GetType(), HealthCheckResult.Warning, "You are running an old and unsupported version of Mono. Please upgrade Mono for improved stability.");
} }
public override bool CheckOnConfigChange => false; public override bool CheckOnConfigChange => false;
@ -70,7 +62,8 @@ namespace NzbDrone.Core.HealthCheck.Checks
return false; return false;
} }
var fieldInfo = numberFormatterType.GetField("userFormatProvider", BindingFlags.Static | BindingFlags.NonPublic); var fieldInfo = numberFormatterType.GetField("userFormatProvider",
BindingFlags.Static | BindingFlags.NonPublic);
if (fieldInfo == null) if (fieldInfo == null)
{ {

@ -1,18 +1,14 @@
using NzbDrone.Common.Messaging; using NzbDrone.Common.Messaging;
namespace NzbDrone.Core.Lifecycle namespace NzbDrone.Core.Lifecycle
{ {
public class ApplicationShutdownRequested : IEvent public class ApplicationShutdownRequested : IEvent
{ {
public bool Restarting { get; set; } public bool Restarting { get; }
public ApplicationShutdownRequested() public ApplicationShutdownRequested(bool restarting = false)
{
}
public ApplicationShutdownRequested(bool restarting)
{ {
Restarting = restarting; Restarting = restarting;
} }
} }
} }

@ -189,7 +189,6 @@ namespace NzbDrone.Core.MediaFiles
catch (Exception ex) catch (Exception ex)
{ {
ex.ExceptronIgnoreOnMono();
_logger.Warn(ex, "Unable to set date of file [" + filePath + "]"); _logger.Warn(ex, "Unable to set date of file [" + filePath + "]");
} }
} }

@ -7,12 +7,10 @@ namespace NzbDrone.Core.Rest
{ {
public static RestClient BuildClient(string baseUrl) public static RestClient BuildClient(string baseUrl)
{ {
var restClient = new RestClient(baseUrl); var restClient = new RestClient(baseUrl)
{
restClient.UserAgent = string.Format("Radarr/{0} (RestSharp/{1}; {2}/{3})", UserAgent = $"Radarr/{BuildInfo.Version} ({OsInfo.Os})"
BuildInfo.Version, };
restClient.GetType().Assembly.GetName().Version,
OsInfo.Os, OsInfo.Version.ToString(2));
return restClient; return restClient;
} }

@ -1,4 +1,3 @@
using NLog;
using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
@ -14,16 +13,11 @@ namespace NzbDrone.Core.Update
private readonly IUpdatePackageProvider _updatePackageProvider; private readonly IUpdatePackageProvider _updatePackageProvider;
private readonly IConfigFileProvider _configFileProvider; private readonly IConfigFileProvider _configFileProvider;
private readonly Logger _logger;
public CheckUpdateService(IUpdatePackageProvider updatePackageProvider, public CheckUpdateService(IUpdatePackageProvider updatePackageProvider,
IConfigFileProvider configFileProvider, IConfigFileProvider configFileProvider)
Logger logger)
{ {
_updatePackageProvider = updatePackageProvider; _updatePackageProvider = updatePackageProvider;
_configFileProvider = configFileProvider; _configFileProvider = configFileProvider;
_logger = logger;
} }
public UpdatePackage AvailableUpdate() public UpdatePackage AvailableUpdate()
@ -31,4 +25,4 @@ namespace NzbDrone.Core.Update
return _updatePackageProvider.GetLatestUpdate(_configFileProvider.Branch, BuildInfo.Version); return _updatePackageProvider.GetLatestUpdate(_configFileProvider.Branch, BuildInfo.Version);
} }
} }
} }

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using NzbDrone.Common.Cloud; using NzbDrone.Common.Cloud;
using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.EnvironmentInfo;
@ -15,11 +15,13 @@ namespace NzbDrone.Core.Update
public class UpdatePackageProvider : IUpdatePackageProvider public class UpdatePackageProvider : IUpdatePackageProvider
{ {
private readonly IHttpClient _httpClient; private readonly IHttpClient _httpClient;
private readonly IPlatformInfo _platformInfo;
private readonly IHttpRequestBuilderFactory _requestBuilder; private readonly IHttpRequestBuilderFactory _requestBuilder;
public UpdatePackageProvider(IHttpClient httpClient, IRadarrCloudRequestBuilder requestBuilder) public UpdatePackageProvider(IHttpClient httpClient, IRadarrCloudRequestBuilder requestBuilder, IPlatformInfo platformInfo)
{ {
_httpClient = httpClient; _httpClient = httpClient;
_platformInfo = platformInfo;
_requestBuilder = requestBuilder.Services; _requestBuilder = requestBuilder.Services;
} }
@ -29,6 +31,7 @@ namespace NzbDrone.Core.Update
.Resource("/update/{branch}") .Resource("/update/{branch}")
.AddQueryParam("version", currentVersion) .AddQueryParam("version", currentVersion)
.AddQueryParam("os", OsInfo.Os.ToString().ToLowerInvariant()) .AddQueryParam("os", OsInfo.Os.ToString().ToLowerInvariant())
.AddQueryParam("runtimeVer", _platformInfo.Version)
.SetSegment("branch", branch) .SetSegment("branch", branch)
.Build(); .Build();
@ -45,6 +48,7 @@ namespace NzbDrone.Core.Update
.Resource("/update/{branch}/changes") .Resource("/update/{branch}/changes")
.AddQueryParam("version", currentVersion) .AddQueryParam("version", currentVersion)
.AddQueryParam("os", OsInfo.Os.ToString().ToLowerInvariant()) .AddQueryParam("os", OsInfo.Os.ToString().ToLowerInvariant())
.AddQueryParam("runtimeVer", _platformInfo.Version)
.SetSegment("branch", branch) .SetSegment("branch", branch)
.Build(); .Build();
@ -53,4 +57,4 @@ namespace NzbDrone.Core.Update
return updates.Resource; return updates.Resource;
} }
} }
} }

@ -20,6 +20,7 @@ namespace Radarr.Host.AccessControl
private readonly INetshProvider _netshProvider; private readonly INetshProvider _netshProvider;
private readonly IConfigFileProvider _configFileProvider; private readonly IConfigFileProvider _configFileProvider;
private readonly IRuntimeInfo _runtimeInfo; private readonly IRuntimeInfo _runtimeInfo;
private readonly IOsInfo _osInfo;
private readonly Logger _logger; private readonly Logger _logger;
public List<string> Urls public List<string> Urls
@ -30,7 +31,7 @@ namespace Radarr.Host.AccessControl
} }
} }
private List<UrlAcl> InternalUrls { get; set; } private List<UrlAcl> InternalUrls { get; }
private List<UrlAcl> RegisteredUrls { get; set; } private List<UrlAcl> RegisteredUrls { get; set; }
private static readonly Regex UrlAclRegex = new Regex(@"(?<scheme>https?)\:\/\/(?<address>.+?)\:(?<port>\d+)/(?<urlbase>.+)?", RegexOptions.Compiled | RegexOptions.IgnoreCase); private static readonly Regex UrlAclRegex = new Regex(@"(?<scheme>https?)\:\/\/(?<address>.+?)\:(?<port>\d+)/(?<urlbase>.+)?", RegexOptions.Compiled | RegexOptions.IgnoreCase);
@ -38,19 +39,26 @@ namespace Radarr.Host.AccessControl
public UrlAclAdapter(INetshProvider netshProvider, public UrlAclAdapter(INetshProvider netshProvider,
IConfigFileProvider configFileProvider, IConfigFileProvider configFileProvider,
IRuntimeInfo runtimeInfo, IRuntimeInfo runtimeInfo,
IOsInfo osInfo,
Logger logger) Logger logger)
{ {
_netshProvider = netshProvider; _netshProvider = netshProvider;
_configFileProvider = configFileProvider; _configFileProvider = configFileProvider;
_runtimeInfo = runtimeInfo; _runtimeInfo = runtimeInfo;
_osInfo = osInfo;
_logger = logger; _logger = logger;
InternalUrls = new List<UrlAcl>(); InternalUrls = new List<UrlAcl>();
RegisteredUrls = GetRegisteredUrls(); RegisteredUrls = new List<UrlAcl>();
} }
public void ConfigureUrls() public void ConfigureUrls()
{ {
if (RegisteredUrls.Empty())
{
GetRegisteredUrls();
}
var localHostHttpUrls = BuildUrlAcls("http", "localhost", _configFileProvider.Port); var localHostHttpUrls = BuildUrlAcls("http", "localhost", _configFileProvider.Port);
var interfaceHttpUrls = BuildUrlAcls("http", _configFileProvider.BindAddress, _configFileProvider.Port); var interfaceHttpUrls = BuildUrlAcls("http", _configFileProvider.BindAddress, _configFileProvider.Port);
@ -105,7 +113,8 @@ namespace Radarr.Host.AccessControl
private void RefreshRegistration() private void RefreshRegistration()
{ {
if (OsInfo.Version.Major < 6) return; var osVersion = new Version(_osInfo.Version);
if (osVersion.Major < 6) return;
foreach (var urlAcl in InternalUrls) foreach (var urlAcl in InternalUrls)
{ {
@ -124,19 +133,24 @@ namespace Radarr.Host.AccessControl
c.UrlBase == urlAcl.UrlBase); c.UrlBase == urlAcl.UrlBase);
} }
private List<UrlAcl> GetRegisteredUrls() private void GetRegisteredUrls()
{ {
if (OsInfo.IsNotWindows) if (OsInfo.IsNotWindows)
{ {
return new List<UrlAcl>(); return;
}
if (RegisteredUrls.Any())
{
return;
} }
var arguments = string.Format("http show urlacl"); var arguments = string.Format("http show urlacl");
var output = _netshProvider.Run(arguments); var output = _netshProvider.Run(arguments);
if (output == null || !output.Standard.Any()) return new List<UrlAcl>(); if (output == null || !output.Standard.Any()) return;
return output.Standard.Select(line => RegisteredUrls = output.Standard.Select(line =>
{ {
var match = UrlAclRegex.Match(line.Content); var match = UrlAclRegex.Match(line.Content);

@ -58,7 +58,7 @@ namespace Radarr.Host
//_cancelHandler = new CancelHandler(); //_cancelHandler = new CancelHandler();
} }
_runtimeInfo.IsRunning = true; _runtimeInfo.IsExiting = false;
DbFactory.RegisterDatabase(_container); DbFactory.RegisterDatabase(_container);
_hostController.StartServer(); _hostController.StartServer();
@ -87,7 +87,7 @@ namespace Radarr.Host
_logger.Info("Attempting to stop application."); _logger.Info("Attempting to stop application.");
_hostController.StopServer(); _hostController.StopServer();
_logger.Info("Application has finished stop routine."); _logger.Info("Application has finished stop routine.");
_runtimeInfo.IsRunning = false; _runtimeInfo.IsExiting = true;
} }
public void Handle(ApplicationShutdownRequested message) public void Handle(ApplicationShutdownRequested message)

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using Nancy.Bootstrapper; using Nancy.Bootstrapper;
using NzbDrone.Api; using NzbDrone.Api;
using NzbDrone.Common.Composition; using NzbDrone.Common.Composition;
@ -15,26 +15,15 @@ namespace Radarr.Host
var assemblies = new List<string> var assemblies = new List<string>
{ {
"Radarr.Host", "Radarr.Host",
"NzbDrone.Common",
"NzbDrone.Core", "NzbDrone.Core",
"NzbDrone.Api", "NzbDrone.Api",
"NzbDrone.SignalR" "NzbDrone.SignalR"
}; };
if (OsInfo.IsWindows) return new MainAppContainerBuilder(args, assemblies).Container;
{
assemblies.Add("NzbDrone.Windows");
}
else
{
assemblies.Add("NzbDrone.Mono");
}
return new MainAppContainerBuilder(args, assemblies.ToArray()).Container;
} }
private MainAppContainerBuilder(StartupContext args, string[] assemblies) private MainAppContainerBuilder(StartupContext args, List<string> assemblies)
: base(args, assemblies) : base(args, assemblies)
{ {
AutoRegisterImplementations<NzbDronePersistentConnection>(); AutoRegisterImplementations<NzbDronePersistentConnection>();
@ -43,4 +32,4 @@ namespace Radarr.Host
Container.Register<IHttpDispatcher, FallbackHttpDispatcher>(); Container.Register<IHttpDispatcher, FallbackHttpDispatcher>();
} }
} }
} }

@ -1,5 +1,6 @@
using NLog; using NLog;
using NzbDrone.Common; using NzbDrone.Common;
using NzbDrone.Common.EnvironmentInfo;
namespace Radarr.Host namespace Radarr.Host
{ {
@ -8,14 +9,16 @@ namespace Radarr.Host
private readonly INzbDroneServiceFactory _nzbDroneServiceFactory; private readonly INzbDroneServiceFactory _nzbDroneServiceFactory;
private readonly IServiceProvider _serviceProvider; private readonly IServiceProvider _serviceProvider;
private readonly IConsoleService _consoleService; private readonly IConsoleService _consoleService;
private readonly IRuntimeInfo _runtimeInfo;
private readonly Logger _logger; private readonly Logger _logger;
public Router(INzbDroneServiceFactory nzbDroneServiceFactory, IServiceProvider serviceProvider, public Router(INzbDroneServiceFactory nzbDroneServiceFactory, IServiceProvider serviceProvider,
IConsoleService consoleService, Logger logger) IConsoleService consoleService, IRuntimeInfo runtimeInfo, Logger logger)
{ {
_nzbDroneServiceFactory = nzbDroneServiceFactory; _nzbDroneServiceFactory = nzbDroneServiceFactory;
_serviceProvider = serviceProvider; _serviceProvider = serviceProvider;
_consoleService = consoleService; _consoleService = consoleService;
_runtimeInfo = runtimeInfo;
_logger = logger; _logger = logger;
} }
@ -34,7 +37,7 @@ namespace Radarr.Host
case ApplicationModes.Interactive: case ApplicationModes.Interactive:
{ {
_logger.Debug("Console selected"); _logger.Debug(_runtimeInfo.IsWindowsTray ? "Tray selected" : "Console selected");
_nzbDroneServiceFactory.Start(); _nzbDroneServiceFactory.Start();
break; break;
} }

@ -1,4 +1,4 @@
using System.Threading; using System.Threading;
using NLog; using NLog;
using NLog.Common; using NLog.Common;
using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.EnvironmentInfo;
@ -28,7 +28,7 @@ namespace Radarr.Host
public void Spin() public void Spin()
{ {
while (_runtimeInfo.IsRunning) while (!_runtimeInfo.IsExiting)
{ {
Thread.Sleep(1000); Thread.Sleep(1000);
} }

@ -14,7 +14,7 @@
<probing privatePath="libs" /> <probing privatePath="libs" />
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" /> <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" /> <bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="NLog" publicKeyToken="5120e14c03d0593c" culture="neutral" /> <assemblyIdentity name="NLog" publicKeyToken="5120e14c03d0593c" culture="neutral" />

@ -4,7 +4,7 @@
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" /> <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" /> <bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="FluentMigrator" publicKeyToken="aacfc7de5acabf05" culture="neutral" /> <assemblyIdentity name="FluentMigrator" publicKeyToken="aacfc7de5acabf05" culture="neutral" />

@ -0,0 +1,23 @@
using System;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Mono.EnvironmentInfo;
using NzbDrone.Test.Common;
namespace NzbDrone.Mono.Test.EnvironmentInfo
{
[TestFixture]
[Platform("Mono")]
public class MonoPlatformInfoFixture : TestBase<MonoPlatformInfo>
{
[Test]
public void should_get_framework_version()
{
Subject.Version.Major.Should().BeOneOf(4, 5);
if (Subject.Version.Major == 4)
{
Subject.Version.Minor.Should().BeOneOf(0, 5, 6);
}
}
}
}

@ -0,0 +1,29 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Common.Disk;
using NzbDrone.Mono.Disk;
using NzbDrone.Mono.EnvironmentInfo.VersionAdapters;
using NzbDrone.Test.Common;
namespace NzbDrone.Mono.Test.EnvironmentInfo
{
[TestFixture]
[Platform("Mono")]
public class ReleaseFileVersionAdapterFixture : TestBase<ReleaseFileVersionAdapter>
{
[SetUp]
public void Setup()
{
Mocker.SetConstant<IDiskProvider>(Mocker.Resolve<DiskProvider>());
}
[Test]
public void should_get_version_info()
{
var info = Subject.Read();
info.FullName.Should().NotBeNullOrWhiteSpace();
info.Name.Should().NotBeNullOrWhiteSpace();
info.Version.Should().NotBeNullOrWhiteSpace();
}
}
}

@ -0,0 +1,77 @@
using System;
using System.IO;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Disk;
using NzbDrone.Mono.EnvironmentInfo.VersionAdapters;
using NzbDrone.Test.Common;
namespace NzbDrone.Mono.Test.EnvironmentInfo.VersionAdapters
{
[TestFixture]
public class MacOsVersionAdapterFixture : TestBase<MacOsVersionAdapter>
{
[TestCase("10.8.0")]
[TestCase("10.8")]
[TestCase("10.8.1")]
[TestCase("10.11.20")]
public void should_get_version_info(string versionString)
{
var fileContent = File.ReadAllText(GetTestPath("Files/macOS/SystemVersion.plist")).Replace("10.0.0", versionString);
const string plistPath = "/System/Library/CoreServices/SystemVersion.plist";
Mocker.GetMock<IDiskProvider>()
.Setup(c => c.FolderExists("/System/Library/CoreServices/")).Returns(true);
Mocker.GetMock<IDiskProvider>()
.Setup(c => c.GetFiles("/System/Library/CoreServices/", SearchOption.TopDirectoryOnly))
.Returns(new[] { plistPath });
Mocker.GetMock<IDiskProvider>()
.Setup(c => c.ReadAllText(plistPath))
.Returns(fileContent);
var versionName = Subject.Read();
versionName.Version.Should().Be(versionString);
versionName.Name.Should().Be("macOS");
versionName.FullName.Should().Be("macOS " + versionString);
}
[TestCase]
public void should_detect_server()
{
var fileContent = File.ReadAllText(GetTestPath("Files/macOS/SystemVersion.plist"));
const string plistPath = "/System/Library/CoreServices/ServerVersion.plist";
Mocker.GetMock<IDiskProvider>()
.Setup(c => c.FolderExists("/System/Library/CoreServices/")).Returns(true);
Mocker.GetMock<IDiskProvider>()
.Setup(c => c.GetFiles("/System/Library/CoreServices/", SearchOption.TopDirectoryOnly))
.Returns(new[] { plistPath });
Mocker.GetMock<IDiskProvider>()
.Setup(c => c.ReadAllText(plistPath))
.Returns(fileContent);
var versionName = Subject.Read();
versionName.Name.Should().Be("macOS Server");
}
[TestCase]
public void should_return_null_if_folder_doesnt_exist()
{
Mocker.GetMock<IDiskProvider>()
.Setup(c => c.FolderExists("/System/Library/CoreServices/")).Returns(false);
Subject.Read().Should().BeNull();
Mocker.GetMock<IDiskProvider>()
.Verify(c => c.GetFiles(It.IsAny<string>(), SearchOption.TopDirectoryOnly), Times.Never());
}
}
}

@ -0,0 +1,82 @@
using System.IO;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Disk;
using NzbDrone.Mono.Disk;
using NzbDrone.Mono.EnvironmentInfo.VersionAdapters;
using NzbDrone.Test.Common;
using NzbDrone.Test.Common.Categories;
namespace NzbDrone.Mono.Test.EnvironmentInfo.VersionAdapters
{
[TestFixture]
public class ReleaseFileVersionAdapterFixture : TestBase<ReleaseFileVersionAdapter>
{
[Test]
[IntegrationTest]
[Platform("Mono")]
public void should_get_version_info_from_actual_linux()
{
Mocker.SetConstant<IDiskProvider>(Mocker.Resolve<DiskProvider>());
var info = Subject.Read();
info.FullName.Should().NotBeNullOrWhiteSpace();
info.Name.Should().NotBeNullOrWhiteSpace();
info.Version.Should().NotBeNullOrWhiteSpace();
}
[Test]
public void should_return_null_if_etc_doestn_exist()
{
Mocker.GetMock<IDiskProvider>().Setup(c => c.FolderExists("/etc/")).Returns(false);
Subject.Read().Should().BeNull();
Mocker.GetMock<IDiskProvider>()
.Verify(c => c.GetFiles(It.IsAny<string>(), SearchOption.TopDirectoryOnly), Times.Never());
Subject.Read().Should().BeNull();
}
[Test]
public void should_return_null_if_release_file_doestn_exist()
{
Mocker.GetMock<IDiskProvider>().Setup(c => c.FolderExists("/etc/")).Returns(true);
Subject.Read().Should().BeNull();
Mocker.GetMock<IDiskProvider>()
.Setup(c => c.GetFiles(It.IsAny<string>(), SearchOption.TopDirectoryOnly)).Returns(new string[0]);
Subject.Read().Should().BeNull();
}
[Test]
public void should_detect_version()
{
Mocker.GetMock<IDiskProvider>().Setup(c => c.FolderExists("/etc/")).Returns(true);
Subject.Read().Should().BeNull();
Mocker.GetMock<IDiskProvider>()
.Setup(c => c.GetFiles(It.IsAny<string>(), SearchOption.TopDirectoryOnly)).Returns(new[]
{
"/etc/lsb-release",
"/etc/os-release"
});
Mocker.GetMock<IDiskProvider>()
.Setup(c => c.ReadAllText("/etc/lsb-release"))
.Returns(File.ReadAllText(GetTestPath("Files/linux/lsb-release")));
Mocker.GetMock<IDiskProvider>()
.Setup(c => c.ReadAllText("/etc/os-release"))
.Returns(File.ReadAllText(GetTestPath("Files/linux/os-release")));
var version = Subject.Read();
version.Should().NotBeNull();
version.Name.Should().Be("ubuntu");
version.Version.Should().Be("14.04");
version.FullName.Should().Be("Ubuntu 14.04.5 LTS");
}
}
}

@ -0,0 +1,4 @@
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=14.04
DISTRIB_CODENAME=trusty
DISTRIB_DESCRIPTION="Ubuntu 14.04.5 LTS"

@ -0,0 +1,9 @@
NAME="Ubuntu"
VERSION="14.04.5 LTS, Trusty Tahr"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 14.04.5 LTS"
VERSION_ID="14.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>ProductBuildVersion</key>
<string>16C68</string>
<key>ProductCopyright</key>
<string>1983-2016 Apple Inc.</string>
<key>ProductName</key>
<string>Mac OS X</string>
<key>ProductUserVisibleVersion</key>
<string>10.0.0</string>
<key>ProductVersion</key>
<string>10.0.0</string>
</dict>
</plist>

@ -0,0 +1,8 @@
majorversion="6"
minorversion="0"
productversion="6.0.2"
buildphase="hotfix"
buildnumber="8451"
smallfixnumber="7"
builddate="2016/12/20"
buildtime="05:11:44"

@ -82,10 +82,26 @@
<ItemGroup> <ItemGroup>
<Compile Include="DiskProviderTests\DiskProviderFixture.cs" /> <Compile Include="DiskProviderTests\DiskProviderFixture.cs" />
<Compile Include="DiskProviderTests\FreeSpaceFixture.cs" /> <Compile Include="DiskProviderTests\FreeSpaceFixture.cs" />
<Compile Include="EnvironmentInfo\MonoPlatformInfoFixture.cs" />
<Compile Include="EnvironmentInfo\ReleaseFileVersionAdapterFixture.cs" />
<Compile Include="EnvironmentInfo\VersionAdapters\MacOsVersionAdapterFixture.cs" />
<Compile Include="EnvironmentInfo\VersionAdapters\ReleaseFileVersionAdapterFixture.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="app.config" /> <None Include="app.config" />
<None Include="Files\linux\lsb-release">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="Files\linux\os-release">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="Files\macOS\SystemVersion.plist">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="Files\synology\VERSION">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="packages.config" /> <None Include="packages.config" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

@ -8,7 +8,7 @@
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" /> <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" /> <bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="FluentMigrator" publicKeyToken="aacfc7de5acabf05" culture="neutral" /> <assemblyIdentity name="FluentMigrator" publicKeyToken="aacfc7de5acabf05" culture="neutral" />

@ -0,0 +1,46 @@
using System;
using System.Reflection;
using System.Text.RegularExpressions;
using NLog;
using NzbDrone.Common.EnvironmentInfo;
namespace NzbDrone.Mono.EnvironmentInfo
{
public class MonoPlatformInfo : PlatformInfo
{
private static readonly Regex VersionRegex = new Regex(@"(?<=\W|^)(?<version>\d+\.\d+(\.\d+)?(\.\d+)?)(?=\W)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
public override Version Version { get; }
public MonoPlatformInfo(Logger logger)
{
var runTimeVersion = new Version();
try
{
var type = Type.GetType("Mono.Runtime");
if (type != null)
{
var displayNameMethod = type.GetMethod("GetDisplayName", BindingFlags.NonPublic | BindingFlags.Static);
if (displayNameMethod != null)
{
var displayName = displayNameMethod.Invoke(null, null).ToString();
var versionMatch = VersionRegex.Match(displayName);
if (versionMatch.Success)
{
runTimeVersion = new Version(versionMatch.Groups["version"].Value);
}
}
}
}
catch (Exception ex)
{
logger.Error(ex, "Unable to get mono version");
}
Version = runTimeVersion;
}
}
}

@ -1,45 +0,0 @@
using System;
using System.Reflection;
using NLog;
using NzbDrone.Common.EnvironmentInfo;
namespace NzbDrone.Mono.EnvironmentInfo
{
public class MonoRuntimeProvider : RuntimeInfoBase
{
private readonly Logger _logger;
public MonoRuntimeProvider(Common.IServiceProvider serviceProvider, Logger logger)
:base(serviceProvider, logger)
{
_logger = logger;
}
public override string RuntimeVersion
{
get
{
try
{
var type = Type.GetType("Mono.Runtime");
if (type != null)
{
var displayName = type.GetMethod("GetDisplayName", BindingFlags.NonPublic | BindingFlags.Static);
if (displayName != null)
{
return displayName.Invoke(null, null).ToString();
}
}
}
catch (Exception ex)
{
_logger.Error(ex, "Unable to get mono version: " + ex.Message);
}
return string.Empty;
}
}
}
}

@ -0,0 +1,52 @@
using System.IO;
using System.Linq;
using NzbDrone.Common.Disk;
using NzbDrone.Common.EnvironmentInfo;
namespace NzbDrone.Mono.EnvironmentInfo.VersionAdapters
{
public class IssueFileVersionAdapter : IOsVersionAdapter
{
private readonly IDiskProvider _diskProvider;
public IssueFileVersionAdapter(IDiskProvider diskProvider)
{
_diskProvider = diskProvider;
}
public OsVersionModel Read()
{
if (!_diskProvider.FolderExists("/etc/"))
{
return null;
}
var issueFile = _diskProvider.GetFiles("/etc/", SearchOption.TopDirectoryOnly).SingleOrDefault(c => c.EndsWith("/issue"));
if (issueFile == null)
{
return null;
}
var fileContent = _diskProvider.ReadAllText(issueFile);
// Ubuntu 14.04.5 LTS \n \l
// Ubuntu 16.04.1 LTS \n \l
// Fedora/Centos
// Kernel \r on an \m (\l)
// Arch Linux \r (\l)
// Debian GNU/Linux 8 \n \l
if (fileContent.Contains("Arch Linux"))
{
return new OsVersionModel("Arch", "1.0", "Arch Linux");
}
return null;
}
public bool Enabled => OsInfo.IsLinux;
}
}

@ -0,0 +1,69 @@
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using NLog;
using NzbDrone.Common.Disk;
using NzbDrone.Common.EnvironmentInfo;
namespace NzbDrone.Mono.EnvironmentInfo.VersionAdapters
{
public class MacOsVersionAdapter : IOsVersionAdapter
{
private static readonly Regex DarwinVersionRegex = new Regex("<string>(?<version>10\\.\\d{1,2}\\.?\\d{0,2}?)<\\/string>",
RegexOptions.Compiled |
RegexOptions.IgnoreCase
);
private const string PLIST_DIR = "/System/Library/CoreServices/";
private readonly IDiskProvider _diskProvider;
private readonly Logger _logger;
public MacOsVersionAdapter(IDiskProvider diskProvider, Logger logger)
{
_diskProvider = diskProvider;
_logger = logger;
}
public OsVersionModel Read()
{
var version = "10.0";
if (!_diskProvider.FolderExists(PLIST_DIR))
{
_logger.Debug("Directory {0} doesn't exist", PLIST_DIR);
return null;
}
var allFiles = _diskProvider.GetFiles(PLIST_DIR, SearchOption.TopDirectoryOnly);
var versionFile = allFiles.SingleOrDefault(c =>
c.EndsWith("SystemVersion.plist") ||
c.EndsWith("ServerVersion.plist")
);
if (string.IsNullOrWhiteSpace(versionFile))
{
_logger.Debug("Couldn't find version plist file in {0}", PLIST_DIR);
return null;
}
var text = _diskProvider.ReadAllText(versionFile);
var match = DarwinVersionRegex.Match(text);
if (match.Success)
{
version = match.Groups["version"].Value;
}
var name = versionFile.Contains("Server") ? "macOS Server" : "macOS";
return new OsVersionModel(name, version);
}
public bool Enabled => OsInfo.IsOsx;
}
}

@ -0,0 +1,79 @@
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using NzbDrone.Common.Disk;
using NzbDrone.Common.EnvironmentInfo;
namespace NzbDrone.Mono.EnvironmentInfo.VersionAdapters
{
public class ReleaseFileVersionAdapter : IOsVersionAdapter
{
private readonly IDiskProvider _diskProvider;
public ReleaseFileVersionAdapter(IDiskProvider diskProvider)
{
_diskProvider = diskProvider;
}
public OsVersionModel Read()
{
if (!_diskProvider.FolderExists("/etc/"))
{
return null;
}
var releaseFiles = _diskProvider.GetFiles("/etc/", SearchOption.TopDirectoryOnly).Where(c => c.EndsWith("release")).ToList();
var name = "Linux";
var fullName = "";
var version = "";
bool success = false;
foreach (var releaseFile in releaseFiles)
{
var fileContent = _diskProvider.ReadAllText(releaseFile);
var lines = Regex.Split(fileContent, "\r\n|\r|\n"); ;
foreach (var line in lines)
{
var parts = line.Split('=');
if (parts.Length >= 2)
{
var key = parts[0];
var value = parts[1];
if (!string.IsNullOrWhiteSpace(value))
{
switch (key)
{
case "ID":
success = true;
name = value;
break;
case "PRETTY_NAME":
success = true;
fullName = value;
break;
case "VERSION_ID":
success = true;
version = value;
break;
}
}
}
}
}
if (!success)
{
return null;
}
return new OsVersionModel(name, version, fullName);
}
public bool Enabled => OsInfo.IsLinux;
}
}

@ -0,0 +1,78 @@
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using NzbDrone.Common.Disk;
using NzbDrone.Common.EnvironmentInfo;
namespace NzbDrone.Mono.EnvironmentInfo.VersionAdapters
{
public class SynologyVersionAdapter : IOsVersionAdapter
{
private readonly IDiskProvider _diskProvider;
private const string NAME = "DSM";
private const string FULL_NAME = "Synology DSM";
public SynologyVersionAdapter(IDiskProvider diskProvider)
{
_diskProvider = diskProvider;
}
public OsVersionModel Read()
{
if (!_diskProvider.FolderExists("/etc.defaults/"))
{
return null;
}
var versionFile = _diskProvider.GetFiles("/etc.defaults/", SearchOption.TopDirectoryOnly).SingleOrDefault(c => c.EndsWith("VERSION"));
if (versionFile == null)
{
return null;
}
var version = "";
var major = "";
var minor = "0";
var fileContent = _diskProvider.ReadAllText(versionFile);
var lines = Regex.Split(fileContent, "\r\n|\r|\n"); ;
foreach (var line in lines)
{
var parts = line.Split('=');
if (parts.Length >= 2)
{
var key = parts[0];
var value = parts[1].Trim('"');
if (!string.IsNullOrWhiteSpace(value))
{
switch (key)
{
case "productversion":
version = value;
break;
case "majorversion":
major = value;
break;
case "minorversion":
minor = value;
break;
}
}
}
}
if (string.IsNullOrWhiteSpace(version) && !string.IsNullOrWhiteSpace(major))
{
version = $"{major}.{minor}";
}
return new OsVersionModel(NAME, version, $"{FULL_NAME} {version}");
}
public bool Enabled => OsInfo.IsLinux;
}
}

@ -79,9 +79,13 @@
<Compile Include="Disk\DiskProvider.cs" /> <Compile Include="Disk\DiskProvider.cs" />
<Compile Include="Disk\FindDriveType.cs" /> <Compile Include="Disk\FindDriveType.cs" />
<Compile Include="Disk\LinuxPermissionsException.cs" /> <Compile Include="Disk\LinuxPermissionsException.cs" />
<Compile Include="EnvironmentInfo\MonoRuntimeProvider.cs" /> <Compile Include="EnvironmentInfo\MonoPlatformInfo.cs" />
<Compile Include="Disk\ProcMount.cs" /> <Compile Include="Disk\ProcMount.cs" />
<Compile Include="Disk\ProcMountProvider.cs" /> <Compile Include="Disk\ProcMountProvider.cs" />
<Compile Include="EnvironmentInfo\VersionAdapters\IssueFileVersionAdapter.cs" />
<Compile Include="EnvironmentInfo\VersionAdapters\MacOsVersionAdapter.cs" />
<Compile Include="EnvironmentInfo\VersionAdapters\ReleaseFileVersionAdapter.cs" />
<Compile Include="EnvironmentInfo\VersionAdapters\SynologyVersionAdapter.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Disk\SymbolicLinkResolver.cs" /> <Compile Include="Disk\SymbolicLinkResolver.cs" />
</ItemGroup> </ItemGroup>
@ -92,6 +96,7 @@
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="app.config" />
<None Include="packages.config" /> <None Include="packages.config" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

@ -4,7 +4,7 @@
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/> <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0"/> <bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0"/>
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="FluentMigrator" publicKeyToken="aacfc7de5acabf05" culture="neutral"/> <assemblyIdentity name="FluentMigrator" publicKeyToken="aacfc7de5acabf05" culture="neutral"/>

@ -1,5 +1,3 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
@ -151,7 +149,7 @@ namespace NzbDrone.Test.Common.AutoMoq
private Mock<T> TheRegisteredMockForThisType<T>(Type type) where T : class private Mock<T> TheRegisteredMockForThisType<T>(Type type) where T : class
{ {
return (Mock<T>)_registeredMocks.Where(x => x.Key == type).First().Value; return (Mock<T>)_registeredMocks.First(x => x.Key == type).Value;
} }
private void CreateANewMockAndRegisterIt<T>(Type type, MockBehavior behavior) where T : class private void CreateANewMockAndRegisterIt<T>(Type type, MockBehavior behavior) where T : class
@ -190,4 +188,4 @@ namespace NzbDrone.Test.Common.AutoMoq
#endregion #endregion
} }
} }

@ -1,4 +1,4 @@
using System; using System;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using FluentAssertions; using FluentAssertions;
@ -133,7 +133,7 @@ namespace NzbDrone.Test.Common
protected void MonoOnly() protected void MonoOnly()
{ {
if (OsInfo.IsWindows) if (!PlatformInfo.IsMono)
{ {
throw new IgnoreException("mono specific test"); throw new IgnoreException("mono specific test");
} }

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using NzbDrone.Common.Composition; using NzbDrone.Common.Composition;
using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Http.Dispatchers; using NzbDrone.Common.Http.Dispatchers;
@ -7,7 +7,7 @@ namespace NzbDrone.Update
{ {
public class UpdateContainerBuilder : ContainerBuilderBase public class UpdateContainerBuilder : ContainerBuilderBase
{ {
private UpdateContainerBuilder(IStartupContext startupContext, string[] assemblies) private UpdateContainerBuilder(IStartupContext startupContext, List<string> assemblies)
: base(startupContext, assemblies) : base(startupContext, assemblies)
{ {
Container.Register<IHttpDispatcher, FallbackHttpDispatcher>(); Container.Register<IHttpDispatcher, FallbackHttpDispatcher>();
@ -17,22 +17,10 @@ namespace NzbDrone.Update
{ {
var assemblies = new List<string> var assemblies = new List<string>
{ {
"Radarr.Update", "Radarr.Update"
"NzbDrone.Common"
}; };
if (OsInfo.IsWindows) return new UpdateContainerBuilder(startupContext, assemblies).Container;
{
assemblies.Add("NzbDrone.Windows");
}
else
{
assemblies.Add("NzbDrone.Mono");
}
return new UpdateContainerBuilder(startupContext, assemblies.ToArray()).Container;
} }
} }
} }

@ -24,7 +24,7 @@ namespace NzbDrone.Update.UpdateEngine
{ {
if (OsInfo.IsNotWindows) if (OsInfo.IsNotWindows)
{ {
//Tehcnically its the console, but its been renamed for mono (Linux/OS X) //Tehcnically it is the console, but it has been renamed for mono (Linux/OS X)
return AppType.Normal; return AppType.Normal;
} }
@ -42,4 +42,4 @@ namespace NzbDrone.Update.UpdateEngine
return AppType.Normal; return AppType.Normal;
} }
} }
} }

@ -11,7 +11,7 @@
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/> <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0"/> <bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0"/>
</dependentAssembly> </dependentAssembly>
</assemblyBinding> </assemblyBinding>
</runtime> </runtime>

@ -0,0 +1,19 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Test.Common;
using NzbDrone.Windows.EnvironmentInfo;
namespace NzbDrone.Windows.Test.EnvironmentInfo
{
[TestFixture]
[Platform("Win")]
public class DotNetPlatformInfoFixture : TestBase<DotNetPlatformInfo>
{
[Test]
public void should_get_framework_version()
{
Subject.Version.Major.Should().Be(4);
Subject.Version.Minor.Should().BeOneOf(0, 5, 6);
}
}
}

@ -0,0 +1,23 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Test.Common;
using NzbDrone.Windows.EnvironmentInfo;
namespace NzbDrone.Windows.Test.EnvironmentInfo
{
[TestFixture]
[Platform("Win")]
public class WindowsVersionInfoFixture : TestBase<WindowsVersionInfo>
{
[Test]
public void should_get_windows_version()
{
var info = Subject.Read();
info.Version.Should().NotBeNullOrWhiteSpace();
info.Name.Should().Contain("Windows");
info.FullName.Should().Contain("Windows");
info.FullName.Should().Contain("NT");
info.FullName.Should().Contain(info.Version);
}
}
}

@ -75,6 +75,8 @@
<ItemGroup> <ItemGroup>
<Compile Include="DiskProviderTests\DiskProviderFixture.cs" /> <Compile Include="DiskProviderTests\DiskProviderFixture.cs" />
<Compile Include="DiskProviderTests\FreeSpaceFixture.cs" /> <Compile Include="DiskProviderTests\FreeSpaceFixture.cs" />
<Compile Include="EnvironmentInfo\DotNetPlatformInfoFixture.cs" />
<Compile Include="EnvironmentInfo\WindowsVersionInfoFixture.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

@ -8,7 +8,7 @@
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" /> <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" /> <bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="FluentMigrator" publicKeyToken="aacfc7de5acabf05" culture="neutral" /> <assemblyIdentity name="FluentMigrator" publicKeyToken="aacfc7de5acabf05" culture="neutral" />

@ -0,0 +1,70 @@
using System;
using Microsoft.Win32;
using NLog;
using NzbDrone.Common.EnvironmentInfo;
namespace NzbDrone.Windows.EnvironmentInfo
{
public class DotNetPlatformInfo : PlatformInfo
{
private readonly Logger _logger;
public DotNetPlatformInfo(Logger logger)
{
_logger = logger;
var version = GetFrameworkVersion();
Environment.SetEnvironmentVariable("RUNTIME_VERSION", version.ToString());
Version = version;
}
public override Version Version { get; }
private Version GetFrameworkVersion()
{
try
{
const string subkey = @"SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full\";
using (var ndpKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32).OpenSubKey(subkey))
{
if (ndpKey == null)
{
return new Version(4, 0);
}
var releaseKey = (int)ndpKey.GetValue("Release");
if (releaseKey >= 394802)
{
return new Version(4, 6, 2);
}
if (releaseKey >= 394254)
{
return new Version(4, 6, 1);
}
if (releaseKey >= 393295)
{
return new Version(4, 6);
}
if (releaseKey >= 379893)
{
return new Version(4, 5, 2);
}
if (releaseKey >= 378675)
{
return new Version(4, 5, 1);
}
if (releaseKey >= 378389)
{
return new Version(4, 5);
}
}
}
catch (Exception e)
{
_logger.Error(e, "Couldnt get .NET framework version");
}
return new Version(4, 0);
}
}
}

@ -1,16 +0,0 @@
using System;
using NLog;
using NzbDrone.Common.EnvironmentInfo;
namespace NzbDrone.Windows.EnvironmentInfo
{
public class DotNetRuntimeProvider : RuntimeInfoBase
{
public DotNetRuntimeProvider(Common.IServiceProvider serviceProvider, Logger logger)
: base(serviceProvider, logger)
{
}
public override string RuntimeVersion => Environment.Version.ToString();
}
}

@ -0,0 +1,49 @@
using System;
using Microsoft.Win32;
using NLog;
using NzbDrone.Common.EnvironmentInfo;
namespace NzbDrone.Windows.EnvironmentInfo
{
public class WindowsVersionInfo : IOsVersionAdapter
{
private readonly Logger _logger;
public bool Enabled => OsInfo.IsWindows;
public WindowsVersionInfo(Logger logger)
{
_logger = logger;
}
public OsVersionModel Read()
{
var windowsServer = IsServer();
var osName = windowsServer ? "Windows Server" : "Windows";
return new OsVersionModel(osName, Environment.OSVersion.Version.ToString(), Environment.OSVersion.VersionString);
}
private bool IsServer()
{
try
{
const string subkey = @"Software\Microsoft\Windows NT\CurrentVersion";
var openSubKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32).OpenSubKey(subkey);
if (openSubKey != null)
{
var productName = openSubKey.GetValue("ProductName").ToString();
if (productName.ToLower().Contains("server"))
{
return true;
}
}
}
catch (Exception e)
{
_logger.Error(e, "Couldn't detect if running Windows Server");
}
return false;
}
}
}

@ -73,7 +73,8 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Disk\DiskProvider.cs" /> <Compile Include="Disk\DiskProvider.cs" />
<Compile Include="EnvironmentInfo\DotNetRuntimeProvider.cs" /> <Compile Include="EnvironmentInfo\DotNetPlatformInfo.cs" />
<Compile Include="EnvironmentInfo\WindowsVersionInfo.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -83,6 +84,7 @@
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="app.config" />
<None Include="packages.config" /> <None Include="packages.config" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

@ -0,0 +1,4 @@
[ViewState]
Mode=
Vid=
FolderType=Documents
Loading…
Cancel
Save