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 018package org.apache.commons.net.ftp.parser; 019 020import java.io.File; 021import java.text.ParseException; 022import java.util.Locale; 023 024import org.apache.commons.net.ftp.FTPClientConfig; 025import org.apache.commons.net.ftp.FTPFile; 026 027/** 028 * <pre> 029 * Example *FILE/*MEM FTP entries, when the current 030 * working directory is a file of file system QSYS: 031 * ------------------------------------------------ 032 * 033 * $ cwd /qsys.lib/rpgunit.lib/rpgunitc1.file 034 * 250-NAMEFMT set to 1. 035 * 250 "/QSYS.LIB/RPGUNIT.LIB/RPGUNITC1.FILE" is current directory. 036 * $ dir 037 * 227 Entering Passive Mode (10,200,36,33,40,249). 038 * 125 List started. 039 * QPGMR 135168 22.06.13 13:18:19 *FILE 040 * QPGMR *MEM MKCMD.MBR 041 * QPGMR *MEM RUCALLTST.MBR 042 * QPGMR *MEM RUCMDHLP.MBR 043 * QPGMR *MEM RUCRTTST.MBR 044 * 250 List completed. 045 * 046 * 047 * Example *FILE entry of an OS/400 save file: 048 * --------------------------------------------------- 049 * 050 * $ cwd /qsys.lib/rpgunit.lib 051 * 250 "/QSYS.LIB/RPGUNIT.LIB" is current library. 052 * $ dir rpgunit.file 053 * 227 Entering Passive Mode (10,200,36,33,188,106). 054 * 125 List started. 055 * QPGMR 16347136 29.06.13 15:45:09 *FILE RPGUNIT.SAVF 056 * 250 List completed. 057 * 058 * 059 * Example *STMF/*DIR FTP entries, when the 060 * current working directory is in file system "root": 061 * --------------------------------------------------- 062 * 063 * $ cwd /home/raddatz 064 * 250 "/home/raddatz" is current directory. 065 * $ dir test* 066 * 227 Entering Passive Mode (10,200,36,33,200,189). 067 * 125 List started. 068 * RADDATZ 200 21.05.11 12:31:18 *STMF TEST_RG_02_CRLF.properties 069 * RADDATZ 187 08.05.11 12:31:40 *STMF TEST_RG_02_LF.properties 070 * RADDATZ 187 08.05.11 12:31:52 *STMF TEST_RG_02_CR.properties 071 * RADDATZ 8192 04.07.13 09:04:14 *DIR testDir1/ 072 * RADDATZ 8192 04.07.13 09:04:17 *DIR testDir2/ 073 * 250 List completed. 074 * 075 * 076 * Example 1, using ANT to list specific members of a file: 077 * -------------------------------------------------------- 078 * 079 * <echo/> 080 * <echo>Listing members of a file:</echo> 081 * 082 * <ftp action="list" 083 * server="${ftp.server}" 084 * userid="${ftp.user}" 085 * password="${ftp.password}" 086 * binary="false" 087 * verbose="true" 088 * remotedir="/QSYS.LIB/RPGUNIT.LIB/RPGUNITY1.FILE" 089 * systemTypeKey="OS/400" 090 * listing="ftp-listing.txt" 091 * > 092 * <fileset dir="./i5-downloads-file" casesensitive="false"> 093 * <include name="run*.mbr" /> 094 * </fileset> 095 * </ftp> 096 * 097 * Output: 098 * ------- 099 * 100 * [echo] Listing members of a file: 101 * [ftp] listing files 102 * [ftp] listing RUN.MBR 103 * [ftp] listing RUNNER.MBR 104 * [ftp] listing RUNNERBND.MBR 105 * [ftp] 3 files listed 106 * 107 * 108 * Example 2, using ANT to list specific members of all files of a library: 109 * ------------------------------------------------------------------------ 110 * 111 * <echo/> 112 * <echo>Listing members of all files of a library:</echo> 113 * 114 * <ftp action="list" 115 * server="${ftp.server}" 116 * userid="${ftp.user}" 117 * password="${ftp.password}" 118 * binary="false" 119 * verbose="true" 120 * remotedir="/QSYS.LIB/RPGUNIT.LIB" 121 * systemTypeKey="OS/400" 122 * listing="ftp-listing.txt" 123 * > 124 * <fileset dir="./i5-downloads-lib" casesensitive="false"> 125 * <include name="**\run*.mbr" /> 126 * </fileset> 127 * </ftp> 128 * 129 * Output: 130 * ------- 131 * 132 * [echo] Listing members of all files of a library: 133 * [ftp] listing files 134 * [ftp] listing RPGUNIT1.FILE\RUN.MBR 135 * [ftp] listing RPGUNIT1.FILE\RUNRMT.MBR 136 * [ftp] listing RPGUNITT1.FILE\RUNT.MBR 137 * [ftp] listing RPGUNITY1.FILE\RUN.MBR 138 * [ftp] listing RPGUNITY1.FILE\RUNNER.MBR 139 * [ftp] listing RPGUNITY1.FILE\RUNNERBND.MBR 140 * [ftp] 6 files listed 141 * 142 * 143 * Example 3, using ANT to download specific members of a file: 144 * ------------------------------------------------------------ 145 * 146 * <echo/> 147 * <echo>Downloading members of a file:</echo> 148 * 149 * <ftp action="get" 150 * server="${ftp.server}" 151 * userid="${ftp.user}" 152 * password="${ftp.password}" 153 * binary="false" 154 * verbose="true" 155 * remotedir="/QSYS.LIB/RPGUNIT.LIB/RPGUNITY1.FILE" 156 * systemTypeKey="OS/400" 157 * > 158 * <fileset dir="./i5-downloads-file" casesensitive="false"> 159 * <include name="run*.mbr" /> 160 * </fileset> 161 * </ftp> 162 * 163 * Output: 164 * ------- 165 * 166 * [echo] Downloading members of a file: 167 * [ftp] getting files 168 * [ftp] transferring RUN.MBR to C:\workspaces\rdp_080\workspace\ANT - FTP\i5-downloads-file\RUN.MBR 169 * [ftp] transferring RUNNER.MBR to C:\workspaces\rdp_080\workspace\ANT - FTP\i5-downloads-file\RUNNER.MBR 170 * [ftp] transferring RUNNERBND.MBR to C:\workspaces\rdp_080\workspace\ANT - FTP\i5-downloads-file\RUNNERBND.MBR 171 * [ftp] 3 files retrieved 172 * 173 * 174 * Example 4, using ANT to download specific members of all files of a library: 175 * ---------------------------------------------------------------------------- 176 * 177 * <echo/> 178 * <echo>Downloading members of all files of a library:</echo> 179 * 180 * <ftp action="get" 181 * server="${ftp.server}" 182 * userid="${ftp.user}" 183 * password="${ftp.password}" 184 * binary="false" 185 * verbose="true" 186 * remotedir="/QSYS.LIB/RPGUNIT.LIB" 187 * systemTypeKey="OS/400" 188 * > 189 * <fileset dir="./i5-downloads-lib" casesensitive="false"> 190 * <include name="**\run*.mbr" /> 191 * </fileset> 192 * </ftp> 193 * 194 * Output: 195 * ------- 196 * 197 * [echo] Downloading members of all files of a library: 198 * [ftp] getting files 199 * [ftp] transferring RPGUNIT1.FILE\RUN.MBR to C:\work\rdp_080\space\ANT - FTP\i5-downloads\RPGUNIT1.FILE\RUN.MBR 200 * [ftp] transferring RPGUNIT1.FILE\RUNRMT.MBR to C:\work\rdp_080\space\ANT - FTP\i5-downloads\RPGUNIT1.FILE\RUNRMT.MBR 201 * [ftp] transferring RPGUNITT1.FILE\RUNT.MBR to C:\work\rdp_080\space\ANT - FTP\i5-downloads\RPGUNITT1.FILE\RUNT.MBR 202 * [ftp] transferring RPGUNITY1.FILE\RUN.MBR to C:\work\rdp_080\space\ANT - FTP\i5-downloads\RPGUNITY1.FILE\RUN.MBR 203 * [ftp] transferring RPGUNITY1.FILE\RUNNER.MBR to C:\work\rdp_080\space\ANT - FTP\i5-downloads\RPGUNITY1.FILE\RUNNER.MBR 204 * [ftp] transferring RPGUNITY1.FILE\RUNNERBND.MBR to C:\work\rdp_080\space\ANT - FTP\i5-downloads\RPGUNITY1.FILE\RUNNERBND.MBR 205 * [ftp] 6 files retrieved 206 * 207 * 208 * Example 5, using ANT to download a save file of a library: 209 * ---------------------------------------------------------- 210 * 211 * <ftp action="get" 212 * server="${ftp.server}" 213 * userid="${ftp.user}" 214 * password="${ftp.password}" 215 * binary="true" 216 * verbose="true" 217 * remotedir="/QSYS.LIB/RPGUNIT.LIB" 218 * systemTypeKey="OS/400" 219 * > 220 * <fileset dir="./i5-downloads-savf" casesensitive="false"> 221 * <include name="RPGUNIT.SAVF" /> 222 * </fileset> 223 * </ftp> 224 * 225 * Output: 226 * ------- 227 * [echo] Downloading save file: 228 * [ftp] getting files 229 * [ftp] transferring RPGUNIT.SAVF to C:\workspaces\rdp_080\workspace\net-Test\i5-downloads-lib\RPGUNIT.SAVF 230 * [ftp] 1 files retrieved 231 * 232 * </pre> 233 */ 234public class OS400FTPEntryParser extends ConfigurableFTPFileEntryParserImpl { 235 private static final String DEFAULT_DATE_FORMAT = "yy/MM/dd HH:mm:ss"; // 01/11/09 12:30:24 236 237 private static final String REGEX = "(\\S+)\\s+" // user 238 + "(?:(\\d+)\\s+)?" // size, empty for members 239 + "(?:(\\S+)\\s+(\\S+)\\s+)?" // date stuff, empty for members 240 + "(\\*STMF|\\*DIR|\\*FILE|\\*MEM)\\s+" // *STMF/*DIR/*FILE/*MEM 241 + "((\\S+\\s*)+)?"; // file name, missing, when CWD is a *FILE 242 243 /** 244 * The default constructor for a OS400FTPEntryParser object. 245 * 246 * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not be seen under normal conditions. If it is seen, this is a 247 * sign that <code>REGEX</code> is not a valid regular expression. 248 */ 249 public OS400FTPEntryParser() { 250 this(null); 251 } 252 253 /** 254 * This constructor allows the creation of an OS400FTPEntryParser object with something other than the default configuration. 255 * 256 * @param config The {@link FTPClientConfig configuration} object used to configure this parser. 257 * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not be seen under normal conditions. If it is seen, this is a 258 * sign that <code>REGEX</code> is not a valid regular expression. 259 * @since 1.4 260 */ 261 public OS400FTPEntryParser(final FTPClientConfig config) { 262 super(REGEX); 263 configure(config); 264 } 265 266 /** 267 * Defines a default configuration to be used when this class is instantiated without a {@link FTPClientConfig FTPClientConfig} parameter being specified. 268 * 269 * @return the default configuration for this parser. 270 */ 271 @Override 272 protected FTPClientConfig getDefaultConfiguration() { 273 return new FTPClientConfig(FTPClientConfig.SYST_OS400, DEFAULT_DATE_FORMAT, null); 274 } 275 276 /** 277 * 278 * @param string String value that is checked for {@code null} or empty. 279 * @return {@code true} for {@code null} or empty values, else {@code false}. 280 */ 281 private boolean isNullOrEmpty(final String string) { 282 return string == null || string.isEmpty(); 283 } 284 285 @Override 286 public FTPFile parseFTPEntry(final String entry) { 287 288 final FTPFile file = new FTPFile(); 289 file.setRawListing(entry); 290 final int type; 291 292 if (matches(entry)) { 293 final String usr = group(1); 294 final String filesize = group(2); 295 String datestr = ""; 296 if (!isNullOrEmpty(group(3)) || !isNullOrEmpty(group(4))) { 297 datestr = group(3) + " " + group(4); 298 } 299 final String typeStr = group(5); 300 String name = group(6); 301 302 boolean mustScanForPathSeparator = true; 303 304 try { 305 file.setTimestamp(super.parseTimestamp(datestr)); 306 } catch (final ParseException e) { 307 // intentionally do nothing 308 } 309 310 if (typeStr.equalsIgnoreCase("*STMF")) { 311 type = FTPFile.FILE_TYPE; 312 if (isNullOrEmpty(filesize) || isNullOrEmpty(name)) { 313 return null; 314 } 315 } else if (typeStr.equalsIgnoreCase("*DIR")) { 316 type = FTPFile.DIRECTORY_TYPE; 317 if (isNullOrEmpty(filesize) || isNullOrEmpty(name)) { 318 return null; 319 } 320 } else if (typeStr.equalsIgnoreCase("*FILE")) { 321 // File, defines the structure of the data (columns of a row) 322 // but the data is stored in one or more members. Typically, a 323 // source file contains multiple members whereas it is 324 // recommended (but not enforced) to use one member per data 325 // file. 326 // Save files are a special type of files which are used 327 // to save objects, e.g. for backups. 328 if (name == null || !name.toUpperCase(Locale.ROOT).endsWith(".SAVF")) { 329 return null; 330 } 331 mustScanForPathSeparator = false; 332 type = FTPFile.FILE_TYPE; 333 } else if (typeStr.equalsIgnoreCase("*MEM")) { 334 mustScanForPathSeparator = false; 335 type = FTPFile.FILE_TYPE; 336 337 if (isNullOrEmpty(name)) { 338 return null; 339 } 340 if (!(isNullOrEmpty(filesize) && isNullOrEmpty(datestr))) { 341 return null; 342 } 343 344 // Quick and dirty bug fix to make SelectorUtils work. 345 // Class SelectorUtils uses 'File.separator' to splitt 346 // a given path into pieces. But actually it had to 347 // use the separator of the FTP server, which is a forward 348 // slash in case of an AS/400. 349 name = name.replace('/', File.separatorChar); 350 } else { 351 type = FTPFile.UNKNOWN_TYPE; 352 } 353 354 file.setType(type); 355 356 file.setUser(usr); 357 358 try { 359 file.setSize(Long.parseLong(filesize)); 360 } catch (final NumberFormatException e) { 361 // intentionally do nothing 362 } 363 364 if (name.endsWith("/")) { 365 name = name.substring(0, name.length() - 1); 366 } 367 if (mustScanForPathSeparator) { 368 final int pos = name.lastIndexOf('/'); 369 if (pos > -1) { 370 name = name.substring(pos + 1); 371 } 372 } 373 374 file.setName(name); 375 376 return file; 377 } 378 return null; 379 } 380 381}