001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018 package org.apache.commons.net.ftp.parser; 019 import java.text.ParseException; 020 import java.util.List; 021 import java.util.ListIterator; 022 023 import org.apache.commons.net.ftp.FTPClientConfig; 024 import org.apache.commons.net.ftp.FTPFile; 025 026 /** 027 * Implementation FTPFileEntryParser and FTPFileListParser for standard 028 * Unix Systems. 029 * 030 * This class is based on the logic of Daniel Savarese's 031 * DefaultFTPListParser, but adapted to use regular expressions and to fit the 032 * new FTPFileEntryParser interface. 033 * @version $Id: UnixFTPEntryParser.java 1299238 2012-03-10 17:12:28Z sebb $ 034 * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions) 035 */ 036 public class UnixFTPEntryParser extends ConfigurableFTPFileEntryParserImpl 037 { 038 039 static final String DEFAULT_DATE_FORMAT 040 = "MMM d yyyy"; //Nov 9 2001 041 042 static final String DEFAULT_RECENT_DATE_FORMAT 043 = "MMM d HH:mm"; //Nov 9 20:06 044 045 static final String NUMERIC_DATE_FORMAT 046 = "yyyy-MM-dd HH:mm"; //2001-11-09 20:06 047 048 /** 049 * Some Linux distributions are now shipping an FTP server which formats 050 * file listing dates in an all-numeric format: 051 * <code>"yyyy-MM-dd HH:mm</code>. 052 * This is a very welcome development, and hopefully it will soon become 053 * the standard. However, since it is so new, for now, and possibly 054 * forever, we merely accomodate it, but do not make it the default. 055 * <p> 056 * For now end users may specify this format only via 057 * <code>UnixFTPEntryParser(FTPClientConfig)</code>. 058 * Steve Cohen - 2005-04-17 059 */ 060 public static final FTPClientConfig NUMERIC_DATE_CONFIG = 061 new FTPClientConfig( 062 FTPClientConfig.SYST_UNIX, 063 NUMERIC_DATE_FORMAT, 064 null, null, null, null); 065 066 /** 067 * this is the regular expression used by this parser. 068 * 069 * Permissions: 070 * r the file is readable 071 * w the file is writable 072 * x the file is executable 073 * - the indicated permission is not granted 074 * L mandatory locking occurs during access (the set-group-ID bit is 075 * on and the group execution bit is off) 076 * s the set-user-ID or set-group-ID bit is on, and the corresponding 077 * user or group execution bit is also on 078 * S undefined bit-state (the set-user-ID bit is on and the user 079 * execution bit is off) 080 * t the 1000 (octal) bit, or sticky bit, is on [see chmod(1)], and 081 * execution is on 082 * T the 1000 bit is turned on, and execution is off (undefined bit- 083 * state) 084 * e z/OS external link bit 085 */ 086 private static final String REGEX = 087 "([bcdelfmpSs-])" 088 +"(((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-])))\\+?\\s*" 089 + "(\\d+)\\s+" // link count 090 + "(?:(\\S+(?:\\s\\S+)*?)\\s+)?" // owner name (optional spaces) 091 + "(?:(\\S+(?:\\s\\S+)*)\\s+)?" // group name (optional spaces) 092 + "(\\d+(?:,\\s*\\d+)?)\\s+" // size or n,m 093 /* 094 * numeric or standard format date: 095 * yyyy-mm-dd (expecting hh:mm to follow) 096 * MMM [d]d 097 * [d]d MMM 098 * N.B. use non-space for MMM to allow for languages such as German which use 099 * 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 public FTPFile parseFTPEntry(String entry) { 168 FTPFile file = new FTPFile(); 169 file.setRawListing(entry); 170 int type; 171 boolean isDevice = false; 172 173 if (matches(entry)) 174 { 175 String typeStr = group(1); 176 String hardLinkCount = group(15); 177 String usr = group(16); 178 String grp = group(17); 179 String filesize = group(18); 180 String datestr = group(19) + " " + group(20); 181 String name = group(21); 182 String endtoken = group(22); 183 184 try 185 { 186 file.setTimestamp(super.parseTimestamp(datestr)); 187 } 188 catch (ParseException e) 189 { 190 // intentionally do nothing 191 } 192 193 // A 'whiteout' file is an ARTIFICIAL entry in any of several types of 194 // 'translucent' filesystems, of which a 'union' filesystem is one. 195 196 // bcdelfmpSs- 197 switch (typeStr.charAt(0)) 198 { 199 case 'd': 200 type = FTPFile.DIRECTORY_TYPE; 201 break; 202 case 'e': // NET-39 => z/OS external link 203 type = FTPFile.SYMBOLIC_LINK_TYPE; 204 break; 205 case 'l': 206 type = FTPFile.SYMBOLIC_LINK_TYPE; 207 break; 208 case 'b': 209 case 'c': 210 isDevice = true; 211 type = FTPFile.FILE_TYPE; // TODO change this if DEVICE_TYPE implemented 212 break; 213 case 'f': 214 case '-': 215 type = FTPFile.FILE_TYPE; 216 break; 217 default: // e.g. ? and w = whiteout 218 type = FTPFile.UNKNOWN_TYPE; 219 } 220 221 file.setType(type); 222 223 int g = 4; 224 for (int access = 0; access < 3; access++, g += 4) 225 { 226 // Use != '-' to avoid having to check for suid and sticky bits 227 file.setPermission(access, FTPFile.READ_PERMISSION, 228 (!group(g).equals("-"))); 229 file.setPermission(access, FTPFile.WRITE_PERMISSION, 230 (!group(g + 1).equals("-"))); 231 232 String execPerm = group(g + 2); 233 if (!execPerm.equals("-") && !Character.isUpperCase(execPerm.charAt(0))) 234 { 235 file.setPermission(access, FTPFile.EXECUTE_PERMISSION, true); 236 } 237 else 238 { 239 file.setPermission(access, FTPFile.EXECUTE_PERMISSION, false); 240 } 241 } 242 243 if (!isDevice) 244 { 245 try 246 { 247 file.setHardLinkCount(Integer.parseInt(hardLinkCount)); 248 } 249 catch (NumberFormatException e) 250 { 251 // intentionally do nothing 252 } 253 } 254 255 file.setUser(usr); 256 file.setGroup(grp); 257 258 try 259 { 260 file.setSize(Long.parseLong(filesize)); 261 } 262 catch (NumberFormatException e) 263 { 264 // intentionally do nothing 265 } 266 267 if (null == endtoken) 268 { 269 file.setName(name); 270 } 271 else 272 { 273 // oddball cases like symbolic links, file names 274 // with spaces in them. 275 name += endtoken; 276 if (type == FTPFile.SYMBOLIC_LINK_TYPE) 277 { 278 279 int end = name.indexOf(" -> "); 280 // Give up if no link indicator is present 281 if (end == -1) 282 { 283 file.setName(name); 284 } 285 else 286 { 287 file.setName(name.substring(0, end)); 288 file.setLink(name.substring(end + 4)); 289 } 290 291 } 292 else 293 { 294 file.setName(name); 295 } 296 } 297 return file; 298 } 299 return null; 300 } 301 302 /** 303 * Defines a default configuration to be used when this class is 304 * instantiated without a {@link FTPClientConfig FTPClientConfig} 305 * parameter being specified. 306 * @return the default configuration for this parser. 307 */ 308 @Override 309 protected FTPClientConfig getDefaultConfiguration() { 310 return new FTPClientConfig( 311 FTPClientConfig.SYST_UNIX, 312 DEFAULT_DATE_FORMAT, 313 DEFAULT_RECENT_DATE_FORMAT, 314 null, null, null); 315 } 316 317 }