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 import java.util.List; 21 import java.util.ListIterator; 22 23 import org.apache.commons.net.ftp.FTPClientConfig; 24 import org.apache.commons.net.ftp.FTPFile; 25 26 /** 27 * Implementation FTPFileEntryParser and FTPFileListParser for standard 28 * Unix Systems. 29 * 30 * This class is based on the logic of Daniel Savarese's 31 * DefaultFTPListParser, but adapted to use regular expressions and to fit the 32 * new FTPFileEntryParser interface. 33 * @version $Id: UnixFTPEntryParser.java 1489361 2013-06-04 09:48:36Z sebb $ 34 * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions) 35 */ 36 public class UnixFTPEntryParser extends ConfigurableFTPFileEntryParserImpl 37 { 38 39 static final String DEFAULT_DATE_FORMAT 40 = "MMM d yyyy"; //Nov 9 2001 41 42 static final String DEFAULT_RECENT_DATE_FORMAT 43 = "MMM d HH:mm"; //Nov 9 20:06 44 45 static final String NUMERIC_DATE_FORMAT 46 = "yyyy-MM-dd HH:mm"; //2001-11-09 20:06 47 48 /** 49 * Some Linux distributions are now shipping an FTP server which formats 50 * file listing dates in an all-numeric format: 51 * <code>"yyyy-MM-dd HH:mm</code>. 52 * This is a very welcome development, and hopefully it will soon become 53 * the standard. However, since it is so new, for now, and possibly 54 * forever, we merely accomodate it, but do not make it the default. 55 * <p> 56 * For now end users may specify this format only via 57 * <code>UnixFTPEntryParser(FTPClientConfig)</code>. 58 * Steve Cohen - 2005-04-17 59 */ 60 public static final FTPClientConfig NUMERIC_DATE_CONFIG = 61 new FTPClientConfig( 62 FTPClientConfig.SYST_UNIX, 63 NUMERIC_DATE_FORMAT, 64 null, null, null, null); 65 66 /** 67 * this is the regular expression used by this parser. 68 * 69 * Permissions: 70 * r the file is readable 71 * w the file is writable 72 * x the file is executable 73 * - the indicated permission is not granted 74 * L mandatory locking occurs during access (the set-group-ID bit is 75 * on and the group execution bit is off) 76 * s the set-user-ID or set-group-ID bit is on, and the corresponding 77 * user or group execution bit is also on 78 * S undefined bit-state (the set-user-ID bit is on and the user 79 * execution bit is off) 80 * t the 1000 (octal) bit, or sticky bit, is on [see chmod(1)], and 81 * execution is on 82 * T the 1000 bit is turned on, and execution is off (undefined bit- 83 * state) 84 * e z/OS external link bit 85 */ 86 private static final String REGEX = 87 "([bcdelfmpSs-])" 88 +"(((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-])))\\+?\\s*" 89 + "(\\d+)\\s+" // link count 90 + "(?:(\\S+(?:\\s\\S+)*?)\\s+)?" // owner name (optional spaces) 91 + "(?:(\\S+(?:\\s\\S+)*)\\s+)?" // group name (optional spaces) 92 + "(\\d+(?:,\\s*\\d+)?)\\s+" // size or n,m 93 /* 94 * numeric or standard format date: 95 * yyyy-mm-dd (expecting hh:mm to follow) 96 * MMM [d]d 97 * [d]d MMM 98 * N.B. use non-space for MMM to allow for languages such as German which use 99 * diacritics (e.g. umlaut) in some abbreviations. 100 */ 101 + "((?:\\d+[-/]\\d+[-/]\\d+)|(?:\\S{3}\\s+\\d{1,2})|(?:\\d{1,2}\\s+\\S{3}))\\s+" 102 /* 103 year (for non-recent standard format) - yyyy 104 or time (for numeric or recent standard format) [h]h:mm 105 */ 106 + "(\\d+(?::\\d+)?)\\s+" 107 108 + "(\\S*)(\\s*.*)"; // the rest 109 110 111 /** 112 * The default constructor for a UnixFTPEntryParser object. 113 * 114 * @exception IllegalArgumentException 115 * Thrown if the regular expression is unparseable. Should not be seen 116 * under normal conditions. It it is seen, this is a sign that 117 * <code>REGEX</code> is not a valid regular expression. 118 */ 119 public UnixFTPEntryParser() 120 { 121 this(null); 122 } 123 124 /** 125 * This constructor allows the creation of a UnixFTPEntryParser object with 126 * something other than the default configuration. 127 * 128 * @param config The {@link FTPClientConfig configuration} object used to 129 * configure this parser. 130 * @exception IllegalArgumentException 131 * Thrown if the regular expression is unparseable. Should not be seen 132 * under normal conditions. It it is seen, this is a sign that 133 * <code>REGEX</code> is not a valid regular expression. 134 * @since 1.4 135 */ 136 public UnixFTPEntryParser(FTPClientConfig config) 137 { 138 super(REGEX); 139 configure(config); 140 } 141 142 /** 143 * Preparse the list to discard "total nnn" lines 144 */ 145 @Override 146 public List<String> preParse(List<String> original) { 147 ListIterator<String> iter = original.listIterator(); 148 while (iter.hasNext()) { 149 String entry = iter.next(); 150 if (entry.matches("^total \\d+$")) { // NET-389 151 iter.remove(); 152 } 153 } 154 return original; 155 } 156 157 /** 158 * Parses a line of a unix (standard) FTP server file listing and converts 159 * it into a usable format in the form of an <code> FTPFile </code> 160 * instance. If the file listing line doesn't describe a file, 161 * <code> null </code> is returned, otherwise a <code> FTPFile </code> 162 * instance representing the files in the directory is returned. 163 * <p> 164 * @param entry A line of text from the file listing 165 * @return An FTPFile instance corresponding to the supplied entry 166 */ 167 // @Override 168 public FTPFile parseFTPEntry(String entry) { 169 FTPFile file = new FTPFile(); 170 file.setRawListing(entry); 171 int type; 172 boolean isDevice = false; 173 174 if (matches(entry)) 175 { 176 String typeStr = group(1); 177 String hardLinkCount = group(15); 178 String usr = group(16); 179 String grp = group(17); 180 String filesize = group(18); 181 String datestr = group(19) + " " + group(20); 182 String name = group(21); 183 String endtoken = group(22); 184 185 try 186 { 187 file.setTimestamp(super.parseTimestamp(datestr)); 188 } 189 catch (ParseException e) 190 { 191 // intentionally do nothing 192 } 193 194 // A 'whiteout' file is an ARTIFICIAL entry in any of several types of 195 // 'translucent' filesystems, of which a 'union' filesystem is one. 196 197 // bcdelfmpSs- 198 switch (typeStr.charAt(0)) 199 { 200 case 'd': 201 type = FTPFile.DIRECTORY_TYPE; 202 break; 203 case 'e': // NET-39 => z/OS external link 204 type = FTPFile.SYMBOLIC_LINK_TYPE; 205 break; 206 case 'l': 207 type = FTPFile.SYMBOLIC_LINK_TYPE; 208 break; 209 case 'b': 210 case 'c': 211 isDevice = true; 212 type = FTPFile.FILE_TYPE; // TODO change this if DEVICE_TYPE implemented 213 break; 214 case 'f': 215 case '-': 216 type = FTPFile.FILE_TYPE; 217 break; 218 default: // e.g. ? and w = whiteout 219 type = FTPFile.UNKNOWN_TYPE; 220 } 221 222 file.setType(type); 223 224 int g = 4; 225 for (int access = 0; access < 3; access++, g += 4) 226 { 227 // Use != '-' to avoid having to check for suid and sticky bits 228 file.setPermission(access, FTPFile.READ_PERMISSION, 229 (!group(g).equals("-"))); 230 file.setPermission(access, FTPFile.WRITE_PERMISSION, 231 (!group(g + 1).equals("-"))); 232 233 String execPerm = group(g + 2); 234 if (!execPerm.equals("-") && !Character.isUpperCase(execPerm.charAt(0))) 235 { 236 file.setPermission(access, FTPFile.EXECUTE_PERMISSION, true); 237 } 238 else 239 { 240 file.setPermission(access, FTPFile.EXECUTE_PERMISSION, false); 241 } 242 } 243 244 if (!isDevice) 245 { 246 try 247 { 248 file.setHardLinkCount(Integer.parseInt(hardLinkCount)); 249 } 250 catch (NumberFormatException e) 251 { 252 // intentionally do nothing 253 } 254 } 255 256 file.setUser(usr); 257 file.setGroup(grp); 258 259 try 260 { 261 file.setSize(Long.parseLong(filesize)); 262 } 263 catch (NumberFormatException e) 264 { 265 // intentionally do nothing 266 } 267 268 if (null == endtoken) 269 { 270 file.setName(name); 271 } 272 else 273 { 274 // oddball cases like symbolic links, file names 275 // with spaces in them. 276 name += endtoken; 277 if (type == FTPFile.SYMBOLIC_LINK_TYPE) 278 { 279 280 int end = name.indexOf(" -> "); 281 // Give up if no link indicator is present 282 if (end == -1) 283 { 284 file.setName(name); 285 } 286 else 287 { 288 file.setName(name.substring(0, end)); 289 file.setLink(name.substring(end + 4)); 290 } 291 292 } 293 else 294 { 295 file.setName(name); 296 } 297 } 298 return file; 299 } 300 return null; 301 } 302 303 /** 304 * Defines a default configuration to be used when this class is 305 * instantiated without a {@link FTPClientConfig FTPClientConfig} 306 * parameter being specified. 307 * @return the default configuration for this parser. 308 */ 309 @Override 310 protected FTPClientConfig getDefaultConfiguration() { 311 return new FTPClientConfig( 312 FTPClientConfig.SYST_UNIX, 313 DEFAULT_DATE_FORMAT, 314 DEFAULT_RECENT_DATE_FORMAT, 315 null, null, null); 316 } 317 318 }