001 /*
002 * Copyright 2001-2005 The Apache Software Foundation
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package org.apache.commons.net.ftp.parser;
017 import java.text.ParseException;
018
019 import org.apache.commons.net.ftp.FTPClientConfig;
020 import org.apache.commons.net.ftp.FTPFile;
021
022 /**
023 * Implementation FTPFileEntryParser and FTPFileListParser for standard
024 * Unix Systems.
025 *
026 * This class is based on the logic of Daniel Savarese's
027 * DefaultFTPListParser, but adapted to use regular expressions and to fit the
028 * new FTPFileEntryParser interface.
029 * @version $Id: UnixFTPEntryParser.java 161712 2005-04-18 02:57:04Z scohen $
030 * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions)
031 */
032 public class UnixFTPEntryParser extends ConfigurableFTPFileEntryParserImpl
033 {
034 /**
035 * months abbreviations looked for by this parser. Also used
036 * to determine which month is matched by the parser
037 */
038 private static final String DEFAULT_MONTHS =
039 "(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)";
040
041 static final String DEFAULT_DATE_FORMAT
042 = "MMM d yyyy"; //Nov 9 2001
043
044 static final String DEFAULT_RECENT_DATE_FORMAT
045 = "MMM d HH:mm"; //Nov 9 20:06
046
047 static final String NUMERIC_DATE_FORMAT
048 = "yyyy-MM-dd HH:mm"; //2001-11-09 20:06
049
050 /**
051 * Some Linux distributions are now shipping an FTP server which formats
052 * file listing dates in an all-numeric format:
053 * <code>"yyyy-MM-dd HH:mm</code>.
054 * This is a very welcome development, and hopefully it will soon become
055 * the standard. However, since it is so new, for now, and possibly
056 * forever, we merely accomodate it, but do not make it the default.
057 * <p>
058 * For now end users may specify this format only via
059 * <code>UnixFTPEntryParser(FTPClientConfig)</code>.
060 * Steve Cohen - 2005-04-17
061 */
062 public static final FTPClientConfig NUMERIC_DATE_CONFIG =
063 new FTPClientConfig(
064 FTPClientConfig.SYST_UNIX,
065 NUMERIC_DATE_FORMAT,
066 null, null, null, null);
067
068 /**
069 * this is the regular expression used by this parser.
070 *
071 * Permissions:
072 * r the file is readable
073 * w the file is writable
074 * x the file is executable
075 * - the indicated permission is not granted
076 * L mandatory locking occurs during access (the set-group-ID bit is
077 * on and the group execution bit is off)
078 * s the set-user-ID or set-group-ID bit is on, and the corresponding
079 * user or group execution bit is also on
080 * S undefined bit-state (the set-user-ID bit is on and the user
081 * execution bit is off)
082 * t the 1000 (octal) bit, or sticky bit, is on [see chmod(1)], and
083 * execution is on
084 * T the 1000 bit is turned on, and execution is off (undefined bit-
085 * state)
086 */
087 private static final String REGEX =
088 "([bcdlfmpSs-])"
089 +"(((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-])))\\+?\\s+"
090 + "(\\d+)\\s+"
091 + "(\\S+)\\s+"
092 + "(?:(\\S+)\\s+)?"
093 + "(\\d+)\\s+"
094
095 /*
096 numeric or standard format date
097 */
098 + "((?:\\d+[-/]\\d+[-/]\\d+)|(?:\\S+\\s+\\S+))\\s+"
099
100 /*
101 year (for non-recent standard format)
102 or time (for numeric or recent standard format
103 */
104 + "(\\d+(?::\\d+)?)\\s+"
105
106 + "(\\S*)(\\s*.*)";
107
108
109 /**
110 * The default constructor for a UnixFTPEntryParser object.
111 *
112 * @exception IllegalArgumentException
113 * Thrown if the regular expression is unparseable. Should not be seen
114 * under normal conditions. It it is seen, this is a sign that
115 * <code>REGEX</code> is not a valid regular expression.
116 */
117 public UnixFTPEntryParser()
118 {
119 this(null);
120 }
121
122 /**
123 * This constructor allows the creation of a UnixFTPEntryParser object with
124 * something other than the default configuration.
125 *
126 * @param config The {@link FTPClientConfig configuration} object used to
127 * configure this parser.
128 * @exception IllegalArgumentException
129 * Thrown if the regular expression is unparseable. Should not be seen
130 * under normal conditions. It it is seen, this is a sign that
131 * <code>REGEX</code> is not a valid regular expression.
132 * @since 1.4
133 */
134 public UnixFTPEntryParser(FTPClientConfig config)
135 {
136 super(REGEX);
137 configure(config);
138 }
139
140
141 /**
142 * Parses a line of a unix (standard) FTP server file listing and converts
143 * it into a usable format in the form of an <code> FTPFile </code>
144 * instance. If the file listing line doesn't describe a file,
145 * <code> null </code> is returned, otherwise a <code> FTPFile </code>
146 * instance representing the files in the directory is returned.
147 * <p>
148 * @param entry A line of text from the file listing
149 * @return An FTPFile instance corresponding to the supplied entry
150 */
151 public FTPFile parseFTPEntry(String entry) {
152 FTPFile file = new FTPFile();
153 file.setRawListing(entry);
154 int type;
155 boolean isDevice = false;
156
157 if (matches(entry))
158 {
159 String typeStr = group(1);
160 String hardLinkCount = group(15);
161 String usr = group(16);
162 String grp = group(17);
163 String filesize = group(18);
164 String datestr = group(19) + " " + group(20);
165 String name = group(21);
166 String endtoken = group(22);
167
168 try
169 {
170 file.setTimestamp(super.parseTimestamp(datestr));
171 }
172 catch (ParseException e)
173 {
174 return null; // this is a parsing failure too.
175 }
176
177
178 // bcdlfmpSs-
179 switch (typeStr.charAt(0))
180 {
181 case 'd':
182 type = FTPFile.DIRECTORY_TYPE;
183 break;
184 case 'l':
185 type = FTPFile.SYMBOLIC_LINK_TYPE;
186 break;
187 case 'b':
188 case 'c':
189 isDevice = true;
190 // break; - fall through
191 case 'f':
192 case '-':
193 type = FTPFile.FILE_TYPE;
194 break;
195 default:
196 type = FTPFile.UNKNOWN_TYPE;
197 }
198
199 file.setType(type);
200
201 int g = 4;
202 for (int access = 0; access < 3; access++, g += 4)
203 {
204 // Use != '-' to avoid having to check for suid and sticky bits
205 file.setPermission(access, FTPFile.READ_PERMISSION,
206 (!group(g).equals("-")));
207 file.setPermission(access, FTPFile.WRITE_PERMISSION,
208 (!group(g + 1).equals("-")));
209
210 String execPerm = group(g + 2);
211 if (!execPerm.equals("-") && !Character.isUpperCase(execPerm.charAt(0)))
212 {
213 file.setPermission(access, FTPFile.EXECUTE_PERMISSION, true);
214 }
215 else
216 {
217 file.setPermission(access, FTPFile.EXECUTE_PERMISSION, false);
218 }
219 }
220
221 if (!isDevice)
222 {
223 try
224 {
225 file.setHardLinkCount(Integer.parseInt(hardLinkCount));
226 }
227 catch (NumberFormatException e)
228 {
229 // intentionally do nothing
230 }
231 }
232
233 file.setUser(usr);
234 file.setGroup(grp);
235
236 try
237 {
238 file.setSize(Long.parseLong(filesize));
239 }
240 catch (NumberFormatException e)
241 {
242 // intentionally do nothing
243 }
244
245 if (null == endtoken)
246 {
247 file.setName(name);
248 }
249 else
250 {
251 // oddball cases like symbolic links, file names
252 // with spaces in them.
253 name += endtoken;
254 if (type == FTPFile.SYMBOLIC_LINK_TYPE)
255 {
256
257 int end = name.indexOf(" -> ");
258 // Give up if no link indicator is present
259 if (end == -1)
260 {
261 file.setName(name);
262 }
263 else
264 {
265 file.setName(name.substring(0, end));
266 file.setLink(name.substring(end + 4));
267 }
268
269 }
270 else
271 {
272 file.setName(name);
273 }
274 }
275 return file;
276 }
277 return null;
278 }
279
280 /**
281 * Defines a default configuration to be used when this class is
282 * instantiated without a {@link FTPClientConfig FTPClientConfig}
283 * parameter being specified.
284 * @return the default configuration for this parser.
285 */
286 protected FTPClientConfig getDefaultConfiguration() {
287 return new FTPClientConfig(
288 FTPClientConfig.SYST_UNIX,
289 DEFAULT_DATE_FORMAT,
290 DEFAULT_RECENT_DATE_FORMAT,
291 null, null, null);
292 }
293
294
295
296
297 }