- 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.
163 lines
4.6 KiB
C#
163 lines
4.6 KiB
C#
using System.IO.Abstractions;
|
|
using System.Web;
|
|
using RdtClient.Data.Models.Data;
|
|
|
|
namespace RdtClient.Service.Helpers;
|
|
|
|
public static class DownloadHelper
|
|
{
|
|
public static String? GetDownloadPath(String downloadPath, Torrent torrent, Download download, IFileSystem? fileSystem = null)
|
|
{
|
|
var fileUrl = download.Link;
|
|
|
|
if (String.IsNullOrWhiteSpace(fileUrl) || torrent.RdName == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var originalDirectory = RemoveInvalidPathChars(torrent.RdName);
|
|
var directory = FilenameSanitizer.SanitizeFilenameIfEnabled(originalDirectory);
|
|
|
|
var torrentPath = Path.Combine(downloadPath, directory);
|
|
|
|
var originalFileName = GetFileName(download);
|
|
|
|
if (originalFileName == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var matchingTorrentFiles = torrent.Files.Where(m => m.Path.EndsWith(originalFileName)).Where(m => !String.IsNullOrWhiteSpace(m.Path)).ToList();
|
|
|
|
if (matchingTorrentFiles.Count > 0)
|
|
{
|
|
var matchingTorrentFile = matchingTorrentFiles[0];
|
|
|
|
var subPath = Path.GetDirectoryName(matchingTorrentFile.Path);
|
|
|
|
if (!String.IsNullOrWhiteSpace(subPath))
|
|
{
|
|
subPath = subPath.Trim('/').Trim('\\');
|
|
|
|
subPath = StripTorrentNamePrefix(subPath, originalDirectory);
|
|
|
|
if (!String.IsNullOrWhiteSpace(subPath))
|
|
{
|
|
subPath = FilenameSanitizer.SanitizePathIfEnabled(subPath);
|
|
|
|
torrentPath = Path.Combine(torrentPath, subPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
fileSystem ??= new FileSystem();
|
|
|
|
if (!fileSystem.Directory.Exists(torrentPath))
|
|
{
|
|
fileSystem.Directory.CreateDirectory(torrentPath);
|
|
}
|
|
|
|
var fileName = FilenameSanitizer.SanitizeFilenameIfEnabled(originalFileName);
|
|
|
|
var filePath = Path.Combine(torrentPath, fileName);
|
|
|
|
return filePath;
|
|
}
|
|
|
|
public static String? GetDownloadPath(Torrent torrent, Download download)
|
|
{
|
|
var fileUrl = download.Link;
|
|
|
|
if (String.IsNullOrWhiteSpace(fileUrl) || torrent.RdName == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var uri = new Uri(fileUrl);
|
|
var torrentPath = RemoveInvalidPathChars(torrent.RdName);
|
|
|
|
var fileName = download.FileName;
|
|
|
|
if (String.IsNullOrWhiteSpace(fileName))
|
|
{
|
|
fileName = uri.Segments.Last();
|
|
|
|
fileName = HttpUtility.UrlDecode(fileName);
|
|
}
|
|
|
|
var matchingTorrentFiles = torrent.Files.Where(m => m.Path.EndsWith(fileName)).Where(m => !String.IsNullOrWhiteSpace(m.Path)).ToList();
|
|
|
|
if (matchingTorrentFiles.Count > 0)
|
|
{
|
|
var matchingTorrentFile = matchingTorrentFiles[0];
|
|
|
|
var subPath = Path.GetDirectoryName(matchingTorrentFile.Path);
|
|
|
|
if (!String.IsNullOrWhiteSpace(subPath))
|
|
{
|
|
subPath = subPath.Trim('/').Trim('\\');
|
|
|
|
subPath = StripTorrentNamePrefix(subPath, torrentPath);
|
|
|
|
if (!String.IsNullOrWhiteSpace(subPath))
|
|
{
|
|
torrentPath = Path.Combine(torrentPath, subPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
var filePath = Path.Combine(torrentPath, fileName);
|
|
|
|
return filePath;
|
|
}
|
|
|
|
public static String? GetFileName(Download download)
|
|
{
|
|
if (String.IsNullOrWhiteSpace(download.Link))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var fileName = download.FileName;
|
|
|
|
if (String.IsNullOrWhiteSpace(fileName))
|
|
{
|
|
fileName = HttpUtility.UrlDecode(new Uri(download.Link).Segments.Last());
|
|
}
|
|
|
|
return FileHelper.RemoveInvalidFileNameChars(fileName);
|
|
}
|
|
|
|
public static String RemoveInvalidPathChars(String path)
|
|
{
|
|
return String.Concat(path.Split(Path.GetInvalidPathChars()));
|
|
}
|
|
|
|
private static String StripTorrentNamePrefix(String subPath, String torrentName)
|
|
{
|
|
var separatorIndex = subPath.IndexOfAny(['/', '\\']);
|
|
|
|
String firstComponent;
|
|
String remainder;
|
|
|
|
if (separatorIndex < 0)
|
|
{
|
|
firstComponent = subPath;
|
|
remainder = String.Empty;
|
|
}
|
|
else
|
|
{
|
|
firstComponent = subPath[..separatorIndex];
|
|
remainder = subPath[(separatorIndex + 1)..];
|
|
}
|
|
|
|
var normalizedFirst = RemoveInvalidPathChars(firstComponent);
|
|
|
|
if (normalizedFirst.Equals(torrentName.TrimEnd('/', '\\'), StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return remainder;
|
|
}
|
|
|
|
return subPath;
|
|
}
|
|
}
|