aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Common
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Common')
-rw-r--r--MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs18
-rw-r--r--MediaBrowser.Common/Configuration/IApplicationPaths.cs84
-rw-r--r--MediaBrowser.Common/Configuration/IConfigurationFactory.cs22
-rw-r--r--MediaBrowser.Common/Configuration/IConfigurationManager.cs82
-rw-r--r--MediaBrowser.Common/Events/EventHelper.cs108
-rw-r--r--MediaBrowser.Common/Extensions/BaseExtensions.cs58
-rw-r--r--MediaBrowser.Common/Extensions/ResourceNotFoundException.cs63
-rw-r--r--MediaBrowser.Common/IApplicationHost.cs147
-rw-r--r--MediaBrowser.Common/MediaBrowser.Common.csproj16
-rw-r--r--MediaBrowser.Common/Net/HttpRequestOptions.cs157
-rw-r--r--MediaBrowser.Common/Net/HttpResponseInfo.cs75
-rw-r--r--MediaBrowser.Common/Net/IHttpClient.cs59
-rw-r--r--MediaBrowser.Common/Net/INetworkManager.cs66
-rw-r--r--MediaBrowser.Common/Plugins/BasePlugin.cs276
-rw-r--r--MediaBrowser.Common/Plugins/IPlugin.cs83
-rw-r--r--MediaBrowser.Common/Progress/ActionableProgress.cs54
-rw-r--r--MediaBrowser.Common/Properties/AssemblyInfo.cs27
-rw-r--r--MediaBrowser.Common/Security/IRequiresRegistration.cs15
-rw-r--r--MediaBrowser.Common/Security/ISecurityManager.cs32
-rw-r--r--MediaBrowser.Common/Security/PaymentRequiredException.cs8
-rw-r--r--MediaBrowser.Common/Updates/GithubUpdater.cs278
-rw-r--r--MediaBrowser.Common/Updates/IInstallationManager.cs121
-rw-r--r--MediaBrowser.Common/Updates/InstallationEventArgs.cs11
-rw-r--r--MediaBrowser.Common/Updates/InstallationFailedEventArgs.cs9
24 files changed, 1869 insertions, 0 deletions
diff --git a/MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs b/MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs
new file mode 100644
index 000000000..310e2aa63
--- /dev/null
+++ b/MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs
@@ -0,0 +1,18 @@
+using System;
+
+namespace MediaBrowser.Common.Configuration
+{
+ public class ConfigurationUpdateEventArgs : EventArgs
+ {
+ /// <summary>
+ /// Gets or sets the key.
+ /// </summary>
+ /// <value>The key.</value>
+ public string Key { get; set; }
+ /// <summary>
+ /// Gets or sets the new configuration.
+ /// </summary>
+ /// <value>The new configuration.</value>
+ public object NewConfiguration { get; set; }
+ }
+}
diff --git a/MediaBrowser.Common/Configuration/IApplicationPaths.cs b/MediaBrowser.Common/Configuration/IApplicationPaths.cs
new file mode 100644
index 000000000..c6256bc5a
--- /dev/null
+++ b/MediaBrowser.Common/Configuration/IApplicationPaths.cs
@@ -0,0 +1,84 @@
+
+namespace MediaBrowser.Common.Configuration
+{
+ /// <summary>
+ /// Interface IApplicationPaths
+ /// </summary>
+ public interface IApplicationPaths
+ {
+ /// <summary>
+ /// Gets the path to the program data folder
+ /// </summary>
+ /// <value>The program data path.</value>
+ string ProgramDataPath { get; }
+
+ /// <summary>
+ /// Gets the path to the program system folder
+ /// </summary>
+ /// <value>The program data path.</value>
+ string ProgramSystemPath { get; }
+
+ /// <summary>
+ /// Gets the folder path to the data directory
+ /// </summary>
+ /// <value>The data directory.</value>
+ string DataPath { get; }
+
+ /// <summary>
+ /// Gets the image cache path.
+ /// </summary>
+ /// <value>The image cache path.</value>
+ string ImageCachePath { get; }
+
+ /// <summary>
+ /// Gets the path to the plugin directory
+ /// </summary>
+ /// <value>The plugins path.</value>
+ string PluginsPath { get; }
+
+ /// <summary>
+ /// Gets the path to the plugin configurations directory
+ /// </summary>
+ /// <value>The plugin configurations path.</value>
+ string PluginConfigurationsPath { get; }
+
+ /// <summary>
+ /// Gets the path to where temporary update files will be stored
+ /// </summary>
+ /// <value>The plugin configurations path.</value>
+ string TempUpdatePath { get; }
+
+ /// <summary>
+ /// Gets the path to the log directory
+ /// </summary>
+ /// <value>The log directory path.</value>
+ string LogDirectoryPath { get; }
+
+ /// <summary>
+ /// Gets the path to the application configuration root directory
+ /// </summary>
+ /// <value>The configuration directory path.</value>
+ string ConfigurationDirectoryPath { get; }
+
+ /// <summary>
+ /// Gets the path to the system configuration file
+ /// </summary>
+ /// <value>The system configuration file path.</value>
+ string SystemConfigurationFilePath { get; }
+
+ /// <summary>
+ /// Gets the folder path to the cache directory
+ /// </summary>
+ /// <value>The cache directory.</value>
+ string CachePath { get; }
+
+ /// <summary>
+ /// Gets the folder path to the temp directory within the cache folder
+ /// </summary>
+ /// <value>The temp directory.</value>
+ string TempDirectory { get; }
+
+ string VirtualDataPath { get; }
+ }
+
+}
diff --git a/MediaBrowser.Common/Configuration/IConfigurationFactory.cs b/MediaBrowser.Common/Configuration/IConfigurationFactory.cs
new file mode 100644
index 000000000..6ed638536
--- /dev/null
+++ b/MediaBrowser.Common/Configuration/IConfigurationFactory.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Common.Configuration
+{
+ public interface IConfigurationFactory
+ {
+ IEnumerable<ConfigurationStore> GetConfigurations();
+ }
+
+ public class ConfigurationStore
+ {
+ public string Key { get; set; }
+
+ public Type ConfigurationType { get; set; }
+ }
+
+ public interface IValidatingConfiguration
+ {
+ void Validate(object oldConfig, object newConfig);
+ }
+}
diff --git a/MediaBrowser.Common/Configuration/IConfigurationManager.cs b/MediaBrowser.Common/Configuration/IConfigurationManager.cs
new file mode 100644
index 000000000..d826a3ee7
--- /dev/null
+++ b/MediaBrowser.Common/Configuration/IConfigurationManager.cs
@@ -0,0 +1,82 @@
+using MediaBrowser.Model.Configuration;
+using System;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Common.Configuration
+{
+ public interface IConfigurationManager
+ {
+ /// <summary>
+ /// Occurs when [configuration updating].
+ /// </summary>
+ event EventHandler<ConfigurationUpdateEventArgs> NamedConfigurationUpdating;
+
+ /// <summary>
+ /// Occurs when [configuration updated].
+ /// </summary>
+ event EventHandler<EventArgs> ConfigurationUpdated;
+
+ /// <summary>
+ /// Occurs when [named configuration updated].
+ /// </summary>
+ event EventHandler<ConfigurationUpdateEventArgs> NamedConfigurationUpdated;
+
+ /// <summary>
+ /// Gets or sets the application paths.
+ /// </summary>
+ /// <value>The application paths.</value>
+ IApplicationPaths CommonApplicationPaths { get; }
+
+ /// <summary>
+ /// Gets the configuration.
+ /// </summary>
+ /// <value>The configuration.</value>
+ BaseApplicationConfiguration CommonConfiguration { get; }
+
+ /// <summary>
+ /// Saves the configuration.
+ /// </summary>
+ void SaveConfiguration();
+
+ /// <summary>
+ /// Replaces the configuration.
+ /// </summary>
+ /// <param name="newConfiguration">The new configuration.</param>
+ void ReplaceConfiguration(BaseApplicationConfiguration newConfiguration);
+
+ /// <summary>
+ /// Gets the configuration.
+ /// </summary>
+ /// <param name="key">The key.</param>
+ /// <returns>System.Object.</returns>
+ object GetConfiguration(string key);
+
+ /// <summary>
+ /// Gets the type of the configuration.
+ /// </summary>
+ /// <param name="key">The key.</param>
+ /// <returns>Type.</returns>
+ Type GetConfigurationType(string key);
+
+ /// <summary>
+ /// Saves the configuration.
+ /// </summary>
+ /// <param name="key">The key.</param>
+ /// <param name="configuration">The configuration.</param>
+ void SaveConfiguration(string key, object configuration);
+
+ /// <summary>
+ /// Adds the parts.
+ /// </summary>
+ /// <param name="factories">The factories.</param>
+ void AddParts(IEnumerable<IConfigurationFactory> factories);
+ }
+
+ public static class ConfigurationManagerExtensions
+ {
+ public static T GetConfiguration<T>(this IConfigurationManager manager, string key)
+ {
+ return (T)manager.GetConfiguration(key);
+ }
+ }
+}
diff --git a/MediaBrowser.Common/Events/EventHelper.cs b/MediaBrowser.Common/Events/EventHelper.cs
new file mode 100644
index 000000000..2bb52f0ae
--- /dev/null
+++ b/MediaBrowser.Common/Events/EventHelper.cs
@@ -0,0 +1,108 @@
+using MediaBrowser.Model.Logging;
+using System;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Common.Events
+{
+ /// <summary>
+ /// Class EventHelper
+ /// </summary>
+ public static class EventHelper
+ {
+ /// <summary>
+ /// Fires the event.
+ /// </summary>
+ /// <param name="handler">The handler.</param>
+ /// <param name="sender">The sender.</param>
+ /// <param name="args">The <see cref="EventArgs" /> instance containing the event data.</param>
+ /// <param name="logger">The logger.</param>
+ public static void QueueEventIfNotNull(EventHandler handler, object sender, EventArgs args, ILogger logger)
+ {
+ if (handler != null)
+ {
+ Task.Run(() =>
+ {
+ try
+ {
+ handler(sender, args);
+ }
+ catch (Exception ex)
+ {
+ logger.ErrorException("Error in event handler", ex);
+ }
+ });
+ }
+ }
+
+ /// <summary>
+ /// Queues the event.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="handler">The handler.</param>
+ /// <param name="sender">The sender.</param>
+ /// <param name="args">The args.</param>
+ /// <param name="logger">The logger.</param>
+ public static void QueueEventIfNotNull<T>(EventHandler<T> handler, object sender, T args, ILogger logger)
+ {
+ if (handler != null)
+ {
+ Task.Run(() =>
+ {
+ try
+ {
+ handler(sender, args);
+ }
+ catch (Exception ex)
+ {
+ logger.ErrorException("Error in event handler", ex);
+ }
+ });
+ }
+ }
+
+ /// <summary>
+ /// Fires the event.
+ /// </summary>
+ /// <param name="handler">The handler.</param>
+ /// <param name="sender">The sender.</param>
+ /// <param name="args">The <see cref="EventArgs" /> instance containing the event data.</param>
+ /// <param name="logger">The logger.</param>
+ public static void FireEventIfNotNull(EventHandler handler, object sender, EventArgs args, ILogger logger)
+ {
+ if (handler != null)
+ {
+ try
+ {
+ handler(sender, args);
+ }
+ catch (Exception ex)
+ {
+ logger.ErrorException("Error in event handler", ex);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Fires the event.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="handler">The handler.</param>
+ /// <param name="sender">The sender.</param>
+ /// <param name="args">The args.</param>
+ /// <param name="logger">The logger.</param>
+ public static void FireEventIfNotNull<T>(EventHandler<T> handler, object sender, T args, ILogger logger)
+ {
+ if (handler != null)
+ {
+ try
+ {
+ handler(sender, args);
+ }
+ catch (Exception ex)
+ {
+ logger.ErrorException("Error in event handler", ex);
+ }
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Common/Extensions/BaseExtensions.cs b/MediaBrowser.Common/Extensions/BaseExtensions.cs
new file mode 100644
index 000000000..d7f4424fa
--- /dev/null
+++ b/MediaBrowser.Common/Extensions/BaseExtensions.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Globalization;
+using System.Text.RegularExpressions;
+using MediaBrowser.Model.Cryptography;
+
+namespace MediaBrowser.Common.Extensions
+{
+ /// <summary>
+ /// Class BaseExtensions
+ /// </summary>
+ public static class BaseExtensions
+ {
+ public static ICryptoProvider CryptographyProvider { get; set; }
+
+ /// <summary>
+ /// Strips the HTML.
+ /// </summary>
+ /// <param name="htmlString">The HTML string.</param>
+ /// <returns>System.String.</returns>
+ public static string StripHtml(this string htmlString)
+ {
+ // http://stackoverflow.com/questions/1349023/how-can-i-strip-html-from-text-in-net
+ const string pattern = @"<(.|\n)*?>";
+
+ return Regex.Replace(htmlString, pattern, string.Empty).Trim();
+ }
+
+ /// <summary>
+ /// Gets the M d5.
+ /// </summary>
+ /// <param name="str">The STR.</param>
+ /// <returns>Guid.</returns>
+ public static Guid GetMD5(this string str)
+ {
+ return CryptographyProvider.GetMD5(str);
+ }
+
+ /// <summary>
+ /// Gets the MB id.
+ /// </summary>
+ /// <param name="str">The STR.</param>
+ /// <param name="type">The type.</param>
+ /// <returns>Guid.</returns>
+ /// <exception cref="System.ArgumentNullException">type</exception>
+ [Obsolete("Use LibraryManager.GetNewItemId")]
+ public static Guid GetMBId(this string str, Type type)
+ {
+ if (type == null)
+ {
+ throw new ArgumentNullException("type");
+ }
+
+ var key = type.FullName + str.ToLower();
+
+ return key.GetMD5();
+ }
+ }
+}
diff --git a/MediaBrowser.Common/Extensions/ResourceNotFoundException.cs b/MediaBrowser.Common/Extensions/ResourceNotFoundException.cs
new file mode 100644
index 000000000..89e20b1b4
--- /dev/null
+++ b/MediaBrowser.Common/Extensions/ResourceNotFoundException.cs
@@ -0,0 +1,63 @@
+using System;
+
+namespace MediaBrowser.Common.Extensions
+{
+ /// <summary>
+ /// Class ResourceNotFoundException
+ /// </summary>
+ public class ResourceNotFoundException : Exception
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ResourceNotFoundException" /> class.
+ /// </summary>
+ public ResourceNotFoundException()
+ {
+
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ResourceNotFoundException" /> class.
+ /// </summary>
+ /// <param name="message">The message.</param>
+ public ResourceNotFoundException(string message)
+ : base(message)
+ {
+
+ }
+ }
+
+ public class RemoteServiceUnavailableException : Exception
+ {
+ public RemoteServiceUnavailableException()
+ {
+
+ }
+
+ public RemoteServiceUnavailableException(string message)
+ : base(message)
+ {
+
+ }
+ }
+
+ public class RateLimitExceededException : Exception
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RateLimitExceededException" /> class.
+ /// </summary>
+ public RateLimitExceededException()
+ {
+
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RateLimitExceededException" /> class.
+ /// </summary>
+ /// <param name="message">The message.</param>
+ public RateLimitExceededException(string message)
+ : base(message)
+ {
+
+ }
+ }
+}
diff --git a/MediaBrowser.Common/IApplicationHost.cs b/MediaBrowser.Common/IApplicationHost.cs
new file mode 100644
index 000000000..32b942b60
--- /dev/null
+++ b/MediaBrowser.Common/IApplicationHost.cs
@@ -0,0 +1,147 @@
+using MediaBrowser.Common.Plugins;
+using MediaBrowser.Model.Events;
+using MediaBrowser.Model.Updates;
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Common
+{
+ /// <summary>
+ /// An interface to be implemented by the applications hosting a kernel
+ /// </summary>
+ public interface IApplicationHost
+ {
+ /// <summary>
+ /// Gets the display name of the operating system.
+ /// </summary>
+ /// <value>The display name of the operating system.</value>
+ string OperatingSystemDisplayName { get; }
+
+ /// <summary>
+ /// Gets the name.
+ /// </summary>
+ /// <value>The name.</value>
+ string Name { get; }
+
+ /// <summary>
+ /// Gets the device identifier.
+ /// </summary>
+ /// <value>The device identifier.</value>
+ string SystemId { get; }
+
+ /// <summary>
+ /// Occurs when [application updated].
+ /// </summary>
+ event EventHandler<GenericEventArgs<PackageVersionInfo>> ApplicationUpdated;
+
+ /// <summary>
+ /// Gets or sets a value indicating whether this instance has pending kernel reload.
+ /// </summary>
+ /// <value><c>true</c> if this instance has pending kernel reload; otherwise, <c>false</c>.</value>
+ bool HasPendingRestart { get; }
+
+ bool IsShuttingDown { get; }
+
+ /// <summary>
+ /// Gets a value indicating whether this instance can self restart.
+ /// </summary>
+ /// <value><c>true</c> if this instance can self restart; otherwise, <c>false</c>.</value>
+ bool CanSelfRestart { get; }
+
+ /// <summary>
+ /// Occurs when [has pending restart changed].
+ /// </summary>
+ event EventHandler HasPendingRestartChanged;
+
+ /// <summary>
+ /// Notifies the pending restart.
+ /// </summary>
+ void NotifyPendingRestart();
+
+ /// <summary>
+ /// Restarts this instance.
+ /// </summary>
+ void Restart();
+
+ /// <summary>
+ /// Gets the application version.
+ /// </summary>
+ /// <value>The application version.</value>
+ Version ApplicationVersion { get; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether this instance can self update.
+ /// </summary>
+ /// <value><c>true</c> if this instance can self update; otherwise, <c>false</c>.</value>
+ bool CanSelfUpdate { get; }
+
+ /// <summary>
+ /// Gets the exports.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="manageLiftime">if set to <c>true</c> [manage liftime].</param>
+ /// <returns>IEnumerable{``0}.</returns>
+ IEnumerable<T> GetExports<T>(bool manageLiftime = true);
+
+ /// <summary>
+ /// Checks for update.
+ /// </summary>
+ /// <returns>Task{CheckForUpdateResult}.</returns>
+ Task<CheckForUpdateResult> CheckForApplicationUpdate(CancellationToken cancellationToken, IProgress<double> progress);
+
+ /// <summary>
+ /// Updates the application.
+ /// </summary>
+ /// <returns>Task.</returns>
+ Task UpdateApplication(PackageVersionInfo package, CancellationToken cancellationToken, IProgress<double> progress);
+
+ /// <summary>
+ /// Resolves this instance.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <returns>``0.</returns>
+ T Resolve<T>();
+
+ /// <summary>
+ /// Resolves this instance.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <returns>``0.</returns>
+ T TryResolve<T>();
+
+ /// <summary>
+ /// Shuts down.
+ /// </summary>
+ Task Shutdown();
+
+ /// <summary>
+ /// Gets the plugins.
+ /// </summary>
+ /// <value>The plugins.</value>
+ IPlugin[] Plugins { get; }
+
+ /// <summary>
+ /// Removes the plugin.
+ /// </summary>
+ /// <param name="plugin">The plugin.</param>
+ void RemovePlugin(IPlugin plugin);
+
+ /// <summary>
+ /// Inits this instance.
+ /// </summary>
+ void Init();
+
+ /// <summary>
+ /// Creates the instance.
+ /// </summary>
+ /// <param name="type">The type.</param>
+ /// <returns>System.Object.</returns>
+ object CreateInstance(Type type);
+
+ PackageVersionClass SystemUpdateLevel { get; }
+
+ string GetValue(string name);
+ }
+}
diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj
new file mode 100644
index 000000000..43f3b0be7
--- /dev/null
+++ b/MediaBrowser.Common/MediaBrowser.Common.csproj
@@ -0,0 +1,16 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <ItemGroup>
+ <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <Compile Include="..\SharedVersion.cs"/>
+ </ItemGroup>
+
+ <PropertyGroup>
+ <TargetFramework>netstandard2.0</TargetFramework>
+ <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ </PropertyGroup>
+
+</Project>
diff --git a/MediaBrowser.Common/Net/HttpRequestOptions.cs b/MediaBrowser.Common/Net/HttpRequestOptions.cs
new file mode 100644
index 000000000..c61e88c87
--- /dev/null
+++ b/MediaBrowser.Common/Net/HttpRequestOptions.cs
@@ -0,0 +1,157 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Threading;
+using System.Text;
+
+namespace MediaBrowser.Common.Net
+{
+ /// <summary>
+ /// Class HttpRequestOptions
+ /// </summary>
+ public class HttpRequestOptions
+ {
+ /// <summary>
+ /// Gets or sets the URL.
+ /// </summary>
+ /// <value>The URL.</value>
+ public string Url { get; set; }
+
+ public CompressionMethod? DecompressionMethod { get; set; }
+
+ /// <summary>
+ /// Gets or sets the accept header.
+ /// </summary>
+ /// <value>The accept header.</value>
+ public string AcceptHeader
+ {
+ get { return GetHeaderValue("Accept"); }
+ set
+ {
+ RequestHeaders["Accept"] = value;
+ }
+ }
+ /// <summary>
+ /// Gets or sets the cancellation token.
+ /// </summary>
+ /// <value>The cancellation token.</value>
+ public CancellationToken CancellationToken { get; set; }
+
+ /// <summary>
+ /// Gets or sets the resource pool.
+ /// </summary>
+ /// <value>The resource pool.</value>
+ public SemaphoreSlim ResourcePool { get; set; }
+
+ /// <summary>
+ /// Gets or sets the user agent.
+ /// </summary>
+ /// <value>The user agent.</value>
+ public string UserAgent
+ {
+ get { return GetHeaderValue("User-Agent"); }
+ set
+ {
+ RequestHeaders["User-Agent"] = value;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the referrer.
+ /// </summary>
+ /// <value>The referrer.</value>
+ public string Referer { get; set; }
+
+ /// <summary>
+ /// Gets or sets the host.
+ /// </summary>
+ /// <value>The host.</value>
+ public string Host { get; set; }
+
+ /// <summary>
+ /// Gets or sets the progress.
+ /// </summary>
+ /// <value>The progress.</value>
+ public IProgress<double> Progress { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether [enable HTTP compression].
+ /// </summary>
+ /// <value><c>true</c> if [enable HTTP compression]; otherwise, <c>false</c>.</value>
+ public bool EnableHttpCompression { get; set; }
+
+ public Dictionary<string, string> RequestHeaders { get; private set; }
+
+ public string RequestContentType { get; set; }
+
+ public string RequestContent { get; set; }
+ public byte[] RequestContentBytes { get; set; }
+
+ public bool BufferContent { get; set; }
+
+ public bool LogRequest { get; set; }
+ public bool LogRequestAsDebug { get; set; }
+ public bool LogErrors { get; set; }
+ public bool LogResponse { get; set; }
+ public bool LogResponseHeaders { get; set; }
+
+ public bool LogErrorResponseBody { get; set; }
+ public bool EnableKeepAlive { get; set; }
+
+ public CacheMode CacheMode { get; set; }
+ public TimeSpan CacheLength { get; set; }
+
+ public int TimeoutMs { get; set; }
+ public bool EnableDefaultUserAgent { get; set; }
+
+ public bool AppendCharsetToMimeType { get; set; }
+ public string DownloadFilePath { get; set; }
+
+ private string GetHeaderValue(string name)
+ {
+ string value;
+
+ RequestHeaders.TryGetValue(name, out value);
+
+ return value;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="HttpRequestOptions"/> class.
+ /// </summary>
+ public HttpRequestOptions()
+ {
+ EnableHttpCompression = true;
+
+ RequestHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+
+ LogRequest = true;
+ LogErrors = true;
+ CacheMode = CacheMode.None;
+
+ TimeoutMs = 20000;
+ }
+
+ public void SetPostData(IDictionary<string,string> values)
+ {
+ var strings = values.Keys.Select(key => string.Format("{0}={1}", key, values[key]));
+ var postContent = string.Join("&", strings.ToArray());
+
+ RequestContent = postContent;
+ RequestContentType = "application/x-www-form-urlencoded";
+ }
+ }
+
+ public enum CacheMode
+ {
+ None = 0,
+ Unconditional = 1
+ }
+
+ public enum CompressionMethod
+ {
+ Deflate,
+ Gzip
+ }
+}
diff --git a/MediaBrowser.Common/Net/HttpResponseInfo.cs b/MediaBrowser.Common/Net/HttpResponseInfo.cs
new file mode 100644
index 000000000..ed941a447
--- /dev/null
+++ b/MediaBrowser.Common/Net/HttpResponseInfo.cs
@@ -0,0 +1,75 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+
+namespace MediaBrowser.Common.Net
+{
+ /// <summary>
+ /// Class HttpResponseInfo
+ /// </summary>
+ public class HttpResponseInfo : IDisposable
+ {
+ /// <summary>
+ /// Gets or sets the type of the content.
+ /// </summary>
+ /// <value>The type of the content.</value>
+ public string ContentType { get; set; }
+
+ /// <summary>
+ /// Gets or sets the response URL.
+ /// </summary>
+ /// <value>The response URL.</value>
+ public string ResponseUrl { get; set; }
+
+ /// <summary>
+ /// Gets or sets the content.
+ /// </summary>
+ /// <value>The content.</value>
+ public Stream Content { get; set; }
+
+ /// <summary>
+ /// Gets or sets the status code.
+ /// </summary>
+ /// <value>The status code.</value>
+ public HttpStatusCode StatusCode { get; set; }
+
+ /// <summary>
+ /// Gets or sets the temp file path.
+ /// </summary>
+ /// <value>The temp file path.</value>
+ public string TempFilePath { get; set; }
+
+ /// <summary>
+ /// Gets or sets the length of the content.
+ /// </summary>
+ /// <value>The length of the content.</value>
+ public long? ContentLength { get; set; }
+
+ /// <summary>
+ /// Gets or sets the headers.
+ /// </summary>
+ /// <value>The headers.</value>
+ public Dictionary<string,string> Headers { get; set; }
+
+ private readonly IDisposable _disposable;
+
+ public HttpResponseInfo(IDisposable disposable)
+ {
+ _disposable = disposable;
+ Headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+ }
+ public HttpResponseInfo()
+ {
+ Headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+ }
+
+ public void Dispose()
+ {
+ if (_disposable != null)
+ {
+ _disposable.Dispose();
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Common/Net/IHttpClient.cs b/MediaBrowser.Common/Net/IHttpClient.cs
new file mode 100644
index 000000000..cf5511965
--- /dev/null
+++ b/MediaBrowser.Common/Net/IHttpClient.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Common.Net
+{
+ /// <summary>
+ /// Interface IHttpClient
+ /// </summary>
+ public interface IHttpClient
+ {
+ /// <summary>
+ /// Gets the response.
+ /// </summary>
+ /// <param name="options">The options.</param>
+ /// <returns>Task{HttpResponseInfo}.</returns>
+ Task<HttpResponseInfo> GetResponse(HttpRequestOptions options);
+
+ /// <summary>
+ /// Gets the specified options.
+ /// </summary>
+ /// <param name="options">The options.</param>
+ /// <returns>Task{Stream}.</returns>
+ Task<Stream> Get(HttpRequestOptions options);
+
+ /// <summary>
+ /// Sends the asynchronous.
+ /// </summary>
+ /// <param name="options">The options.</param>
+ /// <param name="httpMethod">The HTTP method.</param>
+ /// <returns>Task{HttpResponseInfo}.</returns>
+ Task<HttpResponseInfo> SendAsync(HttpRequestOptions options, string httpMethod);
+
+ /// <summary>
+ /// Posts the specified options.
+ /// </summary>
+ /// <param name="options">The options.</param>
+ /// <returns>Task{HttpResponseInfo}.</returns>
+ Task<HttpResponseInfo> Post(HttpRequestOptions options);
+
+ /// <summary>
+ /// Downloads the contents of a given url into a temporary location
+ /// </summary>
+ /// <param name="options">The options.</param>
+ /// <returns>Task{System.String}.</returns>
+ /// <exception cref="System.ArgumentNullException">progress</exception>
+ /// <exception cref="MediaBrowser.Model.Net.HttpException"></exception>
+ Task<string> GetTempFile(HttpRequestOptions options);
+
+ /// <summary>
+ /// Gets the temporary file response.
+ /// </summary>
+ /// <param name="options">The options.</param>
+ /// <returns>Task{HttpResponseInfo}.</returns>
+ Task<HttpResponseInfo> GetTempFileResponse(HttpRequestOptions options);
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.Common/Net/INetworkManager.cs b/MediaBrowser.Common/Net/INetworkManager.cs
new file mode 100644
index 000000000..b2ff797bc
--- /dev/null
+++ b/MediaBrowser.Common/Net/INetworkManager.cs
@@ -0,0 +1,66 @@
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Net;
+using System.Collections.Generic;
+using System;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Common.Net
+{
+ public interface INetworkManager
+ {
+ event EventHandler NetworkChanged;
+
+ /// <summary>
+ /// Gets a random port number that is currently available
+ /// </summary>
+ /// <returns>System.Int32.</returns>
+ int GetRandomUnusedTcpPort();
+
+ int GetRandomUnusedUdpPort();
+
+ Func<string[]> LocalSubnetsFn { get; set; }
+
+ /// <summary>
+ /// Returns MAC Address from first Network Card in Computer
+ /// </summary>
+ /// <returns>[string] MAC Address</returns>
+ List<string> GetMacAddresses();
+
+ /// <summary>
+ /// Determines whether [is in private address space] [the specified endpoint].
+ /// </summary>
+ /// <param name="endpoint">The endpoint.</param>
+ /// <returns><c>true</c> if [is in private address space] [the specified endpoint]; otherwise, <c>false</c>.</returns>
+ bool IsInPrivateAddressSpace(string endpoint);
+
+ /// <summary>
+ /// Gets the network shares.
+ /// </summary>
+ /// <param name="path">The path.</param>
+ /// <returns>IEnumerable{NetworkShare}.</returns>
+ IEnumerable<NetworkShare> GetNetworkShares(string path);
+
+ /// <summary>
+ /// Gets available devices within the domain
+ /// </summary>
+ /// <returns>PC's in the Domain</returns>
+ IEnumerable<FileSystemEntryInfo> GetNetworkDevices();
+
+ /// <summary>
+ /// Determines whether [is in local network] [the specified endpoint].
+ /// </summary>
+ /// <param name="endpoint">The endpoint.</param>
+ /// <returns><c>true</c> if [is in local network] [the specified endpoint]; otherwise, <c>false</c>.</returns>
+ bool IsInLocalNetwork(string endpoint);
+
+ IpAddressInfo[] GetLocalIpAddresses();
+
+ IpAddressInfo ParseIpAddress(string ipAddress);
+
+ bool TryParseIpAddress(string ipAddress, out IpAddressInfo ipAddressInfo);
+
+ Task<IpAddressInfo[]> GetHostAddressesAsync(string host);
+
+ bool IsAddressInSubnets(string addressString, string[] subnets);
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.Common/Plugins/BasePlugin.cs b/MediaBrowser.Common/Plugins/BasePlugin.cs
new file mode 100644
index 000000000..82eb6ba4b
--- /dev/null
+++ b/MediaBrowser.Common/Plugins/BasePlugin.cs
@@ -0,0 +1,276 @@
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Model.Plugins;
+using MediaBrowser.Model.Serialization;
+using System;
+using System.IO;
+
+namespace MediaBrowser.Common.Plugins
+{
+ public abstract class BasePlugin : IPlugin, IPluginAssembly
+ {
+ /// <summary>
+ /// Gets the name of the plugin
+ /// </summary>
+ /// <value>The name.</value>
+ public abstract string Name { get; }
+
+ /// <summary>
+ /// Gets the description.
+ /// </summary>
+ /// <value>The description.</value>
+ public virtual string Description
+ {
+ get { return string.Empty; }
+ }
+
+ /// <summary>
+ /// Gets the unique id.
+ /// </summary>
+ /// <value>The unique id.</value>
+ public virtual Guid Id { get; private set; }
+
+ /// <summary>
+ /// Gets the plugin version
+ /// </summary>
+ /// <value>The version.</value>
+ public Version Version { get; private set; }
+
+ /// <summary>
+ /// Gets the path to the assembly file
+ /// </summary>
+ /// <value>The assembly file path.</value>
+ public string AssemblyFilePath { get; private set; }
+
+ /// <summary>
+ /// Gets the plugin info.
+ /// </summary>
+ /// <returns>PluginInfo.</returns>
+ public virtual PluginInfo GetPluginInfo()
+ {
+ var info = new PluginInfo
+ {
+ Name = Name,
+ Version = Version.ToString(),
+ Description = Description,
+ Id = Id.ToString()
+ };
+
+ return info;
+ }
+
+ /// <summary>
+ /// Called when just before the plugin is uninstalled from the server.
+ /// </summary>
+ public virtual void OnUninstalling()
+ {
+
+ }
+
+ public void SetAttributes(string assemblyFilePath, string dataFolderPath, Version assemblyVersion)
+ {
+ AssemblyFilePath = assemblyFilePath;
+ DataFolderPath = dataFolderPath;
+ Version = assemblyVersion;
+ }
+
+ public void SetId(Guid assemblyId)
+ {
+ Id = assemblyId;
+ }
+
+ /// <summary>
+ /// Gets the full path to the data folder, where the plugin can store any miscellaneous files needed
+ /// </summary>
+ /// <value>The data folder path.</value>
+ public string DataFolderPath { get; private set; }
+ }
+
+ /// <summary>
+ /// Provides a common base class for all plugins
+ /// </summary>
+ /// <typeparam name="TConfigurationType">The type of the T configuration type.</typeparam>
+ public abstract class BasePlugin<TConfigurationType> : BasePlugin, IHasPluginConfiguration
+ where TConfigurationType : BasePluginConfiguration
+ {
+ /// <summary>
+ /// Gets the application paths.
+ /// </summary>
+ /// <value>The application paths.</value>
+ protected IApplicationPaths ApplicationPaths { get; private set; }
+
+ /// <summary>
+ /// Gets the XML serializer.
+ /// </summary>
+ /// <value>The XML serializer.</value>
+ protected IXmlSerializer XmlSerializer { get; private set; }
+
+ /// <summary>
+ /// Gets the type of configuration this plugin uses
+ /// </summary>
+ /// <value>The type of the configuration.</value>
+ public Type ConfigurationType
+ {
+ get { return typeof(TConfigurationType); }
+ }
+
+ private Action<string> _directoryCreateFn;
+ public void SetStartupInfo(Action<string> directoryCreateFn)
+ {
+ // hack alert, until the .net core transition is complete
+ _directoryCreateFn = directoryCreateFn;
+ }
+
+ /// <summary>
+ /// Gets the name the assembly file
+ /// </summary>
+ /// <value>The name of the assembly file.</value>
+ protected string AssemblyFileName
+ {
+ get
+ {
+ return Path.GetFileName(AssemblyFilePath);
+ }
+ }
+
+ /// <summary>
+ /// The _configuration sync lock
+ /// </summary>
+ private readonly object _configurationSyncLock = new object();
+ /// <summary>
+ /// The _configuration
+ /// </summary>
+ private TConfigurationType _configuration;
+ /// <summary>
+ /// Gets the plugin's configuration
+ /// </summary>
+ /// <value>The configuration.</value>
+ public TConfigurationType Configuration
+ {
+ get
+ {
+ // Lazy load
+ if (_configuration == null)
+ {
+ lock (_configurationSyncLock)
+ {
+ if (_configuration == null)
+ {
+ _configuration = LoadConfiguration();
+ }
+ }
+ }
+ return _configuration;
+ }
+ protected set
+ {
+ _configuration = value;
+ }
+ }
+
+ private TConfigurationType LoadConfiguration()
+ {
+ var path = ConfigurationFilePath;
+
+ try
+ {
+ return (TConfigurationType)XmlSerializer.DeserializeFromFile(typeof(TConfigurationType), path);
+ }
+ catch
+ {
+ return (TConfigurationType)Activator.CreateInstance(typeof(TConfigurationType));
+ }
+ }
+
+ /// <summary>
+ /// Gets the name of the configuration file. Subclasses should override
+ /// </summary>
+ /// <value>The name of the configuration file.</value>
+ public virtual string ConfigurationFileName
+ {
+ get { return Path.ChangeExtension(AssemblyFileName, ".xml"); }
+ }
+
+ /// <summary>
+ /// Gets the full path to the configuration file
+ /// </summary>
+ /// <value>The configuration file path.</value>
+ public string ConfigurationFilePath
+ {
+ get
+ {
+ return Path.Combine(ApplicationPaths.PluginConfigurationsPath, ConfigurationFileName);
+ }
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="BasePlugin{TConfigurationType}" /> class.
+ /// </summary>
+ /// <param name="applicationPaths">The application paths.</param>
+ /// <param name="xmlSerializer">The XML serializer.</param>
+ protected BasePlugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer)
+ {
+ ApplicationPaths = applicationPaths;
+ XmlSerializer = xmlSerializer;
+ }
+
+ /// <summary>
+ /// The _save lock
+ /// </summary>
+ private readonly object _configurationSaveLock = new object();
+
+ /// <summary>
+ /// Saves the current configuration to the file system
+ /// </summary>
+ public virtual void SaveConfiguration()
+ {
+ lock (_configurationSaveLock)
+ {
+ _directoryCreateFn(Path.GetDirectoryName(ConfigurationFilePath));
+
+ XmlSerializer.SerializeToFile(Configuration, ConfigurationFilePath);
+ }
+ }
+
+ /// <summary>
+ /// Completely overwrites the current configuration with a new copy
+ /// Returns true or false indicating success or failure
+ /// </summary>
+ /// <param name="configuration">The configuration.</param>
+ /// <exception cref="System.ArgumentNullException">configuration</exception>
+ public virtual void UpdateConfiguration(BasePluginConfiguration configuration)
+ {
+ if (configuration == null)
+ {
+ throw new ArgumentNullException("configuration");
+ }
+
+ Configuration = (TConfigurationType)configuration;
+
+ SaveConfiguration();
+ }
+
+ /// <summary>
+ /// Gets the plugin's configuration
+ /// </summary>
+ /// <value>The configuration.</value>
+ BasePluginConfiguration IHasPluginConfiguration.Configuration
+ {
+ get { return Configuration; }
+ }
+
+ public override PluginInfo GetPluginInfo()
+ {
+ var info = base.GetPluginInfo();
+
+ info.ConfigurationFileName = ConfigurationFileName;
+
+ return info;
+ }
+ }
+
+ public interface IPluginAssembly
+ {
+ void SetAttributes(string assemblyFilePath, string dataFolderPath, Version assemblyVersion);
+ void SetId(Guid assemblyId);
+ }
+}
diff --git a/MediaBrowser.Common/Plugins/IPlugin.cs b/MediaBrowser.Common/Plugins/IPlugin.cs
new file mode 100644
index 000000000..bffd21143
--- /dev/null
+++ b/MediaBrowser.Common/Plugins/IPlugin.cs
@@ -0,0 +1,83 @@
+using MediaBrowser.Model.Plugins;
+using System;
+
+namespace MediaBrowser.Common.Plugins
+{
+ /// <summary>
+ /// Interface IPlugin
+ /// </summary>
+ public interface IPlugin
+ {
+ /// <summary>
+ /// Gets the name of the plugin
+ /// </summary>
+ /// <value>The name.</value>
+ string Name { get; }
+
+ /// <summary>
+ /// Gets the description.
+ /// </summary>
+ /// <value>The description.</value>
+ string Description { get; }
+
+ /// <summary>
+ /// Gets the unique id.
+ /// </summary>
+ /// <value>The unique id.</value>
+ Guid Id { get; }
+
+ /// <summary>
+ /// Gets the plugin version
+ /// </summary>
+ /// <value>The version.</value>
+ Version Version { get; }
+
+ /// <summary>
+ /// Gets the path to the assembly file
+ /// </summary>
+ /// <value>The assembly file path.</value>
+ string AssemblyFilePath { get; }
+
+ /// <summary>
+ /// Gets the full path to the data folder, where the plugin can store any miscellaneous files needed
+ /// </summary>
+ /// <value>The data folder path.</value>
+ string DataFolderPath { get; }
+
+ /// <summary>
+ /// Gets the plugin info.
+ /// </summary>
+ /// <returns>PluginInfo.</returns>
+ PluginInfo GetPluginInfo();
+
+ /// <summary>
+ /// Called when just before the plugin is uninstalled from the server.
+ /// </summary>
+ void OnUninstalling();
+ }
+
+ public interface IHasPluginConfiguration
+ {
+ /// <summary>
+ /// Gets the type of configuration this plugin uses
+ /// </summary>
+ /// <value>The type of the configuration.</value>
+ Type ConfigurationType { get; }
+
+ /// <summary>
+ /// Completely overwrites the current configuration with a new copy
+ /// Returns true or false indicating success or failure
+ /// </summary>
+ /// <param name="configuration">The configuration.</param>
+ /// <exception cref="System.ArgumentNullException">configuration</exception>
+ void UpdateConfiguration(BasePluginConfiguration configuration);
+
+ /// <summary>
+ /// Gets the plugin's configuration
+ /// </summary>
+ /// <value>The configuration.</value>
+ BasePluginConfiguration Configuration { get; }
+
+ void SetStartupInfo(Action<string> directoryCreateFn);
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.Common/Progress/ActionableProgress.cs b/MediaBrowser.Common/Progress/ActionableProgress.cs
new file mode 100644
index 000000000..67347bc15
--- /dev/null
+++ b/MediaBrowser.Common/Progress/ActionableProgress.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Common.Progress
+{
+ /// <summary>
+ /// Class ActionableProgress
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ public class ActionableProgress<T> : IProgress<T>
+ {
+ /// <summary>
+ /// The _actions
+ /// </summary>
+ private Action<T> _action;
+ public event EventHandler<T> ProgressChanged;
+
+ /// <summary>
+ /// Registers the action.
+ /// </summary>
+ /// <param name="action">The action.</param>
+ public void RegisterAction(Action<T> action)
+ {
+ _action = action;
+ }
+
+ public void Report(T value)
+ {
+ if (ProgressChanged != null)
+ {
+ ProgressChanged(this, value);
+ }
+
+ var action = _action;
+ if (action != null)
+ {
+ action(value);
+ }
+ }
+ }
+
+ public class SimpleProgress<T> : IProgress<T>
+ {
+ public event EventHandler<T> ProgressChanged;
+
+ public void Report(T value)
+ {
+ if (ProgressChanged != null)
+ {
+ ProgressChanged(this, value);
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Common/Properties/AssemblyInfo.cs b/MediaBrowser.Common/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..09fd68f93
--- /dev/null
+++ b/MediaBrowser.Common/Properties/AssemblyInfo.cs
@@ -0,0 +1,27 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("MediaBrowser.Common")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("MediaBrowser.Common")]
+[assembly: AssemblyCopyright("Copyright © 2012")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+// \ No newline at end of file
diff --git a/MediaBrowser.Common/Security/IRequiresRegistration.cs b/MediaBrowser.Common/Security/IRequiresRegistration.cs
new file mode 100644
index 000000000..7b1667c2e
--- /dev/null
+++ b/MediaBrowser.Common/Security/IRequiresRegistration.cs
@@ -0,0 +1,15 @@
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Common.Security
+{
+ public interface IRequiresRegistration
+ {
+ /// <summary>
+ /// Load all registration information required for this entity.
+ /// Your class should re-load all MBRegistrationRecords when this is called even if they were
+ /// previously loaded.
+ /// </summary>
+ /// <returns></returns>
+ Task LoadRegistrationInfoAsync();
+ }
+}
diff --git a/MediaBrowser.Common/Security/ISecurityManager.cs b/MediaBrowser.Common/Security/ISecurityManager.cs
new file mode 100644
index 000000000..b63a9efd0
--- /dev/null
+++ b/MediaBrowser.Common/Security/ISecurityManager.cs
@@ -0,0 +1,32 @@
+using MediaBrowser.Model.Entities;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Common.Security
+{
+ public interface ISecurityManager
+ {
+ /// <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>
+ Task<bool> IsSupporter();
+
+ /// <summary>
+ /// Gets or sets the supporter key.
+ /// </summary>
+ /// <value>The supporter key.</value>
+ string SupporterKey { get; }
+
+ /// <summary>
+ /// Gets the registration status. Overload to support existing plug-ins.
+ /// </summary>
+ Task<MBRegistrationRecord> GetRegistrationStatus(string feature);
+
+ /// <summary>
+ /// Register and app store sale with our back-end
+ /// </summary>
+ /// <param name="parameters">Json parameters to pass to admin server</param>
+ Task RegisterAppStoreSale(string parameters);
+ Task UpdateSupporterKey(string newValue);
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.Common/Security/PaymentRequiredException.cs b/MediaBrowser.Common/Security/PaymentRequiredException.cs
new file mode 100644
index 000000000..27b3e6961
--- /dev/null
+++ b/MediaBrowser.Common/Security/PaymentRequiredException.cs
@@ -0,0 +1,8 @@
+using System;
+
+namespace MediaBrowser.Common.Security
+{
+ public class PaymentRequiredException : Exception
+ {
+ }
+}
diff --git a/MediaBrowser.Common/Updates/GithubUpdater.cs b/MediaBrowser.Common/Updates/GithubUpdater.cs
new file mode 100644
index 000000000..4275799a9
--- /dev/null
+++ b/MediaBrowser.Common/Updates/GithubUpdater.cs
@@ -0,0 +1,278 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Model.Serialization;
+using MediaBrowser.Model.Updates;
+
+namespace MediaBrowser.Common.Updates
+{
+ public class GithubUpdater
+ {
+ private readonly IHttpClient _httpClient;
+ private readonly IJsonSerializer _jsonSerializer;
+
+ public GithubUpdater(IHttpClient httpClient, IJsonSerializer jsonSerializer)
+ {
+ _httpClient = httpClient;
+ _jsonSerializer = jsonSerializer;
+ }
+
+ public async Task<CheckForUpdateResult> CheckForUpdateResult(string organzation, string repository, Version minVersion, PackageVersionClass updateLevel, string assetFilename, string packageName, string targetFilename, TimeSpan cacheLength, CancellationToken cancellationToken)
+ {
+ var url = string.Format("https://api.github.com/repos/{0}/{1}/releases", organzation, repository);
+
+ var options = new HttpRequestOptions
+ {
+ Url = url,
+ EnableKeepAlive = false,
+ CancellationToken = cancellationToken,
+ UserAgent = "Emby/3.0",
+ BufferContent = false
+ };
+
+ if (cacheLength.Ticks > 0)
+ {
+ options.CacheMode = CacheMode.Unconditional;
+ options.CacheLength = cacheLength;
+ }
+
+ using (var response = await _httpClient.SendAsync(options, "GET").ConfigureAwait(false))
+ {
+ using (var stream = response.Content)
+ {
+ var obj = _jsonSerializer.DeserializeFromStream<RootObject[]>(stream);
+
+ return CheckForUpdateResult(obj, minVersion, updateLevel, assetFilename, packageName, targetFilename);
+ }
+ }
+ }
+
+ private CheckForUpdateResult CheckForUpdateResult(RootObject[] obj, Version minVersion, PackageVersionClass updateLevel, string assetFilename, string packageName, string targetFilename)
+ {
+ if (updateLevel == PackageVersionClass.Release)
+ {
+ // Technically all we need to do is check that it's not pre-release
+ // But let's addititional checks for -beta and -dev to handle builds that might be temporarily tagged incorrectly.
+ obj = obj.Where(i => !i.prerelease && !i.name.EndsWith("-beta", StringComparison.OrdinalIgnoreCase) && !i.name.EndsWith("-dev", StringComparison.OrdinalIgnoreCase)).ToArray();
+ }
+ else if (updateLevel == PackageVersionClass.Beta)
+ {
+ obj = obj.Where(i => i.prerelease && i.name.EndsWith("-beta", StringComparison.OrdinalIgnoreCase)).ToArray();
+ }
+ else if (updateLevel == PackageVersionClass.Dev)
+ {
+ obj = obj.Where(i => !i.prerelease || i.name.EndsWith("-beta", StringComparison.OrdinalIgnoreCase) || i.name.EndsWith("-dev", StringComparison.OrdinalIgnoreCase)).ToArray();
+ }
+
+ var availableUpdate = obj
+ .Select(i => CheckForUpdateResult(i, minVersion, assetFilename, packageName, targetFilename))
+ .Where(i => i != null)
+ .OrderByDescending(i => Version.Parse(i.AvailableVersion))
+ .FirstOrDefault();
+
+ return availableUpdate ?? new CheckForUpdateResult
+ {
+ IsUpdateAvailable = false
+ };
+ }
+
+ private bool MatchesUpdateLevel(RootObject i, PackageVersionClass updateLevel)
+ {
+ if (updateLevel == PackageVersionClass.Beta)
+ {
+ return i.prerelease && i.name.EndsWith("-beta", StringComparison.OrdinalIgnoreCase);
+ }
+ if (updateLevel == PackageVersionClass.Dev)
+ {
+ return !i.prerelease || i.name.EndsWith("-beta", StringComparison.OrdinalIgnoreCase) ||
+ i.name.EndsWith("-dev", StringComparison.OrdinalIgnoreCase);
+ }
+
+ // Technically all we need to do is check that it's not pre-release
+ // But let's addititional checks for -beta and -dev to handle builds that might be temporarily tagged incorrectly.
+ return !i.prerelease && !i.name.EndsWith("-beta", StringComparison.OrdinalIgnoreCase) &&
+ !i.name.EndsWith("-dev", StringComparison.OrdinalIgnoreCase);
+ }
+
+ public async Task<List<RootObject>> GetLatestReleases(string organzation, string repository, string assetFilename, CancellationToken cancellationToken)
+ {
+ var list = new List<RootObject>();
+
+ var url = string.Format("https://api.github.com/repos/{0}/{1}/releases", organzation, repository);
+
+ var options = new HttpRequestOptions
+ {
+ Url = url,
+ EnableKeepAlive = false,
+ CancellationToken = cancellationToken,
+ UserAgent = "Emby/3.0",
+ BufferContent = false
+ };
+
+ using (var response = await _httpClient.SendAsync(options, "GET").ConfigureAwait(false))
+ {
+ using (var stream = response.Content)
+ {
+ var obj = _jsonSerializer.DeserializeFromStream<RootObject[]>(stream);
+
+ obj = obj.Where(i => (i.assets ?? new List<Asset>()).Any(a => IsAsset(a, assetFilename, i.tag_name))).ToArray();
+
+ list.AddRange(obj.Where(i => MatchesUpdateLevel(i, PackageVersionClass.Release)).OrderByDescending(GetVersion).Take(1));
+ list.AddRange(obj.Where(i => MatchesUpdateLevel(i, PackageVersionClass.Beta)).OrderByDescending(GetVersion).Take(1));
+ list.AddRange(obj.Where(i => MatchesUpdateLevel(i, PackageVersionClass.Dev)).OrderByDescending(GetVersion).Take(1));
+
+ return list;
+ }
+ }
+ }
+
+ public Version GetVersion(RootObject obj)
+ {
+ Version version;
+ if (!Version.TryParse(obj.tag_name, out version))
+ {
+ return new Version(1, 0);
+ }
+
+ return version;
+ }
+
+ private CheckForUpdateResult CheckForUpdateResult(RootObject obj, Version minVersion, string assetFilename, string packageName, string targetFilename)
+ {
+ Version version;
+ var versionString = obj.tag_name;
+ if (!Version.TryParse(versionString, out version))
+ {
+ return null;
+ }
+
+ if (version < minVersion)
+ {
+ return null;
+ }
+
+ var asset = (obj.assets ?? new List<Asset>()).FirstOrDefault(i => IsAsset(i, assetFilename, versionString));
+
+ if (asset == null)
+ {
+ return null;
+ }
+
+ return new CheckForUpdateResult
+ {
+ AvailableVersion = version.ToString(),
+ IsUpdateAvailable = version > minVersion,
+ Package = new PackageVersionInfo
+ {
+ classification = obj.prerelease ?
+ (obj.name.EndsWith("-dev", StringComparison.OrdinalIgnoreCase) ? PackageVersionClass.Dev : PackageVersionClass.Beta) :
+ PackageVersionClass.Release,
+ name = packageName,
+ sourceUrl = asset.browser_download_url,
+ targetFilename = targetFilename,
+ versionStr = version.ToString(),
+ requiredVersionStr = "1.0.0",
+ description = obj.body,
+ infoUrl = obj.html_url
+ }
+ };
+ }
+
+ private bool IsAsset(Asset asset, string assetFilename, string version)
+ {
+ var downloadFilename = Path.GetFileName(asset.browser_download_url) ?? string.Empty;
+
+ assetFilename = assetFilename.Replace("{version}", version);
+
+ if (downloadFilename.IndexOf(assetFilename, StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ return true;
+ }
+
+ return string.Equals(assetFilename, downloadFilename, StringComparison.OrdinalIgnoreCase);
+ }
+
+ public class Uploader
+ {
+ public string login { get; set; }
+ public int id { get; set; }
+ public string avatar_url { get; set; }
+ public string gravatar_id { get; set; }
+ public string url { get; set; }
+ public string html_url { get; set; }
+ public string followers_url { get; set; }
+ public string following_url { get; set; }
+ public string gists_url { get; set; }
+ public string starred_url { get; set; }
+ public string subscriptions_url { get; set; }
+ public string organizations_url { get; set; }
+ public string repos_url { get; set; }
+ public string events_url { get; set; }
+ public string received_events_url { get; set; }
+ public string type { get; set; }
+ public bool site_admin { get; set; }
+ }
+
+ public class Asset
+ {
+ public string url { get; set; }
+ public int id { get; set; }
+ public string name { get; set; }
+ public object label { get; set; }
+ public Uploader uploader { get; set; }
+ public string content_type { get; set; }
+ public string state { get; set; }
+ public int size { get; set; }
+ public int download_count { get; set; }
+ public string created_at { get; set; }
+ public string updated_at { get; set; }
+ public string browser_download_url { get; set; }
+ }
+
+ public class Author
+ {
+ public string login { get; set; }
+ public int id { get; set; }
+ public string avatar_url { get; set; }
+ public string gravatar_id { get; set; }
+ public string url { get; set; }
+ public string html_url { get; set; }
+ public string followers_url { get; set; }
+ public string following_url { get; set; }
+ public string gists_url { get; set; }
+ public string starred_url { get; set; }
+ public string subscriptions_url { get; set; }
+ public string organizations_url { get; set; }
+ public string repos_url { get; set; }
+ public string events_url { get; set; }
+ public string received_events_url { get; set; }
+ public string type { get; set; }
+ public bool site_admin { get; set; }
+ }
+
+ public class RootObject
+ {
+ public string url { get; set; }
+ public string assets_url { get; set; }
+ public string upload_url { get; set; }
+ public string html_url { get; set; }
+ public int id { get; set; }
+ public string tag_name { get; set; }
+ public string target_commitish { get; set; }
+ public string name { get; set; }
+ public bool draft { get; set; }
+ public Author author { get; set; }
+ public bool prerelease { get; set; }
+ public string created_at { get; set; }
+ public string published_at { get; set; }
+ public List<Asset> assets { get; set; }
+ public string tarball_url { get; set; }
+ public string zipball_url { get; set; }
+ public string body { get; set; }
+ }
+ }
+}
diff --git a/MediaBrowser.Common/Updates/IInstallationManager.cs b/MediaBrowser.Common/Updates/IInstallationManager.cs
new file mode 100644
index 000000000..dab38b27c
--- /dev/null
+++ b/MediaBrowser.Common/Updates/IInstallationManager.cs
@@ -0,0 +1,121 @@
+using MediaBrowser.Common.Plugins;
+using MediaBrowser.Model.Events;
+using MediaBrowser.Model.Updates;
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Common.Updates
+{
+ public interface IInstallationManager : IDisposable
+ {
+ event EventHandler<InstallationEventArgs> PackageInstalling;
+ event EventHandler<InstallationEventArgs> PackageInstallationCompleted;
+ event EventHandler<InstallationFailedEventArgs> PackageInstallationFailed;
+ event EventHandler<InstallationEventArgs> PackageInstallationCancelled;
+
+ /// <summary>
+ /// The current installations
+ /// </summary>
+ List<Tuple<InstallationInfo, CancellationTokenSource>> CurrentInstallations { get; set; }
+
+ /// <summary>
+ /// The completed installations
+ /// </summary>
+ IEnumerable<InstallationInfo> CompletedInstallations { get; }
+
+ /// <summary>
+ /// Occurs when [plugin uninstalled].
+ /// </summary>
+ event EventHandler<GenericEventArgs<IPlugin>> PluginUninstalled;
+
+ /// <summary>
+ /// Occurs when [plugin updated].
+ /// </summary>
+ event EventHandler<GenericEventArgs<Tuple<IPlugin, PackageVersionInfo>>> PluginUpdated;
+
+ /// <summary>
+ /// Occurs when [plugin updated].
+ /// </summary>
+ event EventHandler<GenericEventArgs<PackageVersionInfo>> PluginInstalled;
+
+ /// <summary>
+ /// Gets all available packages.
+ /// </summary>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <param name="withRegistration">if set to <c>true</c> [with registration].</param>
+ /// <param name="packageType">Type of the package.</param>
+ /// <param name="applicationVersion">The application version.</param>
+ /// <returns>Task{List{PackageInfo}}.</returns>
+ Task<List<PackageInfo>> GetAvailablePackages(CancellationToken cancellationToken,
+ bool withRegistration = true,
+ string packageType = null,
+ Version applicationVersion = null);
+
+ /// <summary>
+ /// Gets all available packages from a static resource.
+ /// </summary>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task{List{PackageInfo}}.</returns>
+ Task<List<PackageInfo>> GetAvailablePackagesWithoutRegistrationInfo(CancellationToken cancellationToken);
+
+ /// <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>
+ Task<PackageVersionInfo> GetPackage(string name, string guid, PackageVersionClass classification, Version version);
+
+ /// <summary>
+ /// Gets the latest compatible version.
+ /// </summary>
+ /// <param name="name">The name.</param>
+ /// <param name="guid">The assembly guid</param>
+ /// <param name="currentServerVersion">The current server version.</param>
+ /// <param name="classification">The classification.</param>
+ /// <returns>Task{PackageVersionInfo}.</returns>
+ Task<PackageVersionInfo> GetLatestCompatibleVersion(string name, string guid, Version currentServerVersion, PackageVersionClass classification = PackageVersionClass.Release);
+
+ /// <summary>
+ /// Gets the latest compatible version.
+ /// </summary>
+ /// <param name="availablePackages">The available packages.</param>
+ /// <param name="name">The name.</param>
+ /// <param name="guid">The assembly guid</param>
+ /// <param name="currentServerVersion">The current server version.</param>
+ /// <param name="classification">The classification.</param>
+ /// <returns>PackageVersionInfo.</returns>
+ PackageVersionInfo GetLatestCompatibleVersion(IEnumerable<PackageInfo> availablePackages, string name, string guid, Version currentServerVersion, PackageVersionClass classification = PackageVersionClass.Release);
+
+ /// <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>
+ Task<IEnumerable<PackageVersionInfo>> GetAvailablePluginUpdates(Version applicationVersion, bool withAutoUpdateEnabled, CancellationToken cancellationToken);
+
+ /// <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>
+ Task InstallPackage(PackageVersionInfo package, bool isPlugin, IProgress<double> progress, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Uninstalls a plugin
+ /// </summary>
+ /// <param name="plugin">The plugin.</param>
+ /// <exception cref="System.ArgumentException"></exception>
+ void UninstallPlugin(IPlugin plugin);
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.Common/Updates/InstallationEventArgs.cs b/MediaBrowser.Common/Updates/InstallationEventArgs.cs
new file mode 100644
index 000000000..9dc8ead83
--- /dev/null
+++ b/MediaBrowser.Common/Updates/InstallationEventArgs.cs
@@ -0,0 +1,11 @@
+using MediaBrowser.Model.Updates;
+
+namespace MediaBrowser.Common.Updates
+{
+ public class InstallationEventArgs
+ {
+ public InstallationInfo InstallationInfo { get; set; }
+
+ public PackageVersionInfo PackageVersionInfo { get; set; }
+ }
+}
diff --git a/MediaBrowser.Common/Updates/InstallationFailedEventArgs.cs b/MediaBrowser.Common/Updates/InstallationFailedEventArgs.cs
new file mode 100644
index 000000000..69dc1ee98
--- /dev/null
+++ b/MediaBrowser.Common/Updates/InstallationFailedEventArgs.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace MediaBrowser.Common.Updates
+{
+ public class InstallationFailedEventArgs : InstallationEventArgs
+ {
+ public Exception Exception { get; set; }
+ }
+} \ No newline at end of file