Files

94 lines
4.8 KiB
C#

using System;
using System.Text.RegularExpressions;
using FluentTerminal.Models;
namespace FluentTerminal.App.Services.Implementation
{
// The purpose of this class is to ensure backward compatibility of mosh profiles and command history
// TODO: This class should be probably deleted in few months or so
public static class MoshBackwardCompatibility
{
private static readonly string MoshCommandExe = $"{Constants.MoshCommandName}.exe";
private static readonly Regex OldFormatRx = new Regex(@".+\s(?<ports>\d{1,5}:\d{1,5})$", RegexOptions.Compiled);
private static readonly Regex TargetRx =
new Regex(@"(^|\s+)(?<user>[^\s@;]+)@(?<host>[^\s@:]+)(:(?<port>\d{1,5}))?\s+", RegexOptions.Compiled);
private static bool IsMoshProfile(ShellProfile profile) =>
Constants.MoshCommandName.Equals(profile.Location, StringComparison.OrdinalIgnoreCase) ||
MoshCommandExe.Equals(profile.Location, StringComparison.OrdinalIgnoreCase);
/// <summary>
/// Checks if the profile contains obsolete mosh syntax, and changes it if it does.
/// </summary>
/// <param name="profile">The profile. Note that the method doesn't change the input profile at all, but creates and
/// returns its clone if there's something to change.</param>
/// <remarks>
/// The method actually checks if <see cref="ShellProfile.Arguments"/> of the input <paramref name="profile"/> is in
/// obsolete <c>mosh</c> format <c>[ssh_arguments] [target] [mosh_port_range]</c>, and if it is changes it to the new
/// format <c>--ssh="[ssh_arguments]" -p [mosh_ports] [target]</c>.
/// </remarks>
/// <returns>
/// If the method detected that the input profile is obsolete mosh profile, it will create its clone with the updated
/// command line arguments, and return it. Otherwise it will simply return unchanged input profile (the same reference
/// as the input profile). It allows us to simply check if anything is changed by comparing the input and output
/// profiles for reference equality.
/// </returns>
public static ShellProfile FixProfile(ShellProfile profile)
{
// If there aren't any arguments, or if the profile isn't a mosh profile, there's nothing to do. Just return.
if (string.IsNullOrWhiteSpace(profile.Arguments) || !IsMoshProfile(profile)) return profile;
var arguments = profile.Arguments.Trim();
var moshPortsMatch = OldFormatRx.Match(arguments);
if (!moshPortsMatch.Success) return profile;// Arguments don't end with mosh ports (i.e. 60000:61000), so it isn't an obsolete format.
var targetMatches = TargetRx.Matches(arguments);
if (targetMatches.Count != 1) return profile;// Target part (user@host:port) not found or found multiple times. Cannot reliably determine the target, so don't change.
var targetMatch = targetMatches[0];
// Removing [mosh_ports] from the original arguments
arguments = arguments.Substring(0, arguments.Length - moshPortsMatch.Groups["ports"].Length).Trim();
var newProfile = profile.Clone();
// Removing [target] from the original arguments, thus getting "pure" ssh arguments
var sshArguments = targetMatch.Index > 0 ? arguments.Substring(0, targetMatch.Index).Trim() : string.Empty;
if (targetMatch.Index + targetMatch.Length < arguments.Length)
{
sshArguments += $" {arguments.Substring(targetMatch.Index + targetMatch.Length).Trim()}";
}
// If the ssh port is defined in the original target (i.e. user@host:port), which was allowed in the obsolete mosh, extracting the port part into a separate ssh argument
if (targetMatch.Groups["port"].Success && uint.TryParse(targetMatch.Groups["port"].Value, out var port) &&
port != SshProfile.DefaultSshPort)
{
sshArguments += $" -p {targetMatch.Groups["port"].Value}";
}
// Replacing any double quotes in ssh arguments with single quotes, since ssh arguments will be double-quoted as a whole
sshArguments = sshArguments.Replace("\"", "'").Trim();
arguments = $"-p {moshPortsMatch.Groups["ports"].Value}";
if (!string.IsNullOrEmpty(sshArguments))
{
// If there were some ssh arguments, add them as defined in the new format
arguments += $" --ssh=\"ssh {sshArguments}\"";
}
// Finally append the target to the arguments
arguments += $" {targetMatch.Groups["user"]}@{targetMatch.Groups["host"]}";
newProfile.Arguments = arguments;
return newProfile;
}
}
}