using System; using System.IO; using System.Linq; using TweetLib.Core.Application; using TweetLib.Core.Features; using TweetLib.Core.Features.Plugins; using TweetLib.Core.Resources; using TweetLib.Core.Systems.Configuration; using TweetLib.Core.Systems.Logging; using TweetLib.Utils.Static; using Version = TweetDuck.Version; namespace TweetLib.Core { public static class App { private static IAppSetup Setup { get; } = Validate(Builder.Setup, nameof(Builder.Setup)); public static readonly string ProgramPath = AppDomain.CurrentDomain.BaseDirectory; public static readonly bool IsPortable = Setup.IsPortable; public static readonly string ResourcesPath = Path.Combine(ProgramPath, "resources"); public static readonly string PluginPath = Path.Combine(ProgramPath, "plugins"); public static readonly string GuidePath = Path.Combine(ProgramPath, "guide"); public static readonly string StoragePath = IsPortable ? Path.Combine(ProgramPath, "portable", "storage") : GetDataFolder(); public static Logger Logger { get; } = new (Path.Combine(StoragePath, "TD_Log.txt"), Setup.IsDebugLogging); public static ConfigManager ConfigManager { get; } = Setup.CreateConfigManager(StoragePath); public static IAppErrorHandler ErrorHandler { get; } = Validate(Builder.ErrorHandler, nameof(Builder.ErrorHandler)); public static IAppSystemHandler SystemHandler { get; } = Validate(Builder.SystemHandler, nameof(Builder.SystemHandler)); public static IAppMessageDialogs MessageDialogs { get; } = Validate(Builder.MessageDialogs, nameof(Builder.MessageDialogs)); public static IAppFileDialogs? FileDialogs { get; } = Builder.FileDialogs; internal static IAppUserConfiguration UserConfiguration => ConfigManager.User; private static string GetDataFolder() { string? custom = Setup.CustomDataFolder; if (custom != null && (custom.Contains(Path.DirectorySeparatorChar) || custom.Contains(Path.AltDirectorySeparatorChar))) { if (Path.GetInvalidPathChars().Any(custom.Contains)) { throw new AppException("Data Folder Invalid", "The data folder contains invalid characters:\n" + custom); } else if (!Path.IsPathRooted(custom)) { throw new AppException("Data Folder Invalid", "The data folder has to be either a simple folder name, or a full path:\n" + custom); } return Environment.ExpandEnvironmentVariables(custom); } else { return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), custom ?? Lib.BrandName); } } internal static void Launch() { if (!FileUtils.CheckFolderWritePermission(StoragePath)) { throw new AppException("Permission Error", "TweetDuck does not have write permissions to the storage folder: " + StoragePath); } if (!Setup.TryLockDataFolder(Path.Combine(StoragePath, ".lock"))) { return; } ConfigManager.LoadAll(); Setup.BeforeLaunch(); var resourceRewriteRules = Setup.ResourceRewriteRules; if (resourceRewriteRules != null) { try { BaseResourceRequestHandler.LoadResourceRewriteRules(resourceRewriteRules); } catch (Exception e) { throw new AppException("Resource Freeze", "Error parsing resource rewrite rules: " + e.Message); } } WebUtils.DefaultUserAgent = Lib.BrandName + " " + Version.Tag; if (UserConfiguration.UseSystemProxyForAllConnections) { WebUtils.EnableSystemProxy(); } var resourceCache = new ResourceCache(); var pluginManager = new PluginManager(ConfigManager.Plugins, PluginPath, Path.Combine(StoragePath, "TD_Plugins")); Setup.Launch(resourceCache, pluginManager); } public static void Close() { ConfigManager.SaveAll(); } // Setup private static AppBuilder Builder => AppBuilder.Instance ?? throw new InvalidOperationException("App is initializing too early"); private static bool isInitialized = false; internal static void Initialize() { if (isInitialized) { throw new InvalidOperationException("App is already initialized"); } isInitialized = true; } private static T Validate<T>(T? obj, string name) where T : class { return obj ?? throw new InvalidOperationException("Missing property " + name + " on the provided App"); } } public sealed class AppBuilder { public IAppSetup? Setup { get; set; } public IAppErrorHandler? ErrorHandler { get; set; } public IAppSystemHandler? SystemHandler { get; set; } public IAppMessageDialogs? MessageDialogs { get; set; } public IAppFileDialogs? FileDialogs { get; set; } internal static AppBuilder? Instance { get; private set; } internal Lib.AppLauncher Build() { Instance = this; App.Initialize(); Instance = null; return App.Launch; } } }