using System.Globalization;
using Sunfish.Config;
using Sunfish.Shares;
namespace Sunfish.Ftp;
/// <summary>
/// Formatos de listado. LIST usa el de "ls -l" de Unix, que es el que entienden los clientes
/// antiguos (clientes graficos de Mac y Windows de los 90, navegadores con ftp://). MLSD y MLST
/// siguen RFC 3659 para los modernos.
/// </summary>
internal static class FtpListing
{
private static readonly string[] Months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
/// <summary>"drwxr-xr-x 1 ftp ftp 0 Nov 04 13:05 nombre" (o el ano si tiene mas de 6 meses).</summary>
public static string UnixLine(FileEntry e, DateTime now)
{
string mode = e.IsDirectory ? "drwxr-xr-x" : "-rw-r--r--";
var t = e.IsVirtual ? now : e.Modified;
string when = Math.Abs((now - t).TotalDays) < 180
? $"{Months[t.Month - 1]} {t.Day,2} {t:HH':'mm}"
: $"{Months[t.Month - 1]} {t.Day,2} {t.Year}";
string size = (e.IsDirectory ? 0 : e.Size).ToString(CultureInfo.InvariantCulture);
return $"{mode} 1 ftp ftp {size,12} {when} {e.Name}";
}
/// <summary>Hechos de MLSD/MLST: "type=file;size=12;modify=20261104130500;perm=rwdf; nombre".</summary>
public static string Facts(FileEntry e, string type, ShareConfig? share, bool readOnly)
{
string perm = Permissions(e.IsDirectory, share, readOnly || e.IsVirtual);
var facts = $"type={type};";
if (!e.IsDirectory)
facts += $"size={e.Size.ToString(CultureInfo.InvariantCulture)};";
if (!e.IsVirtual)
facts += $"modify={Timestamp(e.Modified)};";
return facts + $"perm={perm}; {e.Name}";
}
/// <summary>Hora en UTC como la piden MDTM y MLSD: yyyyMMddHHmmss.</summary>
public static string Timestamp(DateTime local) =>
local.ToUniversalTime().ToString("yyyyMMddHHmmss", CultureInfo.InvariantCulture);
private static string Permissions(bool directory, ShareConfig? share, bool readOnly)
{
if (directory)
{
string p = "e" + (share is null || share.Listing ? "l" : "");
if (share is not null && !readOnly)
{
if (share.Upload) p += "cm";
if (share.Delete) p += "dp";
if (share.Rename) p += "f";
}
return p;
}
string f = "r";
if (share is not null && !readOnly)
{
if (share.Overwrite) f += "wa";
if (share.Delete) f += "d";
if (share.Rename) f += "f";
}
return f;
}
}