Beispiel #1
0
        /// <summary>
        /// Parses Unix format listings with alternate parser
        /// </summary>
        /// <param name="record">A line from the listing</param>
        /// <returns>FtpListItem if the item is able to be parsed</returns>
        public static FtpListItem ParseUnixAlt(FtpClient client, string record)
        {
            // test it is a valid line, e.g. "total 342522" is invalid
            char ch = record[0];

            if (ch != FileMarker && ch != DirectoryMarker && ch != SymbolicLinkMarker)
            {
                return(null);
            }

            string[] values = record.SplitString();

            if (values.Length < MinFieldCountAlt)
            {
                StringBuilder listing = new StringBuilder("Unexpected number of fields in listing '");
                listing.Append(record)
                .Append("' - expected minimum ").Append(MinFieldCountAlt)
                .Append(" fields but found ").Append(values.Length).Append(" fields");
                throw new FormatException(listing.ToString());
            }

            // field pos
            int index = 0;

            // first field is perms
            string permissions = values[index++];

            ch = permissions[0];
            bool isDir  = false;
            bool isLink = false;

            if (ch == DirectoryMarker)
            {
                isDir = true;
            }
            else if (ch == SymbolicLinkMarker)
            {
                isLink = true;
            }

            string group = values[index++];

            // some servers don't supply the link count
            int linkCount = 0;

            if (Char.IsDigit(values[index][0]))
            {
                // assume it is if a digit
                string linkCountStr = values[index++];
                try {
                    linkCount = System.Int32.Parse(linkCountStr);
                } catch (FormatException) {
                    client.LogStatus(FtpTraceLevel.Error, "Failed to parse link count: " + linkCountStr);
                }
            }

            string owner = values[index++];


            // size
            long   size    = 0L;
            string sizeStr = values[index++];

            try {
                size = Int64.Parse(sizeStr);
            } catch (FormatException) {
                client.LogStatus(FtpTraceLevel.Error, "Failed to parse size: " + sizeStr);
            }

            // next 3 fields are the date time

            // we expect the month first on Unix.
            int           dateTimePos  = index;
            DateTime      lastModified = DateTime.MinValue;
            StringBuilder stamp        = new StringBuilder(values[index++]);

            stamp.Append('-').Append(values[index++]).Append('-');

            string field = values[index++];

            if (field.IndexOf((System.Char) ':') < 0)
            {
                stamp.Append(field);                 // year
                try {
                    lastModified = DateTime.ParseExact(stamp.ToString(), DateTimeAltFormats1, client.ListingCulture.DateTimeFormat, DateTimeStyles.None);
                } catch (FormatException) {
                    client.LogStatus(FtpTraceLevel.Error, "Failed to parse date string '" + stamp.ToString() + "'");
                }
            }
            else
            {
                // add the year ourselves as not present
                int year = client.ListingCulture.Calendar.GetYear(DateTime.Now);
                stamp.Append(year).Append('-').Append(field);
                try {
                    lastModified = DateTime.ParseExact(stamp.ToString(), DateTimeAltFormats2, client.ListingCulture.DateTimeFormat, DateTimeStyles.None);
                } catch (FormatException) {
                    client.LogStatus(FtpTraceLevel.Error, "Failed to parse date string '" + stamp.ToString() + "'");
                }

                // can't be in the future - must be the previous year
                // add 2 days for time zones (thanks hgfischer)
                if (lastModified > DateTime.Now.AddDays(2))
                {
                    lastModified = lastModified.AddYears(-1);
                }
            }

            // name of file or dir. Extract symlink if possible
            string name = null;

            // find the starting point of the name by finding the pos of all the date/time fields
            int  pos = 0;
            bool ok  = true;

            for (int i = dateTimePos; i < dateTimePos + 3; i++)
            {
                pos = record.IndexOf(values[i], pos);
                if (pos < 0)
                {
                    ok = false;
                    break;
                }
                else
                {
                    pos += values[i].Length;
                }
            }
            if (ok)
            {
                name = record.Substring(pos).Trim();
            }
            else
            {
                client.LogStatus(FtpTraceLevel.Error, "Failed to retrieve name: " + record);
            }

            // create a new list item object with the parsed metadata
            FtpListItem file = new FtpListItem(record, name, size, isDir, ref lastModified);

            if (isLink)
            {
                file.Type      = FtpFileSystemObjectType.Link;
                file.LinkCount = linkCount;
            }
            file.RawGroup       = group;
            file.RawOwner       = owner;
            file.RawPermissions = permissions;
            file.CalculateUnixPermissions(permissions);
            return(file);
        }
Beispiel #2
0
        /// <summary>
        /// Parses Unix format listings
        /// </summary>
        /// <param name="record">A line from the listing</param>
        /// <returns>FtpListItem if the item is able to be parsed</returns>
        public static FtpListItem Parse(FtpClient client, string record)
        {
            // test it is a valid line, e.g. "total 342522" is invalid
            char ch = record[0];

            if (ch != FileMarker && ch != DirectoryMarker && ch != SymbolicLinkMarker)
            {
                return(null);
            }

            string[] values = record.SplitString();

            if (values.Length < MinFieldCount)
            {
                StringBuilder msg = new StringBuilder("Unexpected number of fields in listing '");
                msg
                .Append(record)
                .Append("' - expected minimum ").Append(MinFieldCount)
                .Append(" fields but found ").Append(values.Length).Append(" fields");
                client.LogStatus(FtpTraceLevel.Verbose, msg.ToString());
                return(null);
            }

            // field pos
            int index = 0;

            // first field is perms
            string permissions;
            bool   isDir, isLink;

            ParsePermissions(values, ref index, out permissions, out isDir, out isLink);

            // some servers don't supply the link count
            int linkCount = ParseLinkCount(client, values, ref index);

            // parse owner & group permissions
            string owner, group;

            ParseOwnerGroup(values, ref index, out owner, out group);

            // parse size
            long size = ParseFileSize(client, values, ref index);

            // parse the date/time fields
            int dayOfMonth = ParseDayOfMonth(values, ref index);

            int      dateTimePos  = index;
            DateTime lastModified = DateTime.MinValue;

            ParseDateTime(client, values, ref index, dayOfMonth, ref lastModified);

            // parse name of file or dir. Extract symlink if possible
            string name       = null;
            string linkedname = null;

            ParseName(client, record, values, isLink, dayOfMonth, dateTimePos, ref name, ref linkedname);

            // create a new list item object with the parsed metadata
            FtpListItem file = new FtpListItem(record, name, size, isDir, ref lastModified);

            if (isLink)
            {
                file.Type       = FtpFileSystemObjectType.Link;
                file.LinkCount  = linkCount;
                file.LinkTarget = linkedname.Trim();
            }
            file.RawGroup       = group;
            file.RawOwner       = owner;
            file.RawPermissions = permissions;
            file.CalculateUnixPermissions(permissions);
            return(file);
        }
Beispiel #3
0
        /// <summary>
        /// Parses LIST format listings
        /// </summary>
        /// <param name="record">A line from the listing</param>
        /// <param name="capabilities">Server capabilities</param>
        /// <returns>FtpListItem if the item is able to be parsed</returns>
        public static FtpListItem ParseLegacy(string record, FtpCapability capabilities, FtpClient client)
        {
            string regex =
                @"(?<permissions>.+)\s+" +
                @"(?<objectcount>\d+)\s+" +
                @"(?<user>.+)\s+" +
                @"(?<group>.+)\s+" +
                @"(?<size>\d+)\s+" +
                @"(?<modify>\w+\s+\d+\s+\d+:\d+|\w+\s+\d+\s+\d+)\s" +
                @"(?<name>.*)$";
            FtpListItem item = new FtpListItem();
            Match       m;

            if (!(m = Regex.Match(record, regex, RegexOptions.IgnoreCase)).Success)
            {
                return(null);
            }

            // if this field is missing we can't determine
            // what the object is.
            if (m.Groups["permissions"].Value.Length == 0)
            {
                return(null);
            }
            switch (m.Groups["permissions"].Value[0])
            {
            case 'd':
                item.Type = FtpFileSystemObjectType.Directory;
                break;

            case '-':
            case 's':
                item.Type = FtpFileSystemObjectType.File;
                break;

            case 'l':
                item.Type = FtpFileSystemObjectType.Link;
                break;

            default:
                return(null);
            }

            // if we can't determine a file name then
            // we are not considering this a successful parsing operation.
            if (m.Groups["name"].Value.Length < 1)
            {
                return(null);
            }
            item.Name = m.Groups["name"].Value;

            switch (item.Type)
            {
            case FtpFileSystemObjectType.Directory:
                // ignore these...
                if (item.Name == "." || item.Name == "..")
                {
                    return(null);
                }
                break;

            case FtpFileSystemObjectType.Link:
                if (!item.Name.Contains(" -> "))
                {
                    return(null);
                }
                item.LinkTarget = item.Name.Remove(0, item.Name.IndexOf("-> ") + 3).Trim();
                item.Name       = item.Name.Remove(item.Name.IndexOf(" -> "));
                break;
            }


            // Ignore the Modify times sent in LIST format for files
            // when the server has support for the MDTM command
            // because they will never be as accurate as what can be had
            // by using the MDTM command. MDTM does not work on directories
            // so if a modify time was parsed from the listing we will try
            // to convert it to a DateTime object and use it for directories.
            ////
            if (((capabilities & FtpCapability.MDTM) != FtpCapability.MDTM || item.Type == FtpFileSystemObjectType.Directory) && m.Groups["modify"].Value.Length > 0)
            {
                item.Modified = m.Groups["modify"].Value.GetFtpDate(DateTimeStyles.AssumeLocal);
                if (item.Modified == DateTime.MinValue)
                {
                    client.LogStatus(FtpTraceLevel.Warn, "GetFtpDate() failed on " + m.Groups["modify"].Value);
                }
            }
            else
            {
                if (m.Groups["modify"].Value.Length == 0)
                {
                    client.LogStatus(FtpTraceLevel.Warn, "RegEx failed to parse modified date from " + record);
                }
                else if (item.Type == FtpFileSystemObjectType.Directory)
                {
                    client.LogStatus(FtpTraceLevel.Warn, "Modified times of directories are ignored in UNIX long listings.");
                }
                else if ((capabilities & FtpCapability.MDTM) == FtpCapability.MDTM)
                {
                    client.LogStatus(FtpTraceLevel.Warn, "Ignoring modified date because MDTM feature is present. If you aren't already, pass FtpListOption.Modify or FtpListOption.SizeModify to GetListing() to retrieve the modification time.");
                }
            }

            if (m.Groups["size"].Value.Length > 0)
            {
                long size;

                if (long.TryParse(m.Groups["size"].Value, out size))
                {
                    item.Size = size;
                }
            }

            if (m.Groups["permissions"].Value.Length > 0)
            {
                item.CalculateUnixPermissions(m.Groups["permissions"].Value);
            }

            return(item);
        }
Beispiel #4
0
        /// <summary>
        /// Parses Unix format listings with alternate parser
        /// </summary>
        /// <param name="client">The FTP client</param>
        /// <param name="record">A line from the listing</param>
        /// <returns>FtpListItem if the item is able to be parsed</returns>
        public static FtpListItem ParseUnixAlt(FtpClient client, string record)
        {
            // test it is a valid line, e.g. "total 342522" is invalid
            var ch = record[0];

            if (ch != FileMarker && ch != DirectoryMarker && ch != SymbolicLinkMarker)
            {
                return(null);
            }

            var values = record.SplitString();

            if (values.Length < MinFieldCountAlt)
            {
                var listing = new StringBuilder("Unexpected number of fields in listing '");
                listing.Append(record)
                .Append("' - expected minimum ").Append(MinFieldCountAlt)
                .Append(" fields but found ").Append(values.Length).Append(" fields");
                throw new FormatException(listing.ToString());
            }

            // field pos
            var index = 0;

            // first field is perms
            var permissions = values[index++];

            ch = permissions[0];
            var isDir  = false;
            var isLink = false;

            if (ch == DirectoryMarker)
            {
                isDir = true;
            }
            else if (ch == SymbolicLinkMarker)
            {
                isLink = true;
            }

            var group = values[index++];

            // some servers don't supply the link count
            var linkCount = 0;

            if (char.IsDigit(values[index][0]))
            {
                // assume it is if a digit
                var linkCountStr = values[index++];
                try {
                    linkCount = int.Parse(linkCountStr);
                }
                catch (FormatException) {
                    client.LogStatus(FtpTraceLevel.Error, "Failed to parse link count: " + linkCountStr);
                }
            }

            var owner = values[index++];


            // size
            var size    = 0L;
            var sizeStr = values[index++];

            try {
                size = long.Parse(sizeStr);
            }
            catch (FormatException) {
                client.LogStatus(FtpTraceLevel.Error, "Failed to parse size: " + sizeStr);
            }

            // next 3 fields are the date time

            // we expect the month first on Unix.
            var dateTimePos  = index;
            var lastModified = DateTime.MinValue;
            var stamp        = new StringBuilder(values[index++]);

            stamp.Append('-').Append(values[index++]).Append('-');

            var field = values[index++];

            if (field.IndexOf((char)':') < 0)
            {
                stamp.Append(field);                 // year
                lastModified = ParseYear(client, stamp, DateTimeAltFormats1);
            }
            else
            {
                // add the year ourselves as not present
                var year = client.ListingCulture.Calendar.GetYear(DateTime.Now);
                stamp.Append(year).Append('-').Append(field);
                lastModified = ParseDateTime(client, stamp, DateTimeAltFormats2);
            }

            // name of file or dir. Extract symlink if possible
            string name = null;

            // find the starting point of the name by finding the pos of all the date/time fields
            var pos = 0;
            var ok  = true;

            for (var i = dateTimePos; i < dateTimePos + 3; i++)
            {
                pos = record.IndexOf(values[i], pos);
                if (pos < 0)
                {
                    ok = false;
                    break;
                }
                else
                {
                    pos += values[i].Length;
                }
            }

            if (ok)
            {
                name = record.Substring(pos).Trim();
            }
            else
            {
                client.LogStatus(FtpTraceLevel.Error, "Failed to retrieve name: " + record);
            }

            // create a new list item object with the parsed metadata
            var file = new FtpListItem(record, name, size, isDir, ref lastModified);

            if (isLink)
            {
                file.Type      = FtpFileSystemObjectType.Link;
                file.LinkCount = linkCount;
            }

            file.RawGroup       = group;
            file.RawOwner       = owner;
            file.RawPermissions = permissions;
            file.CalculateUnixPermissions(permissions);
            return(file);
        }