    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;
    019import java.io.BufferedReader;
    020import java.io.IOException;
    021import java.text.ParseException;
    022import java.util.StringTokenizer;
    023
    024import org.apache.commons.net.ftp.FTPClientConfig;
    025import org.apache.commons.net.ftp.FTPFile;
    026
    027/**
    028 * Implementation FTPFileEntryParser and FTPFileListParser for VMS Systems.
    029 * This is a sample of VMS LIST output
    030 * <pre>
    031 *  "1-JUN.LIS;1              9/9           2-JUN-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,RE)",
    032 *  "1-JUN.LIS;2              9/9           2-JUN-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,RE)",
    033 *  "DATA.DIR;1               1/9           2-JUN-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,RE)",
    034 * </pre>
    035 * <p>
    036 * Note: VMSFTPEntryParser can only be instantiated through the
    037 * DefaultFTPParserFactory by classname.  It will not be chosen
    038 * by the autodetection scheme.
    039 * </p>
    040 *
    041 * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions)
    042 * @see org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory
    043 */
    044public class VMSFTPEntryParser extends ConfigurableFTPFileEntryParserImpl
    045{
    046
    047    private static final String DEFAULT_DATE_FORMAT
    048        = "d-MMM-yyyy HH:mm:ss"; //9-NOV-2001 12:30:24
    049
    050    /**
    051     * this is the regular expression used by this parser.
    052     */
    053    private static final String REGEX =
    054        "(.*?;[0-9]+)\\s*"                                                  //1  file and version
    055        + "(\\d+)(?:/\\d+)?\\s*"                                                 //2  size/allocated
    056        +"(\\S+)\\s+(\\S+)\\s+"                                             //3+4 date and time
    057        + "\\[(([0-9$A-Za-z_]+)|([0-9$A-Za-z_]+),([0-9$a-zA-Z_]+))\\]?\\s*" //5(6,7,8) owner
    058        + "\\([a-zA-Z]*,([a-zA-Z]*),([a-zA-Z]*),([a-zA-Z]*)\\)";            //9,10,11 Permissions (O,G,W)
    059    // TODO - perhaps restrict permissions to [RWED]* ?
    060
    061
    062
    063    /**
    064     * Constructor for a VMSFTPEntryParser object.
    065     *
    066     * @throws IllegalArgumentException
    067     * Thrown if the regular expression is unparseable.  Should not be seen
    068     * under normal conditions.  It it is seen, this is a sign that
    069     * <code>REGEX</code> is  not a valid regular expression.
    070     */
    071    public VMSFTPEntryParser()
    072    {
    073        this(null);
    074    }
    075
    076    /**
    077     * This constructor allows the creation of a VMSFTPEntryParser object with
    078     * something other than the default configuration.
    079     *
    080     * @param config The {@link FTPClientConfig configuration} object used to
    081     * configure this parser.
    082     * @throws IllegalArgumentException
    083     * Thrown if the regular expression is unparseable.  Should not be seen
    084     * under normal conditions.  It it is seen, this is a sign that
    085     * <code>REGEX</code> is  not a valid regular expression.
    086     * @since 1.4
    087     */
    088    public VMSFTPEntryParser(final FTPClientConfig config)
    089    {
    090        super(REGEX);
    091        configure(config);
    092    }
    093
    094    /**
    095     * Parses a line of a VMS FTP server file listing and converts it into a
    096     * usable format in the form of an <code> FTPFile </code> instance.  If the
    097     * file listing line doesn't describe a file, <code> null </code> is
    098     * returned, otherwise a <code> FTPFile </code> instance representing the
    099     * files in the directory is returned.
    100     *
    101     * @param entry A line of text from the file listing
    102     * @return An FTPFile instance corresponding to the supplied entry
    103     */
    104    @Override
    105    public FTPFile parseFTPEntry(final String entry)
    106    {
    107        //one block in VMS equals 512 bytes
    108        final long longBlock = 512;
    109
    110        if (matches(entry))
    111        {
    112            final FTPFile f = new FTPFile();
    113            f.setRawListing(entry);
    114            String name = group(1);
    115            final String size = group(2);
    116            final String datestr = group(3)+" "+group(4);
    117            final String owner = group(5);
    118            final String permissions[] = new String[3];
    119            permissions[0]= group(9);
    120            permissions[1]= group(10);
    121            permissions[2]= group(11);
    122            try
    123            {
    124                f.setTimestamp(super.parseTimestamp(datestr));
    125            }
    126            catch (final ParseException e)
    127            {
    128                 // intentionally do nothing
    129            }
    130
    131
    132            final String grp;
    133            final String user;
    134            final StringTokenizer t = new StringTokenizer(owner, ",");
    135            switch (t.countTokens()) {
    136                case 1:
    137                    grp  = null;
    138                    user = t.nextToken();
    139                    break;
    140                case 2:
    141                    grp  = t.nextToken();
    142                    user = t.nextToken();
    143                    break;
    144                default:
    145                    grp  = null;
    146                    user = null;
    147            }
    148
    149            if (name.lastIndexOf(".DIR") != -1)
    150            {
    151                f.setType(FTPFile.DIRECTORY_TYPE);
    152            }
    153            else
    154            {
    155                f.setType(FTPFile.FILE_TYPE);
    156            }
    157            //set FTPFile name
    158            //Check also for versions to be returned or not
    159            if (isVersioning())
    160            {
    161                f.setName(name);
    162            }
    163            else
    164            {
    165                name = name.substring(0, name.lastIndexOf(';'));
    166                f.setName(name);
    167            }
    168            //size is retreived in blocks and needs to be put in bytes
    169            //for us humans and added to the FTPFile array
    170            final long sizeInBytes = Long.parseLong(size) * longBlock;
    171            f.setSize(sizeInBytes);
    172
    173            f.setGroup(grp);
    174            f.setUser(user);
    175            //set group and owner
    176
    177            //Set file permission.
    178            //VMS has (SYSTEM,OWNER,GROUP,WORLD) users that can contain
    179            //R (read) W (write) E (execute) D (delete)
    180
    181            //iterate for OWNER GROUP WORLD permissions
    182            for (int access = 0; access < 3; access++)
    183            {
    184                final String permission = permissions[access];
    185
    186                f.setPermission(access, FTPFile.READ_PERMISSION, permission.indexOf('R')>=0);
    187                f.setPermission(access, FTPFile.WRITE_PERMISSION, permission.indexOf('W')>=0);
    188                f.setPermission(access, FTPFile.EXECUTE_PERMISSION, permission.indexOf('E')>=0);
    189            }
    190
    191            return f;
    192        }
    193        return null;
    194    }
    195
    196
    197    /**
    198     * Reads the next entry using the supplied BufferedReader object up to
    199     * whatever delemits one entry from the next.   This parser cannot use
    200     * the default implementation of simply calling BufferedReader.readLine(),
    201     * because one entry may span multiple lines.
    202     *
    203     * @param reader The BufferedReader object from which entries are to be
    204     * read.
    205     *
    206     * @return A string representing the next ftp entry or null if none found.
    207     * @throws IOException thrown on any IO Error reading from the reader.
    208     */
    209    @Override
    210    public String readNextEntry(final BufferedReader reader) throws IOException
    211    {
    212        String line = reader.readLine();
    213        final StringBuilder entry = new StringBuilder();
    214        while (line != null)
    215        {
    216            if (line.startsWith("Directory") || line.startsWith("Total")) {
    217                line = reader.readLine();
    218                continue;
    219            }
    220
    221            entry.append(line);
    222            if (line.trim().endsWith(")"))
    223            {
    224                break;
    225            }
    226            line = reader.readLine();
    227        }
    228        return entry.length() == 0 ? null : entry.toString();
    229    }
    230
    231    protected boolean isVersioning() {
    232        return false;
    233    }
    234
    235    /**
    236     * Defines a default configuration to be used when this class is
    237     * instantiated without a {@link  FTPClientConfig  FTPClientConfig}
    238     * parameter being specified.
    239     * @return the default configuration for this parser.
    240     */
    241    @Override
    242    protected FTPClientConfig getDefaultConfiguration() {
    243        return new FTPClientConfig(
    244                FTPClientConfig.SYST_VMS,
    245                DEFAULT_DATE_FORMAT,
    246                null);
    247    }
    248
    249    // DEPRECATED METHODS - for API compatibility only - DO NOT USE
    250
    251    /**
    252     * DO NOT USE
    253     * @param listStream the stream
    254     * @return the array of files
    255     * @throws IOException on error
    256     * @deprecated (2.2) No other FTPFileEntryParser implementations have this method.
    257     */
    258    @Deprecated
    259    public FTPFile[] parseFileList(final java.io.InputStream listStream) throws IOException {
    260        final org.apache.commons.net.ftp.FTPListParseEngine engine = new org.apache.commons.net.ftp.FTPListParseEngine(this);
    261        engine.readServerList(listStream, null);
    262        return engine.getFiles();
    263    }
    264
    265}