Newer
Older
Sunfish / src / Sunfish.Core / Ftp / FtpListing.cs
@Ivan Dominguez Ivan Dominguez 21 hours ago 2 KB 3.0
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;
    }
}