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 * https://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; 019 020import java.io.BufferedReader; 021import java.io.IOException; 022import java.text.ParseException; 023import java.util.StringTokenizer; 024 025import org.apache.commons.net.ftp.FTPClientConfig; 026import org.apache.commons.net.ftp.FTPFile; 027 028/** 029 * Implementation FTPFileEntryParser and FTPFileListParser for VMS Systems. This is a sample of VMS LIST output 030 * 031 * <pre> 032 * "1-JUN.LIS;1 9/9 2-JUN-1998 07:32:04 [GROUP,OWNER] (RWED,RWED,RWED,RE)", 033 * "1-JUN.LIS;2 9/9 2-JUN-1998 07:32:04 [GROUP,OWNER] (RWED,RWED,RWED,RE)", 034 * "DATA.DIR;1 1/9 2-JUN-1998 07:32:04 [GROUP,OWNER] (RWED,RWED,RWED,RE)", 035 * </pre> 036 * <p> 037 * Note: VMSFTPEntryParser can only be instantiated through the DefaultFTPParserFactory by class name. It will not be chosen by the autodetection scheme. 038 * </p> 039 * 040 * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions) 041 * @see org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory 042 */ 043public class VMSFTPEntryParser extends ConfigurableFTPFileEntryParserImpl { 044 045 private static final String DEFAULT_DATE_FORMAT = "d-MMM-yyyy HH:mm:ss"; // 9-NOV-2001 12:30:24 046 047 /** 048 * this is the regular expression used by this parser. 049 */ 050 private static final String REGEX = "(.*?;[0-9]+)\\s*" // 1 file and version 051 + "(\\d+)(?:/\\d+)?\\s*" // 2 size/allocated 052 + "(\\S+)\\s+(\\S+)\\s+" // 3+4 date and time 053 + "\\[(([0-9$A-Za-z_]+)|([0-9$A-Za-z_]+),([0-9$a-zA-Z_]+))\\]?\\s*" // 5(6,7,8) owner 054 + "\\([a-zA-Z]*,([a-zA-Z]*),([a-zA-Z]*),([a-zA-Z]*)\\)"; // 9,10,11 Permissions (O,G,W) 055 // TODO - perhaps restrict permissions to [RWED]* ? 056 057 /** 058 * Constructor for a VMSFTPEntryParser object. 059 * 060 * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not be seen under normal conditions. If the exception is seen, 061 * this is a sign that {@code REGEX} is not a valid regular expression. 062 */ 063 public VMSFTPEntryParser() { 064 this(null); 065 } 066 067 /** 068 * This constructor allows the creation of a VMSFTPEntryParser object with something other than the default configuration. 069 * 070 * @param config The {@link FTPClientConfig configuration} object used to configure this parser. 071 * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not be seen under normal conditions. If the exception is seen, 072 * this is a sign that {@code REGEX} is not a valid regular expression. 073 * @since 1.4 074 */ 075 public VMSFTPEntryParser(final FTPClientConfig config) { 076 super(REGEX); 077 configure(config); 078 } 079 080 /** 081 * Gets a new default configuration to be used when this class is instantiated without a {@link FTPClientConfig FTPClientConfig} parameter being specified. 082 * 083 * @return the default configuration for this parser. 084 */ 085 @Override 086 protected FTPClientConfig getDefaultConfiguration() { 087 return new FTPClientConfig(FTPClientConfig.SYST_VMS, DEFAULT_DATE_FORMAT, null); 088 } 089 090 /** 091 * Returns {@code false}. 092 * 093 * @return {@code false}. 094 */ 095 protected boolean isVersioning() { 096 return false; 097 } 098 099 /** 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}