Skip to content

Add configuration overrides for BaseEncoding, ForceFileSystem, and ServerCertificateValidationCallback (#46, #41, #33)#53

Merged
sparkeh9 merged 3 commits intomasterfrom
fix/open-issues
Mar 1, 2026
Merged

Add configuration overrides for BaseEncoding, ForceFileSystem, and ServerCertificateValidationCallback (#46, #41, #33)#53
sparkeh9 merged 3 commits intomasterfrom
fix/open-issues

Conversation

@sparkeh9
Copy link
Copy Markdown
Owner

This PR adds three configuration overrides to handle non-compliant, legacy, or obfuscated FTP servers, resolving several open issues.

Resolves

Copilot AI review requested due to automatic review settings February 28, 2026 22:31
@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the CoreFtp library's compatibility and flexibility by introducing three new configuration options. These additions allow users to overcome common challenges encountered with non-standard, legacy, or misconfigured FTP servers, such as character encoding issues, strict certificate validation, and unreliable file system type detection. The changes provide greater control over the client's behavior, making it more robust in diverse environments.

Highlights

  • Base Encoding Configuration: Added FtpClientConfiguration.BaseEncoding to allow users to specify custom encodings (e.g., GBK, Shift_JIS) for control connections, improving compatibility with legacy FTP servers.
  • Server Certificate Validation Override: Introduced FtpClientConfiguration.ServerCertificateValidationCallback to enable custom logic for validating server certificates during FTPS connections, addressing issues with strict policies or self-signed certificates.
  • Forced File System Type: Implemented FtpClientConfiguration.ForceFileSystem which allows users to explicitly set the file system type (Windows or Unix) for directory parsing, useful when servers do not properly report their OS via the FEAT command.
Changelog
  • src/CoreFtp/Components/DirectoryListing/ListDirectoryProvider.cs
    • Added internal ClearParsers method to remove all existing directory parsers.
    • Added internal AddParser method to append a new directory parser to the list.
  • src/CoreFtp/Enum/FtpFileSystemType.cs
    • Introduced a new FtpFileSystemType enumeration with Windows and Unix values.
  • src/CoreFtp/FtpClient.cs
    • Updated the Configure method to assign the BaseEncoding from the client configuration to the control stream.
    • Modified the DetermineDirectoryProvider method to respect the Configuration.ForceFileSystem setting, allowing explicit selection of a directory parser.
  • src/CoreFtp/FtpClientConfiguration.cs
    • Added using System; and using System.Net.Security; directives.
    • Introduced BaseEncoding property (defaulting to ASCII) for specifying the control stream's character encoding.
    • Added ServerCertificateValidationCallback property for custom FTPS server certificate validation logic.
    • Added ForceFileSystem nullable property to override automatic file system type detection for directory listings.
  • src/CoreFtp/Infrastructure/Stream/FtpControlStream.cs
    • Modified the OnValidateCertificate method to invoke the ServerCertificateValidationCallback from the configuration if it is provided.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces three valuable configuration overrides (BaseEncoding, ForceFileSystem, and ServerCertificateValidationCallback) to enhance compatibility with various FTP servers. The implementation is solid and correctly integrates these new options. My review includes one suggestion to improve the maintainability of the file system forcing logic by using a switch statement, which will make it more robust for future extensions.

Comment on lines +501 to +504
if ( Configuration.ForceFileSystem.Value == FtpFileSystemType.Windows )
forcedProvider.AddParser( new Components.DirectoryListing.Parser.DosDirectoryParser( Logger ) );
else
forcedProvider.AddParser( new Components.DirectoryListing.Parser.UnixDirectoryParser( Logger ) );
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current if/else structure implicitly treats any non-Windows file system as Unix. This could lead to unexpected behavior if new FtpFileSystemType values are added in the future. A switch statement would be more robust and explicit, ensuring that only supported file system types are handled and providing a clear place to add new ones. It also allows for a default case to throw an exception for unsupported types, preventing silent failures.

                switch (Configuration.ForceFileSystem.Value)
                {
                    case FtpFileSystemType.Windows:
                        forcedProvider.AddParser(new Components.DirectoryListing.Parser.DosDirectoryParser(Logger));
                        break;
                    case FtpFileSystemType.Unix:
                        forcedProvider.AddParser(new Components.DirectoryListing.Parser.UnixDirectoryParser(Logger));
                        break;
                    default:
                        throw new NotSupportedException($"Unsupported file system type: {Configuration.ForceFileSystem.Value}");
                }

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 14ab6adadb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +472 to +473
if (Configuration.ServerCertificateValidationCallback != null)
return Configuration.ServerCertificateValidationCallback(certificate, chain, errors);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Run custom cert validator before ignore-all certificate path

OnValidateCertificate checks IgnoreCertificateErrors first, so with the default config (IgnoreCertificateErrors = true in FtpClientConfiguration) a provided ServerCertificateValidationCallback is never called and any server certificate is accepted. This is a security footgun for callers who set a callback for pinning or selective trust, because their validation logic is silently bypassed unless they also know to toggle a separate flag.

Useful? React with 👍 / 👎.

Comment on lines +499 to +503
forcedProvider.ClearParsers();

if ( Configuration.ForceFileSystem.Value == FtpFileSystemType.Windows )
forcedProvider.AddParser( new Components.DirectoryListing.Parser.DosDirectoryParser( Logger ) );
else
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bypass first-line parser probing when ForceFileSystem is enabled

This forced-filesystem branch only reduces ListDirectoryProvider to one parser, but parsing still depends on first-line Test(...) probing in ParseLines; if the first LIST line is a header like total 12, the forced parser is rejected and no entries are returned. In that common server output shape, ForceFileSystem does not actually enforce the requested parser and the override can fail.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds three configuration override options to FtpClientConfiguration for handling non-standard, legacy, or otherwise problematic FTP servers, resolving issues #33, #41, and #46.

Changes:

  • BaseEncoding: Allows specifying a custom character encoding (e.g., GBK, Shift_JIS) for the control stream, instead of the default ASCII, to support legacy servers.
  • ServerCertificateValidationCallback: Provides a delegate for custom FTPS certificate validation logic (e.g., accepting self-signed certificates by thumbprint).
  • ForceFileSystem: Adds a new FtpFileSystemType enum and lets users specify Windows or Unix as the directory listing format when auto-detection fails.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/CoreFtp/FtpClientConfiguration.cs Adds three new configuration properties: BaseEncoding, ServerCertificateValidationCallback, and ForceFileSystem
src/CoreFtp/Enum/FtpFileSystemType.cs New enum with Windows and Unix values for ForceFileSystem
src/CoreFtp/FtpClient.cs Sets ControlStream.Encoding from BaseEncoding at construction; adds ForceFileSystem branch in DetermineDirectoryProvider()
src/CoreFtp/Infrastructure/Stream/FtpControlStream.cs Delegates certificate validation to ServerCertificateValidationCallback when set
src/CoreFtp/Components/DirectoryListing/ListDirectoryProvider.cs Adds internal ClearParsers() and AddParser() methods to support forced parser configuration

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +496 to +507
if ( Configuration.ForceFileSystem.HasValue )
{
var forcedProvider = new ListDirectoryProvider( this, Logger, Configuration );
forcedProvider.ClearParsers();

if ( Configuration.ForceFileSystem.Value == FtpFileSystemType.Windows )
forcedProvider.AddParser( new Components.DirectoryListing.Parser.DosDirectoryParser( Logger ) );
else
forcedProvider.AddParser( new Components.DirectoryListing.Parser.UnixDirectoryParser( Logger ) );

return forcedProvider;
}
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When ForceFileSystem is set and the server also supports MLSD, the code bypasses MlsdDirectoryProvider entirely and falls back to ListDirectoryProvider. Since ForceFileSystem is checked first (before the UsesMlsd() check), setting it on a server that does advertise MLSD causes a regression: the client uses the less-capable LIST-based provider instead of the more reliable MLSD-based one.

The intent from issue #41 is to handle the case where MLSD is not available and auto-detection of the LIST format fails. The fix should only apply ForceFileSystem when MLSD is not available — either by changing the condition to !this.UsesMlsd() && Configuration.ForceFileSystem.HasValue, or by restructuring the check so ForceFileSystem only influences the parser selection within the LIST-based path, not the MLSD selection.

Copilot uses AI. Check for mistakes.
public Func<X509Certificate, X509Chain, SslPolicyErrors, bool> ServerCertificateValidationCallback { get; set; }

/// <summary>
/// Allows overriding the OS directory provider. Useful if the server does not report its OS in FEAT (e.g. FtpFileSystemType.Windows).
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring says "Useful if the server does not report its OS in FEAT" but this is inaccurate. FEAT does not report the server OS; it reports supported FTP extension commands. The actual condition requiring ForceFileSystem is when the server does not advertise MLSD in the FEAT response (causing the client to fall back to LIST-based parsing), and the auto-detection of the listing format then fails. The docstring should be updated to accurately describe this condition.

Suggested change
/// Allows overriding the OS directory provider. Useful if the server does not report its OS in FEAT (e.g. FtpFileSystemType.Windows).
/// Allows overriding the detected server file system / directory listing format.
/// Useful when the server does not advertise MLSD in its FEAT response (causing a fallback to LIST),
/// and the automatic detection of the LIST output format fails (e.g. force FtpFileSystemType.Windows).

Copilot uses AI. Check for mistakes.
public System.Text.Encoding BaseEncoding { get; set; } = System.Text.Encoding.ASCII;

/// <summary>
/// Allows overriding the server certificate validation logic (e.g., verifying a specific self-signed certificate thumbprint).
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ServerCertificateValidationCallback is silently bypassed when IgnoreCertificateErrors is true (which is the default). The OnValidateCertificate method returns true immediately in that case, so the callback is never invoked.

This means a user who sets ServerCertificateValidationCallback without also setting IgnoreCertificateErrors = false will have their callback silently ignored — their custom validation logic will never run. This is particularly problematic given that the default value of IgnoreCertificateErrors is true, making the callback effectively dead code unless the user knows to change the flag.

The docstring on ServerCertificateValidationCallback should explicitly note that it requires IgnoreCertificateErrors to be set to false to take effect.

Suggested change
/// Allows overriding the server certificate validation logic (e.g., verifying a specific self-signed certificate thumbprint).
/// Allows overriding the server certificate validation logic (e.g., verifying a specific self-signed certificate thumbprint).
/// Note: This callback is only invoked when <see cref="IgnoreCertificateErrors"/> is set to <c>false</c>.
/// When <see cref="IgnoreCertificateErrors"/> is <c>true</c> (the default), certificate errors are ignored and this callback is not used.

Copilot uses AI. Check for mistakes.
…ck bug, fix parser probe on single parsers, and clarify cert callback docstring
@sparkeh9
Copy link
Copy Markdown
Owner Author

sparkeh9 commented Mar 1, 2026

Thanks for the thorough review! I've addressed the feedback in 8e9ff72:

  1. Robust ForceFileSystem Switch: Replaced the if/else block with an explicit switch statement that throws a NotSupportedException for unhandled types.
  2. MLSD Override Bug: Restored priority to MLSD auto-detection so forcing a legacy filesystem doesn't unnecessarily downgrade modern servers.
  3. Parser Probing Bug: Updated ParseLines to bypass the Test() probe entirely if there is only a single forced parser (fixing the issue where header lines like total 12 would fail the initial probe).
  4. Certificate Docstring: Updated the XML comment for ServerCertificateValidationCallback to clarify that IgnoreCertificateErrors must be explicitly set to false for the callback to trigger.

@sparkeh9 sparkeh9 merged commit 7c2183a into master Mar 1, 2026
1 check passed
@sparkeh9 sparkeh9 deleted the fix/open-issues branch March 1, 2026 00:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants