1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.commons.net.ftp.parser;
19 import java.text.ParseException;
20
21 import org.apache.commons.net.ftp.FTPClientConfig;
22 import org.apache.commons.net.ftp.FTPFile;
23
24 /**
25 * Implementation FTPFileEntryParser and FTPFileListParser for pre MacOS-X Systems.
26 *
27 * @version $Id: MacOsPeterFTPEntryParser.java 1489361 2013-06-04 09:48:36Z sebb $
28 * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions)
29 * @since 3.1
30 */
31 public class MacOsPeterFTPEntryParser extends ConfigurableFTPFileEntryParserImpl
32 {
33
34 static final String DEFAULT_DATE_FORMAT
35 = "MMM d yyyy"; //Nov 9 2001
36
37 static final String DEFAULT_RECENT_DATE_FORMAT
38 = "MMM d HH:mm"; //Nov 9 20:06
39
40 /**
41 * this is the regular expression used by this parser.
42 *
43 * Permissions:
44 * r the file is readable
45 * w the file is writable
46 * x the file is executable
47 * - the indicated permission is not granted
48 * L mandatory locking occurs during access (the set-group-ID bit is
49 * on and the group execution bit is off)
50 * s the set-user-ID or set-group-ID bit is on, and the corresponding
51 * user or group execution bit is also on
52 * S undefined bit-state (the set-user-ID bit is on and the user
53 * execution bit is off)
54 * t the 1000 (octal) bit, or sticky bit, is on [see chmod(1)], and
55 * execution is on
56 * T the 1000 bit is turned on, and execution is off (undefined bit-
57 * state)
58 * e z/OS external link bit
59 */
60 private static final String REGEX =
61 "([bcdelfmpSs-])" // type (1)
62 + "(((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-])))\\+?\\s+" // permission
63 + "("
64 + "(folder\\s+)"
65 + "|"
66 + "((\\d+)\\s+(\\d+)\\s+)" // resource size & data size
67 + ")"
68 + "(\\d+)\\s+" // size
69 /*
70 * numeric or standard format date:
71 * yyyy-mm-dd (expecting hh:mm to follow)
72 * MMM [d]d
73 * [d]d MMM
74 * N.B. use non-space for MMM to allow for languages such as German which use
75 * diacritics (e.g. umlaut) in some abbreviations.
76 */
77 + "((?:\\d+[-/]\\d+[-/]\\d+)|(?:\\S{3}\\s+\\d{1,2})|(?:\\d{1,2}\\s+\\S{3}))\\s+"
78 /*
79 year (for non-recent standard format) - yyyy
80 or time (for numeric or recent standard format) [h]h:mm
81 */
82 + "(\\d+(?::\\d+)?)\\s+"
83
84 + "(\\S*)(\\s*.*)"; // the rest
85
86
87 /**
88 * The default constructor for a UnixFTPEntryParser object.
89 *
90 * @exception IllegalArgumentException
91 * Thrown if the regular expression is unparseable. Should not be seen
92 * under normal conditions. It it is seen, this is a sign that
93 * <code>REGEX</code> is not a valid regular expression.
94 */
95 public MacOsPeterFTPEntryParser()
96 {
97 this(null);
98 }
99
100 /**
101 * This constructor allows the creation of a UnixFTPEntryParser object with
102 * something other than the default configuration.
103 *
104 * @param config The {@link FTPClientConfig configuration} object used to
105 * configure this parser.
106 * @exception IllegalArgumentException
107 * Thrown if the regular expression is unparseable. Should not be seen
108 * under normal conditions. It it is seen, this is a sign that
109 * <code>REGEX</code> is not a valid regular expression.
110 * @since 1.4
111 */
112 public MacOsPeterFTPEntryParser(FTPClientConfig config)
113 {
114 super(REGEX);
115 configure(config);
116 }
117
118 /**
119 * Parses a line of a unix (standard) FTP server file listing and converts
120 * it into a usable format in the form of an <code> FTPFile </code>
121 * instance. If the file listing line doesn't describe a file,
122 * <code> null </code> is returned, otherwise a <code> FTPFile </code>
123 * instance representing the files in the directory is returned.
124 * <p>
125 * @param entry A line of text from the file listing
126 * @return An FTPFile instance corresponding to the supplied entry
127 */
128 // @Override
129 public FTPFile parseFTPEntry(String entry) {
130 FTPFile file = new FTPFile();
131 file.setRawListing(entry);
132 int type;
133 boolean isDevice = false;
134
135 if (matches(entry))
136 {
137 String typeStr = group(1);
138 String hardLinkCount = "0";
139 String usr = null;
140 String grp = null;
141 String filesize = group(20);
142 String datestr = group(21) + " " + group(22);
143 String name = group(23);
144 String endtoken = group(24);
145
146 try
147 {
148 file.setTimestamp(super.parseTimestamp(datestr));
149 }
150 catch (ParseException e)
151 {
152 // intentionally do nothing
153 }
154
155 // A 'whiteout' file is an ARTIFICIAL entry in any of several types of
156 // 'translucent' filesystems, of which a 'union' filesystem is one.
157
158 // bcdelfmpSs-
159 switch (typeStr.charAt(0))
160 {
161 case 'd':
162 type = FTPFile.DIRECTORY_TYPE;
163 break;
164 case 'e': // NET-39 => z/OS external link
165 type = FTPFile.SYMBOLIC_LINK_TYPE;
166 break;
167 case 'l':
168 type = FTPFile.SYMBOLIC_LINK_TYPE;
169 break;
170 case 'b':
171 case 'c':
172 isDevice = true;
173 type = FTPFile.FILE_TYPE; // TODO change this if DEVICE_TYPE implemented
174 break;
175 case 'f':
176 case '-':
177 type = FTPFile.FILE_TYPE;
178 break;
179 default: // e.g. ? and w = whiteout
180 type = FTPFile.UNKNOWN_TYPE;
181 }
182
183 file.setType(type);
184
185 int g = 4;
186 for (int access = 0; access < 3; access++, g += 4)
187 {
188 // Use != '-' to avoid having to check for suid and sticky bits
189 file.setPermission(access, FTPFile.READ_PERMISSION,
190 (!group(g).equals("-")));
191 file.setPermission(access, FTPFile.WRITE_PERMISSION,
192 (!group(g + 1).equals("-")));
193
194 String execPerm = group(g + 2);
195 if (!execPerm.equals("-") && !Character.isUpperCase(execPerm.charAt(0)))
196 {
197 file.setPermission(access, FTPFile.EXECUTE_PERMISSION, true);
198 }
199 else
200 {
201 file.setPermission(access, FTPFile.EXECUTE_PERMISSION, false);
202 }
203 }
204
205 if (!isDevice)
206 {
207 try
208 {
209 file.setHardLinkCount(Integer.parseInt(hardLinkCount));
210 }
211 catch (NumberFormatException e)
212 {
213 // intentionally do nothing
214 }
215 }
216
217 file.setUser(usr);
218 file.setGroup(grp);
219
220 try
221 {
222 file.setSize(Long.parseLong(filesize));
223 }
224 catch (NumberFormatException e)
225 {
226 // intentionally do nothing
227 }
228
229 if (null == endtoken)
230 {
231 file.setName(name);
232 }
233 else
234 {
235 // oddball cases like symbolic links, file names
236 // with spaces in them.
237 name += endtoken;
238 if (type == FTPFile.SYMBOLIC_LINK_TYPE)
239 {
240
241 int end = name.indexOf(" -> ");
242 // Give up if no link indicator is present
243 if (end == -1)
244 {
245 file.setName(name);
246 }
247 else
248 {
249 file.setName(name.substring(0, end));
250 file.setLink(name.substring(end + 4));
251 }
252
253 }
254 else
255 {
256 file.setName(name);
257 }
258 }
259 return file;
260 }
261 return null;
262 }
263
264 /**
265 * Defines a default configuration to be used when this class is
266 * instantiated without a {@link FTPClientConfig FTPClientConfig}
267 * parameter being specified.
268 * @return the default configuration for this parser.
269 */
270 @Override
271 protected FTPClientConfig getDefaultConfiguration() {
272 return new FTPClientConfig(
273 FTPClientConfig.SYST_UNIX,
274 DEFAULT_DATE_FORMAT,
275 DEFAULT_RECENT_DATE_FORMAT,
276 null, null, null);
277 }
278
279 }