The full path to ssh.exe and mosh.exe was being stored in SSH profiles. The path to mosh.exe includes FluentTerminal's version number causing SSH profiles to break after an upgrade. Instead, a token indicating SSH or Mosh is stored in the profile instead. This is resolved just before the shell is started. This change also meant that the infrastructure for the "GetMoshSshExecutable" system tray request is no longer required, so this has been removed.
222 lines
6.3 KiB
C#
222 lines
6.3 KiB
C#
using FluentTerminal.App.Services;
|
|
using FluentTerminal.App.Services.Utilities;
|
|
using FluentTerminal.App.ViewModels.Infrastructure;
|
|
using FluentTerminal.Models;
|
|
using FluentTerminal.Models.Enums;
|
|
using System;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace FluentTerminal.App.ViewModels
|
|
{
|
|
public class SshProfileViewModel : ShellProfileViewModel, ISshConnectionInfo
|
|
{
|
|
#region Fields
|
|
|
|
private readonly ITrayProcessCommunicationService _trayProcessCommunicationService;
|
|
|
|
#endregion Fields
|
|
|
|
#region Properties
|
|
|
|
private string _host;
|
|
|
|
public string Host
|
|
{
|
|
get => _host;
|
|
set => Set(ref _host, value);
|
|
}
|
|
|
|
private ushort _sshPort;
|
|
|
|
public ushort SshPort
|
|
{
|
|
get => _sshPort;
|
|
set => Set(ref _sshPort, value);
|
|
}
|
|
|
|
private string _username;
|
|
|
|
public string Username
|
|
{
|
|
get => _username;
|
|
set => Set(ref _username, value);
|
|
}
|
|
|
|
private string _identityFile;
|
|
|
|
public string IdentityFile
|
|
{
|
|
get => _identityFile;
|
|
set => Set(ref _identityFile, value);
|
|
}
|
|
|
|
private bool _useMosh;
|
|
|
|
public bool UseMosh
|
|
{
|
|
get => _useMosh;
|
|
set => Set(ref _useMosh, value);
|
|
}
|
|
|
|
private ushort _moshPortFrom;
|
|
|
|
public ushort MoshPortFrom
|
|
{
|
|
get => _moshPortFrom;
|
|
set => Set(ref _moshPortFrom, value);
|
|
}
|
|
|
|
private ushort _moshPortTo;
|
|
|
|
public ushort MoshPortTo
|
|
{
|
|
get => _moshPortTo;
|
|
set => Set(ref _moshPortTo, value);
|
|
}
|
|
|
|
#endregion Properties
|
|
|
|
#region Commands
|
|
|
|
public IAsyncCommand BrowseForIdentityFileCommand { get; }
|
|
|
|
#endregion Commands
|
|
|
|
#region Constructor
|
|
|
|
public SshProfileViewModel(SshProfile sshProfile, ISettingsService settingsService,
|
|
IDialogService dialogService, IFileSystemService fileSystemService, IApplicationView applicationView,
|
|
IDefaultValueProvider defaultValueProvider,
|
|
ITrayProcessCommunicationService trayProcessCommunicationService, bool isNew) : base(
|
|
sshProfile ?? new SshProfile(), settingsService, dialogService, fileSystemService, applicationView,
|
|
defaultValueProvider, isNew)
|
|
{
|
|
_trayProcessCommunicationService = trayProcessCommunicationService;
|
|
|
|
InitializeViewModelPropertiesPrivate(sshProfile ?? new SshProfile());
|
|
|
|
BrowseForIdentityFileCommand = new AsyncCommand(BrowseForIdentityFile);
|
|
}
|
|
|
|
#endregion Constructor
|
|
|
|
#region Methods
|
|
|
|
// Fills the remaining view model properties (those which aren't filled by the base method) from the input sshProfile
|
|
private void InitializeViewModelPropertiesPrivate(SshProfile sshProfile)
|
|
{
|
|
Host = sshProfile.Host;
|
|
SshPort = sshProfile.SshPort;
|
|
|
|
Username = sshProfile.Username;
|
|
|
|
if (string.IsNullOrEmpty(Username))
|
|
{
|
|
_trayProcessCommunicationService.GetUserName().ContinueWith(t =>
|
|
{
|
|
if (string.IsNullOrEmpty(Username))
|
|
{
|
|
Username = t.Result;
|
|
}
|
|
}, TaskContinuationOptions.OnlyOnRanToCompletion);
|
|
}
|
|
|
|
IdentityFile = sshProfile.IdentityFile;
|
|
UseMosh = sshProfile.UseMosh;
|
|
MoshPortFrom = sshProfile.MoshPortFrom;
|
|
MoshPortTo = sshProfile.MoshPortTo;
|
|
}
|
|
|
|
protected override void InitializeViewModelProperties(ShellProfile sshProfile)
|
|
{
|
|
base.InitializeViewModelProperties(sshProfile);
|
|
|
|
InitializeViewModelPropertiesPrivate((SshProfile)sshProfile);
|
|
}
|
|
|
|
protected override async Task FillProfileAsync(ShellProfile profile)
|
|
{
|
|
await base.FillProfileAsync(profile);
|
|
|
|
profile.Location = _useMosh ? ShellLocation.Mosh : ShellLocation.SSH;
|
|
profile.Arguments = GetArgumentsString();
|
|
profile.WorkingDirectory = null;
|
|
|
|
var sshProfile = (SshProfile)profile;
|
|
|
|
sshProfile.Host = Host;
|
|
sshProfile.SshPort = SshPort;
|
|
sshProfile.Username = Username;
|
|
sshProfile.IdentityFile = IdentityFile;
|
|
sshProfile.UseMosh = UseMosh;
|
|
sshProfile.MoshPortFrom = MoshPortFrom;
|
|
sshProfile.MoshPortTo = MoshPortTo;
|
|
}
|
|
|
|
public virtual Task AcceptChangesAsync() => FillProfileAsync(Model);
|
|
|
|
protected override async Task<ShellProfile> CreateProfileAsync()
|
|
{
|
|
var profile = new SshProfile();
|
|
|
|
await FillProfileAsync(profile);
|
|
|
|
return profile;
|
|
}
|
|
|
|
private string GetArgumentsString()
|
|
{
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
|
|
if (_sshPort != SshProfile.DefaultSshPort)
|
|
sb.Append($"-p {_sshPort:#####} ");
|
|
|
|
if (!string.IsNullOrEmpty(_identityFile))
|
|
sb.Append($"-i \"{_identityFile}\" ");
|
|
|
|
sb.Append($"{_username}@{_host}");
|
|
|
|
if (_useMosh)
|
|
sb.Append($" {_moshPortFrom}:{_moshPortTo}");
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
public override async Task SaveChangesAsync()
|
|
{
|
|
var result = Validate();
|
|
|
|
if (result != SshConnectionInfoValidationResult.Valid)
|
|
{
|
|
await DialogService.ShowMessageDialogAsnyc(I18N.Translate("InvalidInput"),
|
|
result.GetErrorString(Environment.NewLine), DialogButton.OK);
|
|
|
|
return;
|
|
}
|
|
|
|
await AcceptChangesAsync();
|
|
|
|
SettingsService.SaveSshProfile((SshProfile) Model, IsNew);
|
|
|
|
KeyBindings.Editable = false;
|
|
InEditMode = false;
|
|
IsNew = false;
|
|
}
|
|
|
|
private async Task BrowseForIdentityFile()
|
|
{
|
|
var file = await FileSystemService.OpenFile(new[] { "*" }).ConfigureAwait(true);
|
|
if (file != null)
|
|
{
|
|
IdentityFile = file.Path;
|
|
}
|
|
}
|
|
|
|
public SshConnectionInfoValidationResult Validate(bool allowNoUser = false) =>
|
|
this.GetValidationResult(allowNoUser);
|
|
|
|
#endregion Methods
|
|
}
|
|
} |