diff options
| author | Luke <luke.pulverenti@gmail.com> | 2016-10-30 03:29:27 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2016-10-30 03:29:27 -0400 |
| commit | d31b0f7be4b14e4ada999c97e675b856ad68352b (patch) | |
| tree | a4619513efbb3be62a6204c996526df606cb62c5 /MediaBrowser.Server.Implementations | |
| parent | b19f75fcae017cb51f1e58eb2d54ca84620b6ee0 (diff) | |
| parent | 3094cd7ff3e51578808ce1b6f56b141930c18004 (diff) | |
Merge pull request #2258 from MediaBrowser/dev
Dev
Diffstat (limited to 'MediaBrowser.Server.Implementations')
39 files changed, 1482 insertions, 95 deletions
diff --git a/MediaBrowser.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs b/MediaBrowser.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs index 4202870bd..8bcb3cda9 100644 --- a/MediaBrowser.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs +++ b/MediaBrowser.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs @@ -1,5 +1,4 @@ -using MediaBrowser.Common.ScheduledTasks; -using MediaBrowser.Controller.Channels; +using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Logging; using System; diff --git a/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs b/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs index f48b79674..4b6195192 100644 --- a/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs +++ b/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Events; -using MediaBrowser.Common.Implementations.Configuration; +using Emby.Common.Implementations.Configuration; using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; diff --git a/MediaBrowser.Server.Implementations/EntryPoints/ActivityLogEntryPoint.cs b/MediaBrowser.Server.Implementations/EntryPoints/ActivityLogEntryPoint.cs index de0719a62..96b8aad5d 100644 --- a/MediaBrowser.Server.Implementations/EntryPoints/ActivityLogEntryPoint.cs +++ b/MediaBrowser.Server.Implementations/EntryPoints/ActivityLogEntryPoint.cs @@ -1,7 +1,5 @@ using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Implementations.Logging; using MediaBrowser.Common.Plugins; -using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Common.Updates; using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; diff --git a/MediaBrowser.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs b/MediaBrowser.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs index d5f265dda..1067e052d 100644 --- a/MediaBrowser.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs +++ b/MediaBrowser.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs @@ -1,5 +1,4 @@ -using MediaBrowser.Common.ScheduledTasks; -using MediaBrowser.Controller; +using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Session; diff --git a/MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs b/MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs index 9db49f97a..d86990a40 100644 --- a/MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs +++ b/MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; using System.Globalization; using System.Net; using MediaBrowser.Common.Net; +using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Events; using MediaBrowser.Server.Implementations.Threading; diff --git a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs index f7fe707da..8f35f0e76 100644 --- a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs +++ b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs @@ -1,6 +1,5 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Plugins; -using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Common.Updates; using MediaBrowser.Controller; using MediaBrowser.Controller.Devices; diff --git a/MediaBrowser.Server.Implementations/EntryPoints/ServerEventNotifier.cs b/MediaBrowser.Server.Implementations/EntryPoints/ServerEventNotifier.cs index 9fafc561f..0da48a2d8 100644 --- a/MediaBrowser.Server.Implementations/EntryPoints/ServerEventNotifier.cs +++ b/MediaBrowser.Server.Implementations/EntryPoints/ServerEventNotifier.cs @@ -1,5 +1,4 @@ using MediaBrowser.Common.Plugins; -using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Common.Updates; using MediaBrowser.Controller; using MediaBrowser.Controller.Entities; diff --git a/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationNotifier.cs b/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationNotifier.cs index 940730b3b..141dcf9b4 100644 --- a/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationNotifier.cs +++ b/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationNotifier.cs @@ -1,5 +1,4 @@ -using MediaBrowser.Common.ScheduledTasks; -using MediaBrowser.Controller.FileOrganization; +using MediaBrowser.Controller.FileOrganization; using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Events; diff --git a/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs b/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs index 63cc1dc68..de33c39e6 100644 --- a/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs +++ b/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs @@ -1,5 +1,4 @@ using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.FileOrganization; using MediaBrowser.Controller.Library; diff --git a/MediaBrowser.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs b/MediaBrowser.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs index 8b876904f..ca41db80c 100644 --- a/MediaBrowser.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs +++ b/MediaBrowser.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs @@ -1,5 +1,4 @@ -using MediaBrowser.Common.ScheduledTasks; -using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.FileOrganization; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; diff --git a/MediaBrowser.Server.Implementations/IO/FileRefresher.cs b/MediaBrowser.Server.Implementations/IO/FileRefresher.cs index beb9600c7..eeefdd65a 100644 --- a/MediaBrowser.Server.Implementations/IO/FileRefresher.cs +++ b/MediaBrowser.Server.Implementations/IO/FileRefresher.cs @@ -7,7 +7,6 @@ using System.Threading.Tasks; using MediaBrowser.Model.IO; using MediaBrowser.Common.Events; using MediaBrowser.Common.IO; -using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.IO; diff --git a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs index 8d92c2a03..77981b528 100644 --- a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs +++ b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs @@ -1,5 +1,4 @@ -using MediaBrowser.Common.ScheduledTasks; -using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Plugins; diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index 4b7971fca..18feaa849 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -1,7 +1,5 @@ -using Interfaces.IO; -using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Progress; -using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; @@ -2462,12 +2460,7 @@ namespace MediaBrowser.Server.Implementations.Library var videoListResolver = new VideoListResolver(GetNamingOptions(), new PatternsLogger()); - var videos = videoListResolver.Resolve(fileSystemChildren.Select(i => new FileMetadata - { - Id = i.FullName, - IsFolder = i.IsDirectory - - }).ToList()); + var videos = videoListResolver.Resolve(fileSystemChildren); var currentVideo = videos.FirstOrDefault(i => string.Equals(owner.Path, i.Files.First().Path, StringComparison.OrdinalIgnoreCase)); @@ -2511,12 +2504,7 @@ namespace MediaBrowser.Server.Implementations.Library var videoListResolver = new VideoListResolver(GetNamingOptions(), new PatternsLogger()); - var videos = videoListResolver.Resolve(fileSystemChildren.Select(i => new FileMetadata - { - Id = i.FullName, - IsFolder = i.IsDirectory - - }).ToList()); + var videos = videoListResolver.Resolve(fileSystemChildren); var currentVideo = videos.FirstOrDefault(i => string.Equals(owner.Path, i.Files.First().Path, StringComparison.OrdinalIgnoreCase)); diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index 40a3aa325..bb1d57688 100644 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -1,5 +1,4 @@ -using Interfaces.IO; -using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; @@ -135,12 +134,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies var namingOptions = ((LibraryManager)LibraryManager).GetNamingOptions(); var resolver = new VideoListResolver(namingOptions, new PatternsLogger()); - var resolverResult = resolver.Resolve(files.Select(i => new FileMetadata - { - Id = i.FullName, - IsFolder = i.IsDirectory - - }).ToList(), suppportMultiEditions).ToList(); + var resolverResult = resolver.Resolve(files, suppportMultiEditions).ToList(); var result = new MultiItemResolverResult { diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index 7b88ccf64..894ab5e58 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -2,7 +2,6 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Progress; -using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; diff --git a/MediaBrowser.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs b/MediaBrowser.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs index ad7f839e8..bf0ed90ea 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs @@ -1,5 +1,4 @@ using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Model.LiveTv; using System; diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs index cd168ba58..f039da927 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs @@ -10,6 +10,7 @@ using System; using System.Linq; using System.Threading; using MediaBrowser.Common.Net; +using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Events; using MediaBrowser.Model.Serialization; diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs index a0b8ef5f7..8ecdca46b 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs @@ -14,6 +14,7 @@ using MediaBrowser.Model.Logging; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Extensions; using System.Xml.Linq; +using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Events; namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 854a9cecb..90b331ae1 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -42,6 +42,9 @@ <WarningLevel>4</WarningLevel> </PropertyGroup> <ItemGroup> + <Reference Include="Emby.Common.Implementations"> + <HintPath>..\ThirdParty\emby\Emby.Common.Implementations.dll</HintPath> + </Reference> <Reference Include="Emby.XmlTv, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <HintPath>..\packages\Emby.XmlTv.1.0.0.58\lib\portable-net46+win10\Emby.XmlTv.dll</HintPath> <Private>True</Private> @@ -50,17 +53,17 @@ <HintPath>..\packages\ini-parser.2.3.0\lib\net20\INIFileParser.dll</HintPath> <Private>True</Private> </Reference> - <Reference Include="Interfaces.IO"> - <HintPath>..\packages\Interfaces.IO.1.0.0.5\lib\portable-net45+sl4+wp71+win8+wpa81\Interfaces.IO.dll</HintPath> - </Reference> - <Reference Include="MediaBrowser.Naming, Version=1.0.6059.24054, Culture=neutral, processorArchitecture=MSIL"> - <HintPath>..\packages\MediaBrowser.Naming.1.0.0.55\lib\portable-net45+sl4+wp71+win8+wpa81\MediaBrowser.Naming.dll</HintPath> + <Reference Include="MediaBrowser.Naming, Version=1.0.6146.28476, Culture=neutral, processorArchitecture=MSIL"> + <HintPath>..\packages\MediaBrowser.Naming.1.0.0.57\lib\portable-net45+sl4+wp71+win8+wpa81\MediaBrowser.Naming.dll</HintPath> <Private>True</Private> </Reference> <Reference Include="Microsoft.IO.RecyclableMemoryStream, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> <HintPath>..\packages\Microsoft.IO.RecyclableMemoryStream.1.1.0.0\lib\net45\Microsoft.IO.RecyclableMemoryStream.dll</HintPath> <Private>True</Private> </Reference> + <Reference Include="Mono.Nat"> + <HintPath>..\ThirdParty\emby\Mono.Nat.dll</HintPath> + </Reference> <Reference Include="Patterns.Logging"> <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath> </Reference> @@ -71,21 +74,21 @@ <SpecificVersion>False</SpecificVersion> <HintPath>..\ThirdParty\SharpCompress\SharpCompress.dll</HintPath> </Reference> - <Reference Include="SimpleInjector, Version=3.2.4.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL"> - <HintPath>..\packages\SimpleInjector.3.2.4\lib\net45\SimpleInjector.dll</HintPath> - <Private>True</Private> - </Reference> <Reference Include="SocketHttpListener, Version=1.0.6109.26162, Culture=neutral, processorArchitecture=MSIL"> <HintPath>..\packages\SocketHttpListener.1.0.0.40\lib\net45\SocketHttpListener.dll</HintPath> <Private>True</Private> </Reference> <Reference Include="System" /> + <Reference Include="System.Configuration" /> <Reference Include="System.Core" /> <Reference Include="Microsoft.CSharp" /> <Reference Include="System.Data" /> + <Reference Include="System.IO.Compression" /> <Reference Include="System.Net" /> <Reference Include="System.Runtime.Serialization" /> <Reference Include="System.Security" /> + <Reference Include="System.ServiceModel" /> + <Reference Include="System.Transactions" /> <Reference Include="System.Web" /> <Reference Include="System.Xml" /> <Reference Include="ServiceStack"> @@ -281,14 +284,20 @@ <Compile Include="Persistence\IDbConnector.cs" /> <Compile Include="Persistence\MediaStreamColumns.cs" /> <Compile Include="Reflection\AssemblyInfo.cs" /> + <Compile Include="Security\MBLicenseFile.cs" /> + <Compile Include="Security\PluginSecurityManager.cs" /> + <Compile Include="Security\RegRecord.cs" /> + <Compile Include="Serialization\JsonSerializer.cs" /> <Compile Include="Social\SharingManager.cs" /> <Compile Include="Social\SharingRepository.cs" /> <Compile Include="Sorting\StartDateComparer.cs" /> <Compile Include="Sync\SyncHelper.cs" /> <Compile Include="Sync\SyncJobOptions.cs" /> <Compile Include="Sync\SyncNotificationEntryPoint.cs" /> + <Compile Include="TextEncoding\TextEncoding.cs" /> <Compile Include="Threading\PeriodicTimer.cs" /> <Compile Include="TV\SeriesPostScanTask.cs" /> + <Compile Include="Updates\InstallationManager.cs" /> <Compile Include="UserViews\CollectionFolderImageProvider.cs" /> <Compile Include="UserViews\DynamicImageProvider.cs" /> <Compile Include="News\NewsEntryPoint.cs" /> @@ -378,10 +387,6 @@ <Compile Include="Xml\XmlReaderSettingsFactory.cs" /> </ItemGroup> <ItemGroup> - <ProjectReference Include="..\MediaBrowser.Common.Implementations\MediaBrowser.Common.Implementations.csproj"> - <Project>{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}</Project> - <Name>MediaBrowser.Common.Implementations</Name> - </ProjectReference> <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj"> <Project>{9142EEFA-7570-41E1-BFCC-468BB571AF2F}</Project> <Name>MediaBrowser.Common</Name> @@ -398,10 +403,6 @@ <Project>{442b5058-dcaf-4263-bb6a-f21e31120a1b}</Project> <Name>MediaBrowser.Providers</Name> </ProjectReference> - <ProjectReference Include="..\Mono.Nat\Mono.Nat.csproj"> - <Project>{d7453b88-2266-4805-b39b-2b5a2a33e1ba}</Project> - <Name>Mono.Nat</Name> - </ProjectReference> </ItemGroup> <ItemGroup> <EmbeddedResource Include="Localization\Ratings\us.txt" /> diff --git a/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs b/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs index a3531b9a5..ed1d21d9a 100644 --- a/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs +++ b/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs @@ -1,5 +1,4 @@ using MediaBrowser.Common.Progress; -using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs b/MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs index 41f1ed240..63941f3b6 100644 --- a/MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs +++ b/MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs @@ -1,5 +1,4 @@ using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/PeopleValidationTask.cs b/MediaBrowser.Server.Implementations/ScheduledTasks/PeopleValidationTask.cs index 0315410cd..f90e61902 100644 --- a/MediaBrowser.Server.Implementations/ScheduledTasks/PeopleValidationTask.cs +++ b/MediaBrowser.Server.Implementations/ScheduledTasks/PeopleValidationTask.cs @@ -1,5 +1,4 @@ -using MediaBrowser.Common.ScheduledTasks; -using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Library; using System; using System.Collections.Generic; using System.Threading; diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/PluginUpdateTask.cs b/MediaBrowser.Server.Implementations/ScheduledTasks/PluginUpdateTask.cs index a2d587087..1d81ec043 100644 --- a/MediaBrowser.Server.Implementations/ScheduledTasks/PluginUpdateTask.cs +++ b/MediaBrowser.Server.Implementations/ScheduledTasks/PluginUpdateTask.cs @@ -1,5 +1,4 @@ using MediaBrowser.Common; -using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Common.Updates; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Net; diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs b/MediaBrowser.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs index e8e557e76..e695adb54 100644 --- a/MediaBrowser.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs +++ b/MediaBrowser.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs @@ -1,6 +1,4 @@ -using System.Linq; -using MediaBrowser.Common.ScheduledTasks; -using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; using MediaBrowser.Server.Implementations.Library; using System; diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/SystemUpdateTask.cs b/MediaBrowser.Server.Implementations/ScheduledTasks/SystemUpdateTask.cs index 34a75c0c7..e44eacf3d 100644 --- a/MediaBrowser.Server.Implementations/ScheduledTasks/SystemUpdateTask.cs +++ b/MediaBrowser.Server.Implementations/ScheduledTasks/SystemUpdateTask.cs @@ -1,6 +1,5 @@ using MediaBrowser.Common; using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Model.Logging; using System; using System.Collections.Generic; diff --git a/MediaBrowser.Server.Implementations/Security/MBLicenseFile.cs b/MediaBrowser.Server.Implementations/Security/MBLicenseFile.cs new file mode 100644 index 000000000..7b37925ba --- /dev/null +++ b/MediaBrowser.Server.Implementations/Security/MBLicenseFile.cs @@ -0,0 +1,157 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using MediaBrowser.Common.Configuration; + +namespace MediaBrowser.Server.Implementations.Security +{ + internal class MBLicenseFile + { + private readonly IApplicationPaths _appPaths; + + public string RegKey + { + get { return _regKey; } + set + { + if (value != _regKey) + { + //if key is changed - clear out our saved validations + _updateRecords.Clear(); + _regKey = value; + } + } + } + + private string Filename + { + get + { + return Path.Combine(_appPaths.ConfigurationDirectoryPath, "mb.lic"); + } + } + + private readonly ConcurrentDictionary<Guid, DateTime> _updateRecords = new ConcurrentDictionary<Guid, DateTime>(); + private readonly object _fileLock = new object(); + private string _regKey; + + public MBLicenseFile(IApplicationPaths appPaths) + { + _appPaths = appPaths; + + Load(); + } + + private void SetUpdateRecord(Guid key, DateTime value) + { + _updateRecords.AddOrUpdate(key, value, (k, v) => value); + } + + public void AddRegCheck(string featureId) + { + using (var provider = new MD5CryptoServiceProvider()) + { + var key = new Guid(provider.ComputeHash(Encoding.Unicode.GetBytes(featureId))); + var value = DateTime.UtcNow; + + SetUpdateRecord(key, value); + Save(); + } + + } + + public void RemoveRegCheck(string featureId) + { + using (var provider = new MD5CryptoServiceProvider()) + { + var key = new Guid(provider.ComputeHash(Encoding.Unicode.GetBytes(featureId))); + DateTime val; + + _updateRecords.TryRemove(key, out val); + + Save(); + } + + } + + public DateTime LastChecked(string featureId) + { + using (var provider = new MD5CryptoServiceProvider()) + { + DateTime last; + _updateRecords.TryGetValue(new Guid(provider.ComputeHash(Encoding.Unicode.GetBytes(featureId))), out last); + + // guard agains people just putting a large number in the file + return last < DateTime.UtcNow ? last : DateTime.MinValue; + } + } + + private void Load() + { + string[] contents = null; + var licenseFile = Filename; + lock (_fileLock) + { + try + { + contents = File.ReadAllLines(licenseFile); + } + catch (DirectoryNotFoundException) + { + File.Create(licenseFile).Close(); + } + catch (FileNotFoundException) + { + File.Create(licenseFile).Close(); + } + } + if (contents != null && contents.Length > 0) + { + //first line is reg key + RegKey = contents[0]; + + //next is legacy key + if (contents.Length > 1) + { + // Don't need this anymore + } + + //the rest of the lines should be pairs of features and timestamps + for (var i = 2; i < contents.Length; i = i + 2) + { + var feat = Guid.Parse(contents[i]); + + SetUpdateRecord(feat, new DateTime(Convert.ToInt64(contents[i + 1]))); + } + } + } + + public void Save() + { + //build our array + var lines = new List<string> + { + RegKey, + + // Legacy key + string.Empty + }; + + foreach (var pair in _updateRecords + .ToList()) + { + lines.Add(pair.Key.ToString()); + lines.Add(pair.Value.Ticks.ToString(CultureInfo.InvariantCulture)); + } + + var licenseFile = Filename; + Directory.CreateDirectory(Path.GetDirectoryName(licenseFile)); + lock (_fileLock) File.WriteAllLines(licenseFile, lines); + } + } +} diff --git a/MediaBrowser.Server.Implementations/Security/PluginSecurityManager.cs b/MediaBrowser.Server.Implementations/Security/PluginSecurityManager.cs new file mode 100644 index 000000000..7dc78a3af --- /dev/null +++ b/MediaBrowser.Server.Implementations/Security/PluginSecurityManager.cs @@ -0,0 +1,342 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Common; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Net; +using MediaBrowser.Common.Security; +using MediaBrowser.Controller; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Net; +using MediaBrowser.Model.Serialization; + +namespace MediaBrowser.Server.Implementations.Security +{ + /// <summary> + /// Class PluginSecurityManager + /// </summary> + public class PluginSecurityManager : ISecurityManager + { + private const string MBValidateUrl = "https://mb3admin.com/admin/service/registration/validate"; + private const string AppstoreRegUrl = /*MbAdmin.HttpsUrl*/ "https://mb3admin.com/admin/service/appstore/register"; + + /// <summary> + /// The _is MB supporter + /// </summary> + private bool? _isMbSupporter; + /// <summary> + /// The _is MB supporter initialized + /// </summary> + private bool _isMbSupporterInitialized; + /// <summary> + /// The _is MB supporter sync lock + /// </summary> + private object _isMbSupporterSyncLock = new object(); + + /// <summary> + /// Gets a value indicating whether this instance is MB supporter. + /// </summary> + /// <value><c>true</c> if this instance is MB supporter; otherwise, <c>false</c>.</value> + public bool IsMBSupporter + { + get + { + LazyInitializer.EnsureInitialized(ref _isMbSupporter, ref _isMbSupporterInitialized, ref _isMbSupporterSyncLock, () => GetSupporterRegistrationStatus().Result.IsRegistered); + return _isMbSupporter.Value; + } + } + + private MBLicenseFile _licenseFile; + private MBLicenseFile LicenseFile + { + get { return _licenseFile ?? (_licenseFile = new MBLicenseFile(_appPaths)); } + } + + private readonly IHttpClient _httpClient; + private readonly IJsonSerializer _jsonSerializer; + private readonly IServerApplicationHost _appHost; + private readonly ILogger _logger; + private readonly IApplicationPaths _appPaths; + private readonly IFileSystem _fileSystem; + + private IEnumerable<IRequiresRegistration> _registeredEntities; + protected IEnumerable<IRequiresRegistration> RegisteredEntities + { + get + { + return _registeredEntities ?? (_registeredEntities = _appHost.GetExports<IRequiresRegistration>()); + } + } + + /// <summary> + /// Initializes a new instance of the <see cref="PluginSecurityManager" /> class. + /// </summary> + public PluginSecurityManager(IServerApplicationHost appHost, IHttpClient httpClient, IJsonSerializer jsonSerializer, + IApplicationPaths appPaths, ILogManager logManager, IFileSystem fileSystem) + { + if (httpClient == null) + { + throw new ArgumentNullException("httpClient"); + } + + _appHost = appHost; + _httpClient = httpClient; + _jsonSerializer = jsonSerializer; + _appPaths = appPaths; + _fileSystem = fileSystem; + _logger = logManager.GetLogger("SecurityManager"); + } + + /// <summary> + /// Load all registration info for all entities that require registration + /// </summary> + /// <returns></returns> + public async Task LoadAllRegistrationInfo() + { + var tasks = new List<Task>(); + + ResetSupporterInfo(); + tasks.AddRange(RegisteredEntities.Select(i => i.LoadRegistrationInfoAsync())); + await Task.WhenAll(tasks); + } + + /// <summary> + /// Gets the registration status. + /// This overload supports existing plug-ins. + /// </summary> + /// <param name="feature">The feature.</param> + /// <param name="mb2Equivalent">The MB2 equivalent.</param> + /// <returns>Task{MBRegistrationRecord}.</returns> + public Task<MBRegistrationRecord> GetRegistrationStatus(string feature, string mb2Equivalent = null) + { + return GetRegistrationStatusInternal(feature, mb2Equivalent); + } + + /// <summary> + /// Gets the registration status. + /// </summary> + /// <param name="feature">The feature.</param> + /// <param name="mb2Equivalent">The MB2 equivalent.</param> + /// <param name="version">The version of this feature</param> + /// <returns>Task{MBRegistrationRecord}.</returns> + public Task<MBRegistrationRecord> GetRegistrationStatus(string feature, string mb2Equivalent, string version) + { + return GetRegistrationStatusInternal(feature, mb2Equivalent, version); + } + + private Task<MBRegistrationRecord> GetSupporterRegistrationStatus() + { + return GetRegistrationStatusInternal("MBSupporter", null, _appHost.ApplicationVersion.ToString()); + } + + /// <summary> + /// Gets or sets the supporter key. + /// </summary> + /// <value>The supporter key.</value> + public string SupporterKey + { + get + { + return LicenseFile.RegKey; + } + set + { + var newValue = value; + if (newValue != null) + { + newValue = newValue.Trim(); + } + + if (newValue != LicenseFile.RegKey) + { + LicenseFile.RegKey = newValue; + LicenseFile.Save(); + + // re-load registration info + Task.Run(() => LoadAllRegistrationInfo()); + } + } + } + + /// <summary> + /// Register an app store sale with our back-end. It will validate the transaction with the store + /// and then register the proper feature and then fill in the supporter key on success. + /// </summary> + /// <param name="parameters">Json parameters to send to admin server</param> + public async Task RegisterAppStoreSale(string parameters) + { + var options = new HttpRequestOptions() + { + Url = AppstoreRegUrl, + CancellationToken = CancellationToken.None, + BufferContent = false + }; + options.RequestHeaders.Add("X-Emby-Token", _appHost.SystemId); + options.RequestContent = parameters; + options.RequestContentType = "application/json"; + + try + { + using (var response = await _httpClient.Post(options).ConfigureAwait(false)) + { + var reg = _jsonSerializer.DeserializeFromStream<RegRecord>(response.Content); + + if (reg == null) + { + var msg = "Result from appstore registration was null."; + _logger.Error(msg); + throw new ApplicationException(msg); + } + if (!String.IsNullOrEmpty(reg.key)) + { + SupporterKey = reg.key; + } + } + + } + catch (ApplicationException) + { + SaveAppStoreInfo(parameters); + throw; + } + catch (HttpException e) + { + _logger.ErrorException("Error registering appstore purchase {0}", e, parameters ?? "NO PARMS SENT"); + + if (e.StatusCode.HasValue && e.StatusCode.Value == HttpStatusCode.PaymentRequired) + { + throw new PaymentRequiredException(); + } + throw new ApplicationException("Error registering store sale"); + } + catch (Exception e) + { + _logger.ErrorException("Error registering appstore purchase {0}", e, parameters ?? "NO PARMS SENT"); + SaveAppStoreInfo(parameters); + //TODO - could create a re-try routine on start-up if this file is there. For now we can handle manually. + throw new ApplicationException("Error registering store sale"); + } + + } + + private void SaveAppStoreInfo(string info) + { + // Save all transaction information to a file + + try + { + _fileSystem.WriteAllText(Path.Combine(_appPaths.ProgramDataPath, "apptrans-error.txt"), info); + } + catch (IOException) + { + + } + } + + private async Task<MBRegistrationRecord> GetRegistrationStatusInternal(string feature, + string mb2Equivalent = null, + string version = null) + { + var lastChecked = LicenseFile.LastChecked(feature); + + //check the reg file first to alleviate strain on the MB admin server - must actually check in every 30 days tho + var reg = new RegRecord + { + // Cache the result for up to a week + registered = lastChecked > DateTime.UtcNow.AddDays(-7) + }; + + var success = reg.registered; + + if (!(lastChecked > DateTime.UtcNow.AddDays(-1))) + { + var data = new Dictionary<string, string> + { + { "feature", feature }, + { "key", SupporterKey }, + { "mac", _appHost.SystemId }, + { "systemid", _appHost.SystemId }, + { "mb2equiv", mb2Equivalent }, + { "ver", version }, + { "platform", _appHost.OperatingSystemDisplayName }, + { "isservice", _appHost.IsRunningAsService.ToString().ToLower() } + }; + + try + { + var options = new HttpRequestOptions + { + Url = MBValidateUrl, + + // Seeing block length errors + EnableHttpCompression = false, + BufferContent = false + }; + + options.SetPostData(data); + + using (var json = (await _httpClient.Post(options).ConfigureAwait(false)).Content) + { + reg = _jsonSerializer.DeserializeFromStream<RegRecord>(json); + success = true; + } + + if (reg.registered) + { + LicenseFile.AddRegCheck(feature); + } + else + { + LicenseFile.RemoveRegCheck(feature); + } + + } + catch (Exception e) + { + _logger.ErrorException("Error checking registration status of {0}", e, feature); + } + } + + var record = new MBRegistrationRecord + { + IsRegistered = reg.registered, + ExpirationDate = reg.expDate, + RegChecked = true, + RegError = !success + }; + + record.TrialVersion = IsInTrial(reg.expDate, record.RegChecked, record.IsRegistered); + record.IsValid = !record.RegChecked || record.IsRegistered || record.TrialVersion; + + return record; + } + + private bool IsInTrial(DateTime expirationDate, bool regChecked, bool isRegistered) + { + //don't set this until we've successfully obtained exp date + if (!regChecked) + { + return false; + } + + var isInTrial = expirationDate > DateTime.UtcNow; + + return isInTrial && !isRegistered; + } + + /// <summary> + /// Resets the supporter info. + /// </summary> + private void ResetSupporterInfo() + { + _isMbSupporter = null; + _isMbSupporterInitialized = false; + } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Security/RegRecord.cs b/MediaBrowser.Server.Implementations/Security/RegRecord.cs new file mode 100644 index 000000000..947ec629f --- /dev/null +++ b/MediaBrowser.Server.Implementations/Security/RegRecord.cs @@ -0,0 +1,12 @@ +using System; + +namespace MediaBrowser.Server.Implementations.Security +{ + class RegRecord + { + public string featId { get; set; } + public bool registered { get; set; } + public DateTime expDate { get; set; } + public string key { get; set; } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Serialization/JsonSerializer.cs b/MediaBrowser.Server.Implementations/Serialization/JsonSerializer.cs new file mode 100644 index 000000000..b9a03242c --- /dev/null +++ b/MediaBrowser.Server.Implementations/Serialization/JsonSerializer.cs @@ -0,0 +1,227 @@ +using System; +using System.IO; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; + +namespace MediaBrowser.Server.Implementations.Serialization +{ + /// <summary> + /// Provides a wrapper around third party json serialization. + /// </summary> + public class JsonSerializer : IJsonSerializer + { + private readonly IFileSystem _fileSystem; + private readonly ILogger _logger; + + public JsonSerializer(IFileSystem fileSystem, ILogger logger) + { + _fileSystem = fileSystem; + _logger = logger; + Configure(); + } + + /// <summary> + /// Serializes to stream. + /// </summary> + /// <param name="obj">The obj.</param> + /// <param name="stream">The stream.</param> + /// <exception cref="System.ArgumentNullException">obj</exception> + public void SerializeToStream(object obj, Stream stream) + { + if (obj == null) + { + throw new ArgumentNullException("obj"); + } + + if (stream == null) + { + throw new ArgumentNullException("stream"); + } + + ServiceStack.Text.JsonSerializer.SerializeToStream(obj, obj.GetType(), stream); + } + + /// <summary> + /// Serializes to file. + /// </summary> + /// <param name="obj">The obj.</param> + /// <param name="file">The file.</param> + /// <exception cref="System.ArgumentNullException">obj</exception> + public void SerializeToFile(object obj, string file) + { + if (obj == null) + { + throw new ArgumentNullException("obj"); + } + + if (string.IsNullOrEmpty(file)) + { + throw new ArgumentNullException("file"); + } + + using (Stream stream = _fileSystem.GetFileStream(file, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read)) + { + SerializeToStream(obj, stream); + } + } + + private Stream OpenFile(string path) + { + _logger.Debug("Deserializing file {0}", path); + return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 131072); + } + + /// <summary> + /// Deserializes from file. + /// </summary> + /// <param name="type">The type.</param> + /// <param name="file">The file.</param> + /// <returns>System.Object.</returns> + /// <exception cref="System.ArgumentNullException">type</exception> + public object DeserializeFromFile(Type type, string file) + { + if (type == null) + { + throw new ArgumentNullException("type"); + } + + if (string.IsNullOrEmpty(file)) + { + throw new ArgumentNullException("file"); + } + + using (Stream stream = OpenFile(file)) + { + return DeserializeFromStream(stream, type); + } + } + + /// <summary> + /// Deserializes from file. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="file">The file.</param> + /// <returns>``0.</returns> + /// <exception cref="System.ArgumentNullException">file</exception> + public T DeserializeFromFile<T>(string file) + where T : class + { + if (string.IsNullOrEmpty(file)) + { + throw new ArgumentNullException("file"); + } + + using (Stream stream = OpenFile(file)) + { + return DeserializeFromStream<T>(stream); + } + } + + /// <summary> + /// Deserializes from stream. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="stream">The stream.</param> + /// <returns>``0.</returns> + /// <exception cref="System.ArgumentNullException">stream</exception> + public T DeserializeFromStream<T>(Stream stream) + { + if (stream == null) + { + throw new ArgumentNullException("stream"); + } + + return ServiceStack.Text.JsonSerializer.DeserializeFromStream<T>(stream); + } + + /// <summary> + /// Deserializes from string. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="text">The text.</param> + /// <returns>``0.</returns> + /// <exception cref="System.ArgumentNullException">text</exception> + public T DeserializeFromString<T>(string text) + { + if (string.IsNullOrEmpty(text)) + { + throw new ArgumentNullException("text"); + } + + return ServiceStack.Text.JsonSerializer.DeserializeFromString<T>(text); + } + + /// <summary> + /// Deserializes from stream. + /// </summary> + /// <param name="stream">The stream.</param> + /// <param name="type">The type.</param> + /// <returns>System.Object.</returns> + /// <exception cref="System.ArgumentNullException">stream</exception> + public object DeserializeFromStream(Stream stream, Type type) + { + if (stream == null) + { + throw new ArgumentNullException("stream"); + } + + if (type == null) + { + throw new ArgumentNullException("type"); + } + + return ServiceStack.Text.JsonSerializer.DeserializeFromStream(type, stream); + } + + /// <summary> + /// Configures this instance. + /// </summary> + private void Configure() + { + ServiceStack.Text.JsConfig.DateHandler = ServiceStack.Text.DateHandler.ISO8601; + ServiceStack.Text.JsConfig.ExcludeTypeInfo = true; + ServiceStack.Text.JsConfig.IncludeNullValues = false; + ServiceStack.Text.JsConfig.AlwaysUseUtc = true; + ServiceStack.Text.JsConfig.AssumeUtc = true; + } + + /// <summary> + /// Deserializes from string. + /// </summary> + /// <param name="json">The json.</param> + /// <param name="type">The type.</param> + /// <returns>System.Object.</returns> + /// <exception cref="System.ArgumentNullException">json</exception> + public object DeserializeFromString(string json, Type type) + { + if (string.IsNullOrEmpty(json)) + { + throw new ArgumentNullException("json"); + } + + if (type == null) + { + throw new ArgumentNullException("type"); + } + + return ServiceStack.Text.JsonSerializer.DeserializeFromString(json, type); + } + + /// <summary> + /// Serializes to string. + /// </summary> + /// <param name="obj">The obj.</param> + /// <returns>System.String.</returns> + /// <exception cref="System.ArgumentNullException">obj</exception> + public string SerializeToString(object obj) + { + if (obj == null) + { + throw new ArgumentNullException("obj"); + } + + return ServiceStack.Text.JsonSerializer.SerializeToString(obj, obj.GetType()); + } + } +} diff --git a/MediaBrowser.Server.Implementations/ServerApplicationPaths.cs b/MediaBrowser.Server.Implementations/ServerApplicationPaths.cs index 237d49fda..c8dea9005 100644 --- a/MediaBrowser.Server.Implementations/ServerApplicationPaths.cs +++ b/MediaBrowser.Server.Implementations/ServerApplicationPaths.cs @@ -1,4 +1,4 @@ -using MediaBrowser.Common.Implementations; +using Emby.Common.Implementations; using MediaBrowser.Controller; using System.IO; diff --git a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs b/MediaBrowser.Server.Implementations/Sync/MediaSync.cs index 3e4bff241..b6853267e 100644 --- a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs +++ b/MediaBrowser.Server.Implementations/Sync/MediaSync.cs @@ -18,7 +18,6 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.IO; -using Interfaces.IO; using MediaBrowser.Common.IO; using MediaBrowser.Server.Implementations.IO; @@ -78,8 +77,8 @@ namespace MediaBrowser.Server.Implementations.Sync CancellationToken cancellationToken) { var localItems = await dataProvider.GetLocalItems(target, serverId).ConfigureAwait(false); - var remoteFiles = await provider.GetFiles(new FileQuery(), target, cancellationToken).ConfigureAwait(false); - var remoteIds = remoteFiles.Items.Select(i => i.Id).ToList(); + var remoteFiles = await provider.GetFiles(target, cancellationToken).ConfigureAwait(false); + var remoteIds = remoteFiles.Items.Select(i => i.FullName).ToList(); var jobItemIds = new List<string>(); diff --git a/MediaBrowser.Server.Implementations/Sync/ServerSyncScheduledTask.cs b/MediaBrowser.Server.Implementations/Sync/ServerSyncScheduledTask.cs index bb02e490b..dc7f925a0 100644 --- a/MediaBrowser.Server.Implementations/Sync/ServerSyncScheduledTask.cs +++ b/MediaBrowser.Server.Implementations/Sync/ServerSyncScheduledTask.cs @@ -1,5 +1,4 @@ using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Controller; using MediaBrowser.Controller.Sync; using MediaBrowser.Model.Logging; @@ -8,8 +7,6 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.IO; -using MediaBrowser.Controller.IO; using MediaBrowser.Model.IO; using MediaBrowser.Model.Tasks; diff --git a/MediaBrowser.Server.Implementations/Sync/SyncConvertScheduledTask.cs b/MediaBrowser.Server.Implementations/Sync/SyncConvertScheduledTask.cs index abbf39d39..3a5023fe5 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncConvertScheduledTask.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncConvertScheduledTask.cs @@ -1,5 +1,4 @@ using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Sync; diff --git a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs index 47fb9a6f4..7bcb7b05e 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs @@ -1,7 +1,6 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Events; using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Controller; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; @@ -29,9 +28,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.IO; -using MediaBrowser.Common.IO; -using MediaBrowser.Controller.IO; -using MediaBrowser.Model.IO; using MediaBrowser.Model.Tasks; namespace MediaBrowser.Server.Implementations.Sync diff --git a/MediaBrowser.Server.Implementations/Sync/TargetDataProvider.cs b/MediaBrowser.Server.Implementations/Sync/TargetDataProvider.cs index fca9e763f..03df0d4e6 100644 --- a/MediaBrowser.Server.Implementations/Sync/TargetDataProvider.cs +++ b/MediaBrowser.Server.Implementations/Sync/TargetDataProvider.cs @@ -6,15 +6,10 @@ using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Sync; using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.IO; -using Interfaces.IO; -using MediaBrowser.Common.IO; -using MediaBrowser.Controller.IO; -using MediaBrowser.Model.IO; namespace MediaBrowser.Server.Implementations.Sync { @@ -68,15 +63,11 @@ namespace MediaBrowser.Server.Implementations.Sync { _logger.Debug("Getting {0} from {1}", string.Join(MediaSync.PathSeparatorString, GetRemotePath().ToArray()), _provider.Name); - var fileResult = await _provider.GetFiles(new FileQuery - { - FullPath = GetRemotePath().ToArray() - - }, _target, cancellationToken).ConfigureAwait(false); + var fileResult = await _provider.GetFiles(GetRemotePath().ToArray(), _target, cancellationToken).ConfigureAwait(false); if (fileResult.Items.Length > 0) { - using (var stream = await _provider.GetFile(fileResult.Items[0].Id, _target, new Progress<double>(), cancellationToken)) + using (var stream = await _provider.GetFile(fileResult.Items[0].FullName, _target, new Progress<double>(), cancellationToken)) { return _json.DeserializeFromStream<List<LocalItem>>(stream); } diff --git a/MediaBrowser.Server.Implementations/TextEncoding/TextEncoding.cs b/MediaBrowser.Server.Implementations/TextEncoding/TextEncoding.cs new file mode 100644 index 000000000..c1029dfb5 --- /dev/null +++ b/MediaBrowser.Server.Implementations/TextEncoding/TextEncoding.cs @@ -0,0 +1,18 @@ +using System.Text; +using MediaBrowser.Model.TextEncoding; + +namespace MediaBrowser.Server.Implementations.TextEncoding +{ + public class TextEncoding : IEncoding + { + public byte[] GetASCIIBytes(string text) + { + return Encoding.ASCII.GetBytes(text); + } + + public string GetASCIIString(byte[] bytes, int startIndex, int length) + { + return Encoding.ASCII.GetString(bytes, 0, bytes.Length); + } + } +} diff --git a/MediaBrowser.Server.Implementations/Udp/UdpServer.cs b/MediaBrowser.Server.Implementations/Udp/UdpServer.cs index f7853d1d5..c2082f0d2 100644 --- a/MediaBrowser.Server.Implementations/Udp/UdpServer.cs +++ b/MediaBrowser.Server.Implementations/Udp/UdpServer.cs @@ -10,7 +10,7 @@ using System.Net; using System.Net.Sockets; using System.Text; using System.Threading.Tasks; -using MediaBrowser.Common.Implementations.Networking; +using Emby.Common.Implementations.Networking; namespace MediaBrowser.Server.Implementations.Udp { diff --git a/MediaBrowser.Server.Implementations/Updates/InstallationManager.cs b/MediaBrowser.Server.Implementations/Updates/InstallationManager.cs new file mode 100644 index 000000000..c6930e487 --- /dev/null +++ b/MediaBrowser.Server.Implementations/Updates/InstallationManager.cs @@ -0,0 +1,685 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Common; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Events; +using MediaBrowser.Common.Net; +using MediaBrowser.Common.Plugins; +using MediaBrowser.Common.Progress; +using MediaBrowser.Common.Security; +using MediaBrowser.Common.Updates; +using MediaBrowser.Model.Events; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; +using MediaBrowser.Model.Updates; + +namespace MediaBrowser.Server.Implementations.Updates +{ + /// <summary> + /// Manages all install, uninstall and update operations (both plugins and system) + /// </summary> + public class InstallationManager : IInstallationManager + { + public event EventHandler<InstallationEventArgs> PackageInstalling; + public event EventHandler<InstallationEventArgs> PackageInstallationCompleted; + public event EventHandler<InstallationFailedEventArgs> PackageInstallationFailed; + public event EventHandler<InstallationEventArgs> PackageInstallationCancelled; + + /// <summary> + /// The current installations + /// </summary> + public List<Tuple<InstallationInfo, CancellationTokenSource>> CurrentInstallations { get; set; } + + /// <summary> + /// The completed installations + /// </summary> + public ConcurrentBag<InstallationInfo> CompletedInstallations { get; set; } + + #region PluginUninstalled Event + /// <summary> + /// Occurs when [plugin uninstalled]. + /// </summary> + public event EventHandler<GenericEventArgs<IPlugin>> PluginUninstalled; + + /// <summary> + /// Called when [plugin uninstalled]. + /// </summary> + /// <param name="plugin">The plugin.</param> + private void OnPluginUninstalled(IPlugin plugin) + { + EventHelper.FireEventIfNotNull(PluginUninstalled, this, new GenericEventArgs<IPlugin> { Argument = plugin }, _logger); + } + #endregion + + #region PluginUpdated Event + /// <summary> + /// Occurs when [plugin updated]. + /// </summary> + public event EventHandler<GenericEventArgs<Tuple<IPlugin, PackageVersionInfo>>> PluginUpdated; + /// <summary> + /// Called when [plugin updated]. + /// </summary> + /// <param name="plugin">The plugin.</param> + /// <param name="newVersion">The new version.</param> + private void OnPluginUpdated(IPlugin plugin, PackageVersionInfo newVersion) + { + _logger.Info("Plugin updated: {0} {1} {2}", newVersion.name, newVersion.versionStr ?? string.Empty, newVersion.classification); + + EventHelper.FireEventIfNotNull(PluginUpdated, this, new GenericEventArgs<Tuple<IPlugin, PackageVersionInfo>> { Argument = new Tuple<IPlugin, PackageVersionInfo>(plugin, newVersion) }, _logger); + + _applicationHost.NotifyPendingRestart(); + } + #endregion + + #region PluginInstalled Event + /// <summary> + /// Occurs when [plugin updated]. + /// </summary> + public event EventHandler<GenericEventArgs<PackageVersionInfo>> PluginInstalled; + /// <summary> + /// Called when [plugin installed]. + /// </summary> + /// <param name="package">The package.</param> + private void OnPluginInstalled(PackageVersionInfo package) + { + _logger.Info("New plugin installed: {0} {1} {2}", package.name, package.versionStr ?? string.Empty, package.classification); + + EventHelper.FireEventIfNotNull(PluginInstalled, this, new GenericEventArgs<PackageVersionInfo> { Argument = package }, _logger); + + _applicationHost.NotifyPendingRestart(); + } + #endregion + + /// <summary> + /// The _logger + /// </summary> + private readonly ILogger _logger; + + private readonly IApplicationPaths _appPaths; + private readonly IHttpClient _httpClient; + private readonly IJsonSerializer _jsonSerializer; + private readonly ISecurityManager _securityManager; + private readonly IConfigurationManager _config; + private readonly IFileSystem _fileSystem; + + /// <summary> + /// Gets the application host. + /// </summary> + /// <value>The application host.</value> + private readonly IApplicationHost _applicationHost; + + public InstallationManager(ILogger logger, IApplicationHost appHost, IApplicationPaths appPaths, IHttpClient httpClient, IJsonSerializer jsonSerializer, ISecurityManager securityManager, IConfigurationManager config, IFileSystem fileSystem) + { + if (logger == null) + { + throw new ArgumentNullException("logger"); + } + + CurrentInstallations = new List<Tuple<InstallationInfo, CancellationTokenSource>>(); + CompletedInstallations = new ConcurrentBag<InstallationInfo>(); + + _applicationHost = appHost; + _appPaths = appPaths; + _httpClient = httpClient; + _jsonSerializer = jsonSerializer; + _securityManager = securityManager; + _config = config; + _fileSystem = fileSystem; + _logger = logger; + } + + private Version GetPackageVersion(PackageVersionInfo version) + { + return new Version(ValueOrDefault(version.versionStr, "0.0.0.1")); + } + + private static string ValueOrDefault(string str, string def) + { + return string.IsNullOrEmpty(str) ? def : str; + } + + /// <summary> + /// Gets all available packages. + /// </summary> + /// <returns>Task{List{PackageInfo}}.</returns> + public async Task<IEnumerable<PackageInfo>> GetAvailablePackages(CancellationToken cancellationToken, + bool withRegistration = true, + string packageType = null, + Version applicationVersion = null) + { + var data = new Dictionary<string, string> + { + { "key", _securityManager.SupporterKey }, + { "mac", _applicationHost.SystemId }, + { "systemid", _applicationHost.SystemId } + }; + + if (withRegistration) + { + using (var json = await _httpClient.Post("https://www.mb3admin.com/admin/service/package/retrieveall", data, cancellationToken).ConfigureAwait(false)) + { + cancellationToken.ThrowIfCancellationRequested(); + + var packages = _jsonSerializer.DeserializeFromStream<List<PackageInfo>>(json).ToList(); + + return FilterPackages(packages, packageType, applicationVersion); + } + } + else + { + var packages = await GetAvailablePackagesWithoutRegistrationInfo(cancellationToken).ConfigureAwait(false); + + return FilterPackages(packages.ToList(), packageType, applicationVersion); + } + } + + private DateTime _lastPackageUpdateTime; + + /// <summary> + /// Gets all available packages. + /// </summary> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task{List{PackageInfo}}.</returns> + public async Task<IEnumerable<PackageInfo>> GetAvailablePackagesWithoutRegistrationInfo(CancellationToken cancellationToken) + { + _logger.Info("Opening {0}", PackageCachePath); + try + { + using (var stream = _fileSystem.OpenRead(PackageCachePath)) + { + var packages = _jsonSerializer.DeserializeFromStream<List<PackageInfo>>(stream).ToList(); + + if (DateTime.UtcNow - _lastPackageUpdateTime > GetCacheLength()) + { + UpdateCachedPackages(CancellationToken.None, false); + } + + return packages; + } + } + catch (Exception) + { + + } + + _lastPackageUpdateTime = DateTime.MinValue; + await UpdateCachedPackages(cancellationToken, true).ConfigureAwait(false); + using (var stream = _fileSystem.OpenRead(PackageCachePath)) + { + return _jsonSerializer.DeserializeFromStream<List<PackageInfo>>(stream).ToList(); + } + } + + private string PackageCachePath + { + get { return Path.Combine(_appPaths.CachePath, "serverpackages.json"); } + } + + private readonly SemaphoreSlim _updateSemaphore = new SemaphoreSlim(1, 1); + private async Task UpdateCachedPackages(CancellationToken cancellationToken, bool throwErrors) + { + await _updateSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); + + try + { + if (DateTime.UtcNow - _lastPackageUpdateTime < GetCacheLength()) + { + return; + } + + var tempFile = await _httpClient.GetTempFile(new HttpRequestOptions + { + Url = "https://www.mb3admin.com/admin/service/MB3Packages.json", + CancellationToken = cancellationToken, + Progress = new Progress<Double>() + + }).ConfigureAwait(false); + + _fileSystem.CreateDirectory(Path.GetDirectoryName(PackageCachePath)); + + _fileSystem.CopyFile(tempFile, PackageCachePath, true); + _lastPackageUpdateTime = DateTime.UtcNow; + } + catch (Exception ex) + { + _logger.ErrorException("Error updating package cache", ex); + + if (throwErrors) + { + throw; + } + } + finally + { + _updateSemaphore.Release(); + } + } + + private TimeSpan GetCacheLength() + { + switch (_config.CommonConfiguration.SystemUpdateLevel) + { + case PackageVersionClass.Beta: + return TimeSpan.FromMinutes(30); + case PackageVersionClass.Dev: + return TimeSpan.FromMinutes(3); + default: + return TimeSpan.FromHours(24); + } + } + + protected IEnumerable<PackageInfo> FilterPackages(List<PackageInfo> packages) + { + foreach (var package in packages) + { + package.versions = package.versions.Where(v => !string.IsNullOrWhiteSpace(v.sourceUrl)) + .OrderByDescending(GetPackageVersion).ToList(); + } + + // Remove packages with no versions + packages = packages.Where(p => p.versions.Any()).ToList(); + + return packages; + } + + protected IEnumerable<PackageInfo> FilterPackages(List<PackageInfo> packages, string packageType, Version applicationVersion) + { + foreach (var package in packages) + { + package.versions = package.versions.Where(v => !string.IsNullOrWhiteSpace(v.sourceUrl)) + .OrderByDescending(GetPackageVersion).ToList(); + } + + if (!string.IsNullOrWhiteSpace(packageType)) + { + packages = packages.Where(p => string.Equals(p.type, packageType, StringComparison.OrdinalIgnoreCase)).ToList(); + } + + // If an app version was supplied, filter the versions for each package to only include supported versions + if (applicationVersion != null) + { + foreach (var package in packages) + { + package.versions = package.versions.Where(v => IsPackageVersionUpToDate(v, applicationVersion)).ToList(); + } + } + + // Remove packages with no versions + packages = packages.Where(p => p.versions.Any()).ToList(); + + return packages; + } + + /// <summary> + /// Determines whether [is package version up to date] [the specified package version info]. + /// </summary> + /// <param name="packageVersionInfo">The package version info.</param> + /// <param name="currentServerVersion">The current server version.</param> + /// <returns><c>true</c> if [is package version up to date] [the specified package version info]; otherwise, <c>false</c>.</returns> + private bool IsPackageVersionUpToDate(PackageVersionInfo packageVersionInfo, Version currentServerVersion) + { + if (string.IsNullOrEmpty(packageVersionInfo.requiredVersionStr)) + { + return true; + } + + Version requiredVersion; + + return Version.TryParse(packageVersionInfo.requiredVersionStr, out requiredVersion) && currentServerVersion >= requiredVersion; + } + + /// <summary> + /// Gets the package. + /// </summary> + /// <param name="name">The name.</param> + /// <param name="guid">The assembly guid</param> + /// <param name="classification">The classification.</param> + /// <param name="version">The version.</param> + /// <returns>Task{PackageVersionInfo}.</returns> + public async Task<PackageVersionInfo> GetPackage(string name, string guid, PackageVersionClass classification, Version version) + { + var packages = await GetAvailablePackages(CancellationToken.None).ConfigureAwait(false); + + var package = packages.FirstOrDefault(p => string.Equals(p.guid, guid ?? "none", StringComparison.OrdinalIgnoreCase)) + ?? packages.FirstOrDefault(p => p.name.Equals(name, StringComparison.OrdinalIgnoreCase)); + + if (package == null) + { + return null; + } + + return package.versions.FirstOrDefault(v => GetPackageVersion(v).Equals(version) && v.classification == classification); + } + + /// <summary> + /// Gets the latest compatible version. + /// </summary> + /// <param name="name">The name.</param> + /// <param name="guid">The assembly guid if this is a plug-in</param> + /// <param name="currentServerVersion">The current server version.</param> + /// <param name="classification">The classification.</param> + /// <returns>Task{PackageVersionInfo}.</returns> + public async Task<PackageVersionInfo> GetLatestCompatibleVersion(string name, string guid, Version currentServerVersion, PackageVersionClass classification = PackageVersionClass.Release) + { + var packages = await GetAvailablePackages(CancellationToken.None).ConfigureAwait(false); + + return GetLatestCompatibleVersion(packages, name, guid, currentServerVersion, classification); + } + + /// <summary> + /// Gets the latest compatible version. + /// </summary> + /// <param name="availablePackages">The available packages.</param> + /// <param name="name">The name.</param> + /// <param name="currentServerVersion">The current server version.</param> + /// <param name="classification">The classification.</param> + /// <returns>PackageVersionInfo.</returns> + public PackageVersionInfo GetLatestCompatibleVersion(IEnumerable<PackageInfo> availablePackages, string name, string guid, Version currentServerVersion, PackageVersionClass classification = PackageVersionClass.Release) + { + var package = availablePackages.FirstOrDefault(p => string.Equals(p.guid, guid ?? "none", StringComparison.OrdinalIgnoreCase)) + ?? availablePackages.FirstOrDefault(p => p.name.Equals(name, StringComparison.OrdinalIgnoreCase)); + + if (package == null) + { + return null; + } + + return package.versions + .OrderByDescending(GetPackageVersion) + .FirstOrDefault(v => v.classification <= classification && IsPackageVersionUpToDate(v, currentServerVersion)); + } + + /// <summary> + /// Gets the available plugin updates. + /// </summary> + /// <param name="applicationVersion">The current server version.</param> + /// <param name="withAutoUpdateEnabled">if set to <c>true</c> [with auto update enabled].</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task{IEnumerable{PackageVersionInfo}}.</returns> + public async Task<IEnumerable<PackageVersionInfo>> GetAvailablePluginUpdates(Version applicationVersion, bool withAutoUpdateEnabled, CancellationToken cancellationToken) + { + var catalog = await GetAvailablePackagesWithoutRegistrationInfo(cancellationToken).ConfigureAwait(false); + + var plugins = _applicationHost.Plugins.ToList(); + + if (withAutoUpdateEnabled) + { + plugins = plugins + .Where(p => _config.CommonConfiguration.EnableAutoUpdate) + .ToList(); + } + + // Figure out what needs to be installed + var packages = plugins.Select(p => + { + var latestPluginInfo = GetLatestCompatibleVersion(catalog, p.Name, p.Id.ToString(), applicationVersion, _config.CommonConfiguration.SystemUpdateLevel); + + return latestPluginInfo != null && GetPackageVersion(latestPluginInfo) > p.Version ? latestPluginInfo : null; + + }).Where(i => i != null).ToList(); + + return packages + .Where(p => !string.IsNullOrWhiteSpace(p.sourceUrl) && !CompletedInstallations.Any(i => string.Equals(i.AssemblyGuid, p.guid, StringComparison.OrdinalIgnoreCase))); + } + + /// <summary> + /// Installs the package. + /// </summary> + /// <param name="package">The package.</param> + /// <param name="isPlugin">if set to <c>true</c> [is plugin].</param> + /// <param name="progress">The progress.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + /// <exception cref="System.ArgumentNullException">package</exception> + public async Task InstallPackage(PackageVersionInfo package, bool isPlugin, IProgress<double> progress, CancellationToken cancellationToken) + { + if (package == null) + { + throw new ArgumentNullException("package"); + } + + if (progress == null) + { + throw new ArgumentNullException("progress"); + } + + var installationInfo = new InstallationInfo + { + Id = Guid.NewGuid().ToString("N"), + Name = package.name, + AssemblyGuid = package.guid, + UpdateClass = package.classification, + Version = package.versionStr + }; + + var innerCancellationTokenSource = new CancellationTokenSource(); + + var tuple = new Tuple<InstallationInfo, CancellationTokenSource>(installationInfo, innerCancellationTokenSource); + + // Add it to the in-progress list + lock (CurrentInstallations) + { + CurrentInstallations.Add(tuple); + } + + var innerProgress = new ActionableProgress<double>(); + + // Whenever the progress updates, update the outer progress object and InstallationInfo + innerProgress.RegisterAction(percent => + { + progress.Report(percent); + + installationInfo.PercentComplete = percent; + }); + + var linkedToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, innerCancellationTokenSource.Token).Token; + + var installationEventArgs = new InstallationEventArgs + { + InstallationInfo = installationInfo, + PackageVersionInfo = package + }; + + EventHelper.FireEventIfNotNull(PackageInstalling, this, installationEventArgs, _logger); + + try + { + await InstallPackageInternal(package, isPlugin, innerProgress, linkedToken).ConfigureAwait(false); + + lock (CurrentInstallations) + { + CurrentInstallations.Remove(tuple); + } + + progress.Report(100); + + CompletedInstallations.Add(installationInfo); + + EventHelper.FireEventIfNotNull(PackageInstallationCompleted, this, installationEventArgs, _logger); + } + catch (OperationCanceledException) + { + lock (CurrentInstallations) + { + CurrentInstallations.Remove(tuple); + } + + _logger.Info("Package installation cancelled: {0} {1}", package.name, package.versionStr); + + EventHelper.FireEventIfNotNull(PackageInstallationCancelled, this, installationEventArgs, _logger); + + throw; + } + catch (Exception ex) + { + _logger.ErrorException("Package installation failed", ex); + + lock (CurrentInstallations) + { + CurrentInstallations.Remove(tuple); + } + + EventHelper.FireEventIfNotNull(PackageInstallationFailed, this, new InstallationFailedEventArgs + { + InstallationInfo = installationInfo, + Exception = ex + + }, _logger); + + throw; + } + finally + { + // Dispose the progress object and remove the installation from the in-progress list + innerProgress.Dispose(); + tuple.Item2.Dispose(); + } + } + + /// <summary> + /// Installs the package internal. + /// </summary> + /// <param name="package">The package.</param> + /// <param name="isPlugin">if set to <c>true</c> [is plugin].</param> + /// <param name="progress">The progress.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + private async Task InstallPackageInternal(PackageVersionInfo package, bool isPlugin, IProgress<double> progress, CancellationToken cancellationToken) + { + // Do the install + await PerformPackageInstallation(progress, package, cancellationToken).ConfigureAwait(false); + + // Do plugin-specific processing + if (isPlugin) + { + // Set last update time if we were installed before + var plugin = _applicationHost.Plugins.FirstOrDefault(p => string.Equals(p.Id.ToString(), package.guid, StringComparison.OrdinalIgnoreCase)) + ?? _applicationHost.Plugins.FirstOrDefault(p => p.Name.Equals(package.name, StringComparison.OrdinalIgnoreCase)); + + if (plugin != null) + { + OnPluginUpdated(plugin, package); + } + else + { + OnPluginInstalled(package); + } + } + } + + private async Task PerformPackageInstallation(IProgress<double> progress, PackageVersionInfo package, CancellationToken cancellationToken) + { + // Target based on if it is an archive or single assembly + // zip archives are assumed to contain directory structures relative to our ProgramDataPath + var extension = Path.GetExtension(package.targetFilename); + var isArchive = string.Equals(extension, ".zip", StringComparison.OrdinalIgnoreCase) || string.Equals(extension, ".rar", StringComparison.OrdinalIgnoreCase) || string.Equals(extension, ".7z", StringComparison.OrdinalIgnoreCase); + var target = Path.Combine(isArchive ? _appPaths.TempUpdatePath : _appPaths.PluginsPath, package.targetFilename); + + // Download to temporary file so that, if interrupted, it won't destroy the existing installation + var tempFile = await _httpClient.GetTempFile(new HttpRequestOptions + { + Url = package.sourceUrl, + CancellationToken = cancellationToken, + Progress = progress + + }).ConfigureAwait(false); + + cancellationToken.ThrowIfCancellationRequested(); + + // Validate with a checksum + var packageChecksum = string.IsNullOrWhiteSpace(package.checksum) ? Guid.Empty : new Guid(package.checksum); + if (packageChecksum != Guid.Empty) // support for legacy uploads for now + { + using (var crypto = new MD5CryptoServiceProvider()) + using (var stream = new BufferedStream(_fileSystem.OpenRead(tempFile), 100000)) + { + var check = Guid.Parse(BitConverter.ToString(crypto.ComputeHash(stream)).Replace("-", String.Empty)); + if (check != packageChecksum) + { + throw new ApplicationException(string.Format("Download validation failed for {0}. Probably corrupted during transfer.", package.name)); + } + } + } + + cancellationToken.ThrowIfCancellationRequested(); + + // Success - move it to the real target + try + { + _fileSystem.CreateDirectory(Path.GetDirectoryName(target)); + _fileSystem.CopyFile(tempFile, target, true); + //If it is an archive - write out a version file so we know what it is + if (isArchive) + { + _fileSystem.WriteAllText(target + ".ver", package.versionStr); + } + } + catch (IOException e) + { + _logger.ErrorException("Error attempting to move file from {0} to {1}", e, tempFile, target); + throw; + } + + try + { + _fileSystem.DeleteFile(tempFile); + } + catch (IOException e) + { + // Don't fail because of this + _logger.ErrorException("Error deleting temp file {0]", e, tempFile); + } + } + + /// <summary> + /// Uninstalls a plugin + /// </summary> + /// <param name="plugin">The plugin.</param> + /// <exception cref="System.ArgumentException"></exception> + public void UninstallPlugin(IPlugin plugin) + { + plugin.OnUninstalling(); + + // Remove it the quick way for now + _applicationHost.RemovePlugin(plugin); + + _fileSystem.DeleteFile(plugin.AssemblyFilePath); + + OnPluginUninstalled(plugin); + + _applicationHost.NotifyPendingRestart(); + } + + /// <summary> + /// Releases unmanaged and - optionally - managed resources. + /// </summary> + /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> + protected virtual void Dispose(bool dispose) + { + if (dispose) + { + lock (CurrentInstallations) + { + foreach (var tuple in CurrentInstallations) + { + tuple.Item2.Dispose(); + } + + CurrentInstallations.Clear(); + } + } + } + + public void Dispose() + { + Dispose(true); + } + } +} diff --git a/MediaBrowser.Server.Implementations/packages.config b/MediaBrowser.Server.Implementations/packages.config index 522d2bbfd..b1b1abf14 100644 --- a/MediaBrowser.Server.Implementations/packages.config +++ b/MediaBrowser.Server.Implementations/packages.config @@ -2,10 +2,8 @@ <packages>
<package id="Emby.XmlTv" version="1.0.0.58" targetFramework="net46" />
<package id="ini-parser" version="2.3.0" targetFramework="net45" />
- <package id="Interfaces.IO" version="1.0.0.5" targetFramework="net45" />
- <package id="MediaBrowser.Naming" version="1.0.0.55" targetFramework="net45" />
+ <package id="MediaBrowser.Naming" version="1.0.0.57" targetFramework="net46" />
<package id="Microsoft.IO.RecyclableMemoryStream" version="1.1.0.0" targetFramework="net46" />
<package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
- <package id="SimpleInjector" version="3.2.4" targetFramework="net46" />
<package id="SocketHttpListener" version="1.0.0.40" targetFramework="net45" />
</packages>
\ No newline at end of file |
