FtpListItem.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.Text.RegularExpressions;
  5. using System.Globalization;
  6. using System.Threading;
  7. namespace System.Net.FtpClient {
  8. /// <summary>
  9. /// Represents a file system object on the server
  10. /// </summary>
  11. /// <example><code source="..\Examples\CustomParser.cs" lang="cs" /></example>
  12. public class FtpListItem : IFtpListItem {
  13. FtpFileSystemObjectType m_type = 0;
  14. /// <summary>
  15. /// Gets the type of file system object. This property can be
  16. /// set however this functionality is intended to be done by
  17. /// custom parsers.
  18. /// </summary>
  19. public FtpFileSystemObjectType Type {
  20. get {
  21. return m_type;
  22. }
  23. set {
  24. m_type = value;
  25. }
  26. }
  27. string m_path = null;
  28. /// <summary>
  29. /// Gets the full path name to the object. This property can be
  30. /// set however this functionality is intended to be done by
  31. /// custom parsers.
  32. /// </summary>
  33. public string FullName {
  34. get {
  35. return m_path;
  36. }
  37. set {
  38. m_path = value;
  39. }
  40. }
  41. string m_name = null;
  42. /// <summary>
  43. /// Gets the name of the object. This property can be
  44. /// set however this functionality is intended to be done by
  45. /// custom parsers.
  46. /// </summary>
  47. public string Name {
  48. get {
  49. if (m_name == null && m_path != null)
  50. return m_path.GetFtpFileName();
  51. return m_name;
  52. }
  53. set {
  54. m_name = value;
  55. }
  56. }
  57. string m_linkTarget = null;
  58. /// <summary>
  59. /// Gets the target a symbolic link points to. This property can be
  60. /// set however this functionality is intended to be done by
  61. /// custom parsers.
  62. /// </summary>
  63. public string LinkTarget {
  64. get {
  65. return m_linkTarget;
  66. }
  67. set {
  68. m_linkTarget = value;
  69. }
  70. }
  71. FtpListItem m_linkObject = null;
  72. /// <summary>
  73. /// Gets the object the LinkTarget points to. This property is null unless pass the
  74. /// FtpListOption.DerefLink flag in which case GetListing() will try to resolve
  75. /// the target itself.
  76. /// </summary>
  77. public FtpListItem LinkObject {
  78. get {
  79. return m_linkObject;
  80. }
  81. set {
  82. m_linkObject = value;
  83. }
  84. }
  85. DateTime m_modified = DateTime.MinValue;
  86. /// <summary>
  87. /// Gets the last write time of the object. This property can be
  88. /// set however this functionality is intended to be done by
  89. /// custom parsers.
  90. /// </summary>
  91. public DateTime Modified {
  92. get {
  93. return m_modified;
  94. }
  95. set {
  96. m_modified = value;
  97. }
  98. }
  99. DateTime m_created = DateTime.MinValue;
  100. /// <summary>
  101. /// Gets the created date of the object. This property can be
  102. /// set however this functionality is intended to be done by
  103. /// custom parsers.
  104. /// </summary>
  105. public DateTime Created {
  106. get {
  107. return m_created;
  108. }
  109. set {
  110. m_created = value;
  111. }
  112. }
  113. long m_size = -1;
  114. /// <summary>
  115. /// Gets the size of the object. This property can be
  116. /// set however this functionality is intended to be done by
  117. /// custom parsers.
  118. /// </summary>
  119. public long Size {
  120. get {
  121. return m_size;
  122. }
  123. set {
  124. m_size = value;
  125. }
  126. }
  127. FtpSpecialPermissions m_specialPermissions = FtpSpecialPermissions.None;
  128. /// <summary>
  129. /// Gets special UNIX permissions such as Stiky, SUID and SGID. This property can be
  130. /// set however this functionality is intended to be done by
  131. /// custom parsers.
  132. /// </summary>
  133. public FtpSpecialPermissions SpecialPermissions {
  134. get {
  135. return m_specialPermissions;
  136. }
  137. set {
  138. m_specialPermissions = value;
  139. }
  140. }
  141. FtpPermission m_ownerPermissions = FtpPermission.None;
  142. /// <summary>
  143. /// Gets the owner permissions. This property can be
  144. /// set however this functionality is intended to be done by
  145. /// custom parsers.
  146. /// </summary>
  147. public FtpPermission OwnerPermissions {
  148. get {
  149. return m_ownerPermissions;
  150. }
  151. set {
  152. m_ownerPermissions = value;
  153. }
  154. }
  155. FtpPermission m_groupPermissions = FtpPermission.None;
  156. /// <summary>
  157. /// Gets the group permissions. This property can be
  158. /// set however this functionality is intended to be done by
  159. /// custom parsers.
  160. /// </summary>
  161. public FtpPermission GroupPermissions {
  162. get {
  163. return m_groupPermissions;
  164. }
  165. set {
  166. m_groupPermissions = value;
  167. }
  168. }
  169. FtpPermission m_otherPermissions = FtpPermission.None;
  170. /// <summary>
  171. /// Gets the others permissions. This property can be
  172. /// set however this functionality is intended to be done by
  173. /// custom parsers.
  174. /// </summary>
  175. public FtpPermission OthersPermissions {
  176. get {
  177. return m_otherPermissions;
  178. }
  179. set {
  180. m_otherPermissions = value;
  181. }
  182. }
  183. string m_input = null;
  184. /// <summary>
  185. /// Gets the input string that was parsed to generate the
  186. /// values in this object. This property can be
  187. /// set however this functionality is intended to be done by
  188. /// custom parsers.
  189. /// </summary>
  190. public string Input {
  191. get {
  192. return m_input;
  193. }
  194. private set {
  195. m_input = value;
  196. }
  197. }
  198. /// <summary>
  199. /// Returns a string representation of this object and its properties
  200. /// </summary>
  201. /// <returns>A string value</returns>
  202. public override string ToString() {
  203. StringBuilder sb = new StringBuilder();
  204. foreach (System.Reflection.PropertyInfo p in GetType().GetProperties()) {
  205. sb.AppendLine(string.Format("{0}: {1}", p.Name, p.GetValue(this, null)));
  206. }
  207. return sb.ToString();
  208. }
  209. /// <summary>
  210. /// Parses a line from a file listing using the first successful match in the Parsers collection.
  211. /// </summary>
  212. /// <param name="path">The source path of the file listing</param>
  213. /// <param name="buf">A line from the file listing</param>
  214. /// <param name="capabilities">Server capabilities</param>
  215. /// <returns>A FtpListItem object representing the parsed line, null if the line was
  216. /// unable to be parsed. If you have encountered an unsupported list type add a parser
  217. /// to the public static Parsers collection of FtpListItem.</returns>
  218. public static FtpListItem Parse(string path, string buf, FtpCapability capabilities) {
  219. if (buf != null && buf.Length > 0) {
  220. FtpListItem item;
  221. foreach (Parser parser in Parsers) {
  222. if ((item = parser(buf, capabilities)) != null) {
  223. // if this is a vax/openvms file listing
  224. // there are no slashes in the path name
  225. if (parser == (new Parser(ParseVaxList)))
  226. item.FullName = path + item.Name;
  227. else {
  228. //列出文件列表
  229. // FtpTrace.WriteLine(item.Name);
  230. // remove globbing/wildcard from path
  231. if (path.GetFtpFileName().Contains("*")) {
  232. path = path.GetFtpDirectoryName();
  233. }
  234. if (item.Name != null) {
  235. // absolute path? then ignore the path input to this method.
  236. if (item.Name.StartsWith("/") || item.Name.StartsWith("./") || item.Name.StartsWith("../")) {
  237. item.FullName = item.Name;
  238. item.Name = item.Name.GetFtpFileName();
  239. }
  240. else if(path != null) {
  241. item.FullName = path.GetFtpPath(item.Name); //.GetFtpPathWithoutGlob();
  242. }
  243. else {
  244. FtpTrace.WriteLine("Couldn't determine the full path of this object:{0}{1}",
  245. Environment.NewLine, item.ToString());
  246. }
  247. }
  248. // if a link target is set and it doesn't include an absolute path
  249. // then try to resolve it.
  250. if (item.LinkTarget != null && !item.LinkTarget.StartsWith("/")) {
  251. if (item.LinkTarget.StartsWith("./"))
  252. item.LinkTarget = path.GetFtpPath(item.LinkTarget.Remove(0, 2));
  253. else
  254. item.LinkTarget = path.GetFtpPath(item.LinkTarget);
  255. }
  256. }
  257. item.Input = buf;
  258. return item;
  259. }
  260. }
  261. }
  262. return null;
  263. }
  264. /// <summary>
  265. /// Used for synchronizing access to the Parsers collection
  266. /// </summary>
  267. static Object m_parserLock = new Object();
  268. /// <summary>
  269. /// Initalizes the default list of parsers
  270. /// </summary>
  271. static void InitParsers() {
  272. lock (m_parserLock)
  273. {
  274. if (m_parsers == null) {
  275. m_parsers = new List<Parser>();
  276. m_parsers.Add(new Parser(ParseMachineList));
  277. m_parsers.Add(new Parser(ParseUnixList));
  278. m_parsers.Add(new Parser(ParseDosList));
  279. m_parsers.Add(new Parser(ParseVaxList));
  280. }
  281. }
  282. }
  283. static List<Parser> m_parsers = null;
  284. /// <summary>
  285. /// Collection of parsers. Each parser object contains
  286. /// a regex string that uses named groups, i.e., (?&lt;group_name&gt;foobar).
  287. /// The support group names are modify for last write time, size for the
  288. /// size and name for the name of the file system object. Each group name is
  289. /// optional, if they are present then those values are retrieved from a
  290. /// successful match. In addition, each parser contains a Type property
  291. /// which gets set in the FtpListItem object to distinguish between different
  292. /// types of objects.
  293. /// </summary>
  294. static Parser[] Parsers {
  295. get {
  296. Parser[] parsers;
  297. lock (m_parserLock)
  298. {
  299. if (m_parsers == null)
  300. InitParsers();
  301. parsers = m_parsers.ToArray();
  302. }
  303. return parsers;
  304. }
  305. }
  306. /// <summary>
  307. /// Adds a custom parser
  308. /// </summary>
  309. /// <param name="parser">The parser delegate to add</param>
  310. /// <example><code source="..\Examples\CustomParser.cs" lang="cs" /></example>
  311. public static void AddParser(Parser parser) {
  312. lock (m_parserLock)
  313. {
  314. if (m_parsers == null)
  315. InitParsers();
  316. m_parsers.Add(parser);
  317. }
  318. }
  319. /// <summary>
  320. /// Removes all parser delegates
  321. /// </summary>
  322. public static void ClearParsers() {
  323. lock (m_parserLock)
  324. {
  325. if (m_parsers == null)
  326. InitParsers();
  327. m_parsers.Clear();
  328. }
  329. }
  330. /// <summary>
  331. /// Removes the specified parser
  332. /// </summary>
  333. /// <param name="parser">The parser delegate to remove</param>
  334. public static void RemoveParser(Parser parser) {
  335. lock (m_parserLock)
  336. {
  337. if (m_parsers == null)
  338. InitParsers();
  339. m_parsers.Remove(parser);
  340. }
  341. }
  342. /// <summary>
  343. /// Parses MLS* format listings
  344. /// </summary>
  345. /// <param name="buf">A line from the listing</param>
  346. /// <param name="capabilities">Server capabilities</param>
  347. /// <returns>FtpListItem if the item is able to be parsed</returns>
  348. static FtpListItem ParseMachineList(string buf, FtpCapability capabilities) {
  349. FtpListItem item = new FtpListItem();
  350. Match m;
  351. if (!(m = Regex.Match(buf, "type=(?<type>.+?);", RegexOptions.IgnoreCase)).Success)
  352. return null;
  353. switch (m.Groups["type"].Value.ToLower()) {
  354. case "dir":
  355. case "pdir":
  356. case "cdir":
  357. item.Type = FtpFileSystemObjectType.Directory;
  358. break;
  359. case "file":
  360. item.Type = FtpFileSystemObjectType.File;
  361. break;
  362. // These are not supported for now.
  363. case "link":
  364. case "device":
  365. default:
  366. return null;
  367. }
  368. if ((m = Regex.Match(buf, "; (?<name>.*)$", RegexOptions.IgnoreCase)).Success)
  369. item.Name = m.Groups["name"].Value;
  370. else // if we can't parse the file name there is a problem.
  371. return null;
  372. if ((m = Regex.Match(buf, "modify=(?<modify>.+?);", RegexOptions.IgnoreCase)).Success)
  373. item.Modified = m.Groups["modify"].Value.GetFtpDate(DateTimeStyles.AssumeUniversal);
  374. if ((m = Regex.Match(buf, "created?=(?<create>.+?);", RegexOptions.IgnoreCase)).Success)
  375. item.Created = m.Groups["create"].Value.GetFtpDate(DateTimeStyles.AssumeUniversal);
  376. if ((m = Regex.Match(buf, @"size=(?<size>\d+);", RegexOptions.IgnoreCase)).Success) {
  377. long size;
  378. if (long.TryParse(m.Groups["size"].Value, out size))
  379. item.Size = size;
  380. }
  381. if ((m = Regex.Match(buf, @"unix.mode=(?<mode>\d+);", RegexOptions.IgnoreCase)).Success) {
  382. if (m.Groups["mode"].Value.Length == 4) {
  383. item.SpecialPermissions = (FtpSpecialPermissions)int.Parse(m.Groups["mode"].Value[0].ToString());
  384. item.OwnerPermissions = (FtpPermission)int.Parse(m.Groups["mode"].Value[1].ToString());
  385. item.GroupPermissions = (FtpPermission)int.Parse(m.Groups["mode"].Value[2].ToString());
  386. item.OthersPermissions = (FtpPermission)int.Parse(m.Groups["mode"].Value[3].ToString());
  387. }
  388. else if (m.Groups["mode"].Value.Length == 3) {
  389. item.OwnerPermissions = (FtpPermission)int.Parse(m.Groups["mode"].Value[0].ToString());
  390. item.GroupPermissions = (FtpPermission)int.Parse(m.Groups["mode"].Value[1].ToString());
  391. item.OthersPermissions = (FtpPermission)int.Parse(m.Groups["mode"].Value[2].ToString());
  392. }
  393. }
  394. return item;
  395. }
  396. /// <summary>
  397. /// Parses LIST format listings
  398. /// </summary>
  399. /// <param name="buf">A line from the listing</param>
  400. /// <param name="capabilities">Server capabilities</param>
  401. /// <returns>FtpListItem if the item is able to be parsed</returns>
  402. static FtpListItem ParseUnixList(string buf, FtpCapability capabilities) {
  403. string regex =
  404. @"(?<permissions>.+)\s+" +
  405. @"(?<objectcount>\d+)\s+" +
  406. @"(?<user>.+)\s+" +
  407. @"(?<group>.+)\s+" +
  408. @"(?<size>\d+)\s+" +
  409. @"(?<modify>\w+\s+\d+\s+\d+:\d+|\w+\s+\d+\s+\d+)\s" +
  410. @"(?<name>.*)$";
  411. FtpListItem item = new FtpListItem();
  412. Match m;
  413. if (!(m = Regex.Match(buf, regex, RegexOptions.IgnoreCase)).Success)
  414. return null;
  415. // if this field is missing we can't determine
  416. // what the object is.
  417. if (m.Groups["permissions"].Value.Length == 0)
  418. return null;
  419. switch (m.Groups["permissions"].Value[0]) {
  420. case 'd':
  421. item.Type = FtpFileSystemObjectType.Directory;
  422. break;
  423. case '-':
  424. case 's':
  425. item.Type = FtpFileSystemObjectType.File;
  426. break;
  427. case 'l':
  428. item.Type = FtpFileSystemObjectType.Link;
  429. break;
  430. default:
  431. return null;
  432. }
  433. // if we can't determine a file name then
  434. // we are not considering this a successful parsing operation.
  435. if (m.Groups["name"].Value.Length < 1)
  436. return null;
  437. item.Name = m.Groups["name"].Value;
  438. switch (item.Type) {
  439. case FtpFileSystemObjectType.Directory:
  440. // ignore these...
  441. if (item.Name == "." || item.Name == "..")
  442. return null;
  443. break;
  444. case FtpFileSystemObjectType.Link:
  445. if (!item.Name.Contains(" -> "))
  446. return null;
  447. item.LinkTarget = item.Name.Remove(0, item.Name.IndexOf("-> ") + 3);
  448. item.Name = item.Name.Remove(item.Name.IndexOf(" -> "));
  449. break;
  450. }
  451. // for date parser testing only
  452. //capabilities = ~(capabilities & FtpCapability.MDTM);
  453. ////
  454. // Ignore the Modify times sent in LIST format for files
  455. // when the server has support for the MDTM command
  456. // because they will never be as accurate as what can be had
  457. // by using the MDTM command. MDTM does not work on directories
  458. // so if a modify time was parsed from the listing we will try
  459. // to convert it to a DateTime object and use it for directories.
  460. ////
  461. if (((capabilities & FtpCapability.MDTM) != FtpCapability.MDTM || item.Type == FtpFileSystemObjectType.Directory) && m.Groups["modify"].Value.Length > 0) {
  462. item.Modified = m.Groups["modify"].Value.GetFtpDate(DateTimeStyles.AssumeLocal);
  463. if (item.Modified == DateTime.MinValue) {
  464. FtpTrace.WriteLine("GetFtpDate() failed on {0}", m.Groups["modify"].Value);
  465. }
  466. }
  467. else {
  468. if (m.Groups["modify"].Value.Length == 0)
  469. FtpTrace.WriteLine("RegEx failed to parse modified date from {0}.", buf);
  470. else if (item.Type == FtpFileSystemObjectType.Directory)
  471. FtpTrace.WriteLine("Modified times of directories are ignored in UNIX long listings.");
  472. else if ((capabilities & FtpCapability.MDTM) == FtpCapability.MDTM)
  473. 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.");
  474. }
  475. if (m.Groups["size"].Value.Length > 0) {
  476. long size;
  477. if (long.TryParse(m.Groups["size"].Value, out size))
  478. item.Size = size;
  479. }
  480. if (m.Groups["permissions"].Value.Length > 0) {
  481. Match perms = Regex.Match(m.Groups["permissions"].Value,
  482. @"[\w-]{1}(?<owner>[\w-]{3})(?<group>[\w-]{3})(?<others>[\w-]{3})",
  483. RegexOptions.IgnoreCase);
  484. if (perms.Success) {
  485. if (perms.Groups["owner"].Value.Length == 3) {
  486. if (perms.Groups["owner"].Value[0] == 'r')
  487. item.OwnerPermissions |= FtpPermission.Read;
  488. if (perms.Groups["owner"].Value[1] == 'w')
  489. item.OwnerPermissions |= FtpPermission.Write;
  490. if (perms.Groups["owner"].Value[2] == 'x' || perms.Groups["owner"].Value[2] == 's')
  491. item.OwnerPermissions |= FtpPermission.Execute;
  492. if (perms.Groups["owner"].Value[2] == 's' || perms.Groups["owner"].Value[2] == 'S')
  493. item.SpecialPermissions |= FtpSpecialPermissions.SetUserID;
  494. }
  495. if (perms.Groups["group"].Value.Length == 3) {
  496. if (perms.Groups["group"].Value[0] == 'r')
  497. item.GroupPermissions |= FtpPermission.Read;
  498. if (perms.Groups["group"].Value[1] == 'w')
  499. item.GroupPermissions |= FtpPermission.Write;
  500. if (perms.Groups["group"].Value[2] == 'x' || perms.Groups["group"].Value[2] == 's')
  501. item.GroupPermissions |= FtpPermission.Execute;
  502. if (perms.Groups["group"].Value[2] == 's' || perms.Groups["group"].Value[2] == 'S')
  503. item.SpecialPermissions |= FtpSpecialPermissions.SetGroupID;
  504. }
  505. if (perms.Groups["others"].Value.Length == 3) {
  506. if (perms.Groups["others"].Value[0] == 'r')
  507. item.OthersPermissions |= FtpPermission.Read;
  508. if (perms.Groups["others"].Value[1] == 'w')
  509. item.OthersPermissions |= FtpPermission.Write;
  510. if (perms.Groups["others"].Value[2] == 'x' || perms.Groups["others"].Value[2] == 't')
  511. item.OthersPermissions |= FtpPermission.Execute;
  512. if (perms.Groups["others"].Value[2] == 't' || perms.Groups["others"].Value[2] == 'T')
  513. item.SpecialPermissions |= FtpSpecialPermissions.Sticky;
  514. }
  515. }
  516. }
  517. return item;
  518. }
  519. /// <summary>
  520. /// Parses IIS DOS format listings
  521. /// </summary>
  522. /// <param name="buf">A line from the listing</param>
  523. /// <param name="capabilities">Server capabilities</param>
  524. /// <returns>FtpListItem if the item is able to be parsed</returns>
  525. static FtpListItem ParseDosList(string buf, FtpCapability capabilities) {
  526. FtpListItem item = new FtpListItem();
  527. string[] datefmt = new string[] {
  528. "MM-dd-yy hh:mmtt",
  529. "MM-dd-yyyy hh:mmtt"
  530. };
  531. Match m;
  532. // directory
  533. if ((m = Regex.Match(buf, @"(?<modify>\d+-\d+-\d+\s+\d+:\d+\w+)\s+<DIR>\s+(?<name>.*)$", RegexOptions.IgnoreCase)).Success) {
  534. DateTime modify;
  535. item.Type = FtpFileSystemObjectType.Directory;
  536. item.Name = m.Groups["name"].Value;
  537. //if (DateTime.TryParse(m.Groups["modify"].Value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out modify))
  538. if (DateTime.TryParseExact(m.Groups["modify"].Value, datefmt, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out modify))
  539. item.Modified = modify;
  540. }
  541. // file
  542. else if ((m = Regex.Match(buf, @"(?<modify>\d+-\d+-\d+\s+\d+:\d+\w+)\s+(?<size>\d+)\s+(?<name>.*)$", RegexOptions.IgnoreCase)).Success) {
  543. DateTime modify;
  544. long size;
  545. item.Type = FtpFileSystemObjectType.File;
  546. item.Name = m.Groups["name"].Value;
  547. if (long.TryParse(m.Groups["size"].Value, out size))
  548. item.Size = size;
  549. //if (DateTime.TryParse(m.Groups["modify"].Value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out modify))
  550. if (DateTime.TryParseExact(m.Groups["modify"].Value, datefmt, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out modify))
  551. item.Modified = modify;
  552. }
  553. else
  554. return null;
  555. return item;
  556. }
  557. static FtpListItem ParseVaxList(string buf, FtpCapability capabilities) {
  558. string regex =
  559. @"(?<name>.+)\.(?<extension>.+);(?<version>\d+)\s+" +
  560. @"(?<size>\d+)\s+" +
  561. @"(?<modify>\d+-\w+-\d+\s+\d+:\d+)";
  562. Match m;
  563. if ((m = Regex.Match(buf, regex)).Success) {
  564. FtpListItem item = new FtpListItem();
  565. item.m_name = string.Format("{0}.{1};{2}",
  566. m.Groups["name"].Value,
  567. m.Groups["extension"].Value,
  568. m.Groups["version"].Value);
  569. if (m.Groups["extension"].Value.ToUpper() == "DIR")
  570. item.m_type = FtpFileSystemObjectType.Directory;
  571. else
  572. item.m_type = FtpFileSystemObjectType.File;
  573. if (!long.TryParse(m.Groups["size"].Value, out item.m_size))
  574. item.m_size = -1;
  575. if (!DateTime.TryParse(m.Groups["modify"].Value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out item.m_modified))
  576. item.m_modified = DateTime.MinValue;
  577. return item;
  578. }
  579. return null;
  580. }
  581. /// <summary>
  582. /// Ftp listing line parser
  583. /// </summary>
  584. /// <param name="line">The line from the listing</param>
  585. /// <param name="capabilities">The server capabilities</param>
  586. /// <returns>FtpListItem if the line can be parsed, null otherwise</returns>
  587. public delegate FtpListItem Parser(string line, FtpCapability capabilities);
  588. }
  589. }