1. /*
  2. * Copyright 2001-2004 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.commons.net.ftp.parser;
  17. import java.util.Calendar;
  18. import org.apache.commons.net.ftp.FTPFile;
  19. /**
  20. * Implementation FTPFileEntryParser and FTPFileListParser for standard
  21. * Unix Systems.
  22. *
  23. * This class is based on the logic of Daniel Savarese's
  24. * DefaultFTPListParser, but adapted to use regular expressions and to fit the
  25. * new FTPFileEntryParser interface.
  26. * @author <a href="mailto:scohen@ignitesports.com">Steve Cohen</a>
  27. * @version $Id: UnixFTPEntryParser.java,v 1.17 2004/06/22 02:30:33 scohen Exp $
  28. * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions)
  29. */
  30. public class UnixFTPEntryParser extends RegexFTPFileEntryParserImpl
  31. {
  32. /**
  33. * months abbreviations looked for by this parser. Also used
  34. * to determine which month is matched by the parser
  35. */
  36. private static final String MONTHS =
  37. "(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)";
  38. /**
  39. * this is the regular expression used by this parser.
  40. *
  41. * Permissions:
  42. * r the file is readable
  43. * w the file is writable
  44. * x the file is executable
  45. * - the indicated permission is not granted
  46. * L mandatory locking occurs during access (the set-group-ID bit is
  47. * on and the group execution bit is off)
  48. * s the set-user-ID or set-group-ID bit is on, and the corresponding
  49. * user or group execution bit is also on
  50. * S undefined bit-state (the set-user-ID bit is on and the user
  51. * execution bit is off)
  52. * t the 1000 (octal) bit, or sticky bit, is on [see chmod(1)], and
  53. * execution is on
  54. * T the 1000 bit is turned on, and execution is off (undefined bit-
  55. * state)
  56. */
  57. private static final String REGEX =
  58. "([bcdlfmpSs-])"
  59. + "(((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-])))\\s+"
  60. + "(\\d+)\\s+"
  61. + "(\\S+)\\s+"
  62. + "(?:(\\S+)\\s+)?"
  63. + "(\\d+)\\s+"
  64. + MONTHS + "\\s+"
  65. + "((?:[0-9])|(?:[0-2][0-9])|(?:3[0-1]))\\s+"
  66. + "((\\d\\d\\d\\d)|((?:[01]\\d)|(?:2[0123])|(?:[1-9])):([012345]\\d))\\s+"
  67. + "(\\S+)(\\s*.*)";
  68. /**
  69. * The sole constructor for a UnixFTPEntryParser object.
  70. *
  71. * @exception IllegalArgumentException
  72. * Thrown if the regular expression is unparseable. Should not be seen
  73. * under normal conditions. It it is seen, this is a sign that
  74. * <code>REGEX</code> is not a valid regular expression.
  75. */
  76. public UnixFTPEntryParser()
  77. {
  78. super(REGEX);
  79. }
  80. /**
  81. * Parses a line of a unix (standard) FTP server file listing and converts
  82. * it into a usable format in the form of an <code> FTPFile </code>
  83. * instance. If the file listing line doesn't describe a file,
  84. * <code> null </code> is returned, otherwise a <code> FTPFile </code>
  85. * instance representing the files in the directory is returned.
  86. * <p>
  87. * @param entry A line of text from the file listing
  88. * @return An FTPFile instance corresponding to the supplied entry
  89. */
  90. public FTPFile parseFTPEntry(String entry)
  91. {
  92. FTPFile file = new FTPFile();
  93. file.setRawListing(entry);
  94. int type;
  95. boolean isDevice = false;
  96. if (matches(entry))
  97. {
  98. String typeStr = group(1);
  99. String hardLinkCount = group(15);
  100. String usr = group(16);
  101. String grp = group(17);
  102. String filesize = group(18);
  103. String mo = group(19);
  104. String da = group(20);
  105. String yr = group(22);
  106. String hr = group(23);
  107. String min = group(24);
  108. String name = group(25);
  109. String endtoken = group(26);
  110. // bcdlfmpSs-
  111. switch (typeStr.charAt(0))
  112. {
  113. case 'd':
  114. type = FTPFile.DIRECTORY_TYPE;
  115. break;
  116. case 'l':
  117. type = FTPFile.SYMBOLIC_LINK_TYPE;
  118. break;
  119. case 'b':
  120. case 'c':
  121. isDevice = true;
  122. // break; - fall through
  123. case 'f':
  124. case '-':
  125. type = FTPFile.FILE_TYPE;
  126. break;
  127. default:
  128. type = FTPFile.UNKNOWN_TYPE;
  129. }
  130. file.setType(type);
  131. int g = 4;
  132. for (int access = 0; access < 3; access++, g += 4)
  133. {
  134. // Use != '-' to avoid having to check for suid and sticky bits
  135. file.setPermission(access, FTPFile.READ_PERMISSION,
  136. (!group(g).equals("-")));
  137. file.setPermission(access, FTPFile.WRITE_PERMISSION,
  138. (!group(g + 1).equals("-")));
  139. String execPerm = group(g + 2);
  140. if (!execPerm.equals("-") && !Character.isUpperCase(execPerm.charAt(0)))
  141. {
  142. file.setPermission(access, FTPFile.EXECUTE_PERMISSION, true);
  143. }
  144. else
  145. {
  146. file.setPermission(access, FTPFile.EXECUTE_PERMISSION, false);
  147. }
  148. }
  149. if (!isDevice)
  150. {
  151. try
  152. {
  153. file.setHardLinkCount(Integer.parseInt(hardLinkCount));
  154. }
  155. catch (NumberFormatException e)
  156. {
  157. // intentionally do nothing
  158. }
  159. }
  160. file.setUser(usr);
  161. file.setGroup(grp);
  162. try
  163. {
  164. file.setSize(Integer.parseInt(filesize));
  165. }
  166. catch (NumberFormatException e)
  167. {
  168. // intentionally do nothing
  169. }
  170. Calendar cal = Calendar.getInstance();
  171. cal.set(Calendar.SECOND, 0);
  172. cal.set(Calendar.MINUTE, 0);
  173. cal.set(Calendar.HOUR_OF_DAY, 0);
  174. try
  175. {
  176. int pos = MONTHS.indexOf(mo);
  177. int month = pos / 4;
  178. if (null != yr)
  179. {
  180. // it's a year
  181. cal.set(Calendar.YEAR, Integer.parseInt(yr));
  182. }
  183. else
  184. {
  185. // it must be hour/minute or we wouldn't have matched
  186. int year = cal.get(Calendar.YEAR);
  187. // if the month we're reading is greater than now, it must
  188. // be last year
  189. if (cal.get(Calendar.MONTH) < month)
  190. {
  191. year--;
  192. }
  193. cal.set(Calendar.YEAR, year);
  194. cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(hr));
  195. cal.set(Calendar.MINUTE, Integer.parseInt(min));
  196. }
  197. cal.set(Calendar.MONTH, month);
  198. cal.set(Calendar.DATE, Integer.parseInt(da));
  199. file.setTimestamp(cal);
  200. }
  201. catch (NumberFormatException e)
  202. {
  203. // do nothing, date will be uninitialized
  204. }
  205. if (null == endtoken)
  206. {
  207. file.setName(name);
  208. }
  209. else
  210. {
  211. // oddball cases like symbolic links, file names
  212. // with spaces in them.
  213. name += endtoken;
  214. if (type == FTPFile.SYMBOLIC_LINK_TYPE)
  215. {
  216. int end = name.indexOf(" -> ");
  217. // Give up if no link indicator is present
  218. if (end == -1)
  219. {
  220. file.setName(name);
  221. }
  222. else
  223. {
  224. file.setName(name.substring(0, end));
  225. file.setLink(name.substring(end + 4));
  226. }
  227. }
  228. else
  229. {
  230. file.setName(name);
  231. }
  232. }
  233. return file;
  234. }
  235. return null;
  236. }
  237. }