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