1

The output for FTP.ListDirectoryDetails is often a string similar to the one given below:

-rw-r--r-- 1 ftp ftp 960 Aug 31 09:09 test1.xml

How do I convert this into an object that is easy to use?

Kevat Shah
  • 119
  • 1
  • 12

1 Answers1

1

I could not find a decent solution for converting this into a useful object, so I created my own. I hope the code below helps someone else. If you have suggestions to improve this code, please feel free to contribute.

Here is the class:

/// <summary>
/// This class represents the file/directory information received via FTP.ListDirectoryDetails
/// </summary>
public class ListDirectoryDetailsOutput
{
    public bool IsDirectory { get; set; }
    public bool IsFile {
        get { return !IsDirectory; } 
    }

    //Owner permissions
    public bool OwnerRead { get; set; }
    public bool OwnerWrite { get; set; }
    public bool OwnerExecute { get; set; }

    //Group permissions
    public bool GroupRead { get; set; }
    public bool GroupWrite { get; set; }
    public bool GroupExecute { get; set; }

    //Other permissions
    public bool OtherRead { get; set; }
    public bool OtherWrite { get; set; }
    public bool OtherExecute { get; set; }

    public int NumberOfLinks { get; set; }
    public int Size { get; set; }
    public DateTime ModifiedDate { get; set; }
    public string Name { get; set; }

    public bool ParsingError { get; set; }
    public Exception ParsingException { get; set; }

    /// <summary>
    /// Parses the FTP response for ListDirectoryDetails into the Output object
    /// An example input of a file called test1.xml:
    /// -rw-r--r-- 1 ftp ftp            960 Aug 31 09:09 test1.xml
    /// </summary>
    /// <param name="ftpResponseLine"></param>
    public ListDirectoryDetailsOutput(string ftpResponseLine)
    {
        try
        {
            string[] responseList = ftpResponseLine.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);

            string permissions = responseList[0];
            //Get directory, currently only checking for not "-", might be beneficial to also check for "d" when mapping IsDirectory
            IsDirectory = !string.Equals(permissions.ElementAt(0), '-');

            //Get directory, currently only checking for not "-", might be beneficial to also check for r,w,x when mapping permissions
            //Get Owner Permissions
            OwnerRead = !string.Equals(permissions.ElementAt(1), '-');
            OwnerWrite = !string.Equals(permissions.ElementAt(2), '-');
            OwnerExecute = !string.Equals(permissions.ElementAt(3), '-');

            //Get Group Permissions
            GroupRead = !string.Equals(permissions.ElementAt(4), '-');
            GroupWrite = !string.Equals(permissions.ElementAt(5), '-');
            GroupExecute = !string.Equals(permissions.ElementAt(6), '-');

            //Get Other Permissions
            OtherRead = !string.Equals(permissions.ElementAt(7), '-');
            OtherWrite = !string.Equals(permissions.ElementAt(8), '-');
            OtherExecute = !string.Equals(permissions.ElementAt(9), '-');

            NumberOfLinks = int.Parse(responseList[1]);

            Size = int.Parse(responseList[4]);
            string dateStr = responseList[5] + " " + responseList[6] + " " + responseList[7];
            //Setting Year to the current year, can be changed if needed
            dateStr += " " + DateTime.Now.Year.ToString();

            ModifiedDate = DateTime.ParseExact(dateStr, "MMM dd hh:mm yyyy", CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces);

            Name = (responseList[8]);
            ParsingError = false;
        }
        catch (Exception ex)
        {
            ParsingException = ex;
            ParsingError = true;
        }
    }
}

And here is how I use it:

            // Get the object used to communicate with the server.
            FtpWebRequest request = (FtpWebRequest)WebRequest.Create(FTPURL);
            request.Credentials = new NetworkCredential(FTPUsername, FTPPassword);
            //Only required for SFTP
            //request.EnableSsl = true;
            request.Method = WebRequestMethods.Ftp.ListDirectoryDetails;

            FtpWebResponse response = (FtpWebResponse)request.GetResponse();
            StreamReader streamReader = new StreamReader(response.GetResponseStream());

            List<ListDirectoryDetailsOutput> directories = new List<ListDirectoryDetailsOutput>();

            string line = streamReader.ReadLine();
            while (!string.IsNullOrEmpty(line))
            {
                directories.Add(new ListDirectoryDetailsOutput(line));
                line = streamReader.ReadLine();
            }

            streamReader.Close();

PLEASE NOTE: This will only work if the input string is exactly in the same format. If not, ParsingError will be true, and the exception will be caught as ParsingException.

Note 2: Files older than 1 year have the format MMM dd yyyy , not MMM dd hh:mm. This will require some tweaks in the code above. Missing year does not always mean current year (code given assumes that incorrectly). Missing year just means within the last year. When getting a file in April with the date Dec 20, missing year would mean last year, not this year. (Thanks Martin!)

Kevat Shah
  • 119
  • 1
  • 12
  • 1
    This won't work 1) If the server does not use *nix-style listing at all 2) Typically files older than 1 year have time format `MMM dd yyyy` , not `MMM dd hh:mm`. 3) Missing year can be the last year, not the current year, if say, it's April now, and the date in the listing is `Dec 20`. 4) Some servers support user/group names with spaces (Windows in particular). – Martin Prikryl Sep 01 '16 at 07:46
  • Great points! I've already noted the exact format that this would work for, and that other formats would require some tweaking. I've added additional notes about the time format and missing year. – Kevat Shah Sep 08 '16 at 14:20