using System.Runtime.Versioning;
using DodoSSH.Client.Session;
using Microsoft.Win32;
namespace DodoSSH.Client.App.Platform;
///
/// Registers this executable as the handler for dodossh:// links, for this user, on Windows.
///
///
///
/// Under HKCU\Software\Classes, because the install is per user. Velopack installs into the user's
/// own profile with no elevation, and a handler registered machine-wide would need the elevation the
/// installer deliberately never asks for. Windows merges this key into HKEY_CLASSES_ROOT for the
/// user, which is all a browser consults.
///
///
/// Written by Velopack's install and update hooks, removed by its uninstall hook — see
/// Program.Main. An update rewrites it because the first update to a build that has this is how an
/// existing installation gets it at all, and because it costs nothing to say again.
///
///
/// Removed only when it is still ours. The release and the nightly are separate installations that
/// both answer dodossh://, and the last one installed holds the key. Uninstalling the other must not
/// take the handler away from the one that is still there.
///
///
[SupportedOSPlatform("windows")]
internal static class WindowsUrlScheme
{
private const string KeyPath = @"Software\Classes\" + SpelunkLink.Scheme;
private const string CommandPath = @"shell\open\command";
/// Points dodossh:// at .
internal static void Register(string executable)
{
using var scheme = Registry.CurrentUser.CreateSubKey(KeyPath);
scheme.SetValue(string.Empty, "URL:DodoSSH");
// Empty, and its presence is the whole of it: this value is what makes the key a URL scheme rather
// than a file type.
scheme.SetValue("URL Protocol", string.Empty);
using (var icon = scheme.CreateSubKey("DefaultIcon"))
{
icon.SetValue(string.Empty, $"\"{executable}\",0");
}
using var command = scheme.CreateSubKey(CommandPath);
command.SetValue(string.Empty, CommandFor(executable));
}
/// Takes the handler away, if it is still 's.
internal static void Unregister(string executable)
{
string? registered;
using (var command = Registry.CurrentUser.OpenSubKey($@"{KeyPath}\{CommandPath}"))
{
registered = command?.GetValue(string.Empty) as string;
}
if (IsOurs(registered, executable))
{
Registry.CurrentUser.DeleteSubKeyTree(KeyPath, throwOnMissingSubKey: false);
}
}
/// The command Windows runs for a link: this executable, with the link as its one argument.
///
/// Both halves quoted. The path has spaces in it on any profile whose user name does, and the link is
/// handed over whole as %1 — which Windows quotes into one argument only because it is quoted
/// here. Program.Main then reads it back and checks it before anything acts on it.
///
internal static string CommandFor(string executable) => $"\"{executable}\" \"%1\"";
/// Whether a registered command is the one writes for this executable.
internal static bool IsOurs(string? registered, string executable) =>
string.Equals(registered, CommandFor(executable), StringComparison.OrdinalIgnoreCase);
}