From 13c70ba4b683d458731cb77b8c0c8b6289ab8a67 Mon Sep 17 00:00:00 2001 From: Oliver Schantz Date: Tue, 24 Mar 2026 09:26:01 +0100 Subject: [PATCH 1/4] fix naming error --- ...tainfo.xml => io.github.frequency403.openssh_gui.metainfo.xml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename appimage/{io.github.frequency403.openssh-gui.metainfo.xml => io.github.frequency403.openssh_gui.metainfo.xml} (100%) diff --git a/appimage/io.github.frequency403.openssh-gui.metainfo.xml b/appimage/io.github.frequency403.openssh_gui.metainfo.xml similarity index 100% rename from appimage/io.github.frequency403.openssh-gui.metainfo.xml rename to appimage/io.github.frequency403.openssh_gui.metainfo.xml From 01162ecd9470411ce888733980a00c0240839d12 Mon Sep 17 00:00:00 2001 From: Oliver Schantz Date: Tue, 24 Mar 2026 09:32:04 +0100 Subject: [PATCH 2/4] fix icon name --- appimage/io.github.frequency403.openssh_gui.metainfo.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appimage/io.github.frequency403.openssh_gui.metainfo.xml b/appimage/io.github.frequency403.openssh_gui.metainfo.xml index b2f2b9d..bdd59df 100644 --- a/appimage/io.github.frequency403.openssh_gui.metainfo.xml +++ b/appimage/io.github.frequency403.openssh_gui.metainfo.xml @@ -33,7 +33,7 @@ - opensshgui + openssh-gui From fe3ef201b611488e2b42422e7e7639afe08d5b32 Mon Sep 17 00:00:00 2001 From: Oliver Schantz Date: Tue, 24 Mar 2026 10:00:59 +0100 Subject: [PATCH 3/4] Fix startup error when files are not readable. (#18) --- .../Extensions/SshConfigurationExtensions.cs | 23 +++++++++++-------- .../Options/SshConfigParserOptions.cs | 8 +++++++ .../Parsers/SshConfigParser.cs | 17 +++++++++++++- .../Services/SshConfigurationProvider.cs | 2 +- .../Services/SshConfigurationSource.cs | 8 +++++++ OpenSSH_GUI/Program.cs | 9 ++++++-- 6 files changed, 53 insertions(+), 14 deletions(-) diff --git a/OpenSSH_GUI.SshConfig/Extensions/SshConfigurationExtensions.cs b/OpenSSH_GUI.SshConfig/Extensions/SshConfigurationExtensions.cs index 53fa7a1..ca8d72e 100644 --- a/OpenSSH_GUI.SshConfig/Extensions/SshConfigurationExtensions.cs +++ b/OpenSSH_GUI.SshConfig/Extensions/SshConfigurationExtensions.cs @@ -20,9 +20,9 @@ public static class SshConfigurationExtensions /// . /// /// The . - public IConfigurationBuilder AddSshConfig(string path) + public IConfigurationBuilder AddSshConfig(string path, Action? loggingAction = null) { - return builder.AddSshConfig(null, path, false, false); + return builder.AddSshConfig(null, path, false, false, loggingAction); } /// @@ -34,9 +34,9 @@ public IConfigurationBuilder AddSshConfig(string path) /// /// Whether the file is optional. /// The . - public IConfigurationBuilder AddSshConfig(string path, bool optional) + public IConfigurationBuilder AddSshConfig(string path, bool optional, Action? loggingAction = null) { - return builder.AddSshConfig(null, path, optional, false); + return builder.AddSshConfig(null, path, optional, false, loggingAction); } /// @@ -50,9 +50,9 @@ public IConfigurationBuilder AddSshConfig(string path, bool optional) /// Whether the configuration should be reloaded if the file changes. /// The . public IConfigurationBuilder AddSshConfig(string path, bool optional, - bool reloadOnChange) + bool reloadOnChange, Action? loggingAction = null) { - return builder.AddSshConfig(null, path, optional, reloadOnChange); + return builder.AddSshConfig(null, path, optional, reloadOnChange, loggingAction); } /// @@ -67,7 +67,7 @@ public IConfigurationBuilder AddSshConfig(string path, bool optional, /// Whether the configuration should be reloaded if the file changes. /// The . public IConfigurationBuilder AddSshConfig(IFileProvider? fileProvider, - string path, bool optional, bool reloadOnChange) + string path, bool optional, bool reloadOnChange, Action? loggingAction = null) { ArgumentNullException.ThrowIfNull(builder); ArgumentException.ThrowIfNullOrEmpty(path); @@ -79,7 +79,7 @@ public IConfigurationBuilder AddSshConfig(IFileProvider? fileProvider, s.Optional = optional; s.ReloadOnChange = reloadOnChange; s.ResolveFileProvider(); - }); + }, loggingAction); } /// @@ -87,9 +87,12 @@ public IConfigurationBuilder AddSshConfig(IFileProvider? fileProvider, /// /// Configures the source. /// The . - public IConfigurationBuilder AddSshConfig(Action? configureSource) + public IConfigurationBuilder AddSshConfig(Action? configureSource, Action? loggingAction) { - var source = new SshConfigurationSource(); + var source = new SshConfigurationSource + { + OnSkippedIncludeFile = loggingAction + }; configureSource?.Invoke(source); return builder.Add(source); } diff --git a/OpenSSH_GUI.SshConfig/Options/SshConfigParserOptions.cs b/OpenSSH_GUI.SshConfig/Options/SshConfigParserOptions.cs index 2108b52..0f739db 100644 --- a/OpenSSH_GUI.SshConfig/Options/SshConfigParserOptions.cs +++ b/OpenSSH_GUI.SshConfig/Options/SshConfigParserOptions.cs @@ -40,6 +40,14 @@ public sealed record SshConfigParserOptions /// Defaults to . /// public bool ThrowOnUnknownKey { get; init; } + + /// + /// Optional callback invoked when an included file cannot be read due to + /// insufficient permissions or an I/O error. Receives the file path and the + /// causing exception. When , inaccessible files are + /// silently skipped. + /// + public Action? OnSkippedIncludeFile { get; init; } /// Gets the default options instance. public static SshConfigParserOptions Default { get; } = new(); diff --git a/OpenSSH_GUI.SshConfig/Parsers/SshConfigParser.cs b/OpenSSH_GUI.SshConfig/Parsers/SshConfigParser.cs index 5d5101a..0b5fa79 100644 --- a/OpenSSH_GUI.SshConfig/Parsers/SshConfigParser.cs +++ b/OpenSSH_GUI.SshConfig/Parsers/SshConfigParser.cs @@ -411,7 +411,22 @@ private static SshConfigDocument ResolveInclude( foreach (var file in Directory.GetFiles(dir, filePattern).Order(StringComparer.Ordinal)) { - var fileContent = File.ReadAllText(file); + string fileContent; + try + { + fileContent = File.ReadAllText(file); + } + catch (UnauthorizedAccessException ex) + { + options.OnSkippedIncludeFile?.Invoke(file, ex); + continue; + } + catch (IOException ex) + { + options.OnSkippedIncludeFile?.Invoke(file, ex); + continue; + } + var included = ParseDocument(fileContent, file, options, depth + 1); globalItems.AddRange(included.GlobalItems); blocks.AddRange(included.Blocks); diff --git a/OpenSSH_GUI.SshConfig/Services/SshConfigurationProvider.cs b/OpenSSH_GUI.SshConfig/Services/SshConfigurationProvider.cs index 66a328d..f2b78db 100644 --- a/OpenSSH_GUI.SshConfig/Services/SshConfigurationProvider.cs +++ b/OpenSSH_GUI.SshConfig/Services/SshConfigurationProvider.cs @@ -32,7 +32,7 @@ public override void Load(Stream stream) // We use the file path from the source if available for better error messages. var filePath = Source.Path; var document = SshConfigParser.Parse(content, - new SshConfigParserOptions { IncludeBasePath = filePath is null ? null : Path.GetDirectoryName(filePath) }); + new SshConfigParserOptions { IncludeBasePath = filePath is null ? null : Path.GetDirectoryName(filePath), OnSkippedIncludeFile = Source is SshConfigurationSource source ? source.OnSkippedIncludeFile : null }); var data = new Dictionary(StringComparer.OrdinalIgnoreCase); diff --git a/OpenSSH_GUI.SshConfig/Services/SshConfigurationSource.cs b/OpenSSH_GUI.SshConfig/Services/SshConfigurationSource.cs index 56893e2..78a92b2 100644 --- a/OpenSSH_GUI.SshConfig/Services/SshConfigurationSource.cs +++ b/OpenSSH_GUI.SshConfig/Services/SshConfigurationSource.cs @@ -7,6 +7,14 @@ namespace OpenSSH_GUI.SshConfig.Services; /// public sealed class SshConfigurationSource : FileConfigurationSource { + /// + /// Optional callback invoked when an included file cannot be read due to + /// insufficient permissions or an I/O error. Receives the file path and the + /// causing exception. When , inaccessible files are + /// silently skipped. + /// + public Action? OnSkippedIncludeFile { get; init; } + /// /// Builds the for this source. /// diff --git a/OpenSSH_GUI/Program.cs b/OpenSSH_GUI/Program.cs index 86e6ace..76b4bc7 100644 --- a/OpenSSH_GUI/Program.cs +++ b/OpenSSH_GUI/Program.cs @@ -99,10 +99,15 @@ public static async Task Main(string[] args) private static void ConfigureAppConfiguration(HostBuilderContext builderContext, IConfigurationBuilder configurationBuilder) { - configurationBuilder.AddSshConfig(ConfigFile.GetPathOfFile(), true, true); - configurationBuilder.AddSshConfig(SshdConfig.GetPathOfFile(), true, true); + configurationBuilder.AddSshConfig(ConfigFile.GetPathOfFile(), true, true, LoggingAction); + configurationBuilder.AddSshConfig(SshdConfig.GetPathOfFile(), true, true, LoggingAction); configurationBuilder.AddInMemoryCollection([ new KeyValuePair(VersionEnvVar, GetHostVersion()) ]); } + + private static void LoggingAction(string arg1, Exception arg2) + { + Log.Logger.Error(arg2, "Failed to load SSH config file: {Path}", arg1); + } } \ No newline at end of file From b3e8a9cdbba74ff9d381c15bb3426f88b6f344fa Mon Sep 17 00:00:00 2001 From: Oliver Schantz Date: Tue, 24 Mar 2026 10:04:03 +0100 Subject: [PATCH 4/4] remove warnings as error in csproj --- OpenSSH_GUI.SshConfig/OpenSSH_GUI.SshConfig.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/OpenSSH_GUI.SshConfig/OpenSSH_GUI.SshConfig.csproj b/OpenSSH_GUI.SshConfig/OpenSSH_GUI.SshConfig.csproj index 0042c3b..ed5af58 100644 --- a/OpenSSH_GUI.SshConfig/OpenSSH_GUI.SshConfig.csproj +++ b/OpenSSH_GUI.SshConfig/OpenSSH_GUI.SshConfig.csproj @@ -3,7 +3,6 @@ false true - true true OpenSSH_GUI.SshConfig