OS400FTPEntryParser.java

  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. package org.apache.commons.net.ftp.parser;

  18. import java.io.File;
  19. import java.text.ParseException;
  20. import java.util.Locale;

  21. import org.apache.commons.net.ftp.FTPClientConfig;
  22. import org.apache.commons.net.ftp.FTPFile;

  23. /**
  24.  * <pre>
  25.  * Example *FILE/*MEM FTP entries, when the current
  26.  * working directory is a file of file system QSYS:
  27.  * ------------------------------------------------
  28.  *
  29.  * $ cwd /qsys.lib/rpgunit.lib/rpgunitc1.file
  30.  *   250-NAMEFMT set to 1.
  31.  *   250 "/QSYS.LIB/RPGUNIT.LIB/RPGUNITC1.FILE" is current directory.
  32.  * $ dir
  33.  *   227 Entering Passive Mode (10,200,36,33,40,249).
  34.  *   125 List started.
  35.  *   QPGMR          135168 22.06.13 13:18:19 *FILE
  36.  *   QPGMR                                   *MEM       MKCMD.MBR
  37.  *   QPGMR                                   *MEM       RUCALLTST.MBR
  38.  *   QPGMR                                   *MEM       RUCMDHLP.MBR
  39.  *   QPGMR                                   *MEM       RUCRTTST.MBR
  40.  *   250 List completed.
  41.  *
  42.  *
  43.  * Example *FILE entry of an OS/400 save file:
  44.  * ---------------------------------------------------
  45.  *
  46.  * $ cwd /qsys.lib/rpgunit.lib
  47.  *   250 "/QSYS.LIB/RPGUNIT.LIB" is current library.
  48.  * $ dir rpgunit.file
  49.  *   227 Entering Passive Mode (10,200,36,33,188,106).
  50.  *   125 List started.
  51.  *   QPGMR        16347136 29.06.13 15:45:09 *FILE      RPGUNIT.SAVF
  52.  *   250 List completed.
  53.  *
  54.  *
  55.  * Example *STMF/*DIR FTP entries, when the
  56.  * current working directory is in file system "root":
  57.  * ---------------------------------------------------
  58.  *
  59.  * $ cwd /home/raddatz
  60.  *   250 "/home/raddatz" is current directory.
  61.  * $ dir test*
  62.  *   227 Entering Passive Mode (10,200,36,33,200,189).
  63.  *   125 List started.
  64.  *   RADDATZ           200 21.05.11 12:31:18 *STMF      TEST_RG_02_CRLF.properties
  65.  *   RADDATZ           187 08.05.11 12:31:40 *STMF      TEST_RG_02_LF.properties
  66.  *   RADDATZ           187 08.05.11 12:31:52 *STMF      TEST_RG_02_CR.properties
  67.  *   RADDATZ          8192 04.07.13 09:04:14 *DIR       testDir1/
  68.  *   RADDATZ          8192 04.07.13 09:04:17 *DIR       testDir2/
  69.  *   250 List completed.
  70.  *
  71.  *
  72.  * Example 1, using ANT to list specific members of a file:
  73.  * --------------------------------------------------------
  74.  *
  75.  *      &lt;echo/&gt;
  76.  *      &lt;echo&gt;Listing members of a file:&lt;/echo&gt;
  77.  *
  78.  *      &lt;ftp action="list"
  79.  *           server="${ftp.server}"
  80.  *           userid="${ftp.user}"
  81.  *           password="${ftp.password}"
  82.  *           binary="false"
  83.  *           verbose="true"
  84.  *           remotedir="/QSYS.LIB/RPGUNIT.LIB/RPGUNITY1.FILE"
  85.  *           systemTypeKey="OS/400"
  86.  *           listing="ftp-listing.txt"
  87.  *           &gt;
  88.  *          &lt;fileset dir="./i5-downloads-file" casesensitive="false"&gt;
  89.  *              &lt;include name="run*.mbr" /&gt;
  90.  *          &lt;/fileset&gt;
  91.  *      &lt;/ftp&gt;
  92.  *
  93.  * Output:
  94.  * -------
  95.  *
  96.  *   [echo] Listing members of a file:
  97.  *    [ftp] listing files
  98.  *    [ftp] listing RUN.MBR
  99.  *    [ftp] listing RUNNER.MBR
  100.  *    [ftp] listing RUNNERBND.MBR
  101.  *    [ftp] 3 files listed
  102.  *
  103.  *
  104.  * Example 2, using ANT to list specific members of all files of a library:
  105.  * ------------------------------------------------------------------------
  106.  *
  107.  *      &lt;echo/&gt;
  108.  *      &lt;echo&gt;Listing members of all files of a library:&lt;/echo&gt;
  109.  *
  110.  *      &lt;ftp action="list"
  111.  *           server="${ftp.server}"
  112.  *           userid="${ftp.user}"
  113.  *           password="${ftp.password}"
  114.  *           binary="false"
  115.  *           verbose="true"
  116.  *           remotedir="/QSYS.LIB/RPGUNIT.LIB"
  117.  *           systemTypeKey="OS/400"
  118.  *           listing="ftp-listing.txt"
  119.  *           &gt;
  120.  *          &lt;fileset dir="./i5-downloads-lib" casesensitive="false"&gt;
  121.  *              &lt;include name="**\run*.mbr" /&gt;
  122.  *          &lt;/fileset&gt;
  123.  *      &lt;/ftp&gt;
  124.  *
  125.  * Output:
  126.  * -------
  127.  *
  128.  *   [echo] Listing members of all files of a library:
  129.  *    [ftp] listing files
  130.  *    [ftp] listing RPGUNIT1.FILE\RUN.MBR
  131.  *    [ftp] listing RPGUNIT1.FILE\RUNRMT.MBR
  132.  *    [ftp] listing RPGUNITT1.FILE\RUNT.MBR
  133.  *    [ftp] listing RPGUNITY1.FILE\RUN.MBR
  134.  *    [ftp] listing RPGUNITY1.FILE\RUNNER.MBR
  135.  *    [ftp] listing RPGUNITY1.FILE\RUNNERBND.MBR
  136.  *    [ftp] 6 files listed
  137.  *
  138.  *
  139.  * Example 3, using ANT to download specific members of a file:
  140.  * ------------------------------------------------------------
  141.  *
  142.  *      &lt;echo/&gt;
  143.  *      &lt;echo&gt;Downloading members of a file:&lt;/echo&gt;
  144.  *
  145.  *      &lt;ftp action="get"
  146.  *           server="${ftp.server}"
  147.  *           userid="${ftp.user}"
  148.  *           password="${ftp.password}"
  149.  *           binary="false"
  150.  *           verbose="true"
  151.  *           remotedir="/QSYS.LIB/RPGUNIT.LIB/RPGUNITY1.FILE"
  152.  *           systemTypeKey="OS/400"
  153.  *           &gt;
  154.  *          &lt;fileset dir="./i5-downloads-file" casesensitive="false"&gt;
  155.  *              &lt;include name="run*.mbr" /&gt;
  156.  *          &lt;/fileset&gt;
  157.  *      &lt;/ftp&gt;
  158.  *
  159.  * Output:
  160.  * -------
  161.  *
  162.  *   [echo] Downloading members of a file:
  163.  *    [ftp] getting files
  164.  *    [ftp] transferring RUN.MBR to C:\workspaces\rdp_080\workspace\ANT - FTP\i5-downloads-file\RUN.MBR
  165.  *    [ftp] transferring RUNNER.MBR to C:\workspaces\rdp_080\workspace\ANT - FTP\i5-downloads-file\RUNNER.MBR
  166.  *    [ftp] transferring RUNNERBND.MBR to C:\workspaces\rdp_080\workspace\ANT - FTP\i5-downloads-file\RUNNERBND.MBR
  167.  *    [ftp] 3 files retrieved
  168.  *
  169.  *
  170.  * Example 4, using ANT to download specific members of all files of a library:
  171.  * ----------------------------------------------------------------------------
  172.  *
  173.  *      &lt;echo/&gt;
  174.  *      &lt;echo&gt;Downloading members of all files of a library:&lt;/echo&gt;
  175.  *
  176.  *      &lt;ftp action="get"
  177.  *           server="${ftp.server}"
  178.  *           userid="${ftp.user}"
  179.  *           password="${ftp.password}"
  180.  *           binary="false"
  181.  *           verbose="true"
  182.  *           remotedir="/QSYS.LIB/RPGUNIT.LIB"
  183.  *           systemTypeKey="OS/400"
  184.  *           &gt;
  185.  *          &lt;fileset dir="./i5-downloads-lib" casesensitive="false"&gt;
  186.  *              &lt;include name="**\run*.mbr" /&gt;
  187.  *          &lt;/fileset&gt;
  188.  *      &lt;/ftp&gt;
  189.  *
  190.  * Output:
  191.  * -------
  192.  *
  193.  *   [echo] Downloading members of all files of a library:
  194.  *    [ftp] getting files
  195.  *    [ftp] transferring RPGUNIT1.FILE\RUN.MBR to C:\work\rdp_080\space\ANT - FTP\i5-downloads\RPGUNIT1.FILE\RUN.MBR
  196.  *    [ftp] transferring RPGUNIT1.FILE\RUNRMT.MBR to C:\work\rdp_080\space\ANT - FTP\i5-downloads\RPGUNIT1.FILE\RUNRMT.MBR
  197.  *    [ftp] transferring RPGUNITT1.FILE\RUNT.MBR to C:\work\rdp_080\space\ANT - FTP\i5-downloads\RPGUNITT1.FILE\RUNT.MBR
  198.  *    [ftp] transferring RPGUNITY1.FILE\RUN.MBR to C:\work\rdp_080\space\ANT - FTP\i5-downloads\RPGUNITY1.FILE\RUN.MBR
  199.  *    [ftp] transferring RPGUNITY1.FILE\RUNNER.MBR to C:\work\rdp_080\space\ANT - FTP\i5-downloads\RPGUNITY1.FILE\RUNNER.MBR
  200.  *    [ftp] transferring RPGUNITY1.FILE\RUNNERBND.MBR to C:\work\rdp_080\space\ANT - FTP\i5-downloads\RPGUNITY1.FILE\RUNNERBND.MBR
  201.  *    [ftp] 6 files retrieved
  202.  *
  203.  *
  204.  * Example 5, using ANT to download a save file of a library:
  205.  * ----------------------------------------------------------
  206.  *
  207.  *      &lt;ftp action="get"
  208.  *           server="${ftp.server}"
  209.  *           userid="${ftp.user}"
  210.  *           password="${ftp.password}"
  211.  *           binary="true"
  212.  *           verbose="true"
  213.  *           remotedir="/QSYS.LIB/RPGUNIT.LIB"
  214.  *           systemTypeKey="OS/400"
  215.  *           &gt;
  216.  *        &lt;fileset dir="./i5-downloads-savf" casesensitive="false"&gt;
  217.  *            &lt;include name="RPGUNIT.SAVF" /&gt;
  218.  *        &lt;/fileset&gt;
  219.  *      &lt;/ftp&gt;
  220.  *
  221.  * Output:
  222.  * -------
  223.  *   [echo] Downloading save file:
  224.  *    [ftp] getting files
  225.  *    [ftp] transferring RPGUNIT.SAVF to C:\workspaces\rdp_080\workspace\net-Test\i5-downloads-lib\RPGUNIT.SAVF
  226.  *    [ftp] 1 files retrieved
  227.  *
  228.  * </pre>
  229.  */
  230. public class OS400FTPEntryParser extends ConfigurableFTPFileEntryParserImpl {
  231.     private static final String DEFAULT_DATE_FORMAT = "yy/MM/dd HH:mm:ss"; // 01/11/09 12:30:24

  232.     private static final String REGEX = "(\\S+)\\s+" // user
  233.             + "(?:(\\d+)\\s+)?" // size, empty for members
  234.             + "(?:(\\S+)\\s+(\\S+)\\s+)?" // date stuff, empty for members
  235.             + "(\\*STMF|\\*DIR|\\*FILE|\\*MEM)\\s+" // *STMF/*DIR/*FILE/*MEM
  236.             + "((\\S+\\s*)+)?"; // file name, missing, when CWD is a *FILE

  237.     /**
  238.      * The default constructor for a OS400FTPEntryParser object.
  239.      *
  240.      * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not be seen under normal conditions. If it is seen, this is a
  241.      *                                  sign that <code>REGEX</code> is not a valid regular expression.
  242.      */
  243.     public OS400FTPEntryParser() {
  244.         this(null);
  245.     }

  246.     /**
  247.      * This constructor allows the creation of an OS400FTPEntryParser object with something other than the default configuration.
  248.      *
  249.      * @param config The {@link FTPClientConfig configuration} object used to configure this parser.
  250.      * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not be seen under normal conditions. If it is seen, this is a
  251.      *                                  sign that <code>REGEX</code> is not a valid regular expression.
  252.      * @since 1.4
  253.      */
  254.     public OS400FTPEntryParser(final FTPClientConfig config) {
  255.         super(REGEX);
  256.         configure(config);
  257.     }

  258.     /**
  259.      * Defines a default configuration to be used when this class is instantiated without a {@link FTPClientConfig FTPClientConfig} parameter being specified.
  260.      *
  261.      * @return the default configuration for this parser.
  262.      */
  263.     @Override
  264.     protected FTPClientConfig getDefaultConfiguration() {
  265.         return new FTPClientConfig(FTPClientConfig.SYST_OS400, DEFAULT_DATE_FORMAT, null);
  266.     }

  267.     /**
  268.      *
  269.      * @param string String value that is checked for {@code null} or empty.
  270.      * @return {@code true} for {@code null} or empty values, else {@code false}.
  271.      */
  272.     private boolean isNullOrEmpty(final String string) {
  273.         return string == null || string.isEmpty();
  274.     }

  275.     @Override
  276.     public FTPFile parseFTPEntry(final String entry) {

  277.         final FTPFile file = new FTPFile();
  278.         file.setRawListing(entry);
  279.         final int type;

  280.         if (matches(entry)) {
  281.             final String usr = group(1);
  282.             final String filesize = group(2);
  283.             String datestr = "";
  284.             if (!isNullOrEmpty(group(3)) || !isNullOrEmpty(group(4))) {
  285.                 datestr = group(3) + " " + group(4);
  286.             }
  287.             final String typeStr = group(5);
  288.             String name = group(6);

  289.             boolean mustScanForPathSeparator = true;

  290.             try {
  291.                 file.setTimestamp(super.parseTimestamp(datestr));
  292.             } catch (final ParseException e) {
  293.                 // intentionally do nothing
  294.             }

  295.             if (typeStr.equalsIgnoreCase("*STMF")) {
  296.                 type = FTPFile.FILE_TYPE;
  297.                 if (isNullOrEmpty(filesize) || isNullOrEmpty(name)) {
  298.                     return null;
  299.                 }
  300.             } else if (typeStr.equalsIgnoreCase("*DIR")) {
  301.                 type = FTPFile.DIRECTORY_TYPE;
  302.                 if (isNullOrEmpty(filesize) || isNullOrEmpty(name)) {
  303.                     return null;
  304.                 }
  305.             } else if (typeStr.equalsIgnoreCase("*FILE")) {
  306.                 // File, defines the structure of the data (columns of a row)
  307.                 // but the data is stored in one or more members. Typically, a
  308.                 // source file contains multiple members whereas it is
  309.                 // recommended (but not enforced) to use one member per data
  310.                 // file.
  311.                 // Save files are a special type of files which are used
  312.                 // to save objects, e.g. for backups.
  313.                 if (name == null || !name.toUpperCase(Locale.ROOT).endsWith(".SAVF")) {
  314.                     return null;
  315.                 }
  316.                 mustScanForPathSeparator = false;
  317.                 type = FTPFile.FILE_TYPE;
  318.             } else if (typeStr.equalsIgnoreCase("*MEM")) {
  319.                 mustScanForPathSeparator = false;
  320.                 type = FTPFile.FILE_TYPE;

  321.                 if (isNullOrEmpty(name)) {
  322.                     return null;
  323.                 }
  324.                 if (!(isNullOrEmpty(filesize) && isNullOrEmpty(datestr))) {
  325.                     return null;
  326.                 }

  327.                 // Quick and dirty bug fix to make SelectorUtils work.
  328.                 // Class SelectorUtils uses 'File.separator' to splitt
  329.                 // a given path into pieces. But actually it had to
  330.                 // use the separator of the FTP server, which is a forward
  331.                 // slash in case of an AS/400.
  332.                 name = name.replace('/', File.separatorChar);
  333.             } else {
  334.                 type = FTPFile.UNKNOWN_TYPE;
  335.             }

  336.             file.setType(type);

  337.             file.setUser(usr);

  338.             try {
  339.                 file.setSize(Long.parseLong(filesize));
  340.             } catch (final NumberFormatException e) {
  341.                 // intentionally do nothing
  342.             }

  343.             if (name.endsWith("/")) {
  344.                 name = name.substring(0, name.length() - 1);
  345.             }
  346.             if (mustScanForPathSeparator) {
  347.                 final int pos = name.lastIndexOf('/');
  348.                 if (pos > -1) {
  349.                     name = name.substring(pos + 1);
  350.                 }
  351.             }

  352.             file.setName(name);

  353.             return file;
  354.         }
  355.         return null;
  356.     }

  357. }