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/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
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
diff --git a/appimage/io.github.frequency403.openssh-gui.metainfo.xml b/appimage/io.github.frequency403.openssh_gui.metainfo.xml
similarity index 97%
rename from appimage/io.github.frequency403.openssh-gui.metainfo.xml
rename to 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