Table of Contents

Class AppUpdateCoordinator

Namespace
McServerLauncher.Services
Assembly
McServerLauncher.dll

Gets an update's package onto disk and proves it is the one the publisher made, in the order that makes a refusal cheap: everything that can say "no" is asked for before the download.

public sealed class AppUpdateCoordinator
Inheritance
AppUpdateCoordinator
Inherited Members

Remarks

This is the part of updating that decides whether the package is installed, taken out of MainViewModel so it can be read — and tested — on its own. What stays in the view model is what is about the screen: the text in the banner, stopping the servers, the fallback to the release page. What is here never touches either.

The order is the design. The checksum and the signature are fetched first, so a release that lacks either is refused without costing the transfer of a ~35 MB installer. The checksum proves the download is whole. It comes from the same release as the package, so it proves nothing about who made it; the signature, checked against keys that do not live on GitHub, is what does, and it is just as mandatory (see UpdateSignature). A package that fails either check is deleted, so the next step cannot find it lying there.

None of the awaits here uses ConfigureAwait(false) on purpose: onPhase is called back on the thread that started the update, which is the one allowed to change the banner.

Constructors

AppUpdateCoordinator(IUpdateSource?, IReadOnlyList<string>?)

public AppUpdateCoordinator(IUpdateSource? source = null, IReadOnlyList<string>? trustedKeys = null)

Parameters

source IUpdateSource

Where the files come from. The GitHub release by default.

trustedKeys IReadOnlyList<string>

The public keys whose signature is accepted; the built-in ones when null. For tests: the real ones have no private half in the repository.

Methods

DiscardFolder(string)

Removes a folder an update downloaded into. Never throws: whatever stays is cleared at a later start (see DeleteOldPackages(string, DateTime)).

public static void DiscardFolder(string folder)

Parameters

folder string

DownloadAndVerifyAsync(UpdateInfo, string, Action<Phase>?, CancellationToken)

Downloads the package of offer into folder and returns it, with the checksum it matched, once the checksum and the signature both hold.

public Task<AppUpdateCoordinator.VerifiedPackage> DownloadAndVerifyAsync(UpdateService.UpdateInfo offer, string folder, Action<AppUpdateCoordinator.Phase>? onPhase = null, CancellationToken ct = default)

Parameters

offer UpdateService.UpdateInfo

The release being installed.

folder string

Where the package is saved; see NewPackageFolder().

onPhase Action<AppUpdateCoordinator.Phase>

Told when each step starts. Called on the caller's context.

ct CancellationToken

Cancels the download.

Returns

Task<AppUpdateCoordinator.VerifiedPackage>

Exceptions

UserFacingException

With a message for the user: the release has no readable checksum or signature, the download does not match the checksum, or the signature is not the publisher's.

NewPackageFolder()

A new folder name for one update to download into.

public static string NewPackageFolder()

Returns

string

Remarks

Random on every run: a fixed name in the temporary folder could be planted, or replaced, by another local process between the download and the moment the package runs. The folder is not created here; AtomicDownload creates it.