private static FtpListItem ParseVaxList(string buf, FtpCapability capabilities) { Match match; string pattern = @"(?<name>.+)\.(?<extension>.+);(?<version>\d+)\s+(?<size>\d+)\s+(?<modify>\d+-\w+-\d+\s+\d+:\d+)"; if (!(match = Regex.Match(buf, pattern)).Success) { return(null); } FtpListItem item = new FtpListItem { m_name = string.Format("{0}.{1};{2}", match.Groups["name"].Value, match.Groups["extension"].Value, match.Groups["version"].Value) }; if (match.Groups["extension"].Value.ToUpper() == "DIR") { item.m_type = FtpFileSystemObjectType.Directory; } else { item.m_type = FtpFileSystemObjectType.File; } if (!long.TryParse(match.Groups["size"].Value, out item.m_size)) { item.m_size = -1L; } if (!DateTime.TryParse(match.Groups["modify"].Value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out item.m_modified)) { item.m_modified = DateTime.MinValue; } return(item); }
/// <summary> /// Performs a bitwise and to check if the specified /// flag is set on the <see cref="Capabilities"/> property. /// </summary> /// <param name="cap">The <see cref="FtpCapability"/> to check for</param> /// <returns>True if the feature was found, false otherwise</returns> public bool HasFeature(FtpCapability cap) { if (cap == FtpCapability.NONE && Capabilities.Count == 0) { return true; } return Capabilities.Contains(cap); }
/// <summary> /// Parses IIS DOS format listings /// </summary> /// <param name="buf">A line from the listing</param> /// <param name="capabilities">Server capabilities</param> /// <returns>FtpListItem if the item is able to be parsed</returns> private static FtpListItem ParseDosList(string buf, FtpCapability capabilities) { FtpListItem item = new FtpListItem(); string[] datefmt = new string[] { "MM-dd-yy hh:mmtt", "MM-dd-yyyy hh:mmtt" }; Match m; // directory if ( (m = Regex.Match(buf, @"(?<modify>\d+-\d+-\d+\s+\d+:\d+\w+)\s+<DIR>\s+(?<name>.*)$", RegexOptions.IgnoreCase)) .Success) { DateTime modify; item.Type = FtpFileSystemObjectType.Directory; item.Name = m.Groups["name"].Value; //if (DateTime.TryParse(m.Groups["modify"].Value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out modify)) if (DateTime.TryParseExact(m.Groups["modify"].Value, datefmt, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out modify)) { item.Modified = modify; } } // file else if ( (m = Regex.Match(buf, @"(?<modify>\d+-\d+-\d+\s+\d+:\d+\w+)\s+(?<size>\d+)\s+(?<name>.*)$", RegexOptions.IgnoreCase)).Success) { DateTime modify; long size; item.Type = FtpFileSystemObjectType.File; item.Name = m.Groups["name"].Value; if (long.TryParse(m.Groups["size"].Value, out size)) { item.Size = size; } //if (DateTime.TryParse(m.Groups["modify"].Value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out modify)) if (DateTime.TryParseExact(m.Groups["modify"].Value, datefmt, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out modify)) { item.Modified = modify; } } else { return(null); } return(item); }
private static FtpListItem ParseDosList(string buf, FtpCapability capabilities) { Match match; DateTime time2; long num; FtpListItem item = new FtpListItem(); string[] formats = new string[] { "MM-dd-yy hh:mmtt", "MM-dd-yyyy hh:mmtt" }; if ((match = Regex.Match(buf, @"(?<modify>\d+-\d+-\d+\s+\d+:\d+\w+)\s+<DIR>\s+(?<name>.*)$", RegexOptions.IgnoreCase)).Success) { DateTime time; item.Type = FtpFileSystemObjectType.Directory; item.Name = match.Groups["name"].Value; if (DateTime.TryParseExact(match.Groups["modify"].Value, formats, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out time)) { item.Modified = time; } return(item); } if (!(match = Regex.Match(buf, @"(?<modify>\d+-\d+-\d+\s+\d+:\d+\w+)\s+(?<size>\d+)\s+(?<name>.*)$", RegexOptions.IgnoreCase)).Success) { return(null); } item.Type = FtpFileSystemObjectType.File; item.Name = match.Groups["name"].Value; if (long.TryParse(match.Groups["size"].Value, out num)) { item.Size = num; } if (DateTime.TryParseExact(match.Groups["modify"].Value, formats, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out time2)) { item.Modified = time2; } return(item); }
public static FtpListItem ParseWith(string path, string buf, FtpCapability capabilities, Parser parser) { var item = parser(buf, capabilities); if (item != null) { PostParse(path, buf, parser, item); } return(item); }
public static FtpListItem Parse(string path, string buf, FtpCapability capabilities) { if ((buf != null) && (buf.Length > 0)) { foreach (Parser parser in Parsers) { FtpListItem item = parser(buf, capabilities); if (item != null) { if (parser == new Parser(FtpListItem.ParseVaxList)) { item.FullName = path + item.Name; } else { FtpTrace.WriteLine(item.Name); if (path.GetFtpFileName().Contains("*")) { path = path.GetFtpDirectoryName(); } if (item.Name != null) { if ((item.Name.StartsWith("/") || item.Name.StartsWith("./")) || item.Name.StartsWith("../")) { item.FullName = item.Name; item.Name = item.Name.GetFtpFileName(); } else if (path != null) { item.FullName = path.GetFtpPath(new string[] { item.Name }); } else { FtpTrace.WriteLine("Couldn't determine the full path of this object:{0}{1}", new object[] { Environment.NewLine, item.ToString() }); } } if ((item.LinkTarget != null) && !item.LinkTarget.StartsWith("/")) { if (item.LinkTarget.StartsWith("./")) { item.LinkTarget = path.GetFtpPath(new string[] { item.LinkTarget.Remove(0, 2) }); } else { item.LinkTarget = path.GetFtpPath(new string[] { item.LinkTarget }); } } } item.Input = buf; return(item); } } } return(null); }
/// <summary> /// Parses a line from a file listing using the first successful match in the Parsers collection. /// </summary> /// <param name="path">The source path of the file listing</param> /// <param name="buf">A line from the file listing</param> /// <param name="capabilities">Server capabilities</param> /// <returns>A FtpListItem object representing the parsed line, null if the line was /// unable to be parsed. If you have encountered an unsupported list type add a parser /// to the public static Parsers collection of FtpListItem.</returns> public static FtpListItem Parse(string path, string buf, FtpCapability capabilities) { if (buf != null && buf.Length > 0) { FtpListItem item; foreach (Parser parser in Parsers) { if ((item = parser(buf, capabilities)) != null) { // if this is a vax/openvms file listing // there are no slashes in the path name if (parser == (new Parser(ParseVaxList))) { item.FullName = path + item.Name; } else { FtpTrace.WriteLine(item.Name); // remove globbing/wildcard from path if (path.GetFtpFileName().Contains("*")) { path = path.GetFtpDirectoryName(); } item.FullName = path.GetFtpPath(item.Name); //.GetFtpPathWithoutGlob(); // if a link target is set and it doesn't include an absolute path // then try to resolve it. if (item.LinkTarget != null && !item.LinkTarget.StartsWith("/")) { if (item.LinkTarget.StartsWith("./")) { item.LinkTarget = path.GetFtpPath(item.LinkTarget.Remove(0, 2)); } else { item.LinkTarget = path.GetFtpPath(item.LinkTarget); } } } item.Input = buf; return(item); } } } return(null); }
/// <summary> /// Parses GrassValley K2 videoserver format listings /// </summary> /// <param name="buf">A line from the listing</param> /// <param name="capabilities">Server capabilities</param> /// <returns>FtpListItem if the item is able to be parsed</returns> static FtpListItem ParseK2List(string buf, FtpCapability capabilities) { Match m; string regex = @"(?<permissions>.+)\s+" + @"(?<objectcount>\d+)\s+" + @"(?<user>\w+)\s+" + @"(?<group>\w+)\s+" + @"(?<size>\d*)\s+" + @"(?<modify>\w+\s+\d+\s+\d+:\d+:\d+|\w+\s+\d+\s+\d+)\s" + @"(?<name>.*)$"; if ((m = Regex.Match(buf, regex)).Success) { FtpListItem item = new FtpListItem(); item.Name = m.Groups["name"].Value; item.Modified = m.Groups["modify"].Value.GetK2Date(); long size; if (long.TryParse(m.Groups["size"].Value, out size)) { item.Size = size; } switch (m.Groups["permissions"].Value[0]) { case 'd': item.Type = FtpFileSystemObjectType.Directory; break; case '-': case 's': item.Type = FtpFileSystemObjectType.Movie; break; case 'l': item.Type = FtpFileSystemObjectType.Link; break; default: return(null); } return(item); } else { return(null); } }
/// <summary> /// Parses MLSD/MLST 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 Parse(string record, FtpCapability capabilities, FtpClient client) { FtpListItem item = new FtpListItem(); Match m; if (!(m = Regex.Match(record, "type=(?<type>.+?);", RegexOptions.IgnoreCase)).Success) { return(null); } switch (m.Groups["type"].Value.ToLower()) { case "dir": case "pdir": case "cdir": item.Type = FtpFileSystemObjectType.Directory; break; case "file": item.Type = FtpFileSystemObjectType.File; break; // These are not supported for now. case "link": case "device": default: return(null); } if ((m = Regex.Match(record, "; (?<name>.*)$", RegexOptions.IgnoreCase)).Success) { item.Name = m.Groups["name"].Value; } else // if we can't parse the file name there is a problem. { return(null); } ParseDateTime(record, item); ParseFileSize(record, item); ParsePermissions(record, item); return(item); }
private FtpCapability ParseFeatures() { FtpCapability features = FtpCapability.NONE; foreach (string feat in base.InfoMessages.Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries)) { if (feat.ToUpper().Trim().StartsWith("MLST") || feat.ToUpper().Trim().StartsWith("MLSD")) { features |= FtpCapability.MLSD; } else if (feat.ToUpper().Trim().StartsWith("MDTM")) { features |= FtpCapability.MDTM; } else if (feat.ToUpper().Trim().StartsWith("REST STREAM")) { features |= FtpCapability.REST; } else if (feat.ToUpper().Trim().StartsWith("SIZE")) { features |= FtpCapability.SIZE; } else if (feat.ToUpper().Trim().StartsWith("UTF8")) { features |= FtpCapability.UTF8; } else if (feat.ToUpper().Trim().StartsWith("PRET")) { features |= FtpCapability.PRET; } else if (feat.ToUpper().Trim().StartsWith("MFMT")) { features |= FtpCapability.MFMT; } else if (feat.ToUpper().Trim().StartsWith("MFCT")) { features |= FtpCapability.MFCT; } else if (feat.ToUpper().Trim().StartsWith("MFF")) { features |= FtpCapability.MFF; } } return(features); }
/// <summary> /// Parses a line from a file listing using the first successful match in the Parsers collection. /// </summary> /// <param name="path">The source path of the file listing</param> /// <param name="buf">A line from the file listing</param> /// <param name="capabilities">Server capabilities</param> /// <returns>A FtpListItem object representing the parsed line, null if the line was /// unable to be parsed. If you have encountered an unsupported list type add a parser /// to the public static Parsers collection of FtpListItem.</returns> public static FtpListItem ParseLegacy(string path, string buf, FtpCapability capabilities, FtpClient client) { if (!string.IsNullOrEmpty(buf)) { FtpListItem item; foreach (Parser parser in Parsers) { if ((item = parser(buf, capabilities, client)) != null) { item.Input = buf; return(item); } } } return(null); }
/// <summary> /// Parses Vax/VMS format listings /// </summary> public static FtpListItem ParseLegacy(string record, FtpCapability capabilities, FtpClient client) { string regex = @"(?<name>.+)\.(?<extension>.+);(?<version>\d+)\s+" + @"(?<size>\d+)\s+" + @"(?<modify>\d+-\w+-\d+\s+\d+:\d+)"; Match m; if ((m = Regex.Match(record, regex)).Success) { FtpListItem item = new FtpListItem(); item.Name = (m.Groups["name"].Value + "." + m.Groups["extension"].Value + ";" + m.Groups["version"].Value); if (m.Groups["extension"].Value.ToUpper() == "DIR") { item.Type = FtpFileSystemObjectType.Directory; } else { item.Type = FtpFileSystemObjectType.File; } long itemSize = 0; if (!long.TryParse(m.Groups["size"].Value, out itemSize)) { itemSize = -1; } item.Size = itemSize; DateTime itemModified = DateTime.MinValue; if (!DateTime.TryParse(m.Groups["modify"].Value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out itemModified)) { itemModified = DateTime.MinValue; } item.Modified = itemModified; return(item); } return(null); }
/// <summary> /// Parses IIS DOS format listings /// </summary> /// <param name="buf">A line from the listing</param> /// <param name="capabilities">Server capabilities</param> /// <returns>FtpListItem if the item is able to be parsed</returns> static FtpListItem ParseDosList(string buf, FtpCapability capabilities) { FtpListItem item = new FtpListItem(); Match m; // directory if ((m = Regex.Match(buf, @"(?<modify>\d+-\d+-\d+\s+\d+:\d+\w+)\s+<DIR>\s+(?<name>.*)$", RegexOptions.IgnoreCase)).Success) { DateTime modify; item.Type = FtpFileSystemObjectType.Directory; item.Name = m.Groups["name"].Value; if (DateTime.TryParse(m.Groups["modify"].Value, out modify)) { item.Modified = modify; } } // file else if ((m = Regex.Match(buf, @"(?<modify>\d+-\d+-\d+\s+\d+:\d+\w+)\s+(?<size>\d+)\s+(?<name>.*)$", RegexOptions.IgnoreCase)).Success) { DateTime modify; long size; item.Type = FtpFileSystemObjectType.File; item.Name = m.Groups["name"].Value; if (long.TryParse(m.Groups["size"].Value, out size)) { item.Size = size; } if (DateTime.TryParse(m.Groups["modify"].Value, out modify)) { item.Modified = modify; } } else { return(null); } return(item); }
/// <summary> /// Parses a line from a file listing using the first successful match in the Parsers collection. /// </summary> /// <param name="path">The source path of the file listing</param> /// <param name="buf">A line from the file listing</param> /// <param name="capabilities">Server capabilities</param> /// <returns>A FtpListItem object representing the parsed line, null if the line was /// unable to be parsed. If you have encountered an unsupported list type add a parser /// to the public static Parsers collection of FtpListItem.</returns> public static FtpListItem Parse(string path, string buf, FtpCapability capabilities) { if (buf != null && buf.Length > 0) { FtpListItem item; foreach (Parser parser in Parsers) { if ((item = parser(buf, capabilities)) != null) { item.FullName = path.GetFtpPath(item.Name); item.Input = buf; return(item); } } } return(null); }
/// <summary> /// Populates the capabilities flags based on capabilities /// supported by this server. This method is overridable /// so that new features can be supported /// </summary> /// <param name="reply">The reply object from the FEAT command. The InfoMessages property will /// contain a list of the features the server supported delimited by a new line '\n' character.</param> internal void ParseFeatures(FtpResponse reply) { foreach (string feat in reply.InfoMessages.Split(new [] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries)) { if (feat.ToUpper().Trim().StartsWith("MLST") || feat.ToUpper().Trim().StartsWith("MLSD")) { m_Caps |= FtpCapability.MLSD; } else if (feat.ToUpper().Trim().StartsWith("MDTM")) { m_Caps |= FtpCapability.MDTM; } else if (feat.ToUpper().Trim().StartsWith("REST STREAM")) { m_Caps |= FtpCapability.REST; } else if (feat.ToUpper().Trim().StartsWith("SIZE")) { m_Caps |= FtpCapability.SIZE; } else if (feat.ToUpper().Trim().StartsWith("UTF8")) { m_Caps |= FtpCapability.UTF8; } else if (feat.ToUpper().Trim().StartsWith("PRET")) { m_Caps |= FtpCapability.PRET; } else if (feat.ToUpper().Trim().StartsWith("MFMT")) { m_Caps |= FtpCapability.MFMT; } else if (feat.ToUpper().Trim().StartsWith("MFCT")) { m_Caps |= FtpCapability.MFCT; } else if (feat.ToUpper().Trim().StartsWith("MFF")) { m_Caps |= FtpCapability.MFF; } } }
/// <summary> /// Parses a line from a file listing using the first successful match in the Parsers collection. /// </summary> /// <param name="path">The source path of the file listing</param> /// <param name="buf">A line from the file listing</param> /// <param name="capabilities">Server capabilities</param> /// <param name="matchingParser"></param> /// <returns>A FtpListItem object representing the parsed line, null if the line was /// unable to be parsed. If you have encountered an unsupported list type add a parser /// to the public static Parsers collection of FtpListItem.</returns> public static FtpListItem Parse(string path, string buf, FtpCapability capabilities, out FtpListItem.Parser matchingParser) { matchingParser = null; if (string.IsNullOrEmpty(buf)) { return(null); } foreach (var parser in Parsers) { FtpListItem item; if ((item = parser(buf, capabilities)) == null) { continue; } matchingParser = parser; PostParse(path, buf, parser, item); return(item); } return(null); }
static FtpListItem ParseVaxList(string buf, FtpCapability capabilities) { string regex = @"(?<name>.+)\.(?<extension>.+);(?<version>\d+)\s+" + @"(?<size>\d+)\s+" + @"(?<modify>\d+-\w+-\d+\s+\d+:\d+)"; Match m; if ((m = Regex.Match(buf, regex)).Success) { FtpListItem item = new FtpListItem(); item.m_name = string.Format("{0}.{1};{2}", m.Groups["name"].Value, m.Groups["extension"].Value, m.Groups["version"].Value); if (m.Groups["extension"].Value.ToUpper() == "DIR") item.m_type = FtpFileSystemObjectType.Directory; else item.m_type = FtpFileSystemObjectType.File; if (!long.TryParse(m.Groups["size"].Value, out item.m_size)) item.m_size = -1; if (!DateTime.TryParse(m.Groups["modify"].Value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out item.m_modified)) item.m_modified = DateTime.MinValue; return item; } return null; }
/// <summary> /// Connect to the server /// </summary> public async Task ConnectAsync() { if (m_Stream == null) { m_Stream = new StreamChannel(); } else { if (IsConnected) await DisconnectAsync(); } if (HostName == null) throw new FtpException("No host has been specified"); if (Credentials == null) throw new FtpException("No credentials have been specified"); FtpResponse reply; if (await m_Stream.ConnectAsync(HostName, ServiceName)) { if (!(reply = await GetReplyAsync()).Success) { if (reply.Code == null) { throw new IOException("The connection was terminated before a greeting could be read."); } throw new FtpCommandException(reply); } } // TODO: If the encryption mode is set to explicit, raise to SSL if (Credentials != null) { await AuthenticateAsync(Credentials); } if ((reply = await m_Stream.GetFeaturesAsync()).Success) m_Caps = ((FtpFeaturesResponse) reply).Features; // Enable UTF8 if it's available if (m_Caps.HasFlag(FtpCapability.UTF8)) { // If the server supports UTF8 it should already be enabled and this // command should not matter however there are conflicting drafts // about this so we'll just execute it to be safe. await m_Stream.SetOptionsAsync("UTF8 ON"); m_Stream.Encoding = Encoding.UTF8; } }
/// <summary> /// Parse raw file from server into a file object, using the currently active parser. /// </summary> public FtpListItem ParseSingleLine(string path, string file, FtpCapability caps, bool isMachineList) { FtpListItem result = null; // force machine listing if it is if (isMachineList) { result = FtpMachineListParser.Parse(file, caps, client); } else { // use custom parser if given if (m_customParser != null) { result = m_customParser(file, caps, client); } else { if (IsWrongParser()) { ValidateParser(new string[] { file }); } // use one of the in-built parsers switch (CurrentParser) { case FtpParser.Legacy: result = ParseLegacy(path, file, caps, client); break; case FtpParser.Machine: result = FtpMachineListParser.Parse(file, caps, client); break; case FtpParser.Windows: result = FtpWindowsParser.Parse(client, file); break; case FtpParser.Unix: result = FtpUnixParser.Parse(client, file); break; case FtpParser.UnixAlt: result = FtpUnixParser.ParseUnixAlt(client, file); break; case FtpParser.VMS: result = FtpVMSParser.Parse(client, file); break; case FtpParser.IBM: result = FtpIBMParser.Parse(client, file); break; case FtpParser.NonStop: result = FtpNonStopParser.Parse(client, file); break; } } } // if parsed file successfully if (result != null) { // apply time difference between server/client if (HasTimeOffset) { result.Modified = result.Modified - TimeOffset; } // calc absolute file paths result.CalculateFullFtpPath(this.client, path, false); } return(result); }
/// <summary> /// Parses MLS* format listings /// </summary> /// <param name="buf">A line from the listing</param> /// <param name="capabilities">Server capabilities</param> /// <returns>FtpListItem if the item is able to be parsed</returns> static FtpListItem ParseMachineList(string buf, FtpCapability capabilities) { Match m; if (!(m = Regex.Match(buf, "type=(?<type>.+?);", RegexOptions.IgnoreCase)).Success) { return(null); } FtpListItem item = new FtpListItem(); switch (m.Groups["type"].Value.ToLower()) { case "dir": case "pdir": case "cdir": item.Type = FtpFileSystemObjectType.Directory; break; case "file": item.Type = FtpFileSystemObjectType.File; break; // These are not supported for now. case "link": case "device": default: return(null); } if ((m = Regex.Match(buf, "; (?<name>.*)$", RegexOptions.IgnoreCase)).Success) { item.Name = m.Groups["name"].Value; } else // if we can't parse the file name there is a problem. { return(null); } if ((m = Regex.Match(buf, "modify=(?<modify>.+?);", RegexOptions.IgnoreCase)).Success) { item.Modified = m.Groups["modify"].Value.GetFtpDate(DateTimeStyles.AssumeUniversal); } if ((m = Regex.Match(buf, "created?=(?<create>.+?);", RegexOptions.IgnoreCase)).Success) { item.Created = m.Groups["create"].Value.GetFtpDate(DateTimeStyles.AssumeUniversal); } if ((m = Regex.Match(buf, @"size=(?<size>\d+);", RegexOptions.IgnoreCase)).Success) { long size; if (long.TryParse(m.Groups["size"].Value, out size)) { item.Size = size; } } if ((m = Regex.Match(buf, @"unix.mode=(?<mode>\d+);", RegexOptions.IgnoreCase)).Success) { if (m.Groups["mode"].Value.Length == 4) { item.SpecialPermissions = (FtpSpecialPermissions)int.Parse(m.Groups["mode"].Value[0].ToString()); item.OwnerPermissions = (FtpPermission)int.Parse(m.Groups["mode"].Value[1].ToString()); item.GroupPermissions = (FtpPermission)int.Parse(m.Groups["mode"].Value[2].ToString()); item.OthersPermissions = (FtpPermission)int.Parse(m.Groups["mode"].Value[3].ToString()); } else if (m.Groups["mode"].Value.Length == 3) { item.OwnerPermissions = (FtpPermission)int.Parse(m.Groups["mode"].Value[0].ToString()); item.GroupPermissions = (FtpPermission)int.Parse(m.Groups["mode"].Value[1].ToString()); item.OthersPermissions = (FtpPermission)int.Parse(m.Groups["mode"].Value[2].ToString()); } } return(item); }
/// <summary> /// Parses MLS* format listings /// </summary> /// <param name="buf">A line from the listing</param> /// <param name="capabilities">Server capabilities</param> /// <returns>FtpListItem if the item is able to be parsed</returns> static FtpListItem ParseMachineList(string buf, FtpCapability capabilities) { FtpListItem item = new FtpListItem(); Match m; if (!(m = Regex.Match(buf, "^type=(?<type>.+?);", RegexOptions.IgnoreCase)).Success) return null; switch (m.Groups["type"].Value.ToLower()) { case "dir": item.Type = FtpFileSystemObjectType.Directory; break; case "file": item.Type = FtpFileSystemObjectType.File; break; // These are not supported for now. case "link": case "device": default: return null; } if ((m = Regex.Match(buf, "; (?<name>.*)$", RegexOptions.IgnoreCase)).Success) item.Name = m.Groups["name"].Value; else // if we can't parse the file name there is a problem. return null; if ((m = Regex.Match(buf, "modify=(?<modify>.+?);", RegexOptions.IgnoreCase)).Success) item.Modified = m.Groups["modify"].Value.GetFtpDate(); if ((m = Regex.Match(buf, "created?=(?<create>.+?);", RegexOptions.IgnoreCase)).Success) item.Created = m.Groups["create"].Value.GetFtpDate(); if ((m = Regex.Match(buf, @"size=(?<size>\d+);", RegexOptions.IgnoreCase)).Success) { long size; if (long.TryParse(m.Groups["size"].Value, out size)) item.Size = size; } if((m = Regex.Match(buf, @"unix.mode=(?<mode>\d+);", RegexOptions.IgnoreCase)).Success) { if (m.Groups["mode"].Value.Length == 4) { item.SpecialPermissions = (FtpSpecialPermissions)int.Parse(m.Groups["mode"].Value[0].ToString()); item.OwnerPermissions = (FtpPermission)int.Parse(m.Groups["mode"].Value[1].ToString()); item.GroupPermissions = (FtpPermission)int.Parse(m.Groups["mode"].Value[2].ToString()); item.OthersPermissions = (FtpPermission)int.Parse(m.Groups["mode"].Value[3].ToString()); } else if(m.Groups["mode"].Value.Length == 3) { item.OwnerPermissions = (FtpPermission)int.Parse(m.Groups["mode"].Value[0].ToString()); item.GroupPermissions = (FtpPermission)int.Parse(m.Groups["mode"].Value[1].ToString()); item.OthersPermissions = (FtpPermission)int.Parse(m.Groups["mode"].Value[2].ToString()); } } return item; }
/// <summary> /// Parses a line from a file listing using the first successful match in the Parsers collection. /// </summary> /// <param name="path">The source path of the file listing</param> /// <param name="buf">A line from the file listing</param> /// <param name="capabilities">Server capabilities</param> /// <returns>A FtpListItem object representing the parsed line, null if the line was /// unable to be parsed. If you have encountered an unsupported list type add a parser /// to the public static Parsers collection of FtpListItem.</returns> public static FtpListItem Parse(string path, string buf, FtpCapability capabilities) { if (buf != null && buf.Length > 0) { FtpListItem item; foreach (Parser parser in Parsers) { if ((item = parser(buf, capabilities)) != null) { item.FullName = path.GetFtpPath(item.Name); item.Input = buf; return item; } } } return null; }
/// <summary> /// Parses LIST format listings /// </summary> /// <param name="buf">A line from the listing</param> /// <param name="capabilities">Server capabilities</param> /// <returns>FtpListItem if the item is able to be parsed</returns> private static FtpListItem ParseUnixList(string buf, FtpCapability capabilities) { string regex = @"(?<permissions>[dslrwx-]+)\s+" + @"(?<objectcount>\d+)\s+" + @"(?<user>.+?)\s+" + @"(?<group>.+?)\s+" + @"(?<size>\d+)\s+" + //@"(?<modify>\w+\s+\d+\s+\d+:\d+|\w+\s+\d+\s+\d+|\d+\-\d+\-\d+\-\d+:\d+)\s+" + @"(?<modify>\w+\s+\d+\s+\d+:\d+|\w+\s+\d+\s+\d+|\d+\-\d+\-\d+\-\d+:\d+)?\s+" + @"(?<name>.*)$"; FtpListItem item = new FtpListItem(); Match m; if (!(m = Regex.Match(buf, 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); item.Name = item.Name.Remove(item.Name.IndexOf(" -> ")); break; } //// // Modify times for files in LIST format isn't as accurate as what can be had by using the MDTM command. // MDTM does not work on 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 (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) { Match perms = Regex.Match(m.Groups["permissions"].Value, @"[\w-]{1}(?<owner>[\w-]{3})(?<group>[\w-]{3})(?<others>[\w-]{3})", RegexOptions.IgnoreCase); if (perms.Success) { if (perms.Groups["owner"].Value.Length == 3) { if (perms.Groups["owner"].Value[0] == 'r') { item.OwnerPermissions |= FtpPermission.Read; } if (perms.Groups["owner"].Value[1] == 'w') { item.OwnerPermissions |= FtpPermission.Write; } if (perms.Groups["owner"].Value[2] == 'x' || perms.Groups["owner"].Value[2] == 's') { item.OwnerPermissions |= FtpPermission.Execute; } if (perms.Groups["owner"].Value[2] == 's' || perms.Groups["owner"].Value[2] == 'S') { item.SpecialPermissions |= FtpSpecialPermissions.SetUserID; } } if (perms.Groups["group"].Value.Length == 3) { if (perms.Groups["group"].Value[0] == 'r') { item.GroupPermissions |= FtpPermission.Read; } if (perms.Groups["group"].Value[1] == 'w') { item.GroupPermissions |= FtpPermission.Write; } if (perms.Groups["group"].Value[2] == 'x' || perms.Groups["group"].Value[2] == 's') { item.GroupPermissions |= FtpPermission.Execute; } if (perms.Groups["group"].Value[2] == 's' || perms.Groups["group"].Value[2] == 'S') { item.SpecialPermissions |= FtpSpecialPermissions.SetGroupID; } } if (perms.Groups["others"].Value.Length == 3) { if (perms.Groups["others"].Value[0] == 'r') { item.OthersPermissions |= FtpPermission.Read; } if (perms.Groups["others"].Value[1] == 'w') { item.OthersPermissions |= FtpPermission.Write; } if (perms.Groups["others"].Value[2] == 'x' || perms.Groups["others"].Value[2] == 't') { item.OthersPermissions |= FtpPermission.Execute; } if (perms.Groups["others"].Value[2] == 't' || perms.Groups["others"].Value[2] == 'T') { item.SpecialPermissions |= FtpSpecialPermissions.Sticky; } } } } return(item); }
/// <summary> /// Connect to the server /// </summary> public async Task ConnectAsync() { if (m_Stream == null) { m_Stream = new FtpStreamChannel(); } else { if (IsConnected) { await DisconnectAsync(); } } if (HostName == null) { throw new FtpException("No host has been specified"); } if (Credentials == null) { throw new FtpException("No credentials have been specified"); } FtpResponse reply; if (await m_Stream.ConnectAsync(HostName, ServiceName)) { if (!(reply = await GetReplyAsync()).Success) { if (reply.Code == null) { throw new IOException("The connection was terminated before a greeting could be read."); } throw new FtpCommandException(reply); } } // TODO: If the encryption mode is set to explicit, raise to SSL if (Credentials != null) { await AuthenticateAsync(Credentials); } if ((reply = await m_Stream.GetFeaturesAsync()).Success) { m_Caps = ((FtpFeaturesResponse)reply).Features; } // Enable UTF8 if it's available if (m_Caps.HasFlag(FtpCapability.UTF8)) { // If the server supports UTF8 it should already be enabled and this // command should not matter however there are conflicting drafts // about this so we'll just execute it to be safe. await m_Stream.SetOptionsAsync("UTF8 ON"); m_Stream.Encoding = Encoding.UTF8; } }
/// <summary> /// Removes the specified capability from the list /// </summary> /// <param name="cap"></param> public void RemoveCapability(FtpCapability cap) { this.Capabilities &= ~(cap); }
/// <summary> /// Parses LIST format listings /// </summary> /// <param name="buf">A line from the listing</param> /// <param name="capabilities">Server capabilities</param> /// <returns>FtpListItem if the item is able to be parsed</returns> static FtpListItem ParseUnixList(string buf, FtpCapability capabilities, FtpClient client) { FtpListItem item = new FtpListItem(); Match m = null; string regex = @"(?<permissions>[\w-]{10})\s+" + @"(?<objectcount>\d+)\s+" + @"(?<user>[\w\d]+)\s+" + @"(?<group>[\w\d]+)\s+" + @"(?<size>\d+)\s+" + @"(?<modify>\w+\s+\d+\s+\d+:\d+|\w+\s+\d+\s+\d+)\s+" + @"(?<name>.*)$"; if (!(m = Regex.Match(buf, regex, RegexOptions.IgnoreCase)).Success) { return(null); } if (m.Groups["permissions"].Value.StartsWith("d")) { item.Type = FtpFileSystemObjectType.Directory; } else if (m.Groups["permissions"].Value.StartsWith("-")) { item.Type = FtpFileSystemObjectType.File; } else { 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; if (item.Type == FtpFileSystemObjectType.Directory && (item.Name == "." || item.Name == "..")) { return(null); } //// // 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.HasFlag(FtpCapability.MDTM) || item.Type == FtpFileSystemObjectType.Directory) && m.Groups["modify"].Value.Length > 0) { item.Modified = m.Groups["modify"].Value.GetFtpDate(DateTimeStyles.AssumeUniversal); } 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) { Match perms = Regex.Match(m.Groups["permissions"].Value, @"[\w-]{1}(?<owner>[\w-]{3})(?<group>[\w-]{3})(?<others>[\w-]{3})", RegexOptions.IgnoreCase); if (perms.Success) { if (perms.Groups["owner"].Value.Length == 3) { if (perms.Groups["owner"].Value[0] == 'r') { item.OwnerPermissions |= FtpPermission.Read; } if (perms.Groups["owner"].Value[1] == 'w') { item.OwnerPermissions |= FtpPermission.Write; } if (perms.Groups["owner"].Value[2] == 'x' || perms.Groups["owner"].Value[2] == 's') { item.OwnerPermissions |= FtpPermission.Execute; } if (perms.Groups["owner"].Value[2] == 's' || perms.Groups["owner"].Value[2] == 'S') { item.SpecialPermissions |= FtpSpecialPermissions.SetUserID; } } if (perms.Groups["group"].Value.Length == 3) { if (perms.Groups["group"].Value[0] == 'r') { item.GroupPermissions |= FtpPermission.Read; } if (perms.Groups["group"].Value[1] == 'w') { item.GroupPermissions |= FtpPermission.Write; } if (perms.Groups["group"].Value[2] == 'x' || perms.Groups["group"].Value[2] == 's') { item.GroupPermissions |= FtpPermission.Execute; } if (perms.Groups["group"].Value[2] == 's' || perms.Groups["group"].Value[2] == 'S') { item.SpecialPermissions |= FtpSpecialPermissions.SetGroupID; } } if (perms.Groups["others"].Value.Length == 3) { if (perms.Groups["others"].Value[0] == 'r') { item.OthersPermissions |= FtpPermission.Read; } if (perms.Groups["others"].Value[1] == 'w') { item.OthersPermissions |= FtpPermission.Write; } if (perms.Groups["others"].Value[2] == 'x' || perms.Groups["others"].Value[2] == 't') { item.OthersPermissions |= FtpPermission.Execute; } if (perms.Groups["others"].Value[2] == 't' || perms.Groups["others"].Value[2] == 'T') { item.SpecialPermissions |= FtpSpecialPermissions.Sticky; } } } } return(item); }
/// <summary> /// Checks if the server supports the specified capability /// </summary> /// <param name="cap"></param> public bool HasCapability(FtpCapability cap) { return((this.Capabilities & cap) == cap); }
/// <summary> /// Parses a line from a file listing using the first successful match in the Parsers collection. /// </summary> /// <param name="path">The source path of the file listing</param> /// <param name="buf">A line from the file listing</param> /// <param name="capabilities">Server capabilities</param> /// <param name="matchingParser"></param> /// <returns>A FtpListItem object representing the parsed line, null if the line was /// unable to be parsed. If you have encountered an unsupported list type add a parser /// to the public static Parsers collection of FtpListItem.</returns> public static FtpListItem Parse(string path, string buf, FtpCapability capabilities, out Parser matchingParser) { matchingParser = null; if (string.IsNullOrEmpty(buf)) return null; foreach (var parser in Parsers) { FtpListItem item; if ((item = parser(buf, capabilities)) == null) continue; matchingParser = parser; PostParse(path, buf, parser, item); return item; } return null; }
/// <summary> /// Parses IIS DOS format listings /// </summary> /// <param name="buf">A line from the listing</param> /// <param name="capabilities">Server capabilities</param> /// <returns>FtpListItem if the item is able to be parsed</returns> static FtpListItem ParseDosList(string buf, FtpCapability capabilities) { FtpListItem item = new FtpListItem(); Match m; // directory if ((m = Regex.Match(buf, @"(?<modify>\d+-\d+-\d+\s+\d+:\d+\w+)\s+<DIR>\s+(?<name>.*)$", RegexOptions.IgnoreCase)).Success) { DateTime modify; item.Type = FtpFileSystemObjectType.Directory; item.Name = m.Groups["name"].Value; if (DateTime.TryParse(m.Groups["modify"].Value, out modify)) item.Modified = modify; } // file else if ((m = Regex.Match(buf, @"(?<modify>\d+-\d+-\d+\s+\d+:\d+\w+)\s+(?<size>\d+)\s+(?<name>.*)$", RegexOptions.IgnoreCase)).Success) { DateTime modify; long size; item.Type = FtpFileSystemObjectType.File; item.Name = m.Groups["name"].Value; if (long.TryParse(m.Groups["size"].Value, out size)) item.Size = size; if (DateTime.TryParse(m.Groups["modify"].Value, out modify)) item.Modified = modify; } else return null; return item; }
public static FtpListItem ParseWith(string path, string buf, FtpCapability capabilities, Parser parser) { var item = parser(buf, capabilities); if (item != null) PostParse(path, buf, parser, item); return item; }
/// <summary> /// Parses LIST format listings /// </summary> /// <param name="buf">A line from the listing</param> /// <param name="capabilities">Server capabilities</param> /// <returns>FtpListItem if the item is able to be parsed</returns> static FtpListItem ParseUnixList(string buf, FtpCapability capabilities) { string regex = @"(?<permissions>[\w-]{10})\s+" + @"(?<objectcount>\d+)\s+" + @"(?<user>[\w\d]+)\s+" + @"(?<group>[\w\d]+)\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(buf, regex, RegexOptions.IgnoreCase)).Success) return null; if (m.Groups["permissions"].Value.StartsWith("d")) item.Type = FtpFileSystemObjectType.Directory; else if (m.Groups["permissions"].Value.StartsWith("-")) item.Type = FtpFileSystemObjectType.File; else 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; if (item.Type == FtpFileSystemObjectType.Directory && (item.Name == "." || item.Name == "..")) return null; //// // 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.HasFlag(FtpCapability.MDTM) || item.Type == FtpFileSystemObjectType.Directory) && m.Groups["modify"].Value.Length > 0) item.Modified = m.Groups["modify"].Value.GetFtpDate(); 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) { Match perms = Regex.Match(m.Groups["permissions"].Value, @"[\w-]{1}(?<owner>[\w-]{3})(?<group>[\w-]{3})(?<others>[\w-]{3})", RegexOptions.IgnoreCase); if (perms.Success) { if (perms.Groups["owner"].Value.Length == 3) { if (perms.Groups["owner"].Value[0] == 'r') item.OwnerPermissions |= FtpPermission.Read; if (perms.Groups["owner"].Value[1] == 'w') item.OwnerPermissions |= FtpPermission.Write; if (perms.Groups["owner"].Value[2] == 'x' || perms.Groups["owner"].Value[2] == 's') item.OwnerPermissions |= FtpPermission.Execute; if (perms.Groups["owner"].Value[2] == 's' || perms.Groups["owner"].Value[2] == 'S') item.SpecialPermissions |= FtpSpecialPermissions.SetUserID; } if (perms.Groups["group"].Value.Length == 3) { if (perms.Groups["group"].Value[0] == 'r') item.GroupPermissions |= FtpPermission.Read; if (perms.Groups["group"].Value[1] == 'w') item.GroupPermissions |= FtpPermission.Write; if (perms.Groups["group"].Value[2] == 'x' || perms.Groups["group"].Value[2] == 's') item.GroupPermissions |= FtpPermission.Execute; if (perms.Groups["group"].Value[2] == 's' || perms.Groups["group"].Value[2] == 'S') item.SpecialPermissions |= FtpSpecialPermissions.SetGroupID; } if (perms.Groups["others"].Value.Length == 3) { if (perms.Groups["others"].Value[0] == 'r') item.OthersPermissions |= FtpPermission.Read; if (perms.Groups["others"].Value[1] == 'w') item.OthersPermissions |= FtpPermission.Write; if (perms.Groups["others"].Value[2] == 'x' || perms.Groups["others"].Value[2] == 't') item.OthersPermissions |= FtpPermission.Execute; if (perms.Groups["others"].Value[2] == 't' || perms.Groups["others"].Value[2] == 'T') item.SpecialPermissions |= FtpSpecialPermissions.Sticky; } } } return item; }
private static FtpListItem ParseMachineList(string buf, FtpCapability capabilities) { Match match; long num; FtpListItem item = new FtpListItem(); if ((match = Regex.Match(buf, "type=(?<type>.+?);", RegexOptions.IgnoreCase)).Success) { switch (match.Groups["type"].Value.ToLower()) { case "dir": case "pdir": case "cdir": item.Type = FtpFileSystemObjectType.Directory; goto Label_00E2; case "file": item.Type = FtpFileSystemObjectType.File; goto Label_00E2; } } return(null); Label_00E2: if ((match = Regex.Match(buf, "; (?<name>.*)$", RegexOptions.IgnoreCase)).Success) { item.Name = match.Groups["name"].Value; } else { return(null); } if ((match = Regex.Match(buf, "modify=(?<modify>.+?);", RegexOptions.IgnoreCase)).Success) { item.Modified = match.Groups["modify"].Value.GetFtpDate(DateTimeStyles.AssumeUniversal); } if ((match = Regex.Match(buf, "created?=(?<create>.+?);", RegexOptions.IgnoreCase)).Success) { item.Created = match.Groups["create"].Value.GetFtpDate(DateTimeStyles.AssumeUniversal); } if ((match = Regex.Match(buf, @"size=(?<size>\d+);", RegexOptions.IgnoreCase)).Success && long.TryParse(match.Groups["size"].Value, out num)) { item.Size = num; } if ((match = Regex.Match(buf, @"unix.mode=(?<mode>\d+);", RegexOptions.IgnoreCase)).Success) { if (match.Groups["mode"].Value.Length == 4) { char ch = match.Groups["mode"].Value[0]; item.SpecialPermissions = (FtpSpecialPermissions)int.Parse(ch.ToString()); char ch2 = match.Groups["mode"].Value[1]; item.OwnerPermissions = (FtpPermission)int.Parse(ch2.ToString()); char ch3 = match.Groups["mode"].Value[2]; item.GroupPermissions = (FtpPermission)int.Parse(ch3.ToString()); char ch4 = match.Groups["mode"].Value[3]; item.OthersPermissions = (FtpPermission)int.Parse(ch4.ToString()); return(item); } if (match.Groups["mode"].Value.Length == 3) { char ch5 = match.Groups["mode"].Value[0]; item.OwnerPermissions = (FtpPermission)int.Parse(ch5.ToString()); char ch6 = match.Groups["mode"].Value[1]; item.GroupPermissions = (FtpPermission)int.Parse(ch6.ToString()); char ch7 = match.Groups["mode"].Value[2]; item.OthersPermissions = (FtpPermission)int.Parse(ch7.ToString()); } } return(item); }
public static bool HasFlag(this FtpCapability flags, FtpCapability flag) { return((flags & flag) == flag); }
/// <summary> /// Parses LIST format listings /// </summary> /// <param name="buf">A line from the listing</param> /// <param name="capabilities">Server capabilities</param> /// <returns>FtpListItem if the item is able to be parsed</returns> internal static FtpListItem ParseUnixList(string buf, FtpCapability capabilities) { 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>.*)$"; Match m; if (!(m = Regex.Match(buf, 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); } FtpListItem item = new FtpListItem(); 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); item.Name = item.Name.Remove(item.Name.IndexOf(" -> ")); break; } // for date parser testing only //capabilities = ~(capabilities & FtpCapability.MDTM); //// // 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) { FtpTrace.WriteLine("GetFtpDate() failed on {0}", m.Groups["modify"].Value); } } else { if (m.Groups["modify"].Value.Length == 0) { FtpTrace.WriteLine("RegEx failed to parse modified date from {0}.", buf); } else if (item.Type == FtpFileSystemObjectType.Directory) { FtpTrace.WriteLine("Modified times of directories are ignored in UNIX long listings."); } else if ((capabilities & FtpCapability.MDTM) == FtpCapability.MDTM) { FtpTrace.WriteLine("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) { Match perms = Regex.Match(m.Groups["permissions"].Value, @"[\w-]{1}(?<owner>[\w-]{3})(?<group>[\w-]{3})(?<others>[\w-]{3})", RegexOptions.IgnoreCase); if (perms.Success) { if (perms.Groups["owner"].Value.Length == 3) { if (perms.Groups["owner"].Value[0] == 'r') { item.OwnerPermissions |= FtpPermission.Read; } if (perms.Groups["owner"].Value[1] == 'w') { item.OwnerPermissions |= FtpPermission.Write; } if (perms.Groups["owner"].Value[2] == 'x' || perms.Groups["owner"].Value[2] == 's') { item.OwnerPermissions |= FtpPermission.Execute; } if (perms.Groups["owner"].Value[2] == 's' || perms.Groups["owner"].Value[2] == 'S') { item.SpecialPermissions |= FtpSpecialPermissions.SetUserID; } } if (perms.Groups["group"].Value.Length == 3) { if (perms.Groups["group"].Value[0] == 'r') { item.GroupPermissions |= FtpPermission.Read; } if (perms.Groups["group"].Value[1] == 'w') { item.GroupPermissions |= FtpPermission.Write; } if (perms.Groups["group"].Value[2] == 'x' || perms.Groups["group"].Value[2] == 's') { item.GroupPermissions |= FtpPermission.Execute; } if (perms.Groups["group"].Value[2] == 's' || perms.Groups["group"].Value[2] == 'S') { item.SpecialPermissions |= FtpSpecialPermissions.SetGroupID; } } if (perms.Groups["others"].Value.Length == 3) { if (perms.Groups["others"].Value[0] == 'r') { item.OthersPermissions |= FtpPermission.Read; } if (perms.Groups["others"].Value[1] == 'w') { item.OthersPermissions |= FtpPermission.Write; } if (perms.Groups["others"].Value[2] == 'x' || perms.Groups["others"].Value[2] == 't') { item.OthersPermissions |= FtpPermission.Execute; } if (perms.Groups["others"].Value[2] == 't' || perms.Groups["others"].Value[2] == 'T') { item.SpecialPermissions |= FtpSpecialPermissions.Sticky; } } } } return(item); }
/// <summary> /// Parses IIS DOS format listings /// </summary> /// <param name="buf">A line from the listing</param> /// <param name="capabilities">Server capabilities</param> /// <returns>FtpListItem if the item is able to be parsed</returns> static FtpListItem ParseDosList(string buf, FtpCapability capabilities) { FtpListItem item = new FtpListItem(); string[] datefmt = new string[] { "MM-dd-yy hh:mmtt", "MM-dd-yyyy hh:mmtt" }; Match m; // directory if ((m = Regex.Match(buf, @"(?<modify>\d+-\d+-\d+\s+\d+:\d+\w+)\s+<DIR>\s+(?<name>.*)$", RegexOptions.IgnoreCase)).Success) { DateTime modify; item.Type = FtpFileSystemObjectType.Directory; item.Name = m.Groups["name"].Value; //if (DateTime.TryParse(m.Groups["modify"].Value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out modify)) if (DateTime.TryParseExact(m.Groups["modify"].Value, datefmt, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out modify)) item.Modified = modify; } // file else if ((m = Regex.Match(buf, @"(?<modify>\d+-\d+-\d+\s+\d+:\d+\w+)\s+(?<size>\d+)\s+(?<name>.*)$", RegexOptions.IgnoreCase)).Success) { DateTime modify; long size; item.Type = FtpFileSystemObjectType.File; item.Name = m.Groups["name"].Value; if (long.TryParse(m.Groups["size"].Value, out size)) item.Size = size; //if (DateTime.TryParse(m.Groups["modify"].Value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out modify)) if (DateTime.TryParseExact(m.Groups["modify"].Value, datefmt, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out modify)) item.Modified = modify; } else return null; return item; }
/// <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); }
/// <summary> /// Populates the capabilities flags based on capabilities /// supported by this server. This method is overridable /// so that new features can be supported /// </summary> /// <param name="reply">The reply object from the FEAT command. The InfoMessages property will /// contain a list of the features the server supported delimited by a new line '\n' character.</param> internal void ParseFeatures(FtpResponse reply) { foreach (string feat in reply.InfoMessages.Split(new[] {"\r\n"}, StringSplitOptions.RemoveEmptyEntries)) { if (feat.ToUpper().Trim().StartsWith("MLST") || feat.ToUpper().Trim().StartsWith("MLSD")) m_Caps |= FtpCapability.MLSD; else if (feat.ToUpper().Trim().StartsWith("MDTM")) m_Caps |= FtpCapability.MDTM; else if (feat.ToUpper().Trim().StartsWith("REST STREAM")) m_Caps |= FtpCapability.REST; else if (feat.ToUpper().Trim().StartsWith("SIZE")) m_Caps |= FtpCapability.SIZE; else if (feat.ToUpper().Trim().StartsWith("UTF8")) m_Caps |= FtpCapability.UTF8; else if (feat.ToUpper().Trim().StartsWith("PRET")) m_Caps |= FtpCapability.PRET; else if (feat.ToUpper().Trim().StartsWith("MFMT")) m_Caps |= FtpCapability.MFMT; else if (feat.ToUpper().Trim().StartsWith("MFCT")) m_Caps |= FtpCapability.MFCT; else if (feat.ToUpper().Trim().StartsWith("MFF")) m_Caps |= FtpCapability.MFF; } }
private static FtpListItem ParseUnixList(string buf, FtpCapability capabilities) { Match match; long num; string pattern = @"(?<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(); if ((match = Regex.Match(buf, pattern, RegexOptions.IgnoreCase)).Success) { if (match.Groups["permissions"].Value.Length == 0) { return(null); } switch (match.Groups["permissions"].Value[0]) { case 'l': item.Type = FtpFileSystemObjectType.Link; goto Label_0099; case 's': case '-': item.Type = FtpFileSystemObjectType.File; goto Label_0099; case 'd': item.Type = FtpFileSystemObjectType.Directory; goto Label_0099; } } return(null); Label_0099: if (match.Groups["name"].Value.Length < 1) { return(null); } item.Name = match.Groups["name"].Value; switch (item.Type) { case FtpFileSystemObjectType.Directory: if (!(item.Name == ".") && !(item.Name == "..")) { break; } return(null); case FtpFileSystemObjectType.Link: if (item.Name.Contains(" -> ")) { item.LinkTarget = item.Name.Remove(0, item.Name.IndexOf("-> ") + 3); item.Name = item.Name.Remove(item.Name.IndexOf(" -> ")); break; } return(null); } if ((((capabilities & FtpCapability.MDTM) != FtpCapability.MDTM) || (item.Type == FtpFileSystemObjectType.Directory)) && (match.Groups["modify"].Value.Length > 0)) { item.Modified = match.Groups["modify"].Value.GetFtpDate(DateTimeStyles.AssumeLocal); if (item.Modified == DateTime.MinValue) { FtpTrace.WriteLine("GetFtpDate() failed on {0}", new object[] { match.Groups["modify"].Value }); } } else if (match.Groups["modify"].Value.Length == 0) { FtpTrace.WriteLine("RegEx failed to parse modified date from {0}.", new object[] { buf }); } else if (item.Type == FtpFileSystemObjectType.Directory) { FtpTrace.WriteLine("Modified times of directories are ignored in UNIX long listings."); } else if ((capabilities & FtpCapability.MDTM) == FtpCapability.MDTM) { FtpTrace.WriteLine("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 ((match.Groups["size"].Value.Length > 0) && long.TryParse(match.Groups["size"].Value, out num)) { item.Size = num; } if (match.Groups["permissions"].Value.Length > 0) { Match match2 = Regex.Match(match.Groups["permissions"].Value, @"[\w-]{1}(?<owner>[\w-]{3})(?<group>[\w-]{3})(?<others>[\w-]{3})", RegexOptions.IgnoreCase); if (!match2.Success) { return(item); } if (match2.Groups["owner"].Value.Length == 3) { if (match2.Groups["owner"].Value[0] == 'r') { item.OwnerPermissions |= FtpPermission.None | FtpPermission.Read; } if (match2.Groups["owner"].Value[1] == 'w') { item.OwnerPermissions |= FtpPermission.None | FtpPermission.Write; } if ((match2.Groups["owner"].Value[2] == 'x') || (match2.Groups["owner"].Value[2] == 's')) { item.OwnerPermissions |= FtpPermission.Execute; } if ((match2.Groups["owner"].Value[2] == 's') || (match2.Groups["owner"].Value[2] == 'S')) { item.SpecialPermissions |= FtpSpecialPermissions.SetUserID; } } if (match2.Groups["group"].Value.Length == 3) { if (match2.Groups["group"].Value[0] == 'r') { item.GroupPermissions |= FtpPermission.None | FtpPermission.Read; } if (match2.Groups["group"].Value[1] == 'w') { item.GroupPermissions |= FtpPermission.None | FtpPermission.Write; } if ((match2.Groups["group"].Value[2] == 'x') || (match2.Groups["group"].Value[2] == 's')) { item.GroupPermissions |= FtpPermission.Execute; } if ((match2.Groups["group"].Value[2] == 's') || (match2.Groups["group"].Value[2] == 'S')) { item.SpecialPermissions |= FtpSpecialPermissions.SetGroupID; } } if (match2.Groups["others"].Value.Length == 3) { if (match2.Groups["others"].Value[0] == 'r') { item.OthersPermissions |= FtpPermission.None | FtpPermission.Read; } if (match2.Groups["others"].Value[1] == 'w') { item.OthersPermissions |= FtpPermission.None | FtpPermission.Write; } if ((match2.Groups["others"].Value[2] == 'x') || (match2.Groups["others"].Value[2] == 't')) { item.OthersPermissions |= FtpPermission.Execute; } if ((match2.Groups["others"].Value[2] != 't') && (match2.Groups["others"].Value[2] != 'T')) { return(item); } item.SpecialPermissions |= FtpSpecialPermissions.Sticky; } } return(item); }
/// <summary> /// Parses a line from a file listing using the first successful match in the Parsers collection. /// </summary> /// <param name="path">The source path of the file listing</param> /// <param name="buf">A line from the file listing</param> /// <param name="capabilities">Server capabilities</param> /// <returns>A FtpListItem object representing the parsed line, null if the line was /// unable to be parsed. If you have encountered an unsupported list type add a parser /// to the public static Parsers collection of FtpListItem.</returns> public static FtpListItem Parse(string path, string buf, FtpCapability capabilities) { if (buf != null && buf.Length > 0) { FtpListItem item; foreach (Parser parser in Parsers) { if ((item = parser(buf, capabilities)) != null) { // if this is a vax/openvms file listing // there are no slashes in the path name if (parser == (new Parser(ParseVaxList))) item.FullName = path + item.Name; else { FtpTrace.WriteLine(item.Name); // remove globbing/wildcard from path if (path.GetFtpFileName().Contains("*")) { path = path.GetFtpDirectoryName(); } if (item.Name != null) { // absolute path? then ignore the path input to this method. if (item.Name.StartsWith("/") || item.Name.StartsWith("./") || item.Name.StartsWith("../")) { item.FullName = item.Name; item.Name = item.Name.GetFtpFileName(); } else if(path != null) { item.FullName = path.GetFtpPath(item.Name); //.GetFtpPathWithoutGlob(); } else { FtpTrace.WriteLine("Couldn't determine the full path of this object:{0}{1}", Environment.NewLine, item.ToString()); } } // if a link target is set and it doesn't include an absolute path // then try to resolve it. if (item.LinkTarget != null && !item.LinkTarget.StartsWith("/")) { if (item.LinkTarget.StartsWith("./")) item.LinkTarget = path.GetFtpPath(item.LinkTarget.Remove(0, 2)); else item.LinkTarget = path.GetFtpPath(item.LinkTarget); } } item.Input = buf; return item; } } } return null; }
/// <summary> /// Parses LIST format listings /// </summary> /// <param name="buf">A line from the listing</param> /// <param name="capabilities">Server capabilities</param> /// <returns>FtpListItem if the item is able to be parsed</returns> static FtpListItem ParseUnixList(string buf, FtpCapability capabilities) { 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(buf, 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); item.Name = item.Name.Remove(item.Name.IndexOf(" -> ")); break; } // for date parser testing only //capabilities = ~(capabilities & FtpCapability.MDTM); //// // 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) { FtpTrace.WriteLine("GetFtpDate() failed on {0}", m.Groups["modify"].Value); } } else { if (m.Groups["modify"].Value.Length == 0) FtpTrace.WriteLine("RegEx failed to parse modified date from {0}.", buf); else if (item.Type == FtpFileSystemObjectType.Directory) FtpTrace.WriteLine("Modified times of directories are ignored in UNIX long listings."); else if ((capabilities & FtpCapability.MDTM) == FtpCapability.MDTM) FtpTrace.WriteLine("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) { Match perms = Regex.Match(m.Groups["permissions"].Value, @"[\w-]{1}(?<owner>[\w-]{3})(?<group>[\w-]{3})(?<others>[\w-]{3})", RegexOptions.IgnoreCase); if (perms.Success) { if (perms.Groups["owner"].Value.Length == 3) { if (perms.Groups["owner"].Value[0] == 'r') item.OwnerPermissions |= FtpPermission.Read; if (perms.Groups["owner"].Value[1] == 'w') item.OwnerPermissions |= FtpPermission.Write; if (perms.Groups["owner"].Value[2] == 'x' || perms.Groups["owner"].Value[2] == 's') item.OwnerPermissions |= FtpPermission.Execute; if (perms.Groups["owner"].Value[2] == 's' || perms.Groups["owner"].Value[2] == 'S') item.SpecialPermissions |= FtpSpecialPermissions.SetUserID; } if (perms.Groups["group"].Value.Length == 3) { if (perms.Groups["group"].Value[0] == 'r') item.GroupPermissions |= FtpPermission.Read; if (perms.Groups["group"].Value[1] == 'w') item.GroupPermissions |= FtpPermission.Write; if (perms.Groups["group"].Value[2] == 'x' || perms.Groups["group"].Value[2] == 's') item.GroupPermissions |= FtpPermission.Execute; if (perms.Groups["group"].Value[2] == 's' || perms.Groups["group"].Value[2] == 'S') item.SpecialPermissions |= FtpSpecialPermissions.SetGroupID; } if (perms.Groups["others"].Value.Length == 3) { if (perms.Groups["others"].Value[0] == 'r') item.OthersPermissions |= FtpPermission.Read; if (perms.Groups["others"].Value[1] == 'w') item.OthersPermissions |= FtpPermission.Write; if (perms.Groups["others"].Value[2] == 'x' || perms.Groups["others"].Value[2] == 't') item.OthersPermissions |= FtpPermission.Execute; if (perms.Groups["others"].Value[2] == 't' || perms.Groups["others"].Value[2] == 'T') item.SpecialPermissions |= FtpSpecialPermissions.Sticky; } } } return item; }
/// <summary> /// Checks if the server supports the specified capability /// </summary> /// <param name="cap"></param> public bool HasCapability(FtpCapability cap) { return (this.Capabilities & cap) == cap; }