From ed2e2829ca5261995edcc360a2dee8c3e9ec9507 Mon Sep 17 00:00:00 2001 From: BornToBeRoot <16019165+BornToBeRoot@users.noreply.github.com> Date: Sun, 22 Dec 2024 00:29:14 +0100 Subject: [PATCH 1/8] Feature: Find app path --- .../PowerShell/PowerShell.cs | 49 ++++---- Source/NETworkManager.Models/PuTTY/PuTTY.cs | 106 ++++++++---------- .../ApplicationHelper.cs | 34 ++++++ .../ExternalProcessStarter.cs | 13 +-- Source/NETworkManager/MainWindow.xaml.cs | 15 +-- .../AWSSessionManagerHostViewModel.cs | 27 +---- .../ViewModels/PowerShellHostViewModel.cs | 25 ----- .../ViewModels/PuTTYHostViewModel.cs | 81 ++++++------- 8 files changed, 156 insertions(+), 194 deletions(-) create mode 100644 Source/NETworkManager.Utilities/ApplicationHelper.cs diff --git a/Source/NETworkManager.Models/PowerShell/PowerShell.cs b/Source/NETworkManager.Models/PowerShell/PowerShell.cs index b93099c8f7..5ac5c6918d 100644 --- a/Source/NETworkManager.Models/PowerShell/PowerShell.cs +++ b/Source/NETworkManager.Models/PowerShell/PowerShell.cs @@ -14,41 +14,42 @@ public static class PowerShell /// /// Default installation paths for PowerShell. /// - public static readonly List GetDefaultInstallationPaths = new() - { + + public static readonly List GetDefaultInstallationPaths = + [ Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "PowerShell", "7", "pwsh.exe"), Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "PowerShell", "7", "pwsh.exe"), + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), @"System32\WindowsPowerShell\v1.0\powershell.exe") - }; + ]; /// /// Default SZ registry keys for the global PowerShell profile. /// - private static readonly List> DefaultProfileRegkeysSzBase = new() - { - new Tuple("FaceName", "Consolas") - }; + private static readonly List> DefaultProfileRegkeysSzBase = + [ + new("FaceName", "Consolas") + ]; /// /// Default DWORD registry keys for the global PowerShell profile. /// - private static readonly List> DefaultProfileRegkeysDwordBase = new() - { - new Tuple("CursorType", 1), - new Tuple("FontFamily", 54), // 36 - new Tuple("FontSize", 1179648), // 120000 - new Tuple("FontWeight", 400) // 190 - }; + private static readonly List> DefaultProfileRegkeysDwordBase = + [ + new("CursorType", 1), + new("FontFamily", 54), // 36 + new("FontSize", 1179648), // 120000 + new("FontWeight", 400) + ]; /// /// Default DWORD registry keys for the global PowerShell profile to delete. /// - private static readonly List DefaultProfileRegkeysDwordDelete = new() - { + private static readonly List DefaultProfileRegkeysDwordDelete = [ "ScreenColors" - }; + ]; /// /// Default DWORD registry keys for the global PowerShell profile with dark theme. @@ -57,12 +58,11 @@ public static class PowerShell private static List> GetProfileRegkeysDwordDark() { return DefaultProfileRegkeysDwordBase.Concat( - new[] - { - new Tuple("DefaultBackground", 2434341), // HEX: 252525 + [ + new Tuple("DefaultBackground", 2434341), // HEX: 252525 new Tuple("ColorTable00", 2434341), // HEX: 252525 new Tuple("ColorTable07", 13421772) // HEX: cccccc - }).ToList(); + ]).ToList(); } /// @@ -72,12 +72,11 @@ private static List> GetProfileRegkeysDwordDark() private static List> GetProfileRegkeysDwordWhite() { return DefaultProfileRegkeysDwordBase.Concat( - new[] - { - new Tuple("DefaultBackground", 16777215), // HEX: FFFFFF + [ + new Tuple("DefaultBackground", 16777215), // HEX: FFFFFF new Tuple("ColorTable00", 16777215), // HEX: FFFFFF new Tuple("ColorTable07", 2434341) // HEX: 252525 - }).ToList(); + ]).ToList(); } /// diff --git a/Source/NETworkManager.Models/PuTTY/PuTTY.cs b/Source/NETworkManager.Models/PuTTY/PuTTY.cs index 2b4fee5ce8..7b3f843754 100644 --- a/Source/NETworkManager.Models/PuTTY/PuTTY.cs +++ b/Source/NETworkManager.Models/PuTTY/PuTTY.cs @@ -13,92 +13,76 @@ namespace NETworkManager.Models.PuTTY; public class PuTTY { /// - /// Default PuTTY installation paths. + /// PuTTY file name. /// - public static readonly List GetDefaultInstallationPaths = new() - { - Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), _puttyFolder, _puttyFile), - Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), _puttyFolder, _puttyFile) - }; + public static readonly string FileName = "putty.exe"; /// /// Default SZ registry keys for PuTTY profile NETworkManager. /// - private static readonly List> DefaultProfileRegkeysSZBase = new() - { - new Tuple("Colour1", "255,255,255"), - new Tuple("Colour3", "85,85,85"), - new Tuple("Colour4", "0,0,0"), - new Tuple("Colour5", "0,255,0"), - new Tuple("Colour6", "0,0,0"), - new Tuple("Colour7", "85,85,85"), - new Tuple("Colour8", "187,0,0"), - new Tuple("Colour9", "255,85,85"), - new Tuple("Colour10", "0,187,0"), - new Tuple("Colour11", "85,255,85"), - new Tuple("Colour12", "187,187,0"), - new Tuple("Colour13", "255,255,85"), - new Tuple("Colour14", "0,0,187"), - new Tuple("Colour15", "85,85,255"), - new Tuple("Colour16", "187,0,187"), - new Tuple("Colour17", "255,85,255"), - new Tuple("Colour18", "0,187,187"), - new Tuple("Colour19", "85,255,255"), - new Tuple("Colour20", "187,187,187"), - new Tuple("Colour21", "255,255,255"), - new Tuple("LineCodePage", "UTF-8"), - new Tuple("Font", "Consolas") - }; + private static readonly List> DefaultProfileRegkeysSzBase = + [ + new("Colour1", "255,255,255"), + new("Colour3", "85,85,85"), + new("Colour4", "0,0,0"), + new("Colour5", "0,255,0"), + new("Colour6", "0,0,0"), + new("Colour7", "85,85,85"), + new("Colour8", "187,0,0"), + new("Colour9", "255,85,85"), + new("Colour10", "0,187,0"), + new("Colour11", "85,255,85"), + new("Colour12", "187,187,0"), + new("Colour13", "255,255,85"), + new("Colour14", "0,0,187"), + new("Colour15", "85,85,255"), + new("Colour16", "187,0,187"), + new("Colour17", "255,85,255"), + new("Colour18", "0,187,187"), + new("Colour19", "85,255,255"), + new("Colour20", "187,187,187"), + new("Colour21", "255,255,255"), + new("LineCodePage", "UTF-8"), + new("Font", "Consolas") + ]; /// /// Default DWORD registry keys for PuTTY profile NETworkManager. /// - private static readonly List> DefaultProfileRegkeysDwordBase = new() - { - new Tuple("CurType", 2), - new Tuple("FontHeight", 12), - new Tuple("BlinkCur", 1), - new Tuple("ScrollBar", 0) - }; - - /// - /// Name of the PuTTY folder. - /// - private static string _puttyFolder => "PuTTY"; - - /// - /// Name of the PuTTY executable. - /// - private static string _puttyFile => "putty.exe"; + private static readonly List> DefaultProfileRegkeysDwordBase = + [ + new("CurType", 2), + new("FontHeight", 12), + new("BlinkCur", 1), + new("ScrollBar", 0) + ]; /// /// SZ registry keys for PuTTY profile NETworkManager if app theme is dark. /// /// List with SZ registry keys. - private static List> GetProfileRegkeysSZDark() + private static List> GetProfileRegkeysSzDark() { - return DefaultProfileRegkeysSZBase.Concat( - new[] - { - // new Tuple("Colour0", "255,255,255"), + return DefaultProfileRegkeysSzBase.Concat( + [ + // new Tuple("Colour0", "255,255,255"), new Tuple("Colour0", "187,187,187"), // Foreground new Tuple("Colour2", "37,37,37") // Background - }).ToList(); + ]).ToList(); } /// /// SZ registry keys for PuTTY profile NETworkManager if app theme is white. /// /// List with DWORD registry keys. - private static List> GetProfileRegkeysSZWhite() + private static List> GetProfileRegkeysSzWhite() { - return DefaultProfileRegkeysSZBase.Concat( - new[] - { - // new Tuple("Colour0", "68,68,68"), + return DefaultProfileRegkeysSzBase.Concat( + [ + // new Tuple("Colour0", "68,68,68"), new Tuple("Colour0", "0,0,0"), // Foreground new Tuple("Colour2", "255,255,255") // Background - }).ToList(); + ]).ToList(); } /// @@ -116,7 +100,7 @@ public static void WriteDefaultProfileToRegistry(string theme) if (registryKey != null) { - foreach (var item in theme == "Dark" ? GetProfileRegkeysSZDark() : GetProfileRegkeysSZWhite()) + foreach (var item in theme == "Dark" ? GetProfileRegkeysSzDark() : GetProfileRegkeysSzWhite()) registryKey.SetValue(item.Item1, item.Item2); foreach (var item in DefaultProfileRegkeysDwordBase) diff --git a/Source/NETworkManager.Utilities/ApplicationHelper.cs b/Source/NETworkManager.Utilities/ApplicationHelper.cs new file mode 100644 index 0000000000..9f2767059f --- /dev/null +++ b/Source/NETworkManager.Utilities/ApplicationHelper.cs @@ -0,0 +1,34 @@ +using System; +using System.IO; +using System.Linq; + +namespace NETworkManager.Utilities; + +/// +/// Helper class to interact with the applications on the system. +/// +public static class ApplicationHelper +{ + /// + /// Find an application in the system PATH. + /// This is similar to the `where` command in Windows. + /// + /// The name of the application to find (like `notepad` or `notepad.exe`). + /// The full path to the application if found, otherwise `null`. + public static string Find(string fileName) + { + var path = Environment.GetEnvironmentVariable("PATH"); + + if (path == null) + return null; + + var directories = path.Split(';'); + + if (!fileName.EndsWith(".exe")) + fileName += ".exe"; + + return directories + .Select(dir => Path.Combine(dir, fileName)) + .FirstOrDefault(File.Exists); + } +} \ No newline at end of file diff --git a/Source/NETworkManager.Utilities/ExternalProcessStarter.cs b/Source/NETworkManager.Utilities/ExternalProcessStarter.cs index 8c340bafba..d18a4620c3 100644 --- a/Source/NETworkManager.Utilities/ExternalProcessStarter.cs +++ b/Source/NETworkManager.Utilities/ExternalProcessStarter.cs @@ -10,23 +10,18 @@ public static class ExternalProcessStarter /// Url like: https://github.com/BornToBeRoot public static void OpenUrl(string url) { - // Escape the $ in the command promp + // Escape the $ in the command prompt url = url.Replace("&", "^&"); Process.Start(new ProcessStartInfo("cmd", $"/c start {url}") { CreateNoWindow = true }); } - public static void RunProcess(string filename) + public static void RunProcess(string filename, bool asAdmin = false) { - RunProcess(filename, false); + RunProcess(filename, null, asAdmin); } - public static void RunProcess(string filename, bool asAdmin) - { - RunProcess(filename, "", asAdmin); - } - - public static void RunProcess(string filename, string arguments = "", bool asAdmin = false) + public static void RunProcess(string filename, string arguments, bool asAdmin = false) { ProcessStartInfo info = new() { diff --git a/Source/NETworkManager/MainWindow.xaml.cs b/Source/NETworkManager/MainWindow.xaml.cs index fb2f2dcb6a..106cadc796 100644 --- a/Source/NETworkManager/MainWindow.xaml.cs +++ b/Source/NETworkManager/MainWindow.xaml.cs @@ -504,13 +504,13 @@ await this.ShowMessageAsync(Strings.SettingsHaveBeenReset, if (SettingsManager.Current.WelcomeDialog_Show) { - var x = new WelcomeChildWindow(); + var welcomeChildWindow = new WelcomeChildWindow(); var welcomeViewModel = new WelcomeViewModel(async instance => { IsWelcomeWindowOpen = false; - x.IsOpen = false; + welcomeChildWindow.IsOpen = false; // Set settings based on user choice SettingsManager.Current.Update_CheckForUpdatesAtStartup = instance.CheckForUpdatesAtStartup; @@ -547,13 +547,6 @@ await this.ShowMessageAsync(Strings.SettingsHaveBeenReset, break; } - // Check if PuTTY is installed - foreach (var file in PuTTY.GetDefaultInstallationPaths.Where(File.Exists)) - { - SettingsManager.Current.PuTTY_ApplicationFilePath = file; - break; - } - SettingsManager.Current.WelcomeDialog_Show = false; // Save it to create a settings file @@ -562,11 +555,11 @@ await this.ShowMessageAsync(Strings.SettingsHaveBeenReset, Load(); }); - x.DataContext = welcomeViewModel; + welcomeChildWindow.DataContext = welcomeViewModel; IsWelcomeWindowOpen = true; - await this.ShowChildWindowAsync(x); + await this.ShowChildWindowAsync(welcomeChildWindow); } else { diff --git a/Source/NETworkManager/ViewModels/AWSSessionManagerHostViewModel.cs b/Source/NETworkManager/ViewModels/AWSSessionManagerHostViewModel.cs index bd2ed275c9..8ba8e69690 100644 --- a/Source/NETworkManager/ViewModels/AWSSessionManagerHostViewModel.cs +++ b/Source/NETworkManager/ViewModels/AWSSessionManagerHostViewModel.cs @@ -63,8 +63,7 @@ public string InterTabPartition private readonly bool _isLoading; private bool _isViewActive = true; - private bool _disableFocusEmbeddedWindow; - + private bool _isAWSCLIInstalled; public bool IsAWSCLIInstalled @@ -154,27 +153,7 @@ public int SelectedTabIndex OnPropertyChanged(); } } - - private DragablzTabItem _selectedTabItem; - - public DragablzTabItem SelectedTabItem - { - get => _selectedTabItem; - set - { - if (value == _selectedTabItem) - return; - - _selectedTabItem = value; - - // Focus embedded window on switching tab - if (!_disableFocusEmbeddedWindow) - FocusEmbeddedWindow(); - - OnPropertyChanged(); - } - } - + private bool _headerContextMenuIsOpen; public bool HeaderContextMenuIsOpen @@ -854,9 +833,7 @@ private void Connect(AWSSessionManagerSessionInfo sessionInfo, string header = n new AWSSessionManagerControl(tabId, sessionInfo), tabId)); // Select the added tab - _disableFocusEmbeddedWindow = true; SelectedTabIndex = TabItems.Count - 1; - _disableFocusEmbeddedWindow = false; } // Modify history list diff --git a/Source/NETworkManager/ViewModels/PowerShellHostViewModel.cs b/Source/NETworkManager/ViewModels/PowerShellHostViewModel.cs index c2214ce002..e78e5966e3 100644 --- a/Source/NETworkManager/ViewModels/PowerShellHostViewModel.cs +++ b/Source/NETworkManager/ViewModels/PowerShellHostViewModel.cs @@ -53,7 +53,6 @@ public string InterTabPartition private readonly bool _isLoading; private bool _isViewActive = true; - private bool _disableFocusEmbeddedWindow; private bool _isConfigured; @@ -85,28 +84,6 @@ public int SelectedTabIndex } } - /* - private DragablzTabItem _selectedTabItem; - - public DragablzTabItem SelectedTabItem - { - get => _selectedTabItem; - set - { - if (value == _selectedTabItem) - return; - - _selectedTabItem = value; - - // Focus embedded window on switching tab - if (!_disableFocusEmbeddedWindow) - FocusEmbeddedWindow(); - - OnPropertyChanged(); - } - } - */ - private bool _headerContextMenuIsOpen; public bool HeaderContextMenuIsOpen @@ -520,9 +497,7 @@ private void Connect(PowerShellSessionInfo sessionInfo, string header = null) new PowerShellControl(tabId, sessionInfo), tabId)); // Select the added tab - _disableFocusEmbeddedWindow = true; SelectedTabIndex = TabItems.Count - 1; - _disableFocusEmbeddedWindow = false; } public void AddTab(string host) diff --git a/Source/NETworkManager/ViewModels/PuTTYHostViewModel.cs b/Source/NETworkManager/ViewModels/PuTTYHostViewModel.cs index 1af436f6f6..5ca7c4e5e1 100644 --- a/Source/NETworkManager/ViewModels/PuTTYHostViewModel.cs +++ b/Source/NETworkManager/ViewModels/PuTTYHostViewModel.cs @@ -11,6 +11,7 @@ using System.Windows.Input; using System.Windows.Threading; using Dragablz; +using log4net; using MahApps.Metro.Controls.Dialogs; using NETworkManager.Controls; using NETworkManager.Localization.Resources; @@ -28,7 +29,8 @@ namespace NETworkManager.ViewModels; public class PuTTYHostViewModel : ViewModelBase, IProfileManager { #region Variables - + private static readonly ILog Log = LogManager.GetLogger(typeof(PuTTYHostViewModel)); + private readonly IDialogCoordinator _dialogCoordinator; private readonly DispatcherTimer _searchDispatcherTimer = new(); @@ -53,7 +55,6 @@ public string InterTabPartition private readonly bool _isLoading; private bool _isViewActive = true; - private bool _disableFocusEmbeddedWindow; private bool _isConfigured; @@ -85,26 +86,6 @@ public int SelectedTabIndex } } - private DragablzTabItem _selectedTabItem; - - public DragablzTabItem SelectedTabItem - { - get => _selectedTabItem; - set - { - if (value == _selectedTabItem) - return; - - _selectedTabItem = value; - - // Focus embedded window on switching tab - if (!_disableFocusEmbeddedWindow) - FocusEmbeddedWindow(); - - OnPropertyChanged(); - } - } - private bool _headerContextMenuIsOpen; public bool HeaderContextMenuIsOpen @@ -255,7 +236,6 @@ public bool ProfileContextMenuIsOpen #endregion #endregion - #region Constructor, load settings public PuTTYHostViewModel(IDialogCoordinator instance) @@ -263,8 +243,15 @@ public PuTTYHostViewModel(IDialogCoordinator instance) _isLoading = true; _dialogCoordinator = instance; - - CheckSettings(); + + // Check if PuTTY is configured + CheckExecutable(); + + // Try to find PuTTY executable if not configured + if (!IsConfigured) + TryFindExecutable(); + + WriteDefaultProfileToRegistry(); InterTabClient = new DragablzInterTabClient(ApplicationName.PuTTY); InterTabPartition = ApplicationName.PuTTY.ToString(); @@ -445,14 +432,30 @@ private static void OpenSettingsAction() #endregion #region Methods - - private void CheckSettings() + /// + /// Check if PuTTY executable is configured. + /// + private void CheckExecutable() { IsConfigured = !string.IsNullOrEmpty(SettingsManager.Current.PuTTY_ApplicationFilePath) && File.Exists(SettingsManager.Current.PuTTY_ApplicationFilePath); - - // Create default PuTTY profile for NETworkManager - WriteDefaultProfileToRegistry(); + + if(IsConfigured) + Log.Info($"PuTTY executable configured: {SettingsManager.Current.PuTTY_ApplicationFilePath}"); + else + Log.Warn("PuTTY executable not found."); + } + + /// + /// Try to find PuTTY executabl in + /// + private void TryFindExecutable() + { + Log.Info("Try to find PuTTY executable..."); + + SettingsManager.Current.PuTTY_ApplicationFilePath = ApplicationHelper.Find(Models.PuTTY.PuTTY.FileName); + + CheckExecutable(); } private async Task Connect(string host = null) @@ -544,9 +547,7 @@ private void Connect(PuTTYSessionInfo sessionInfo, string header = null) tabId)); // Select the added tab - _disableFocusEmbeddedWindow = true; SelectedTabIndex = TabItems.Count - 1; - _disableFocusEmbeddedWindow = false; } public void AddTab(string host) @@ -767,12 +768,16 @@ private void WriteDefaultProfileToRegistry() private void SettingsManager_PropertyChanged(object sender, PropertyChangedEventArgs e) { - if (e.PropertyName == nameof(SettingsInfo.PuTTY_ApplicationFilePath)) - CheckSettings(); - - // Update PuTTY profile "NETworkManager" if application theme has changed - if (e.PropertyName == nameof(SettingsInfo.Appearance_Theme)) - WriteDefaultProfileToRegistry(); + switch (e.PropertyName) + { + case nameof(SettingsInfo.PuTTY_ApplicationFilePath): + CheckExecutable(); + break; + // Update PuTTY profile "NETworkManager" if application theme has changed + case nameof(SettingsInfo.Appearance_Theme): + WriteDefaultProfileToRegistry(); + break; + } } private void ProfileManager_OnProfilesUpdated(object sender, EventArgs e) From 78e49024df3ebae203ded0123fa07331e36180e7 Mon Sep 17 00:00:00 2001 From: BornToBeRoot <16019165+BornToBeRoot@users.noreply.github.com> Date: Sun, 22 Dec 2024 00:34:58 +0100 Subject: [PATCH 2/8] Fix: Docs --- .github/pull_request_template.md | 6 +++--- CONTRIBUTING.md | 2 +- README.md | 2 +- Contributors.md => _CONTRIBUTORS.md | 0 4 files changed, 5 insertions(+), 5 deletions(-) rename Contributors.md => _CONTRIBUTORS.md (100%) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 54f3c412ec..e844774248 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -7,13 +7,13 @@ Fixes #{issue number} **ToDo:** -- [ ] Update [documentation](https://github.com/BornToBeRoot/NETworkManager/tree/main/docs/Documentation) to reflect this changes -- [ ] Update [changelog](https://github.com/BornToBeRoot/NETworkManager/tree/main/docs/Changelog) to reflect this changes +- [ ] Update [documentation](https://github.com/BornToBeRoot/NETworkManager/tree/main/Website/docs) to reflect this changes +- [ ] Update [changelog](https://github.com/BornToBeRoot/NETworkManager/tree/main/Website/docs/changelog) to reflect this changes --- **By submitting this pull request, I confirm the following:** - [ ] I have read and understood the [contributing guidelines](https://github.com/BornToBeRoot/NETworkManager/blob/main/CONTRIBUTING.md) and the [code of conduct](https://github.com/BornToBeRoot/NETworkManager/blob/main/CODE_OF_CONDUCT.md). -- [ ] I have have added my name, username or email to the [contributors](https://github.com/BornToBeRoot/NETworkManager/blob/main/Contributors.md) list or don't want to. +- [ ] I have have added my name, username or email to the [contributors](https://github.com/BornToBeRoot/NETworkManager/blob/main/CONTRIBUTORS.md) list or don't want to. - [ ] The code or resource is compatible with the [GNU General Public License v3.0](https://github.com/BornToBeRoot/NETworkManager/blob/main/LICENSE). \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 662a5815f8..0d26c081fe 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,7 @@ - You found a bug or have a feature request? Feel free to create a [new issue](https://github.com/BornToBeRoot/NETworkManager/issues/new/choose). - General questions can be discussed in the [GitHub discussions](https://github.com/BornToBeRoot/NETworkManager/discussions). -- If you contribute to the code, documentation or translation, you can add your name to the [contributors](https://github.com/BornToBeRoot/NETworkManager/blob/main/Contributors.md) list with a pull request. +- If you contribute to the code, documentation or translation, you can add your name to the [contributors](https://github.com/BornToBeRoot/NETworkManager/blob/main/CONTRIBUTORS.md) list with a pull request. ## Code - If you want to fix a bug or implement a new features, let me know in the issues that you are work on it. diff --git a/README.md b/README.md index adbf90be35..a88b3e9e6d 100644 --- a/README.md +++ b/README.md @@ -121,7 +121,7 @@ Want to contribute to NETworkManager? Here are a few information on how to get s - [Improve the documentation](CONTRIBUTING.md#documentation) - [Report a security vulnerability](https://github.com/BornToBeRoot/NETworkManager/blob/main/SECURITY.md) -A list of all contributors can be found [here](https://github.com/BornToBeRoot/NETworkManager/blob/main/Contributors.md). +A list of all contributors can be found [here](https://github.com/BornToBeRoot/NETworkManager/blob/main/CONTRIBUTORS.md). This project has adopted the [code of conduct](https://github.com/BornToBeRoot/NETworkManager/blob/main/CODE_OF_CONDUCT.md) defined by the [Contributor Covenant](https://contributor-covenant.org/). diff --git a/Contributors.md b/_CONTRIBUTORS.md similarity index 100% rename from Contributors.md rename to _CONTRIBUTORS.md From 378512a79f52462970cc71450fd6330401ec2b11 Mon Sep 17 00:00:00 2001 From: BornToBeRoot <16019165+BornToBeRoot@users.noreply.github.com> Date: Sun, 22 Dec 2024 00:35:10 +0100 Subject: [PATCH 3/8] Fix: Docs --- _CONTRIBUTORS.md => CONTRIBUTORS.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename _CONTRIBUTORS.md => CONTRIBUTORS.md (100%) diff --git a/_CONTRIBUTORS.md b/CONTRIBUTORS.md similarity index 100% rename from _CONTRIBUTORS.md rename to CONTRIBUTORS.md From 3d2833e964a711ac9e37e812efa7cbb83f4bd8c0 Mon Sep 17 00:00:00 2001 From: BornToBeRoot <16019165+BornToBeRoot@users.noreply.github.com> Date: Sun, 22 Dec 2024 01:50:35 +0100 Subject: [PATCH 4/8] Feature: Detect executable in AWS --- .../ApplicationManager.cs | 2 +- .../PowerShell/PowerShell.cs | 27 +++--- Source/NETworkManager.Models/PuTTY/PuTTY.cs | 4 +- .../SettingsManager.cs | 55 ++++------- Source/NETworkManager/MainWindow.xaml.cs | 50 +--------- .../AWSSessionManagerHostViewModel.cs | 97 +++++++++++++++---- .../ViewModels/PuTTYHostViewModel.cs | 43 ++++---- .../Views/AWSSessionManagerHostView.xaml | 8 +- .../NETworkManager/Views/PuTTYHostView.xaml | 4 +- .../Views/PuTTYHostView.xaml.cs | 2 +- Website/docs/changelog/next-release.md | 1 + 11 files changed, 148 insertions(+), 145 deletions(-) diff --git a/Source/NETworkManager.Models/ApplicationManager.cs b/Source/NETworkManager.Models/ApplicationManager.cs index 036ba1228e..072dacd875 100644 --- a/Source/NETworkManager.Models/ApplicationManager.cs +++ b/Source/NETworkManager.Models/ApplicationManager.cs @@ -78,7 +78,7 @@ public static Canvas GetIcon(ApplicationName name) canvas.Children.Add(new PackIconFontAwesome { Kind = PackIconFontAwesomeKind.TerminalSolid }); break; case ApplicationName.AWSSessionManager: - canvas.Children.Add(new PackIconMaterial { Kind = PackIconMaterialKind.Aws }); + canvas.Children.Add(new PackIconFontAwesome { Kind = PackIconFontAwesomeKind.AwsBrands }); break; case ApplicationName.TigerVNC: canvas.Children.Add(new PackIconMaterial { Kind = PackIconMaterialKind.EyeOutline }); diff --git a/Source/NETworkManager.Models/PowerShell/PowerShell.cs b/Source/NETworkManager.Models/PowerShell/PowerShell.cs index 5ac5c6918d..83439a49b0 100644 --- a/Source/NETworkManager.Models/PowerShell/PowerShell.cs +++ b/Source/NETworkManager.Models/PowerShell/PowerShell.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; +using log4net; using Microsoft.Win32; namespace NETworkManager.Models.PowerShell; @@ -11,20 +11,18 @@ namespace NETworkManager.Models.PowerShell; /// public static class PowerShell { + private static readonly ILog Log = LogManager.GetLogger(typeof(PowerShell)); + /// - /// Default installation paths for PowerShell. + /// Windows PowerShell file name. /// + public const string WindowsPowerShellFileName = "powershell.exe"; + + /// + /// PowerShell Core file name. + /// + public const string PwshFileName = "pwsh.exe"; - public static readonly List GetDefaultInstallationPaths = - [ - Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "PowerShell", "7", "pwsh.exe"), - Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "PowerShell", "7", - "pwsh.exe"), - - Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), - @"System32\WindowsPowerShell\v1.0\powershell.exe") - ]; - /// /// Default SZ registry keys for the global PowerShell profile. /// @@ -92,12 +90,13 @@ public static void WriteDefaultProfileToRegistry(string theme, string powerShell // Windows PowerShell --> HKCU:\Console\%SystemRoot%_System32_WindowsPowerShell_v1.0_powershell.exe if (powerShellPath.StartsWith(systemRoot)) - registryPath += "%SystemRoot%" + powerShellPath - .Substring(systemRoot.Length, powerShellPath.Length - systemRoot.Length).Replace(@"\", "_"); + registryPath += "%SystemRoot%" + powerShellPath.Substring(systemRoot.Length, powerShellPath.Length - systemRoot.Length).Replace(@"\", "_"); // PWSH --> HKCU:\Console\C:_Program Files_PowerShell_7_pwsh.exe else registryPath += powerShellPath.Replace(@"\", "_"); + Log.Info($"Registry path for PowerShell profile: \"{registryPath}\""); + var registryKey = Registry.CurrentUser.OpenSubKey(registryPath, true); registryKey ??= Registry.CurrentUser.CreateSubKey(registryPath); diff --git a/Source/NETworkManager.Models/PuTTY/PuTTY.cs b/Source/NETworkManager.Models/PuTTY/PuTTY.cs index 7b3f843754..645a5dc2bb 100644 --- a/Source/NETworkManager.Models/PuTTY/PuTTY.cs +++ b/Source/NETworkManager.Models/PuTTY/PuTTY.cs @@ -10,12 +10,12 @@ namespace NETworkManager.Models.PuTTY; /// /// Class control PuTTY. /// -public class PuTTY +public static class PuTTY { /// /// PuTTY file name. /// - public static readonly string FileName = "putty.exe"; + public const string FileName = "putty.exe"; /// /// Default SZ registry keys for PuTTY profile NETworkManager. diff --git a/Source/NETworkManager.Settings/SettingsManager.cs b/Source/NETworkManager.Settings/SettingsManager.cs index bd77a3b107..c222411b11 100644 --- a/Source/NETworkManager.Settings/SettingsManager.cs +++ b/Source/NETworkManager.Settings/SettingsManager.cs @@ -173,10 +173,6 @@ public static void Upgrade(Version fromVersion, Version toVersion) { Log.Info($"Start settings upgrade from {fromVersion} to {toVersion}..."); - // 2022.12.20.0 - if (fromVersion < new Version(2022, 12, 20, 0)) - UpgradeTo_2022_12_20_0(); - // 2023.3.7.0 if (fromVersion < new Version(2023, 3, 7, 0)) UpgradeTo_2023_3_7_0(); @@ -193,6 +189,11 @@ public static void Upgrade(Version fromVersion, Version toVersion) if (fromVersion < new Version(2023, 11, 28, 0)) UpgradeTo_2023_11_28_0(); + + // 2024.11.11.0 + if (fromVersion < new Version(2024, 11, 11, 0)) + UpgradeTo_2024_11_11_0(); + // Latest if (fromVersion < toVersion) UpgradeToLatest(toVersion); @@ -204,35 +205,7 @@ public static void Upgrade(Version fromVersion, Version toVersion) Log.Info("Settings upgrade finished!"); } - /// - /// Method to apply changes for version 2022.12.20.0. - /// - private static void UpgradeTo_2022_12_20_0() - { - Log.Info("Apply update to 2022.12.20.0..."); - - // Add AWS Session Manager application - Log.Info("Add new app \"AWSSessionManager\"..."); - Current.General_ApplicationList.Add(ApplicationManager.GetDefaultList() - .First(x => x.Name == ApplicationName.AWSSessionManager)); - - var powerShellPath = ""; - foreach (var file in PowerShell.GetDefaultInstallationPaths.Where(File.Exists)) - { - powerShellPath = file; - break; - } - - Log.Info($"Set \"AWSSessionManager_ApplicationFilePath\" to \"{powerShellPath}\"..."); - Current.AWSSessionManager_ApplicationFilePath = powerShellPath; - - // Add Bit Calculator application - Log.Info("Add new app \"BitCalculator\"..."); - Current.General_ApplicationList.Add(ApplicationManager.GetDefaultList() - .First(x => x.Name == ApplicationName.BitCalculator)); - } - - /// + /// /// Method to apply changes for version 2023.3.7.0. /// private static void UpgradeTo_2023_3_7_0() @@ -326,6 +299,18 @@ private static void UpgradeTo_2023_11_28_0() Current.DNSLookup_DNSServers = new ObservableCollection(DNSServer.GetDefaultList()); } + + /// + /// Method to apply changes for version 2024.11.11.0. + /// + private static void UpgradeTo_2024_11_11_0() + { + Log.Info("Apply upgrade to 2024.11.11.0..."); + + Log.Info("Reset ApplicationList to default..."); + Current.General_ApplicationList = + new ObservableSetCollection(ApplicationManager.GetDefaultList()); + } /// /// Method to apply changes for the latest version. @@ -335,9 +320,7 @@ private static void UpgradeToLatest(Version version) { Log.Info($"Apply upgrade to {version}..."); - Log.Info("Reset ApplicationList to default..."); - Current.General_ApplicationList = - new ObservableSetCollection(ApplicationManager.GetDefaultList()); + } #endregion diff --git a/Source/NETworkManager/MainWindow.xaml.cs b/Source/NETworkManager/MainWindow.xaml.cs index 106cadc796..b71f9f3d6f 100644 --- a/Source/NETworkManager/MainWindow.xaml.cs +++ b/Source/NETworkManager/MainWindow.xaml.cs @@ -73,19 +73,6 @@ private void SettingsManager_PropertyChanged(object sender, PropertyChangedEvent case nameof(SettingsInfo.Network_CustomDNSServer): ConfigureDNSServer(); - break; - - // Update PowerShell profile if changed in the settings - case nameof(SettingsInfo.Appearance_PowerShellModifyGlobalProfile): - case nameof(SettingsInfo.Appearance_Theme): - case nameof(SettingsInfo.PowerShell_ApplicationFilePath): - case nameof(SettingsInfo.AWSSessionManager_ApplicationFilePath): - // Skip on welcome dialog - if (SettingsManager.Current.WelcomeDialog_Show) - return; - - WriteDefaultPowerShellProfileToRegistry(); - break; } } @@ -538,15 +525,6 @@ await this.ShowMessageAsync(Strings.SettingsHaveBeenReset, SettingsManager.Current.SNTPLookup_SNTPServers = new ObservableCollection(SNTPServer.GetDefaultList()); - // Check if PowerShell is installed - foreach (var file in PowerShell.GetDefaultInstallationPaths.Where(File.Exists)) - { - SettingsManager.Current.PowerShell_ApplicationFilePath = file; - SettingsManager.Current.AWSSessionManager_ApplicationFilePath = file; - - break; - } - SettingsManager.Current.WelcomeDialog_Show = false; // Save it to create a settings file @@ -593,9 +571,6 @@ private void Load() NetworkChange.NetworkAvailabilityChanged += (_, _) => OnNetworkHasChanged(); NetworkChange.NetworkAddressChanged += (_, _) => OnNetworkHasChanged(); - // Set PowerShell global profile - WriteDefaultPowerShellProfileToRegistry(); - // Search for updates... if (SettingsManager.Current.Update_CheckForUpdatesAtStartup) CheckForUpdates(); @@ -1895,28 +1870,7 @@ private void ConfigureDNSServer() DNSClient.GetInstance().Configure(dnsSettings); } - - private void WriteDefaultPowerShellProfileToRegistry() - { - if (!SettingsManager.Current.Appearance_PowerShellModifyGlobalProfile) - return; - - HashSet paths = []; - - // PowerShell - if (!string.IsNullOrEmpty(SettingsManager.Current.PowerShell_ApplicationFilePath) && - File.Exists(SettingsManager.Current.PowerShell_ApplicationFilePath)) - paths.Add(SettingsManager.Current.PowerShell_ApplicationFilePath); - - // AWS Session Manager - if (!string.IsNullOrEmpty(SettingsManager.Current.AWSSessionManager_ApplicationFilePath) && - File.Exists(SettingsManager.Current.AWSSessionManager_ApplicationFilePath)) - paths.Add(SettingsManager.Current.AWSSessionManager_ApplicationFilePath); - - foreach (var path in paths) - PowerShell.WriteDefaultProfileToRegistry(SettingsManager.Current.Appearance_Theme, path); - } - + #endregion #region Status window @@ -2003,4 +1957,4 @@ private async void FocusEmbeddedWindow() } #endregion -} \ No newline at end of file +} diff --git a/Source/NETworkManager/ViewModels/AWSSessionManagerHostViewModel.cs b/Source/NETworkManager/ViewModels/AWSSessionManagerHostViewModel.cs index 8ba8e69690..8aaea97ea5 100644 --- a/Source/NETworkManager/ViewModels/AWSSessionManagerHostViewModel.cs +++ b/Source/NETworkManager/ViewModels/AWSSessionManagerHostViewModel.cs @@ -26,6 +26,7 @@ using NETworkManager.Models; using NETworkManager.Models.AWS; using NETworkManager.Models.EventSystem; +using NETworkManager.Models.PowerShell; using NETworkManager.Profiles; using NETworkManager.Settings; using NETworkManager.Utilities; @@ -39,6 +40,7 @@ public class AWSSessionManagerHostViewModel : ViewModelBase, IProfileManager #region Variables private static readonly ILog Log = LogManager.GetLogger(typeof(AWSSessionManagerHostViewModel)); + private readonly IDialogCoordinator _dialogCoordinator; private readonly DispatcherTimer _searchDispatcherTimer = new(); @@ -94,17 +96,17 @@ public bool IsAWSSessionManagerPluginInstalled } } - private bool _isPowerShellConfigured; + private bool _isExecutableConfigured; - public bool IsPowerShellConfigured + public bool IsExecutableConfigured { - get => _isPowerShellConfigured; + get => _isExecutableConfigured; set { - if (value == _isPowerShellConfigured) + if (value == _isExecutableConfigured) return; - _isPowerShellConfigured = value; + _isExecutableConfigured = value; OnPropertyChanged(); } } @@ -313,9 +315,18 @@ public AWSSessionManagerHostViewModel(IDialogCoordinator instance) _dialogCoordinator = instance; - CheckInstallationStatus(); - CheckSettings(); - + // Check if AWS tools are installed + CheckRequirements(); + + // Check if PowerShell executable is configured + CheckExecutable(); + + // Try to find PowerShell executable + if(!IsExecutableConfigured) + TryFindExecutable(); + + WriteDefaultProfileToRegistry(); + InterTabClient = new DragablzInterTabClient(ApplicationName.AWSSessionManager); InterTabPartition = ApplicationName.AWSSessionManager.ToString(); @@ -357,11 +368,11 @@ private void LoadSettings() #region ICommand & Actions - public ICommand CheckInstallationStatusCommand => new RelayCommand(_ => CheckInstallationStatusAction()); + public ICommand CheckRequirementsCommand => new RelayCommand(_ => CheckRequirementsAction()); - private void CheckInstallationStatusAction() + private void CheckRequirementsAction() { - CheckInstallationStatus(); + CheckRequirements(); } public ItemActionCallback CloseItemCommand => CloseItemAction; @@ -373,7 +384,7 @@ private void CloseItemAction(ItemActionCallbackArgs args) private bool Connect_CanExecute(object obj) { - return IsPowerShellConfigured; + return IsExecutableConfigured; } public ICommand ConnectCommand => new RelayCommand(_ => ConnectAction(), Connect_CanExecute); @@ -531,7 +542,7 @@ private static void OpenSettingsAction() #region Methods - private void CheckInstallationStatus() + private void CheckRequirements() { using var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"); @@ -559,13 +570,41 @@ private void CheckInstallationStatus() } } - private void CheckSettings() + /// + /// Check if the PowerShell executable is configured and exists. + /// + private void CheckExecutable() { - IsPowerShellConfigured = !string.IsNullOrEmpty(SettingsManager.Current.AWSSessionManager_ApplicationFilePath) && + IsExecutableConfigured = !string.IsNullOrEmpty(SettingsManager.Current.AWSSessionManager_ApplicationFilePath) && File.Exists(SettingsManager.Current.AWSSessionManager_ApplicationFilePath); + + if(IsExecutableConfigured) + Log.Info($"PowerShell executable found: \"{SettingsManager.Current.AWSSessionManager_ApplicationFilePath}\""); + else + Log.Warn("PowerShell executable not found!"); + } + + /// + /// Try to find PuTTY executable. + /// + private void TryFindExecutable() + { + Log.Info("Try to find PowerShell executable..."); + + var applicationFilePath = ApplicationHelper.Find(Models.PowerShell.PowerShell.PwshFileName); + + if(string.IsNullOrEmpty(applicationFilePath)) + applicationFilePath = ApplicationHelper.Find(Models.PowerShell.PowerShell.WindowsPowerShellFileName); + + SettingsManager.Current.AWSSessionManager_ApplicationFilePath = applicationFilePath; + + CheckExecutable(); + + if(!IsExecutableConfigured) + Log.Warn("Install PowerShell or configure the path in the settings."); } - private bool IsConfigured => IsAWSCLIInstalled && IsAWSSessionManagerPluginInstalled && IsPowerShellConfigured; + private bool IsConfigured => IsAWSCLIInstalled && IsAWSSessionManagerPluginInstalled && IsExecutableConfigured; private async Task SyncAllInstanceIDsFromAWS() { @@ -580,7 +619,7 @@ private async Task SyncAllInstanceIDsFromAWS() if (!IsConfigured) { Log.Warn( - $"Preconditions not met! AWS CLI installed {IsAWSCLIInstalled}. AWS Session Manager plugin installed {IsAWSSessionManagerPluginInstalled}. PowerShell configured {IsPowerShellConfigured}."); + $"Preconditions not met! AWS CLI installed {IsAWSCLIInstalled}. AWS Session Manager plugin installed {IsAWSSessionManagerPluginInstalled}. PowerShell configured {IsExecutableConfigured}."); return; } @@ -1004,6 +1043,21 @@ public void OnProfileManagerDialogClose() { ConfigurationManager.OnDialogClose(); } + + private void WriteDefaultProfileToRegistry() + { + if (!SettingsManager.Current.Appearance_PowerShellModifyGlobalProfile) + return; + + if(!IsExecutableConfigured) + return; + + Log.Info("Write PowerShell profile to registry..."); + + PowerShell.WriteDefaultProfileToRegistry( + SettingsManager.Current.Appearance_Theme, + SettingsManager.Current.AWSSessionManager_ApplicationFilePath); + } #endregion @@ -1027,7 +1081,12 @@ private void SettingsManager_PropertyChanged(object sender, PropertyChangedEvent SyncAllInstanceIDsFromAWS().ConfigureAwait(false); break; case nameof(SettingsInfo.AWSSessionManager_ApplicationFilePath): - CheckSettings(); + CheckExecutable(); + WriteDefaultProfileToRegistry(); + break; + case nameof(SettingsInfo.Appearance_PowerShellModifyGlobalProfile): + case nameof(SettingsInfo.Appearance_Theme): + WriteDefaultProfileToRegistry(); break; } } @@ -1064,4 +1123,4 @@ private void SearchDispatcherTimer_Tick(object sender, EventArgs e) } #endregion -} \ No newline at end of file +} diff --git a/Source/NETworkManager/ViewModels/PuTTYHostViewModel.cs b/Source/NETworkManager/ViewModels/PuTTYHostViewModel.cs index 5ca7c4e5e1..97ec6ab7dd 100644 --- a/Source/NETworkManager/ViewModels/PuTTYHostViewModel.cs +++ b/Source/NETworkManager/ViewModels/PuTTYHostViewModel.cs @@ -56,17 +56,17 @@ public string InterTabPartition private readonly bool _isLoading; private bool _isViewActive = true; - private bool _isConfigured; + private bool _isExecutableConfigured; - public bool IsConfigured + public bool IsExecutableConfigured { - get => _isConfigured; + get => _isExecutableConfigured; set { - if (value == _isConfigured) + if (value == _isExecutableConfigured) return; - _isConfigured = value; + _isExecutableConfigured = value; OnPropertyChanged(); } } @@ -236,6 +236,7 @@ public bool ProfileContextMenuIsOpen #endregion #endregion + #region Constructor, load settings public PuTTYHostViewModel(IDialogCoordinator instance) @@ -244,11 +245,11 @@ public PuTTYHostViewModel(IDialogCoordinator instance) _dialogCoordinator = instance; - // Check if PuTTY is configured + // Check if PuTTY executable is configured CheckExecutable(); - // Try to find PuTTY executable if not configured - if (!IsConfigured) + // Try to find PuTTY executable + if (!IsExecutableConfigured) TryFindExecutable(); WriteDefaultProfileToRegistry(); @@ -297,7 +298,7 @@ private void CloseItemAction(ItemActionCallbackArgs args) private bool Connect_CanExecute(object obj) { - return IsConfigured; + return IsExecutableConfigured; } public ICommand ConnectCommand => new RelayCommand(_ => ConnectAction(), Connect_CanExecute); @@ -433,21 +434,21 @@ private static void OpenSettingsAction() #region Methods /// - /// Check if PuTTY executable is configured. + /// Check if PuTTY executable is configured and exists. /// private void CheckExecutable() { - IsConfigured = !string.IsNullOrEmpty(SettingsManager.Current.PuTTY_ApplicationFilePath) && + IsExecutableConfigured = !string.IsNullOrEmpty(SettingsManager.Current.PuTTY_ApplicationFilePath) && File.Exists(SettingsManager.Current.PuTTY_ApplicationFilePath); - if(IsConfigured) - Log.Info($"PuTTY executable configured: {SettingsManager.Current.PuTTY_ApplicationFilePath}"); + if(IsExecutableConfigured) + Log.Info($"PuTTY executable configured: \"{SettingsManager.Current.PuTTY_ApplicationFilePath}\""); else - Log.Warn("PuTTY executable not found."); + Log.Warn("PuTTY executable not found!"); } /// - /// Try to find PuTTY executabl in + /// Try to find PuTTY executable. /// private void TryFindExecutable() { @@ -456,6 +457,9 @@ private void TryFindExecutable() SettingsManager.Current.PuTTY_ApplicationFilePath = ApplicationHelper.Find(Models.PuTTY.PuTTY.FileName); CheckExecutable(); + + if(!IsExecutableConfigured) + Log.Warn("Install PuTTY or configure the path in the settings."); } private async Task Connect(string host = null) @@ -758,8 +762,12 @@ public void OnProfileManagerDialogClose() private void WriteDefaultProfileToRegistry() { - if (IsConfigured) - Models.PuTTY.PuTTY.WriteDefaultProfileToRegistry(SettingsManager.Current.Appearance_Theme); + if (!IsExecutableConfigured) + return; + + Log.Info("Write PuTTY profile to registry..."); + + Models.PuTTY.PuTTY.WriteDefaultProfileToRegistry(SettingsManager.Current.Appearance_Theme); } #endregion @@ -773,7 +781,6 @@ private void SettingsManager_PropertyChanged(object sender, PropertyChangedEvent case nameof(SettingsInfo.PuTTY_ApplicationFilePath): CheckExecutable(); break; - // Update PuTTY profile "NETworkManager" if application theme has changed case nameof(SettingsInfo.Appearance_Theme): WriteDefaultProfileToRegistry(); break; diff --git a/Source/NETworkManager/Views/AWSSessionManagerHostView.xaml b/Source/NETworkManager/Views/AWSSessionManagerHostView.xaml index 40f9b19ada..2e4127dab1 100644 --- a/Source/NETworkManager/Views/AWSSessionManagerHostView.xaml +++ b/Source/NETworkManager/Views/AWSSessionManagerHostView.xaml @@ -41,7 +41,7 @@ - + @@ -794,7 +794,7 @@ - + @@ -849,7 +849,7 @@ -