View Javadoc
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    *      https://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  
18  package org.apache.commons.net.ftp.parser;
19  
20  import java.io.BufferedReader;
21  import java.io.IOException;
22  import java.text.ParseException;
23  import java.util.StringTokenizer;
24  
25  import org.apache.commons.net.ftp.FTPClientConfig;
26  import org.apache.commons.net.ftp.FTPFile;
27  
28  /**
29   * Implementation FTPFileEntryParser and FTPFileListParser for VMS Systems. This is a sample of VMS LIST output
30   *
31   * <pre>
32   *  "1-JUN.LIS;1              9/9           2-JUN-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,RE)",
33   *  "1-JUN.LIS;2              9/9           2-JUN-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,RE)",
34   *  "DATA.DIR;1               1/9           2-JUN-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,RE)",
35   * </pre>
36   * <p>
37   * Note: VMSFTPEntryParser can only be instantiated through the DefaultFTPParserFactory by class name. It will not be chosen by the autodetection scheme.
38   * </p>
39   *
40   * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions)
41   * @see org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory
42   */
43  public class VMSFTPEntryParser extends ConfigurableFTPFileEntryParserImpl {
44  
45      private static final String DEFAULT_DATE_FORMAT = "d-MMM-yyyy HH:mm:ss"; // 9-NOV-2001 12:30:24
46  
47      /**
48       * this is the regular expression used by this parser.
49       */
50      private static final String REGEX = "(.*?;[0-9]+)\\s*" // 1 file and version
51              + "(\\d+)(?:/\\d+)?\\s*" // 2 size/allocated
52              + "(\\S+)\\s+(\\S+)\\s+" // 3+4 date and time
53              + "\\[(([0-9$A-Za-z_]+)|([0-9$A-Za-z_]+),([0-9$a-zA-Z_]+))\\]?\\s*" // 5(6,7,8) owner
54              + "\\([a-zA-Z]*,([a-zA-Z]*),([a-zA-Z]*),([a-zA-Z]*)\\)"; // 9,10,11 Permissions (O,G,W)
55      // TODO - perhaps restrict permissions to [RWED]* ?
56  
57      /**
58       * Constructor for a VMSFTPEntryParser object.
59       *
60       * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not be seen under normal conditions. If the exception is seen,
61       *                                  this is a sign that {@code REGEX} is not a valid regular expression.
62       */
63      public VMSFTPEntryParser() {
64          this(null);
65      }
66  
67      /**
68       * This constructor allows the creation of a VMSFTPEntryParser object with something other than the default configuration.
69       *
70       * @param config The {@link FTPClientConfig configuration} object used to configure this parser.
71       * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not be seen under normal conditions. If the exception is seen,
72       *                                  this is a sign that {@code REGEX} is not a valid regular expression.
73       * @since 1.4
74       */
75      public VMSFTPEntryParser(final FTPClientConfig config) {
76          super(REGEX);
77          configure(config);
78      }
79  
80      /**
81       * Gets a new default configuration to be used when this class is instantiated without a {@link FTPClientConfig FTPClientConfig} parameter being specified.
82       *
83       * @return the default configuration for this parser.
84       */
85      @Override
86      protected FTPClientConfig getDefaultConfiguration() {
87          return new FTPClientConfig(FTPClientConfig.SYST_VMS, DEFAULT_DATE_FORMAT, null);
88      }
89  
90      /**
91       * Returns {@code false}.
92       *
93       * @return {@code false}.
94       */
95      protected boolean isVersioning() {
96          return false;
97      }
98  
99      /**
100      * DO NOT USE.
101      *
102      * @param listStream the stream
103      * @return the array of files
104      * @throws IOException on error
105      * @deprecated (2.2) No other FTPFileEntryParser implementations have this method.
106      */
107     @Deprecated
108     public FTPFile[] parseFileList(final java.io.InputStream listStream) throws IOException {
109         final org.apache.commons.net.ftp.FTPListParseEngine engine = new org.apache.commons.net.ftp.FTPListParseEngine(this);
110         engine.readServerList(listStream, null);
111         return engine.getFiles();
112     }
113 
114     /**
115      * Parses a line of a VMS FTP server file listing and converts it into a usable format in the form of an {@code FTPFile} instance. If the file
116      * listing line doesn't describe a file, {@code null} is returned, otherwise a {@code FTPFile} instance representing the files in the
117      * directory is returned.
118      *
119      * @param entry A line of text from the file listing
120      * @return An FTPFile instance corresponding to the supplied entry
121      */
122     @Override
123     public FTPFile parseFTPEntry(final String entry) {
124         // one block in VMS equals 512 bytes
125         final long longBlock = 512;
126 
127         if (matches(entry)) {
128             final FTPFile f = new FTPFile();
129             f.setRawListing(entry);
130             String name = group(1);
131             final String size = group(2);
132             final String datestr = group(3) + " " + group(4);
133             final String owner = group(5);
134             final String[] permissions = new String[3];
135             permissions[0] = group(9);
136             permissions[1] = group(10);
137             permissions[2] = group(11);
138             try {
139                 f.setTimestamp(super.parseTimestamp(datestr));
140             } catch (final ParseException e) {
141                 // intentionally do nothing
142             }
143 
144             final String grp;
145             final String user;
146             final StringTokenizer t = new StringTokenizer(owner, ",");
147             switch (t.countTokens()) {
148             case 1:
149                 grp = null;
150                 user = t.nextToken();
151                 break;
152             case 2:
153                 grp = t.nextToken();
154                 user = t.nextToken();
155                 break;
156             default:
157                 grp = null;
158                 user = null;
159             }
160 
161             if (name.lastIndexOf(".DIR") != -1) {
162                 f.setType(FTPFile.DIRECTORY_TYPE);
163             } else {
164                 f.setType(FTPFile.FILE_TYPE);
165             }
166             // set FTPFile name
167             // Check also for versions to be returned or not
168             if (!isVersioning()) {
169                 name = name.substring(0, name.lastIndexOf(';'));
170             }
171             f.setName(name);
172             // size is retreived in blocks and needs to be put in bytes
173             // for us humans and added to the FTPFile array
174             final long sizeInBytes = Long.parseLong(size) * longBlock;
175             f.setSize(sizeInBytes);
176 
177             f.setGroup(grp);
178             f.setUser(user);
179             // set group and owner
180 
181             // Set file permission.
182             // VMS has (SYSTEM,OWNER,GROUP,WORLD) users that can contain
183             // R (read) W (write) E (execute) D (delete)
184 
185             // iterate for OWNER GROUP WORLD permissions
186             for (int access = 0; access < 3; access++) {
187                 final String permission = permissions[access];
188 
189                 f.setPermission(access, FTPFile.READ_PERMISSION, permission.indexOf('R') >= 0);
190                 f.setPermission(access, FTPFile.WRITE_PERMISSION, permission.indexOf('W') >= 0);
191                 f.setPermission(access, FTPFile.EXECUTE_PERMISSION, permission.indexOf('E') >= 0);
192             }
193 
194             return f;
195         }
196         return null;
197     }
198 
199     // DEPRECATED METHODS - for API compatibility only - DO NOT USE
200 
201     /**
202      * Reads the next entry using the supplied BufferedReader object up to whatever delimits one entry from the next. This parser cannot use the default
203      * implementation of simply calling BufferedReader.readLine(), because one entry may span multiple lines.
204      *
205      * @param reader The BufferedReader object from which entries are to be read.
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         String line = reader.readLine();
212         final StringBuilder entry = new StringBuilder();
213         while (line != null) {
214             if (line.startsWith("Directory") || line.startsWith("Total")) {
215                 line = reader.readLine();
216                 continue;
217             }
218 
219             entry.append(line);
220             if (line.trim().endsWith(")")) {
221                 break;
222             }
223             line = reader.readLine();
224         }
225         return entry.length() == 0 ? null : entry.toString();
226     }
227 
228 }