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; 022 023import org.apache.commons.net.ftp.FTPClientConfig; 024import org.apache.commons.net.ftp.FTPFile; 025 026/** 027 * @version $Id: OS400FTPEntryParser.java 1752660 2016-07-14 13:25:39Z sebb $ 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{ 236 private static final String DEFAULT_DATE_FORMAT 237 = "yy/MM/dd HH:mm:ss"; //01/11/09 12:30:24 238 239 240 241 private static final String REGEX = 242 "(\\S+)\\s+" // user 243 + "(?:(\\d+)\\s+)?" // size, empty for members 244 + "(?:(\\S+)\\s+(\\S+)\\s+)?" // date stuff, empty for members 245 + "(\\*STMF|\\*DIR|\\*FILE|\\*MEM)\\s+" // *STMF/*DIR/*FILE/*MEM 246 + "(?:(\\S+)\\s*)?"; // filename, missing, when CWD is a *FILE 247 248 249 /** 250 * The default constructor for a OS400FTPEntryParser object. 251 * 252 * @throws IllegalArgumentException 253 * Thrown if the regular expression is unparseable. Should not be seen 254 * under normal conditions. It it is seen, this is a sign that 255 * <code>REGEX</code> is not a valid regular expression. 256 */ 257 public OS400FTPEntryParser() 258 { 259 this(null); 260 } 261 262 /** 263 * This constructor allows the creation of an OS400FTPEntryParser object 264 * with something other than the default configuration. 265 * 266 * @param config The {@link FTPClientConfig configuration} object used to 267 * configure this parser. 268 * @throws IllegalArgumentException 269 * Thrown if the regular expression is unparseable. Should not be seen 270 * under normal conditions. It it is seen, this is a sign that 271 * <code>REGEX</code> is not a valid regular expression. 272 * @since 1.4 273 */ 274 public OS400FTPEntryParser(FTPClientConfig config) 275 { 276 super(REGEX); 277 configure(config); 278 } 279 280 281 @Override 282 public FTPFile parseFTPEntry(String entry) 283 { 284 285 FTPFile file = new FTPFile(); 286 file.setRawListing(entry); 287 int type; 288 289 if (matches(entry)) 290 { 291 String usr = group(1); 292 String filesize = group(2); 293 String datestr = ""; 294 if (!isNullOrEmpty(group(3)) || !isNullOrEmpty(group(4))) 295 { 296 datestr = group(3)+" "+group(4); 297 } 298 String typeStr = group(5); 299 String name = group(6); 300 301 boolean mustScanForPathSeparator = true; 302 303 try 304 { 305 file.setTimestamp(super.parseTimestamp(datestr)); 306 } 307 catch (ParseException e) 308 { 309 // intentionally do nothing 310 } 311 312 313 if (typeStr.equalsIgnoreCase("*STMF")) 314 { 315 type = FTPFile.FILE_TYPE; 316 if (isNullOrEmpty(filesize) || isNullOrEmpty(name)) 317 { 318 return null; 319 } 320 } 321 else if (typeStr.equalsIgnoreCase("*DIR")) 322 { 323 type = FTPFile.DIRECTORY_TYPE; 324 if (isNullOrEmpty(filesize) || isNullOrEmpty(name)) 325 { 326 return null; 327 } 328 } 329 else if (typeStr.equalsIgnoreCase("*FILE")) 330 { 331 // File, defines the structure of the data (columns of a row) 332 // but the data is stored in one or more members. Typically a 333 // source file contains multiple members whereas it is 334 // recommended (but not enforced) to use one member per data 335 // file. 336 // Save files are a special type of files which are used 337 // to save objects, e.g. for backups. 338 if (name != null && name.toUpperCase().endsWith(".SAVF")) 339 { 340 mustScanForPathSeparator = false; 341 type = FTPFile.FILE_TYPE; 342 } 343 else 344 { 345 return null; 346 } 347 } 348 else if (typeStr.equalsIgnoreCase("*MEM")) 349 { 350 mustScanForPathSeparator = false; 351 type = FTPFile.FILE_TYPE; 352 353 if (isNullOrEmpty(name)) 354 { 355 return null; 356 } 357 if (!(isNullOrEmpty(filesize) && isNullOrEmpty(datestr))) 358 { 359 return null; 360 } 361 362 // Quick and dirty bug fix to make SelectorUtils work. 363 // Class SelectorUtils uses 'File.separator' to splitt 364 // a given path into pieces. But actually it had to 365 // use the separator of the FTP server, which is a forward 366 // slash in case of an AS/400. 367 name = name.replace('/', File.separatorChar); 368 } 369 else 370 { 371 type = FTPFile.UNKNOWN_TYPE; 372 } 373 374 file.setType(type); 375 376 file.setUser(usr); 377 378 try 379 { 380 file.setSize(Long.parseLong(filesize)); 381 } 382 catch (NumberFormatException e) 383 { 384 // intentionally do nothing 385 } 386 387 if (name.endsWith("/")) 388 { 389 name = name.substring(0, name.length() - 1); 390 } 391 if (mustScanForPathSeparator) 392 { 393 int pos = name.lastIndexOf('/'); 394 if (pos > -1) 395 { 396 name = name.substring(pos + 1); 397 } 398 } 399 400 file.setName(name); 401 402 return file; 403 } 404 return null; 405 } 406 407 /** 408 * 409 * @param string String value that is checked for <code>null</code> 410 * or empty. 411 * @return <code>true</code> for <code>null</code> or empty values, 412 * else <code>false</code>. 413 */ 414 private boolean isNullOrEmpty(String string) { 415 if (string == null || string.length() == 0) { 416 return true; 417 } 418 return false; 419 } 420 421 /** 422 * Defines a default configuration to be used when this class is 423 * instantiated without a {@link FTPClientConfig FTPClientConfig} 424 * parameter being specified. 425 * @return the default configuration for this parser. 426 */ 427 @Override 428 protected FTPClientConfig getDefaultConfiguration() { 429 return new FTPClientConfig( 430 FTPClientConfig.SYST_OS400, 431 DEFAULT_DATE_FORMAT, 432 null); 433 } 434 435}