- Revert GetFileName to return unsanitized name. AllDebridDebridClient. GetSymlinkPath matches torrent.Files[].Path (original debrid data) against GetFileName, so sanitizing it broke AllDebrid Symlink lookups for any torrent with bracketed filenames. Sanitization now happens inside GetDownloadPath at the point the filesystem path is assembled, while matching still uses the unsanitized name. - Make SanitizeFilename / SanitizePath accept String? and return String.Empty for null/empty input so the declared non-null return type is honoured. - SanitizePath splits on both '/' and '\\' regardless of host OS, so paths arriving from the debrid provider with foreign separators are still segmented correctly on Linux containers. - Rewrite the control-character tests to use explicit \u escape sequences (, , , , ) so the test inputs are visible in diffs and survive editor/CI round-trips. Replace the single-row empty [Theory] with a [Fact] and add a null case. Add tests asserting SanitizeFilenameIfEnabled and SanitizePathIfEnabled return input unchanged when the toggle is disabled, and sanitize when enabled.
101 lines
3.1 KiB
C#
101 lines
3.1 KiB
C#
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using RdtClient.Service.Services;
|
|
|
|
namespace RdtClient.Service.Helpers;
|
|
|
|
/// <summary>
|
|
/// Sanitizes filenames to prevent issues with special characters on Linux containers.
|
|
/// Path.GetInvalidFileNameChars() on Linux only returns NUL and '/', so characters
|
|
/// like [ ] { } that cause problems with shell globbing, URI handling, and various
|
|
/// download clients pass through the built-in filter unchanged.
|
|
/// </summary>
|
|
public static partial class FilenameSanitizer
|
|
{
|
|
[GeneratedRegex(@" {2,}")]
|
|
private static partial Regex CreateMultipleSpacesRegex();
|
|
|
|
private static readonly Regex MultipleSpaces = CreateMultipleSpacesRegex();
|
|
|
|
/// <summary>
|
|
/// Returns whether sanitization is enabled in the current settings.
|
|
/// </summary>
|
|
public static Boolean IsEnabled => Settings.Get.DownloadClient.SanitizeFilenames;
|
|
|
|
/// <summary>
|
|
/// Sanitizes a filename if enabled in settings; otherwise returns it unchanged.
|
|
/// </summary>
|
|
public static String SanitizeFilenameIfEnabled(String? filename)
|
|
{
|
|
return IsEnabled ? SanitizeFilename(filename) : filename ?? String.Empty;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sanitizes a full path if enabled in settings; otherwise returns it unchanged.
|
|
/// </summary>
|
|
public static String SanitizePathIfEnabled(String? filePath)
|
|
{
|
|
return IsEnabled ? SanitizePath(filePath) : filePath ?? String.Empty;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sanitizes a filename by stripping problematic characters and normalizing whitespace.
|
|
/// Does NOT touch directory separators — only a single filename segment.
|
|
/// </summary>
|
|
public static String SanitizeFilename(String? filename)
|
|
{
|
|
if (String.IsNullOrEmpty(filename))
|
|
{
|
|
return String.Empty;
|
|
}
|
|
|
|
var sb = new StringBuilder(filename.Length);
|
|
|
|
foreach (var c in filename)
|
|
{
|
|
if (Char.IsControl(c))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (c is '[' or ']' or '{' or '}')
|
|
{
|
|
continue;
|
|
}
|
|
|
|
sb.Append(c);
|
|
}
|
|
|
|
var result = sb.ToString();
|
|
result = MultipleSpaces.Replace(result, " ");
|
|
result = result.Trim();
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sanitizes each segment of a full file path, preserving directory separators.
|
|
/// Splits on both '/' and '\' regardless of host OS so paths that arrive
|
|
/// from the debrid provider with foreign separators are still handled;
|
|
/// the result is re-joined with the host's native separator.
|
|
/// </summary>
|
|
public static String SanitizePath(String? filePath)
|
|
{
|
|
if (String.IsNullOrEmpty(filePath))
|
|
{
|
|
return String.Empty;
|
|
}
|
|
|
|
var segments = filePath.Split('/', '\\');
|
|
|
|
for (var i = 0; i < segments.Length; i++)
|
|
{
|
|
if (!String.IsNullOrEmpty(segments[i]))
|
|
{
|
|
segments[i] = SanitizeFilename(segments[i]);
|
|
}
|
|
}
|
|
|
|
return String.Join(Path.DirectorySeparatorChar, segments);
|
|
}
|
|
}
|