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