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}