using Emby.Common.Implementations; using Emby.Common.Implementations.Serialization; using Emby.Dlna; using Emby.Dlna.ConnectionManager; using Emby.Dlna.ContentDirectory; using Emby.Dlna.Main; using Emby.Dlna.MediaReceiverRegistrar; using Emby.Dlna.Ssdp; using Emby.Drawing; using Emby.Photos; using Emby.Server.Implementations.Activity; using Emby.Server.Implementations.Channels; using Emby.Server.Implementations.Collections; using Emby.Server.Implementations.Configuration; using Emby.Server.Implementations.Data; using Emby.Server.Implementations.Devices; using Emby.Server.Implementations.Dto; using Emby.Server.Implementations.FFMpeg; using Emby.Server.Implementations.HttpServer; using Emby.Server.Implementations.HttpServer.Security; using Emby.Server.Implementations.IO; using Emby.Server.Implementations.Library; using Emby.Server.Implementations.LiveTv; using Emby.Server.Implementations.Localization; using Emby.Server.Implementations.MediaEncoder; using Emby.Server.Implementations.Migrations; using Emby.Server.Implementations.Notifications; using Emby.Server.Implementations.Playlists; using Emby.Server.Implementations.Security; using Emby.Server.Implementations.Session; using Emby.Server.Implementations.Social; using Emby.Server.Implementations.TV; using Emby.Server.Implementations.Updates; using MediaBrowser.Api; using MediaBrowser.Common; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Events; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Common.Plugins; using MediaBrowser.Common.Progress; using MediaBrowser.Common.Security; using MediaBrowser.Common.Updates; using MediaBrowser.Controller; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Chapters; using MediaBrowser.Controller.Collections; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Connect; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Notifications; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Playlists; using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Resolvers; using MediaBrowser.Controller.Security; using MediaBrowser.Controller.Session; using MediaBrowser.Controller.Sorting; using MediaBrowser.Controller.Subtitles; using MediaBrowser.Controller.Sync; using MediaBrowser.Controller.TV; using MediaBrowser.LocalMetadata.Savers; using MediaBrowser.Model.Activity; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Diagnostics; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Net; using MediaBrowser.Model.News; using MediaBrowser.Model.Reflection; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Services; using MediaBrowser.Model.Social; using MediaBrowser.Model.System; using MediaBrowser.Model.Text; using MediaBrowser.Model.Updates; using MediaBrowser.Model.Xml; using MediaBrowser.Providers.Chapters; using MediaBrowser.Providers.Manager; using MediaBrowser.Providers.Subtitles; using MediaBrowser.WebDashboard.Api; using MediaBrowser.XbmcMetadata.Providers; using OpenSubtitlesHandler; using ServiceStack; using SocketHttpListener.Primitives; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Net; using System.Reflection; using System.Runtime.InteropServices; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading; using System.Threading.Tasks; using Emby.Server.Implementations.Archiving; using Emby.Server.Implementations.Cryptography; using Emby.Server.Implementations.Diagnostics; using Emby.Server.Implementations.Net; using Emby.Server.Implementations.Reflection; using Emby.Server.Implementations.ScheduledTasks; using Emby.Server.Implementations.Serialization; using Emby.Server.Implementations.Threading; using Emby.Server.Implementations.Xml; using Emby.Server.MediaEncoding.Subtitles; using MediaBrowser.MediaEncoding.BdInfo; using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Events; using MediaBrowser.Model.Tasks; using MediaBrowser.Model.Threading; using StringExtensions = MediaBrowser.Controller.Extensions.StringExtensions; namespace Emby.Server.Implementations { /// /// Class CompositionRoot /// public abstract class ApplicationHost : IServerApplicationHost, IDependencyContainer { /// /// Gets a value indicating whether this instance can self restart. /// /// true if this instance can self restart; otherwise, false. public abstract bool CanSelfRestart { get; } /// /// Gets or sets a value indicating whether this instance can self update. /// /// true if this instance can self update; otherwise, false. public virtual bool CanSelfUpdate { get { return false; } } /// /// Occurs when [has pending restart changed]. /// public event EventHandler HasPendingRestartChanged; /// /// Occurs when [application updated]. /// public event EventHandler> ApplicationUpdated; /// /// Gets or sets a value indicating whether this instance has changes that require the entire application to restart. /// /// true if this instance has pending application restart; otherwise, false. public bool HasPendingRestart { get; private set; } /// /// Gets or sets the logger. /// /// The logger. protected ILogger Logger { get; set; } /// /// Gets or sets the plugins. /// /// The plugins. public IPlugin[] Plugins { get; protected set; } /// /// Gets or sets the log manager. /// /// The log manager. public ILogManager LogManager { get; protected set; } /// /// Gets the application paths. /// /// The application paths. protected ServerApplicationPaths ApplicationPaths { get; set; } /// /// Gets assemblies that failed to load /// /// The failed assemblies. public List FailedAssemblies { get; protected set; } /// /// Gets all concrete types. /// /// All concrete types. public Type[] AllConcreteTypes { get; protected set; } /// /// The disposable parts /// protected readonly List DisposableParts = new List(); /// /// Gets a value indicating whether this instance is first run. /// /// true if this instance is first run; otherwise, false. public bool IsFirstRun { get; private set; } /// /// Gets the configuration manager. /// /// The configuration manager. protected IConfigurationManager ConfigurationManager { get; set; } public IFileSystem FileSystemManager { get; set; } protected IEnvironmentInfo EnvironmentInfo { get; set; } public PackageVersionClass SystemUpdateLevel { get { #if BETA return PackageVersionClass.Beta; #endif return PackageVersionClass.Release; } } public virtual string OperatingSystemDisplayName { get { return EnvironmentInfo.OperatingSystemName; } } /// /// The container /// protected readonly SimpleInjector.Container Container = new SimpleInjector.Container(); protected ISystemEvents SystemEvents { get; set; } protected IMemoryStreamFactory MemoryStreamFactory { get; set; } /// /// Gets the server configuration manager. /// /// The server configuration manager. public IServerConfigurationManager ServerConfigurationManager { get { return (IServerConfigurationManager)ConfigurationManager; } } /// /// Gets the configuration manager. /// /// IConfigurationManager. protected IConfigurationManager GetConfigurationManager() { return new ServerConfigurationManager(ApplicationPaths, LogManager, XmlSerializer, FileSystemManager); } /// /// Gets or sets the server manager. /// /// The server manager. private IServerManager ServerManager { get; set; } /// /// Gets or sets the user manager. /// /// The user manager. public IUserManager UserManager { get; set; } /// /// Gets or sets the library manager. /// /// The library manager. internal ILibraryManager LibraryManager { get; set; } /// /// Gets or sets the directory watchers. /// /// The directory watchers. private ILibraryMonitor LibraryMonitor { get; set; } /// /// Gets or sets the provider manager. /// /// The provider manager. private IProviderManager ProviderManager { get; set; } /// /// Gets or sets the HTTP server. /// /// The HTTP server. private IHttpServer HttpServer { get; set; } private IDtoService DtoService { get; set; } public IImageProcessor ImageProcessor { get; set; } /// /// Gets or sets the media encoder. /// /// The media encoder. private IMediaEncoder MediaEncoder { get; set; } private ISubtitleEncoder SubtitleEncoder { get; set; } private IConnectManager ConnectManager { get; set; } private ISessionManager SessionManager { get; set; } private ILiveTvManager LiveTvManager { get; set; } public ILocalizationManager LocalizationManager { get; set; } private IEncodingManager EncodingManager { get; set; } private IChannelManager ChannelManager { get; set; } private ISyncManager SyncManager { get; set; } /// /// Gets or sets the user data repository. /// /// The user data repository. private IUserDataManager UserDataManager { get; set; } private IUserRepository UserRepository { get; set; } internal IDisplayPreferencesRepository DisplayPreferencesRepository { get; set; } internal IItemRepository ItemRepository { get; set; } private INotificationsRepository NotificationsRepository { get; set; } private INotificationManager NotificationManager { get; set; } private ISubtitleManager SubtitleManager { get; set; } private IChapterManager ChapterManager { get; set; } private IDeviceManager DeviceManager { get; set; } internal IUserViewManager UserViewManager { get; set; } private IAuthenticationRepository AuthenticationRepository { get; set; } private ITVSeriesManager TVSeriesManager { get; set; } private ICollectionManager CollectionManager { get; set; } private IMediaSourceManager MediaSourceManager { get; set; } private IPlaylistManager PlaylistManager { get; set; } /// /// Gets or sets the installation manager. /// /// The installation manager. protected IInstallationManager InstallationManager { get; private set; } /// /// Gets the security manager. /// /// The security manager. protected ISecurityManager SecurityManager { get; private set; } /// /// Gets or sets the zip client. /// /// The zip client. protected IZipClient ZipClient { get; private set; } protected IAuthService AuthService { get; private set; } protected readonly StartupOptions StartupOptions; private readonly string _releaseAssetFilename; internal IPowerManagement PowerManagement { get; private set; } internal IImageEncoder ImageEncoder { get; private set; } private readonly Action _certificateGenerator; private readonly Func _defaultUserNameFactory; protected IProcessFactory ProcessFactory { get; private set; } protected ITimerFactory TimerFactory { get; private set; } protected ICryptoProvider CryptographyProvider = new CryptographyProvider(); protected readonly IXmlSerializer XmlSerializer; protected ISocketFactory SocketFactory { get; private set; } protected ITaskManager TaskManager { get; private set; } public IHttpClient HttpClient { get; private set; } protected INetworkManager NetworkManager { get; set; } public IJsonSerializer JsonSerializer { get; private set; } protected IIsoManager IsoManager { get; private set; } /// /// Initializes a new instance of the class. /// public ApplicationHost(ServerApplicationPaths applicationPaths, ILogManager logManager, StartupOptions options, IFileSystem fileSystem, IPowerManagement powerManagement, string releaseAssetFilename, IEnvironmentInfo environmentInfo, IImageEncoder imageEncoder, ISystemEvents systemEvents, IMemoryStreamFactory memoryStreamFactory, INetworkManager networkManager, Action certificateGenerator, Func defaultUsernameFactory) { // hack alert, until common can target .net core BaseExtensions.CryptographyProvider = CryptographyProvider; XmlSerializer = new MyXmlSerializer(fileSystem, logManager.GetLogger("XmlSerializer")); NetworkManager = networkManager; EnvironmentInfo = environmentInfo; SystemEvents = systemEvents; MemoryStreamFactory = memoryStreamFactory; FailedAssemblies = new List(); ApplicationPaths = applicationPaths; LogManager = logManager; FileSystemManager = fileSystem; ConfigurationManager = GetConfigurationManager(); // Initialize this early in case the -v command line option is used Logger = LogManager.GetLogger("App"); StartupOptions = options; _certificateGenerator = certificateGenerator; _releaseAssetFilename = releaseAssetFilename; _defaultUserNameFactory = defaultUsernameFactory; PowerManagement = powerManagement; ImageEncoder = imageEncoder; SetBaseExceptionMessage(); if (environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows) { fileSystem.AddShortcutHandler(new LnkShortcutHandler()); } fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem)); } private Version _version; /// /// Gets the current application version /// /// The application version. public Version ApplicationVersion { get { return _version ?? (_version = GetAssembly(GetType()).GetName().Version); } } public virtual bool SupportsRunningAsService { get { return false; } } private DeviceId _deviceId; public string SystemId { get { if (_deviceId == null) { _deviceId = new DeviceId(ApplicationPaths, LogManager.GetLogger("SystemId"), FileSystemManager); } return _deviceId.Value; } } /// /// Gets the name. /// /// The name. public string Name { get { return "Emby Server"; } } public virtual bool IsRunningAsService { get { return false; } } private Assembly GetAssembly(Type type) { return type.GetTypeInfo().Assembly; } public virtual bool SupportsAutoRunAtStartup { get { return EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows; } } /// /// Creates an instance of type and resolves all constructor dependancies /// /// The type. /// System.Object. public object CreateInstance(Type type) { try { return Container.GetInstance(type); } catch (Exception ex) { Logger.ErrorException("Error creating {0}", ex, type.FullName); throw; } } /// /// Creates the instance safe. /// /// The type. /// System.Object. protected object CreateInstanceSafe(Type type) { try { return Container.GetInstance(type); } catch (Exception ex) { Logger.ErrorException("Error creating {0}", ex, type.FullName); // Don't blow up in release mode return null; } } /// /// Registers the specified obj. /// /// /// The obj. /// if set to true [manage lifetime]. protected void RegisterSingleInstance(T obj, bool manageLifetime = true) where T : class { Container.RegisterSingleton(obj); if (manageLifetime) { var disposable = obj as IDisposable; if (disposable != null) { DisposableParts.Add(disposable); } } } /// /// Registers the single instance. /// /// /// The func. protected void RegisterSingleInstance(Func func) where T : class { Container.RegisterSingleton(func); } /// /// Resolves this instance. /// /// /// ``0. public T Resolve() { return (T)Container.GetRegistration(typeof(T), true).GetInstance(); } /// /// Resolves this instance. /// /// /// ``0. public T TryResolve() { var result = Container.GetRegistration(typeof(T), false); if (result == null) { return default(T); } return (T)result.GetInstance(); } /// /// Loads the assembly. /// /// The file. /// Assembly. protected Assembly LoadAssembly(string file) { try { return Assembly.Load(File.ReadAllBytes(file)); } catch (Exception ex) { FailedAssemblies.Add(file); Logger.ErrorException("Error loading assembly {0}", ex, file); return null; } } /// /// Gets the export types. /// /// /// IEnumerable{Type}. public IEnumerable GetExportTypes() { var currentType = typeof(T); return AllConcreteTypes.Where(currentType.IsAssignableFrom); } /// /// Gets the exports. /// /// /// if set to true [manage liftime]. /// IEnumerable{``0}. public IEnumerable GetExports(bool manageLiftime = true) { var parts = GetExportTypes() .Select(CreateInstanceSafe) .Where(i => i != null) .Cast() .ToList(); if (manageLiftime) { lock (DisposableParts) { DisposableParts.AddRange(parts.OfType()); } } return parts; } private void SetBaseExceptionMessage() { var builder = GetBaseExceptionMessage(ApplicationPaths); builder.Insert(0, string.Format("Version: {0}{1}", ApplicationVersion, Environment.NewLine)); builder.Insert(0, "*** Error Report ***" + Environment.NewLine); LogManager.ExceptionMessagePrefix = builder.ToString(); } /// /// Runs the startup tasks. /// public async Task RunStartupTasks() { Resolve().AddTasks(GetExports(false)); ConfigureAutorun(); ConfigurationManager.ConfigurationUpdated += OnConfigurationUpdated; await MediaEncoder.Init().ConfigureAwait(false); if (string.IsNullOrWhiteSpace(MediaEncoder.EncoderPath)) { if (ServerConfigurationManager.Configuration.IsStartupWizardCompleted) { ServerConfigurationManager.Configuration.IsStartupWizardCompleted = false; ServerConfigurationManager.SaveConfiguration(); } } Logger.Info("ServerId: {0}", SystemId); Logger.Info("Core startup complete"); HttpServer.GlobalResponse = null; Logger.Info("Post-init migrations complete"); foreach (var entryPoint in GetExports().ToList()) { var name = entryPoint.GetType().FullName; Logger.Info("Starting entry point {0}", name); var now = DateTime.UtcNow; try { entryPoint.Run(); } catch (Exception ex) { Logger.ErrorException("Error in {0}", ex, name); } Logger.Info("Entry point completed: {0}. Duration: {1} seconds", name, (DateTime.UtcNow - now).TotalSeconds.ToString(CultureInfo.InvariantCulture), "ImageInfos"); } Logger.Info("All entry points have started"); LogManager.RemoveConsoleOutput(); } /// /// Configures the autorun. /// private void ConfigureAutorun() { try { ConfigureAutoRunAtStartup(ConfigurationManager.CommonConfiguration.RunAtStartup); } catch (Exception ex) { Logger.ErrorException("Error configuring autorun", ex); } } private IJsonSerializer CreateJsonSerializer() { try { // https://github.com/ServiceStack/ServiceStack/blob/master/tests/ServiceStack.WebHost.IntegrationTests/Web.config#L4 Licensing.RegisterLicense("1001-e1JlZjoxMDAxLE5hbWU6VGVzdCBCdXNpbmVzcyxUeXBlOkJ1c2luZXNzLEhhc2g6UHVNTVRPclhvT2ZIbjQ5MG5LZE1mUTd5RUMzQnBucTFEbTE3TDczVEF4QUNMT1FhNXJMOWkzVjFGL2ZkVTE3Q2pDNENqTkQyUktRWmhvUVBhYTBiekJGUUZ3ZE5aZHFDYm9hL3lydGlwUHI5K1JsaTBYbzNsUC85cjVJNHE5QVhldDN6QkE4aTlvdldrdTgyTk1relY2eis2dFFqTThYN2lmc0JveHgycFdjPSxFeHBpcnk6MjAxMy0wMS0wMX0="); } catch { // Failing under mono } return new JsonSerializer(FileSystemManager, LogManager.GetLogger("JsonSerializer")); } public async Task Init(IProgress progress) { HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber; HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber; // Safeguard against invalid configuration if (HttpPort == HttpsPort) { HttpPort = ServerConfiguration.DefaultHttpPort; HttpsPort = ServerConfiguration.DefaultHttpsPort; } progress.Report(1); JsonSerializer = CreateJsonSerializer(); OnLoggerLoaded(true); LogManager.LoggerLoaded += (s, e) => OnLoggerLoaded(false); IsFirstRun = !ConfigurationManager.CommonConfiguration.IsStartupWizardCompleted; progress.Report(2); LogManager.LogSeverity = ConfigurationManager.CommonConfiguration.EnableDebugLevelLogging ? LogSeverity.Debug : LogSeverity.Info; progress.Report(3); DiscoverTypes(); progress.Report(14); SetHttpLimit(); progress.Report(15); var innerProgress = new ActionableProgress(); innerProgress.RegisterAction(p => progress.Report(.8 * p + 15)); await RegisterResources(innerProgress).ConfigureAwait(false); FindParts(); progress.Report(95); await InstallIsoMounters(CancellationToken.None).ConfigureAwait(false); progress.Report(100); } protected virtual void OnLoggerLoaded(bool isFirstLoad) { Logger.Info("Application version: {0}", ApplicationVersion); if (!isFirstLoad) { LogEnvironmentInfo(Logger, ApplicationPaths, false); } // Put the app config in the log for troubleshooting purposes Logger.LogMultiline("Application configuration:", LogSeverity.Info, new StringBuilder(JsonSerializer.SerializeToString(ConfigurationManager.CommonConfiguration))); if (Plugins != null) { var pluginBuilder = new StringBuilder(); foreach (var plugin in Plugins) { pluginBuilder.AppendLine(string.Format("{0} {1}", plugin.Name, plugin.Version)); } Logger.LogMultiline("Plugins:", LogSeverity.Info, pluginBuilder); } } protected abstract IConnectManager CreateConnectManager(); protected abstract ISyncManager CreateSyncManager(); /// /// Registers resources that classes will depend on /// protected async Task RegisterResources(IProgress progress) { RegisterSingleInstance(ConfigurationManager); RegisterSingleInstance(this); RegisterSingleInstance(ApplicationPaths); RegisterSingleInstance(JsonSerializer); RegisterSingleInstance(MemoryStreamFactory); RegisterSingleInstance(SystemEvents); RegisterSingleInstance(LogManager); RegisterSingleInstance(Logger); RegisterSingleInstance(EnvironmentInfo); RegisterSingleInstance(FileSystemManager); HttpClient = new HttpClientManager.HttpClientManager(ApplicationPaths, LogManager.GetLogger("HttpClient"), FileSystemManager, MemoryStreamFactory, GetDefaultUserAgent); RegisterSingleInstance(HttpClient); RegisterSingleInstance(NetworkManager); IsoManager = new IsoManager(); RegisterSingleInstance(IsoManager); TaskManager = new TaskManager(ApplicationPaths, JsonSerializer, LogManager.GetLogger("TaskManager"), FileSystemManager, SystemEvents); RegisterSingleInstance(TaskManager); RegisterSingleInstance(XmlSerializer); ProcessFactory = new ProcessFactory(); RegisterSingleInstance(ProcessFactory); TimerFactory = new TimerFactory(); RegisterSingleInstance(TimerFactory); RegisterSingleInstance(CryptographyProvider); SocketFactory = new SocketFactory(LogManager.GetLogger("SocketFactory")); RegisterSingleInstance(SocketFactory); RegisterSingleInstance(PowerManagement); SecurityManager = new PluginSecurityManager(this, HttpClient, JsonSerializer, ApplicationPaths, LogManager, FileSystemManager, CryptographyProvider); RegisterSingleInstance(SecurityManager); InstallationManager = new InstallationManager(LogManager.GetLogger("InstallationManager"), this, ApplicationPaths, HttpClient, JsonSerializer, SecurityManager, ConfigurationManager, FileSystemManager, CryptographyProvider); RegisterSingleInstance(InstallationManager); ZipClient = new ZipClient(FileSystemManager); RegisterSingleInstance(ZipClient); RegisterSingleInstance(new HttpResultFactory(LogManager, FileSystemManager, JsonSerializer, MemoryStreamFactory)); RegisterSingleInstance(this); RegisterSingleInstance(ApplicationPaths); RegisterSingleInstance(ServerConfigurationManager); IAssemblyInfo assemblyInfo = new AssemblyInfo(); RegisterSingleInstance(assemblyInfo); LocalizationManager = new LocalizationManager(ServerConfigurationManager, FileSystemManager, JsonSerializer, LogManager.GetLogger("LocalizationManager"), assemblyInfo, new TextLocalizer()); StringExtensions.LocalizationManager = LocalizationManager; RegisterSingleInstance(LocalizationManager); ITextEncoding textEncoding = new TextEncoding.TextEncoding(FileSystemManager, LogManager.GetLogger("TextEncoding"), JsonSerializer); RegisterSingleInstance(textEncoding); Utilities.EncodingHelper = textEncoding; RegisterSingleInstance(() => new BdInfoExaminer(FileSystemManager, textEncoding)); RegisterSingleInstance(new XmlReaderSettingsFactory()); UserDataManager = new UserDataManager(LogManager, ServerConfigurationManager); RegisterSingleInstance(UserDataManager); UserRepository = GetUserRepository(); // This is only needed for disposal purposes. If removing this, make sure to have the manager handle disposing it RegisterSingleInstance(UserRepository); var displayPreferencesRepo = new SqliteDisplayPreferencesRepository(LogManager.GetLogger("SqliteDisplayPreferencesRepository"), JsonSerializer, ApplicationPaths, MemoryStreamFactory); DisplayPreferencesRepository = displayPreferencesRepo; RegisterSingleInstance(DisplayPreferencesRepository); var itemRepo = new SqliteItemRepository(ServerConfigurationManager, JsonSerializer, LogManager.GetLogger("SqliteItemRepository"), MemoryStreamFactory, assemblyInfo, FileSystemManager, EnvironmentInfo, TimerFactory); ItemRepository = itemRepo; RegisterSingleInstance(ItemRepository); AuthenticationRepository = GetAuthenticationRepository(); RegisterSingleInstance(AuthenticationRepository); UserManager = new UserManager(LogManager.GetLogger("UserManager"), ServerConfigurationManager, UserRepository, XmlSerializer, NetworkManager, () => ImageProcessor, () => DtoService, () => ConnectManager, this, JsonSerializer, FileSystemManager, CryptographyProvider, _defaultUserNameFactory()); RegisterSingleInstance(UserManager); LibraryManager = new LibraryManager(Logger, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager, () => ProviderManager, () => UserViewManager); RegisterSingleInstance(LibraryManager); var musicManager = new MusicManager(LibraryManager); RegisterSingleInstance(new MusicManager(LibraryManager)); LibraryMonitor = new LibraryMonitor(LogManager, TaskManager, LibraryManager, ServerConfigurationManager, FileSystemManager, TimerFactory, SystemEvents, EnvironmentInfo); RegisterSingleInstance(LibraryMonitor); ProviderManager = new ProviderManager(HttpClient, ServerConfigurationManager, LibraryMonitor, LogManager, FileSystemManager, ApplicationPaths, () => LibraryManager, JsonSerializer, MemoryStreamFactory); RegisterSingleInstance(ProviderManager); RegisterSingleInstance(() => new SearchEngine(LogManager, LibraryManager, UserManager)); CertificateInfo = GetCertificateInfo(true); Certificate = GetCertificate(CertificateInfo); HttpServer = HttpServerFactory.CreateServer(this, LogManager, ServerConfigurationManager, NetworkManager, MemoryStreamFactory, "Emby", "web/index.html", textEncoding, SocketFactory, CryptographyProvider, JsonSerializer, XmlSerializer, EnvironmentInfo, Certificate, FileSystemManager, SupportsDualModeSockets); HttpServer.GlobalResponse = LocalizationManager.GetLocalizedString("StartupEmbyServerIsLoading"); RegisterSingleInstance(HttpServer, false); progress.Report(10); ServerManager = new ServerManager.ServerManager(this, JsonSerializer, LogManager.GetLogger("ServerManager"), ServerConfigurationManager, MemoryStreamFactory, textEncoding); RegisterSingleInstance(ServerManager); var innerProgress = new ActionableProgress(); innerProgress.RegisterAction(p => progress.Report((.75 * p) + 15)); ImageProcessor = GetImageProcessor(); RegisterSingleInstance(ImageProcessor); TVSeriesManager = new TVSeriesManager(UserManager, UserDataManager, LibraryManager, ServerConfigurationManager); RegisterSingleInstance(TVSeriesManager); SyncManager = CreateSyncManager(); RegisterSingleInstance(SyncManager); DtoService = new DtoService(LogManager.GetLogger("DtoService"), LibraryManager, UserDataManager, ItemRepository, ImageProcessor, ServerConfigurationManager, FileSystemManager, ProviderManager, () => ChannelManager, SyncManager, this, () => DeviceManager, () => MediaSourceManager, () => LiveTvManager); RegisterSingleInstance(DtoService); var encryptionManager = new EncryptionManager(); RegisterSingleInstance(encryptionManager); ConnectManager = CreateConnectManager(); RegisterSingleInstance(ConnectManager); DeviceManager = new DeviceManager(new DeviceRepository(ApplicationPaths, JsonSerializer, LogManager.GetLogger("DeviceManager"), FileSystemManager), UserManager, FileSystemManager, LibraryMonitor, ServerConfigurationManager, LogManager.GetLogger("DeviceManager"), NetworkManager); RegisterSingleInstance(DeviceManager); var newsService = new Emby.Server.Implementations.News.NewsService(ApplicationPaths, JsonSerializer); RegisterSingleInstance(newsService); progress.Report(15); ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, LogManager.GetLogger("ChannelManager"), ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, LocalizationManager, HttpClient, ProviderManager); RegisterSingleInstance(ChannelManager); MediaSourceManager = new MediaSourceManager(ItemRepository, UserManager, LibraryManager, LogManager.GetLogger("MediaSourceManager"), JsonSerializer, FileSystemManager, UserDataManager, TimerFactory); RegisterSingleInstance(MediaSourceManager); SessionManager = new SessionManager(UserDataManager, LogManager.GetLogger("SessionManager"), LibraryManager, UserManager, musicManager, DtoService, ImageProcessor, JsonSerializer, this, HttpClient, AuthenticationRepository, DeviceManager, MediaSourceManager, TimerFactory); RegisterSingleInstance(SessionManager); var dlnaManager = new DlnaManager(XmlSerializer, FileSystemManager, ApplicationPaths, LogManager.GetLogger("Dlna"), JsonSerializer, this, assemblyInfo); RegisterSingleInstance(dlnaManager); var connectionManager = new ConnectionManager(dlnaManager, ServerConfigurationManager, LogManager.GetLogger("UpnpConnectionManager"), HttpClient, new XmlReaderSettingsFactory()); RegisterSingleInstance(connectionManager); CollectionManager = new CollectionManager(LibraryManager, FileSystemManager, LibraryMonitor, LogManager.GetLogger("CollectionManager"), ProviderManager); RegisterSingleInstance(CollectionManager); PlaylistManager = new PlaylistManager(LibraryManager, FileSystemManager, LibraryMonitor, LogManager.GetLogger("PlaylistManager"), UserManager, ProviderManager); RegisterSingleInstance(PlaylistManager); LiveTvManager = new LiveTvManager(this, ServerConfigurationManager, Logger, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager, LocalizationManager, JsonSerializer, ProviderManager, FileSystemManager, SecurityManager); RegisterSingleInstance(LiveTvManager); UserViewManager = new UserViewManager(LibraryManager, LocalizationManager, UserManager, ChannelManager, LiveTvManager, ServerConfigurationManager); RegisterSingleInstance(UserViewManager); var contentDirectory = new ContentDirectory(dlnaManager, UserDataManager, ImageProcessor, LibraryManager, ServerConfigurationManager, UserManager, LogManager.GetLogger("UpnpContentDirectory"), HttpClient, LocalizationManager, ChannelManager, MediaSourceManager, UserViewManager, () => MediaEncoder, new XmlReaderSettingsFactory(), TVSeriesManager); RegisterSingleInstance(contentDirectory); var mediaRegistrar = new MediaReceiverRegistrar(LogManager.GetLogger("MediaReceiverRegistrar"), HttpClient, ServerConfigurationManager, new XmlReaderSettingsFactory()); RegisterSingleInstance(mediaRegistrar); NotificationManager = new NotificationManager(LogManager, UserManager, ServerConfigurationManager); RegisterSingleInstance(NotificationManager); SubtitleManager = new SubtitleManager(LogManager.GetLogger("SubtitleManager"), FileSystemManager, LibraryMonitor, LibraryManager, MediaSourceManager); RegisterSingleInstance(SubtitleManager); RegisterSingleInstance(new DeviceDiscovery(LogManager.GetLogger("IDeviceDiscovery"), ServerConfigurationManager, SocketFactory, TimerFactory)); ChapterManager = new ChapterManager(LibraryManager, LogManager.GetLogger("ChapterManager"), ServerConfigurationManager, ItemRepository); RegisterSingleInstance(ChapterManager); await RegisterMediaEncoder(innerProgress).ConfigureAwait(false); progress.Report(90); EncodingManager = new EncodingManager(FileSystemManager, Logger, MediaEncoder, ChapterManager, LibraryManager); RegisterSingleInstance(EncodingManager); var sharingRepo = new SharingRepository(LogManager.GetLogger("SharingRepository"), ApplicationPaths); sharingRepo.Initialize(); // This is only needed for disposal purposes. If removing this, make sure to have the manager handle disposing it RegisterSingleInstance(sharingRepo); RegisterSingleInstance(new SharingManager(sharingRepo, ServerConfigurationManager, LibraryManager, this)); var activityLogRepo = GetActivityLogRepository(); RegisterSingleInstance(activityLogRepo); RegisterSingleInstance(new ActivityManager(LogManager.GetLogger("ActivityManager"), activityLogRepo, UserManager)); var authContext = new AuthorizationContext(AuthenticationRepository, ConnectManager); RegisterSingleInstance(authContext); RegisterSingleInstance(new SessionContext(UserManager, authContext, SessionManager)); AuthService = new AuthService(UserManager, authContext, ServerConfigurationManager, ConnectManager, SessionManager, DeviceManager); RegisterSingleInstance(AuthService); SubtitleEncoder = new SubtitleEncoder(LibraryManager, LogManager.GetLogger("SubtitleEncoder"), ApplicationPaths, FileSystemManager, MediaEncoder, JsonSerializer, HttpClient, MediaSourceManager, MemoryStreamFactory, ProcessFactory, textEncoding); RegisterSingleInstance(SubtitleEncoder); displayPreferencesRepo.Initialize(); var userDataRepo = new SqliteUserDataRepository(LogManager.GetLogger("SqliteUserDataRepository"), ApplicationPaths, FileSystemManager); ((UserDataManager)UserDataManager).Repository = userDataRepo; itemRepo.Initialize(userDataRepo); ((LibraryManager)LibraryManager).ItemRepository = ItemRepository; ConfigureNotificationsRepository(); progress.Report(100); SetStaticProperties(); await ((UserManager)UserManager).Initialize().ConfigureAwait(false); } public static void LogEnvironmentInfo(ILogger logger, IApplicationPaths appPaths, bool isStartup) { logger.LogMultiline("Emby", LogSeverity.Info, GetBaseExceptionMessage(appPaths)); } protected static StringBuilder GetBaseExceptionMessage(IApplicationPaths appPaths) { var builder = new StringBuilder(); builder.AppendLine(string.Format("Command line: {0}", string.Join(" ", Environment.GetCommandLineArgs()))); builder.AppendLine(string.Format("Operating system: {0}", Environment.OSVersion)); builder.AppendLine(string.Format("64-Bit OS: {0}", Environment.Is64BitOperatingSystem)); builder.AppendLine(string.Format("64-Bit Process: {0}", Environment.Is64BitProcess)); Type type = Type.GetType("Mono.Runtime"); if (type != null) { MethodInfo displayName = type.GetMethod("GetDisplayName", BindingFlags.NonPublic | BindingFlags.Static); if (displayName != null) { builder.AppendLine("Mono: " + displayName.Invoke(null, null)); } } builder.AppendLine(string.Format("Processor count: {0}", Environment.ProcessorCount)); builder.AppendLine(string.Format("Program data path: {0}", appPaths.ProgramDataPath)); builder.AppendLine(string.Format("Application directory: {0}", appPaths.ProgramSystemPath)); return builder; } private void SetHttpLimit() { try { // Increase the max http request limit ServicePointManager.DefaultConnectionLimit = Math.Max(96, ServicePointManager.DefaultConnectionLimit); } catch (Exception ex) { Logger.ErrorException("Error setting http limit", ex); } } /// /// Installs the iso mounters. /// /// The cancellation token. /// Task. private async Task InstallIsoMounters(CancellationToken cancellationToken) { var list = new List(); foreach (var isoMounter in GetExports()) { try { if (isoMounter.RequiresInstallation && !isoMounter.IsInstalled) { Logger.Info("Installing {0}", isoMounter.Name); await isoMounter.Install(cancellationToken).ConfigureAwait(false); } list.Add(isoMounter); } catch (Exception ex) { Logger.ErrorException("{0} failed to load.", ex, isoMounter.Name); } } IsoManager.AddParts(list); } private string GetDefaultUserAgent() { var name = FormatAttribute(Name); return name + "/" + ApplicationVersion; } private string FormatAttribute(string str) { var arr = str.ToCharArray(); arr = Array.FindAll(arr, (c => (char.IsLetterOrDigit(c) || char.IsWhiteSpace(c)))); var result = new string(arr); if (string.IsNullOrWhiteSpace(result)) { result = "Emby"; } return result; } protected virtual bool SupportsDualModeSockets { get { return true; } } private ICertificate GetCertificate(CertificateInfo info) { var certificateLocation = info == null ? null : info.Path; if (string.IsNullOrWhiteSpace(certificateLocation)) { return null; } try { if (!FileSystemManager.FileExists(certificateLocation)) { return null; } // Don't use an empty string password var password = string.IsNullOrWhiteSpace(info.Password) ? null : info.Password; X509Certificate2 localCert = new X509Certificate2(certificateLocation, password); //localCert.PrivateKey = PrivateKey.CreateFromFile(pvk_file).RSA; if (!localCert.HasPrivateKey) { //throw new FileNotFoundException("Secure requested, no private key included", certificateLocation); return null; } return new Certificate(localCert); } catch (Exception ex) { Logger.ErrorException("Error loading cert from {0}", ex, certificateLocation); return null; } } private IImageProcessor GetImageProcessor() { return new ImageProcessor(LogManager.GetLogger("ImageProcessor"), ServerConfigurationManager.ApplicationPaths, FileSystemManager, JsonSerializer, ImageEncoder, () => LibraryManager, TimerFactory); } protected virtual FFMpegInstallInfo GetFfmpegInstallInfo() { var info = new FFMpegInstallInfo(); // Windows builds: http://ffmpeg.zeranoe.com/builds/ // Linux builds: http://johnvansickle.com/ffmpeg/ // OS X builds: http://ffmpegmac.net/ // OS X x64: http://www.evermeet.cx/ffmpeg/ if (EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Linux) { info.FFMpegFilename = "ffmpeg"; info.FFProbeFilename = "ffprobe"; info.ArchiveType = "7z"; info.Version = "20170308"; info.DownloadUrls = GetLinuxDownloadUrls(); } else if (EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows) { info.FFMpegFilename = "ffmpeg.exe"; info.FFProbeFilename = "ffprobe.exe"; info.Version = "20170308"; info.ArchiveType = "7z"; info.DownloadUrls = GetWindowsDownloadUrls(); } else if (EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.OSX) { info.FFMpegFilename = "ffmpeg"; info.FFProbeFilename = "ffprobe"; info.ArchiveType = "7z"; info.Version = "20170308"; info.DownloadUrls = GetMacDownloadUrls(); } else { // No version available - user requirement info.DownloadUrls = new string[] { }; } return info; } private string[] GetMacDownloadUrls() { switch (EnvironmentInfo.SystemArchitecture) { case MediaBrowser.Model.System.Architecture.X64: return new[] { "https://embydata.com/downloads/ffmpeg/osx/ffmpeg-x64-20170308.7z" }; } return new string[] { }; } private string[] GetWindowsDownloadUrls() { switch (EnvironmentInfo.SystemArchitecture) { case MediaBrowser.Model.System.Architecture.X64: return new[] { "https://embydata.com/downloads/ffmpeg/windows/ffmpeg-20170308-win64.7z" }; case MediaBrowser.Model.System.Architecture.X86: return new[] { "https://embydata.com/downloads/ffmpeg/windows/ffmpeg-20170308-win32.7z" }; } return new string[] { }; } private string[] GetLinuxDownloadUrls() { switch (EnvironmentInfo.SystemArchitecture) { case MediaBrowser.Model.System.Architecture.X64: return new[] { "https://embydata.com/downloads/ffmpeg/linux/ffmpeg-git-20170301-64bit-static.7z" }; case MediaBrowser.Model.System.Architecture.X86: return new[] { "https://embydata.com/downloads/ffmpeg/linux/ffmpeg-git-20170301-32bit-static.7z" }; } return new string[] { }; } /// /// Registers the media encoder. /// /// Task. private async Task RegisterMediaEncoder(IProgress progress) { string encoderPath = null; string probePath = null; var info = await new FFMpegLoader(Logger, ApplicationPaths, HttpClient, ZipClient, FileSystemManager, GetFfmpegInstallInfo()) .GetFFMpegInfo(StartupOptions, progress).ConfigureAwait(false); encoderPath = info.EncoderPath; probePath = info.ProbePath; var hasExternalEncoder = string.Equals(info.Version, "external", StringComparison.OrdinalIgnoreCase); var mediaEncoder = new MediaEncoding.Encoder.MediaEncoder(LogManager.GetLogger("MediaEncoder"), JsonSerializer, encoderPath, probePath, hasExternalEncoder, ServerConfigurationManager, FileSystemManager, LiveTvManager, IsoManager, LibraryManager, ChannelManager, SessionManager, () => SubtitleEncoder, () => MediaSourceManager, HttpClient, ZipClient, MemoryStreamFactory, ProcessFactory, (Environment.ProcessorCount > 2 ? 14000 : 40000), EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows, EnvironmentInfo); MediaEncoder = mediaEncoder; RegisterSingleInstance(MediaEncoder); } /// /// Gets the user repository. /// /// Task{IUserRepository}. private IUserRepository GetUserRepository() { var repo = new SqliteUserRepository(LogManager.GetLogger("SqliteUserRepository"), ApplicationPaths, JsonSerializer, MemoryStreamFactory); repo.Initialize(); return repo; } private IAuthenticationRepository GetAuthenticationRepository() { var repo = new AuthenticationRepository(LogManager.GetLogger("AuthenticationRepository"), ServerConfigurationManager.ApplicationPaths); repo.Initialize(); return repo; } private IActivityRepository GetActivityLogRepository() { var repo = new ActivityRepository(LogManager.GetLogger("ActivityRepository"), ServerConfigurationManager.ApplicationPaths); repo.Initialize(); return repo; } /// /// Configures the repositories. /// private void ConfigureNotificationsRepository() { var repo = new SqliteNotificationsRepository(LogManager.GetLogger("SqliteNotificationsRepository"), ServerConfigurationManager.ApplicationPaths, FileSystemManager); repo.Initialize(); NotificationsRepository = repo; RegisterSingleInstance(NotificationsRepository); } /// /// Dirty hacks /// private void SetStaticProperties() { // For now there's no real way to inject these properly BaseItem.Logger = LogManager.GetLogger("BaseItem"); BaseItem.ConfigurationManager = ServerConfigurationManager; BaseItem.LibraryManager = LibraryManager; BaseItem.ProviderManager = ProviderManager; BaseItem.LocalizationManager = LocalizationManager; BaseItem.ItemRepository = ItemRepository; User.XmlSerializer = XmlSerializer; User.UserManager = UserManager; Folder.UserManager = UserManager; BaseItem.FileSystem = FileSystemManager; BaseItem.UserDataManager = UserDataManager; BaseItem.ChannelManager = ChannelManager; BaseItem.LiveTvManager = LiveTvManager; Folder.UserViewManager = UserViewManager; UserView.TVSeriesManager = TVSeriesManager; UserView.PlaylistManager = PlaylistManager; BaseItem.CollectionManager = CollectionManager; BaseItem.MediaSourceManager = MediaSourceManager; CollectionFolder.XmlSerializer = XmlSerializer; Utilities.CryptographyProvider = CryptographyProvider; AuthenticatedAttribute.AuthService = AuthService; } /// /// Finds the parts. /// protected void FindParts() { if (!ServerConfigurationManager.Configuration.IsPortAuthorized) { RegisterServerWithAdministratorAccess(); ServerConfigurationManager.Configuration.IsPortAuthorized = true; ConfigurationManager.SaveConfiguration(); } RegisterModules(); ConfigurationManager.AddParts(GetExports()); Plugins = GetExports().Select(LoadPlugin).Where(i => i != null).ToArray(); HttpServer.Init(GetExports(false)); ServerManager.AddWebSocketListeners(GetExports(false)); StartServer(); LibraryManager.AddParts(GetExports(), GetExports(), GetExports(), GetExports(), GetExports(), GetExports()); ProviderManager.AddParts(GetExports(), GetExports(), GetExports(), GetExports(), GetExports()); ImageProcessor.AddParts(GetExports()); LiveTvManager.AddParts(GetExports(), GetExports(), GetExports()); SubtitleManager.AddParts(GetExports()); SessionManager.AddParts(GetExports()); ChannelManager.AddParts(GetExports()); MediaSourceManager.AddParts(GetExports()); NotificationManager.AddParts(GetExports(), GetExports()); SyncManager.AddParts(GetExports()); } private IPlugin LoadPlugin(IPlugin plugin) { try { var assemblyPlugin = plugin as IPluginAssembly; if (assemblyPlugin != null) { var assembly = plugin.GetType().Assembly; var assemblyName = assembly.GetName(); var attribute = (GuidAttribute)assembly.GetCustomAttributes(typeof(GuidAttribute), true)[0]; var assemblyId = new Guid(attribute.Value); var assemblyFileName = assemblyName.Name + ".dll"; var assemblyFilePath = Path.Combine(ApplicationPaths.PluginsPath, assemblyFileName); assemblyPlugin.SetAttributes(assemblyFilePath, assemblyFileName, assemblyName.Version, assemblyId); } var isFirstRun = !File.Exists(plugin.ConfigurationFilePath); plugin.SetStartupInfo(isFirstRun, File.GetLastWriteTimeUtc, s => Directory.CreateDirectory(s)); } catch (Exception ex) { Logger.ErrorException("Error loading plugin {0}", ex, plugin.GetType().FullName); return null; } return plugin; } /// /// Discovers the types. /// protected void DiscoverTypes() { FailedAssemblies.Clear(); var assemblies = GetComposablePartAssemblies().ToList(); foreach (var assembly in assemblies) { Logger.Info("Loading {0}", assembly.FullName); } AllConcreteTypes = assemblies .SelectMany(GetTypes) .Where(t => t.IsClass && !t.IsAbstract && !t.IsInterface && !t.IsGenericType) .ToArray(); } /// /// Gets a list of types within an assembly /// This will handle situations that would normally throw an exception - such as a type within the assembly that depends on some other non-existant reference /// /// The assembly. /// IEnumerable{Type}. /// assembly protected List GetTypes(Assembly assembly) { if (assembly == null) { return new List(); } try { // This null checking really shouldn't be needed but adding it due to some // unhandled exceptions in mono 5.0 that are a little hard to hunt down var types = assembly.GetTypes() ?? new Type[] { }; return types.Where(t => t != null).ToList(); } catch (ReflectionTypeLoadException ex) { if (ex.LoaderExceptions != null) { foreach (var loaderException in ex.LoaderExceptions) { if (loaderException != null) { Logger.Error("LoaderException: " + loaderException.Message); } } } // If it fails we can still get a list of the Types it was able to resolve var types = ex.Types ?? new Type[] { }; return types.Where(t => t != null).ToList(); } catch (Exception ex) { Logger.ErrorException("Error loading types from assembly", ex); return new List(); } } private CertificateInfo CertificateInfo { get; set; } private ICertificate Certificate { get; set; } private IEnumerable GetUrlPrefixes() { var hosts = new List(); hosts.Add("+"); return hosts.SelectMany(i => { var prefixes = new List { "http://"+i+":" + HttpPort + "/" }; if (CertificateInfo != null) { prefixes.Add("https://" + i + ":" + HttpsPort + "/"); } return prefixes; }); } /// /// Starts the server. /// private void StartServer() { try { ServerManager.Start(GetUrlPrefixes()); return; } catch (Exception ex) { Logger.ErrorException("Error starting http server", ex); if (HttpPort == ServerConfiguration.DefaultHttpPort) { throw; } } HttpPort = ServerConfiguration.DefaultHttpPort; try { ServerManager.Start(GetUrlPrefixes()); } catch (Exception ex) { Logger.ErrorException("Error starting http server", ex); throw; } } private CertificateInfo GetCertificateInfo(bool generateCertificate) { if (!string.IsNullOrWhiteSpace(ServerConfigurationManager.Configuration.CertificatePath)) { // Custom cert return new CertificateInfo { Path = ServerConfigurationManager.Configuration.CertificatePath, Password = ServerConfigurationManager.Configuration.CertificatePassword }; } // Generate self-signed cert var certHost = GetHostnameFromExternalDns(ServerConfigurationManager.Configuration.WanDdns); var certPath = Path.Combine(ServerConfigurationManager.ApplicationPaths.ProgramDataPath, "ssl", "cert_" + (certHost + "2").GetMD5().ToString("N") + ".pfx"); var password = "embycert"; if (generateCertificate) { if (!FileSystemManager.FileExists(certPath)) { FileSystemManager.CreateDirectory(FileSystemManager.GetDirectoryName(certPath)); try { _certificateGenerator(certPath, certHost, password); } catch (Exception ex) { Logger.ErrorException("Error creating ssl cert", ex); return null; } } } return new CertificateInfo { Path = certPath, Password = password }; } /// /// Called when [configuration updated]. /// /// The sender. /// The instance containing the event data. protected void OnConfigurationUpdated(object sender, EventArgs e) { ConfigureAutorun(); var requiresRestart = false; // Don't do anything if these haven't been set yet if (HttpPort != 0 && HttpsPort != 0) { // Need to restart if ports have changed if (ServerConfigurationManager.Configuration.HttpServerPortNumber != HttpPort || ServerConfigurationManager.Configuration.HttpsPortNumber != HttpsPort) { if (ServerConfigurationManager.Configuration.IsPortAuthorized) { ServerConfigurationManager.Configuration.IsPortAuthorized = false; ServerConfigurationManager.SaveConfiguration(); requiresRestart = true; } } } if (!HttpServer.UrlPrefixes.SequenceEqual(GetUrlPrefixes(), StringComparer.OrdinalIgnoreCase)) { requiresRestart = true; } var currentCertPath = CertificateInfo == null ? null : CertificateInfo.Path; var newCertInfo = GetCertificateInfo(false); var newCertPath = newCertInfo == null ? null : newCertInfo.Path; if (!string.Equals(currentCertPath, newCertPath, StringComparison.OrdinalIgnoreCase)) { requiresRestart = true; } if (requiresRestart) { Logger.Info("App needs to be restarted due to configuration change."); NotifyPendingRestart(); } } /// /// Notifies that the kernel that a change has been made that requires a restart /// public void NotifyPendingRestart() { Logger.Info("App needs to be restarted."); var changed = !HasPendingRestart; HasPendingRestart = true; if (changed) { EventHelper.QueueEventIfNotNull(HasPendingRestartChanged, this, EventArgs.Empty, Logger); } } /// /// Restarts this instance. /// public async Task Restart() { if (!CanSelfRestart) { throw new PlatformNotSupportedException("The server is unable to self-restart. Please restart manually."); } try { await SessionManager.SendServerRestartNotification(CancellationToken.None).ConfigureAwait(false); } catch (Exception ex) { Logger.ErrorException("Error sending server restart notification", ex); } Logger.Info("Calling RestartInternal"); RestartInternal(); } protected abstract void RestartInternal(); /// /// Gets the composable part assemblies. /// /// IEnumerable{Assembly}. protected IEnumerable GetComposablePartAssemblies() { var list = GetPluginAssemblies() .ToList(); // Gets all plugin assemblies by first reading all bytes of the .dll and calling Assembly.Load against that // This will prevent the .dll file from getting locked, and allow us to replace it when needed // Include composable parts in the Api assembly list.Add(GetAssembly(typeof(ApiEntryPoint))); // Include composable parts in the Dashboard assembly list.Add(GetAssembly(typeof(DashboardService))); // Include composable parts in the Model assembly list.Add(GetAssembly(typeof(SystemInfo))); // Include composable parts in the Common assembly list.Add(GetAssembly(typeof(IApplicationHost))); // Include composable parts in the Controller assembly list.Add(GetAssembly(typeof(IServerApplicationHost))); // Include composable parts in the Providers assembly list.Add(GetAssembly(typeof(ProviderUtils))); // Include composable parts in the Photos assembly list.Add(GetAssembly(typeof(PhotoProvider))); // Emby.Server implementations list.Add(GetAssembly(typeof(InstallationManager))); // MediaEncoding list.Add(GetAssembly(typeof(MediaEncoding.Encoder.MediaEncoder))); // Dlna list.Add(GetAssembly(typeof(DlnaEntryPoint))); // Local metadata list.Add(GetAssembly(typeof(BoxSetXmlSaver))); // Xbmc list.Add(GetAssembly(typeof(ArtistNfoProvider))); list.AddRange(GetAssembliesWithPartsInternal()); return list.ToList(); } protected abstract List GetAssembliesWithPartsInternal(); /// /// Gets the plugin assemblies. /// /// IEnumerable{Assembly}. private IEnumerable GetPluginAssemblies() { try { return Directory.EnumerateFiles(ApplicationPaths.PluginsPath, "*.dll", SearchOption.TopDirectoryOnly) .Where(EnablePlugin) .Select(LoadAssembly) .Where(a => a != null) .ToList(); } catch (DirectoryNotFoundException) { return new List(); } } private bool EnablePlugin(string path) { var filename = Path.GetFileName(path); var exclude = new[] { "mbplus.dll", "mbintros.dll", "embytv.dll" }; return !exclude.Contains(filename ?? string.Empty, StringComparer.OrdinalIgnoreCase); } /// /// Gets the system status. /// /// SystemInfo. public async Task GetSystemInfo() { var localAddress = await GetLocalApiUrl().ConfigureAwait(false); return new SystemInfo { HasPendingRestart = HasPendingRestart, Version = ApplicationVersion.ToString(), WebSocketPortNumber = HttpPort, FailedPluginAssemblies = FailedAssemblies.ToList(), InProgressInstallations = InstallationManager.CurrentInstallations.Select(i => i.Item1).ToList(), CompletedInstallations = InstallationManager.CompletedInstallations.ToList(), Id = SystemId, ProgramDataPath = ApplicationPaths.ProgramDataPath, LogPath = ApplicationPaths.LogDirectoryPath, ItemsByNamePath = ApplicationPaths.ItemsByNamePath, InternalMetadataPath = ApplicationPaths.InternalMetadataPath, CachePath = ApplicationPaths.CachePath, MacAddress = GetMacAddress(), HttpServerPortNumber = HttpPort, SupportsHttps = SupportsHttps, HttpsPortNumber = HttpsPort, OperatingSystem = EnvironmentInfo.OperatingSystem.ToString(), OperatingSystemDisplayName = OperatingSystemDisplayName, CanSelfRestart = CanSelfRestart, CanSelfUpdate = CanSelfUpdate, WanAddress = ConnectManager.WanApiAddress, HasUpdateAvailable = HasUpdateAvailable, SupportsAutoRunAtStartup = SupportsAutoRunAtStartup, TranscodingTempPath = ApplicationPaths.TranscodingTempPath, SupportsRunningAsService = SupportsRunningAsService, ServerName = FriendlyName, LocalAddress = localAddress, SupportsLibraryMonitor = true, EncoderLocationType = MediaEncoder.EncoderLocationType, SystemArchitecture = EnvironmentInfo.SystemArchitecture, SystemUpdateLevel = SystemUpdateLevel, PackageName = StartupOptions.GetOption("-package") }; } public bool EnableHttps { get { return SupportsHttps && ServerConfigurationManager.Configuration.EnableHttps; } } public bool SupportsHttps { get { return Certificate != null; } } public async Task GetLocalApiUrl() { try { // Return the first matched address, if found, or the first known local address var address = (await GetLocalIpAddresses().ConfigureAwait(false)).FirstOrDefault(i => !i.Equals(IpAddressInfo.Loopback) && !i.Equals(IpAddressInfo.IPv6Loopback)); if (address != null) { return GetLocalApiUrl(address); } return null; } catch (Exception ex) { Logger.ErrorException("Error getting local Ip address information", ex); } return null; } public string GetLocalApiUrl(IpAddressInfo ipAddress) { if (ipAddress.AddressFamily == IpAddressFamily.InterNetworkV6) { return GetLocalApiUrl("[" + ipAddress.Address + "]"); } return GetLocalApiUrl(ipAddress.Address); } public string GetLocalApiUrl(string host) { return string.Format("http://{0}:{1}", host, HttpPort.ToString(CultureInfo.InvariantCulture)); } public async Task> GetLocalIpAddresses() { var addresses = ServerConfigurationManager .Configuration .LocalNetworkAddresses .Select(NormalizeConfiguredLocalAddress) .Where(i => i != null) .ToList(); if (addresses.Count == 0) { addresses.AddRange(NetworkManager.GetLocalIpAddresses()); var list = new List(); foreach (var address in addresses) { var valid = await IsIpAddressValidAsync(address).ConfigureAwait(false); if (valid) { list.Add(address); } } addresses = list; } return addresses; } private IpAddressInfo NormalizeConfiguredLocalAddress(string address) { var index = address.Trim('/').IndexOf('/'); if (index != -1) { address = address.Substring(index + 1); } IpAddressInfo result; if (NetworkManager.TryParseIpAddress(address.Trim('/'), out result)) { return result; } return null; } private readonly ConcurrentDictionary _validAddressResults = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); private DateTime _lastAddressCacheClear; private async Task IsIpAddressValidAsync(IpAddressInfo address) { if (address.Equals(IpAddressInfo.Loopback) || address.Equals(IpAddressInfo.IPv6Loopback)) { return true; } var apiUrl = GetLocalApiUrl(address); apiUrl += "/system/ping"; if ((DateTime.UtcNow - _lastAddressCacheClear).TotalMinutes >= 15) { _lastAddressCacheClear = DateTime.UtcNow; _validAddressResults.Clear(); } bool cachedResult; if (_validAddressResults.TryGetValue(apiUrl, out cachedResult)) { return cachedResult; } try { using (var response = await HttpClient.SendAsync(new HttpRequestOptions { Url = apiUrl, LogErrorResponseBody = false, LogErrors = false, LogRequest = false, TimeoutMs = 30000, BufferContent = false }, "POST").ConfigureAwait(false)) { using (var reader = new StreamReader(response.Content)) { var result = reader.ReadToEnd(); var valid = string.Equals(Name, result, StringComparison.OrdinalIgnoreCase); _validAddressResults.AddOrUpdate(apiUrl, valid, (k, v) => valid); //Logger.Debug("Ping test result to {0}. Success: {1}", apiUrl, valid); return valid; } } } catch { //Logger.Debug("Ping test result to {0}. Success: {1}", apiUrl, false); _validAddressResults.AddOrUpdate(apiUrl, false, (k, v) => false); return false; } } public string FriendlyName { get { return string.IsNullOrWhiteSpace(ServerConfigurationManager.Configuration.ServerName) ? Environment.MachineName : ServerConfigurationManager.Configuration.ServerName; } } public int HttpPort { get; private set; } public int HttpsPort { get; private set; } /// /// Gets the mac address. /// /// System.String. private string GetMacAddress() { try { return NetworkManager.GetMacAddress(); } catch (Exception ex) { Logger.ErrorException("Error getting mac address", ex); return null; } } /// /// Shuts down. /// public async Task Shutdown() { try { await SessionManager.SendServerShutdownNotification(CancellationToken.None).ConfigureAwait(false); } catch (Exception ex) { Logger.ErrorException("Error sending server shutdown notification", ex); } ShutdownInternal(); } protected abstract void ShutdownInternal(); /// /// Registers the server with administrator access. /// private void RegisterServerWithAdministratorAccess() { Logger.Info("Requesting administrative access to authorize http server"); try { AuthorizeServer(); } catch (NotImplementedException) { } catch (Exception ex) { Logger.ErrorException("Error authorizing server", ex); } } protected virtual void AuthorizeServer() { throw new NotImplementedException(); } public event EventHandler HasUpdateAvailableChanged; private bool _hasUpdateAvailable; public bool HasUpdateAvailable { get { return _hasUpdateAvailable; } set { var fireEvent = value && !_hasUpdateAvailable; _hasUpdateAvailable = value; if (fireEvent) { EventHelper.FireEventIfNotNull(HasUpdateAvailableChanged, this, EventArgs.Empty, Logger); } } } /// /// Removes the plugin. /// /// The plugin. public void RemovePlugin(IPlugin plugin) { var list = Plugins.ToList(); list.Remove(plugin); Plugins = list.ToArray(); } /// /// Checks for update. /// /// The cancellation token. /// The progress. /// Task{CheckForUpdateResult}. public async Task CheckForApplicationUpdate(CancellationToken cancellationToken, IProgress progress) { var cacheLength = TimeSpan.FromHours(1); var updateLevel = SystemUpdateLevel; if (updateLevel != PackageVersionClass.Release) { cacheLength = TimeSpan.FromMinutes(5); } var result = await new GithubUpdater(HttpClient, JsonSerializer).CheckForUpdateResult("MediaBrowser", "Emby", ApplicationVersion, updateLevel, _releaseAssetFilename, "MBServer", "Mbserver.zip", cacheLength, cancellationToken).ConfigureAwait(false); HasUpdateAvailable = result.IsUpdateAvailable; return result; } /// /// Updates the application. /// /// The package that contains the update /// The cancellation token. /// The progress. public async Task UpdateApplication(PackageVersionInfo package, CancellationToken cancellationToken, IProgress progress) { await InstallationManager.InstallPackage(package, false, progress, cancellationToken).ConfigureAwait(false); HasUpdateAvailable = false; OnApplicationUpdated(package); } /// /// Configures the automatic run at startup. /// /// if set to true [autorun]. protected void ConfigureAutoRunAtStartup(bool autorun) { if (SupportsAutoRunAtStartup) { ConfigureAutoRunInternal(autorun); } } protected virtual void ConfigureAutoRunInternal(bool autorun) { throw new NotImplementedException(); } /// /// This returns localhost in the case of no external dns, and the hostname if the /// dns is prefixed with a valid Uri prefix. /// /// The external dns prefix to get the hostname of. /// The hostname in private static string GetHostnameFromExternalDns(string externalDns) { if (string.IsNullOrWhiteSpace(externalDns)) { return "localhost"; } try { return new Uri(externalDns).Host; } catch { return externalDns; } } public void LaunchUrl(string url) { if (EnvironmentInfo.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows && EnvironmentInfo.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.OSX) { throw new NotImplementedException(); } var process = ProcessFactory.Create(new ProcessOptions { FileName = url, //EnableRaisingEvents = true, UseShellExecute = true, ErrorDialog = false }); process.Exited += ProcessExited; try { process.Start(); } catch (Exception ex) { Console.WriteLine("Error launching url: {0}", url); Logger.ErrorException("Error launching url: {0}", ex, url); throw; } } private static void ProcessExited(object sender, EventArgs e) { ((IProcess)sender).Dispose(); } public virtual void EnableLoopback(string appName) { } private void RegisterModules() { var moduleTypes = GetExportTypes(); foreach (var type in moduleTypes) { try { var instance = Activator.CreateInstance(type) as IDependencyModule; if (instance != null) instance.BindDependencies(this); } catch (Exception ex) { Logger.ErrorException("Error setting up dependency bindings for " + type.Name, ex); } } } /// /// Called when [application updated]. /// /// The package. protected void OnApplicationUpdated(PackageVersionInfo package) { Logger.Info("Application has been updated to version {0}", package.versionStr); EventHelper.FireEventIfNotNull(ApplicationUpdated, this, new GenericEventArgs { Argument = package }, Logger); NotifyPendingRestart(); } /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { Dispose(true); } /// /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool dispose) { if (dispose) { var type = GetType(); Logger.Info("Disposing " + type.Name); var parts = DisposableParts.Distinct().Where(i => i.GetType() != type).ToList(); DisposableParts.Clear(); foreach (var part in parts) { Logger.Info("Disposing " + part.GetType().Name); try { part.Dispose(); } catch (Exception ex) { Logger.ErrorException("Error disposing {0}", ex, part.GetType().Name); } } } } void IDependencyContainer.RegisterSingleInstance(T obj, bool manageLifetime) { RegisterSingleInstance(obj, manageLifetime); } void IDependencyContainer.RegisterSingleInstance(Func func) { RegisterSingleInstance(func); } void IDependencyContainer.Register(Type typeInterface, Type typeImplementation) { Container.Register(typeInterface, typeImplementation); } } internal class CertificateInfo { public string Path { get; set; } public string Password { get; set; } } }