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 }