using System.Net; using System.Security; using System.Text.Json; using AcDream.Launcher.Core.Orchestration; using AcDream.Launcher.Core.Updates; using AcDream.Launcher.ViewModels; using AcDream.Platform; namespace AcDream.Launcher; /// /// Testable startup transaction for versioned-client/update services. Storage /// failures produce a fail-closed executable resolver and an unavailable UI /// projection; they do not abort profile/installer window construction. /// internal sealed class LauncherUpdateComposition : IDisposable { private readonly HttpClient? _artifactClient; private readonly ReleaseManifestClient? _manifestClient; private LauncherUpdateComposition( ClientVersionStore versions, LauncherExecutableSet executables, ILauncherUpdater updater, HttpClient? artifactClient, ReleaseManifestClient? manifestClient) { Versions = versions; Executables = executables; Updater = updater; _artifactClient = artifactClient; _manifestClient = manifestClient; } public ClientVersionStore Versions { get; } public LauncherExecutableSet Executables { get; } public ILauncherUpdater Updater { get; } public static LauncherUpdateComposition Create( ApplicationPathSet paths, string rid, LauncherVersion launcherVersion, string launcherTargetDirectory, Func hasRunningSessions, Func? initialize = null) { ArgumentNullException.ThrowIfNull(paths); ArgumentNullException.ThrowIfNull(launcherVersion); ArgumentNullException.ThrowIfNull(hasRunningSessions); var versions = new ClientVersionStore(paths); HttpClient? artifactClient = null; ReleaseManifestClient? manifestClient = null; try { _ = initialize is null ? versions.LoadAndRecoverAsync(rid).GetAwaiter().GetResult() : initialize(versions, rid); artifactClient = new HttpClient( new HttpClientHandler { AllowAutoRedirect = false, UseCookies = false, AutomaticDecompression = DecompressionMethods.None, }, disposeHandler: true) { Timeout = TimeSpan.FromSeconds(15), }; artifactClient.DefaultRequestHeaders.UserAgent.ParseAdd("acdream-launcher/1"); manifestClient = new ReleaseManifestClient(TimeSpan.FromSeconds(15)); var selfUpdates = new LauncherSelfUpdateManager(paths, artifactClient); var updater = new LauncherUpdater( manifestClient, artifactClient, versions, selfUpdates, launcherVersion, rid, launcherTargetDirectory, hasRunningSessions); return new LauncherUpdateComposition( versions, LauncherExecutableSet.FromCurrentVersionStore(versions), updater, artifactClient, manifestClient); } catch (Exception ex) when (IsStorageFailure(ex)) { manifestClient?.Dispose(); artifactClient?.Dispose(); string status = "Versioned client update storage is unavailable: " + (string.IsNullOrWhiteSpace(ex.Message) ? "the storage operation failed." : ex.Message); var resolution = new ClientVersionResolution( ClientVersionState.Invalid, status, null, null, null, null); return new LauncherUpdateComposition( versions, LauncherExecutableSet.Unavailable(status), new UnavailableLauncherUpdater(status, resolution), artifactClient: null, manifestClient: null); } } public void Dispose() { _manifestClient?.Dispose(); _artifactClient?.Dispose(); } private static bool IsStorageFailure(Exception exception) => exception is IOException or UnauthorizedAccessException or SecurityException or JsonException or FormatException or NotSupportedException or LauncherUpdateException; }