PageRenderTime 26ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 0ms

/Visual Studio 2008/CSFTPDownload/FTPFileSystem.cs

#
C# | 319 lines | 138 code | 62 blank | 119 comment | 37 complexity | 4630abaa43d02a0beb5a772c7e81cd13 MD5 | raw file
  1. /****************************** Module Header ******************************\
  2. * Module Name: FTPFileSystem.cs
  3. * Project: CSFTPDownload
  4. * Copyright (c) Microsoft Corporation.
  5. *
  6. * The class FTPFileSystem represents a file on the remote FTP server. When run
  7. * the FTP LIST protocol method to get a detailed listing of the files on an
  8. * FTP server, the server will response many records of information. Each record
  9. * represents a file. Depended on the FTP Directory Listing Style of the server,
  10. * the record is like
  11. * 1. MSDOS
  12. * 1.1. Directory
  13. * 12-13-10 12:41PM <DIR> Folder A
  14. * 1.2. File
  15. * 12-13-10 12:41PM [Size] File B
  16. *
  17. * NOTE: The date segment is like "12-13-10" instead of "12-13-2010" if Four-digit
  18. * years is not checked in IIS.
  19. *
  20. * 2. UNIX
  21. * 2.1. Directory
  22. * drwxrwxrwx 1 owner group 0 Dec 1 12:00 Folder A
  23. * 2.2. File
  24. * -rwxrwxrwx 1 owner group [Size] Dec 1 12:00 File B
  25. *
  26. * NOTE: The date segment does not contains year.
  27. *
  28. *
  29. * This source is subject to the Microsoft Public License.
  30. * See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
  31. * All other rights reserved.
  32. *
  33. * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
  34. * EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
  35. * WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
  36. \***************************************************************************/
  37. using System;
  38. using System.Text.RegularExpressions;
  39. using System.Text;
  40. namespace CSFTPDownload
  41. {
  42. public class FTPFileSystem
  43. {
  44. /// <summary>
  45. /// The original record string.
  46. /// </summary>
  47. public string OriginalRecordString { get; set; }
  48. /// <summary>
  49. /// MSDOS or UNIX.
  50. /// </summary>
  51. public FTPDirectoryListingStyle DirectoryListingStyle { get; set; }
  52. /// <summary>
  53. /// The server Path.
  54. /// </summary>
  55. public Uri Url { get; set; }
  56. /// <summary>
  57. /// The name of this FTPFileSystem instance.
  58. /// </summary>
  59. public string Name { get; set; }
  60. /// <summary>
  61. /// Specify whether this FTPFileSystem instance is a directory.
  62. /// </summary>
  63. public bool IsDirectory { get; set; }
  64. /// <summary>
  65. /// The last modified time of this FTPFileSystem instance.
  66. /// </summary>
  67. public DateTime ModifiedTime { get; set; }
  68. /// <summary>
  69. /// The size of this FTPFileSystem instance if it is not a directory.
  70. /// </summary>
  71. public int Size { get; set; }
  72. private FTPFileSystem() { }
  73. /// <summary>
  74. /// Override the method ToString() to display a more friendly message.
  75. /// </summary>
  76. public override string ToString()
  77. {
  78. return string.Format("{0}\t{1}\t\t{2}",
  79. this.ModifiedTime.ToString("yyyy-MM-dd HH:mm"),
  80. this.IsDirectory ? "<DIR>" : this.Size.ToString(),
  81. this.Name);
  82. }
  83. /// <summary>
  84. /// Find out the FTP Directory Listing Style from the recordString.
  85. /// </summary>
  86. public static FTPDirectoryListingStyle GetDirectoryListingStyle(string recordString)
  87. {
  88. Regex regex = new System.Text.RegularExpressions.Regex(@"^[d-]([r-][w-][x-]){3}$");
  89. string header = recordString.Substring(0, 10);
  90. // If the style is UNIX, then the header is like "drwxrwxrwx".
  91. if (regex.IsMatch(header))
  92. {
  93. return FTPDirectoryListingStyle.UNIX;
  94. }
  95. else
  96. {
  97. return FTPDirectoryListingStyle.MSDOS;
  98. }
  99. }
  100. /// <summary>
  101. /// Get an FTPFileSystem from the recordString.
  102. /// </summary>
  103. public static FTPFileSystem ParseRecordString(Uri baseUrl, string recordString, FTPDirectoryListingStyle type)
  104. {
  105. FTPFileSystem fileSystem = null;
  106. if (type == FTPDirectoryListingStyle.UNIX)
  107. {
  108. fileSystem = ParseUNIXRecordString(recordString);
  109. }
  110. else
  111. {
  112. fileSystem = ParseMSDOSRecordString(recordString);
  113. }
  114. // Add "/" to the url if it is a directory
  115. fileSystem.Url = new Uri(baseUrl, fileSystem.Name + (fileSystem.IsDirectory ? "/" : string.Empty));
  116. return fileSystem;
  117. }
  118. /// <summary>
  119. /// The recordString is like
  120. /// Directory: drwxrwxrwx 1 owner group 0 Dec 13 11:25 Folder A
  121. /// File: -rwxrwxrwx 1 owner group 1024 Dec 13 11:25 File B
  122. /// NOTE: The date segment does not contains year.
  123. /// </summary>
  124. static FTPFileSystem ParseUNIXRecordString(string recordString)
  125. {
  126. FTPFileSystem fileSystem = new FTPFileSystem();
  127. fileSystem.OriginalRecordString = recordString.Trim();
  128. fileSystem.DirectoryListingStyle = FTPDirectoryListingStyle.UNIX;
  129. // The segments is like "drwxrwxrwx", "", "", "1", "owner", "", "", "",
  130. // "group", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
  131. // "0", "Dec", "13", "11:25", "Folder", "A".
  132. string[] segments = fileSystem.OriginalRecordString.Split(' ');
  133. int index = 0;
  134. // The permission segment is like "drwxrwxrwx".
  135. string permissionsegment = segments[index];
  136. // If the property start with 'd', then it means a directory.
  137. fileSystem.IsDirectory = permissionsegment[0] == 'd';
  138. // Skip the empty segments.
  139. while (segments[++index] == string.Empty) { }
  140. // Skip the directories segment.
  141. // Skip the empty segments.
  142. while (segments[++index] == string.Empty) { }
  143. // Skip the owner segment.
  144. // Skip the empty segments.
  145. while (segments[++index] == string.Empty) { }
  146. // Skip the group segment.
  147. // Skip the empty segments.
  148. while (segments[++index] == string.Empty) { }
  149. // If this fileSystem is a file, then the size is larger than 0.
  150. fileSystem.Size = int.Parse(segments[index]);
  151. // Skip the empty segments.
  152. while (segments[++index] == string.Empty) { }
  153. // The month segment.
  154. string monthsegment = segments[index];
  155. // Skip the empty segments.
  156. while (segments[++index] == string.Empty) { }
  157. // The day segment.
  158. string daysegment = segments[index];
  159. // Skip the empty segments.
  160. while (segments[++index] == string.Empty) { }
  161. // The time segment.
  162. string timesegment = segments[index];
  163. fileSystem.ModifiedTime = DateTime.Parse(string.Format("{0} {1} {2} ",
  164. timesegment, monthsegment, daysegment));
  165. // Skip the empty segments.
  166. while (segments[++index] == string.Empty) { }
  167. // Calculate the index of the file name part in the original string.
  168. int filenameIndex = 0;
  169. for (int i = 0; i < index; i++)
  170. {
  171. // "" represents ' ' in the original string.
  172. if (segments[i] == string.Empty)
  173. {
  174. filenameIndex += 1;
  175. }
  176. else
  177. {
  178. filenameIndex += segments[i].Length + 1;
  179. }
  180. }
  181. // The file name may include many segments because the name can contain ' '.
  182. fileSystem.Name = fileSystem.OriginalRecordString.Substring(filenameIndex).Trim();
  183. return fileSystem;
  184. }
  185. /// <summary>
  186. /// 12-13-10 12:41PM <DIR> Folder A
  187. /// </summary>
  188. /// <param name="recordString"></param>
  189. /// <returns></returns>
  190. static FTPFileSystem ParseMSDOSRecordString(string recordString)
  191. {
  192. FTPFileSystem fileSystem = new FTPFileSystem();
  193. fileSystem.OriginalRecordString = recordString.Trim();
  194. fileSystem.DirectoryListingStyle = FTPDirectoryListingStyle.MSDOS;
  195. // The segments is like "12-13-10", "", "12:41PM", "", "","", "",
  196. // "", "", "<DIR>", "", "", "", "", "", "", "", "", "", "Folder", "A".
  197. string[] segments = fileSystem.OriginalRecordString.Split(' ');
  198. int index = 0;
  199. // The date segment is like "12-13-10" instead of "12-13-2010" if Four-digit years
  200. // is not checked in IIS.
  201. string dateSegment = segments[index];
  202. string[] dateSegments = dateSegment.Split(new char[] { '-' },
  203. StringSplitOptions.RemoveEmptyEntries);
  204. int month = int.Parse(dateSegments[0]);
  205. int day = int.Parse(dateSegments[1]);
  206. int year = int.Parse(dateSegments[2]);
  207. // If year >=50 and year <100, then it means the year 19**
  208. if (year >= 50 && year < 100)
  209. {
  210. year += 1900;
  211. }
  212. // If year <50, then it means the year 20**
  213. else if (year < 50)
  214. {
  215. year += 2000;
  216. }
  217. // Skip the empty segments.
  218. while (segments[++index] == string.Empty) { }
  219. // The time segment.
  220. string timesegment = segments[index];
  221. fileSystem.ModifiedTime = DateTime.Parse(string.Format("{0}-{1}-{2} {3}",
  222. year, month, day, timesegment));
  223. // Skip the empty segments.
  224. while (segments[++index] == string.Empty) { }
  225. // The size or directory segment.
  226. // If this segment is "<DIR>", then it means a directory, else it means the
  227. // file size.
  228. string sizeOrDirSegment = segments[index];
  229. fileSystem.IsDirectory = sizeOrDirSegment.Equals("<DIR>",
  230. StringComparison.OrdinalIgnoreCase);
  231. // If this fileSystem is a file, then the size is larger than 0.
  232. if (!fileSystem.IsDirectory)
  233. {
  234. fileSystem.Size = int.Parse(sizeOrDirSegment);
  235. }
  236. // Skip the empty segments.
  237. while (segments[++index] == string.Empty) { }
  238. // Calculate the index of the file name part in the original string.
  239. int filenameIndex = 0;
  240. for (int i = 0; i < index; i++)
  241. {
  242. // "" represents ' ' in the original string.
  243. if (segments[i] == string.Empty)
  244. {
  245. filenameIndex += 1;
  246. }
  247. else
  248. {
  249. filenameIndex += segments[i].Length + 1;
  250. }
  251. }
  252. // The file name may include many segments because the name can contain ' '.
  253. fileSystem.Name = fileSystem.OriginalRecordString.Substring(filenameIndex).Trim();
  254. return fileSystem;
  255. }
  256. }
  257. }