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; 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</code> 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</code> 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 * Defines a 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 protected boolean isVersioning() { 091 return false; 092 } 093 094 /** 095 * DO NOT USE 096 * 097 * @param listStream the stream 098 * @return the array of files 099 * @throws IOException on error 100 * @deprecated (2.2) No other FTPFileEntryParser implementations have this method. 101 */ 102 @Deprecated 103 public FTPFile[] parseFileList(final java.io.InputStream listStream) throws IOException { 104 final org.apache.commons.net.ftp.FTPListParseEngine engine = new org.apache.commons.net.ftp.FTPListParseEngine(this); 105 engine.readServerList(listStream, null); 106 return engine.getFiles(); 107 } 108 109 /** 110 * Parses a line of a VMS FTP server file listing and converts it into a usable format in the form of an <code>FTPFile</code> instance. If the file 111 * listing line doesn't describe a file, <code>null</code> is returned, otherwise a <code>FTPFile</code> instance representing the files in the 112 * directory is returned. 113 * 114 * @param entry A line of text from the file listing 115 * @return An FTPFile instance corresponding to the supplied entry 116 */ 117 @Override 118 public FTPFile parseFTPEntry(final String entry) { 119 // one block in VMS equals 512 bytes 120 final long longBlock = 512; 121 122 if (matches(entry)) { 123 final FTPFile f = new FTPFile(); 124 f.setRawListing(entry); 125 String name = group(1); 126 final String size = group(2); 127 final String datestr = group(3) + " " + group(4); 128 final String owner = group(5); 129 final String[] permissions = new String[3]; 130 permissions[0] = group(9); 131 permissions[1] = group(10); 132 permissions[2] = group(11); 133 try { 134 f.setTimestamp(super.parseTimestamp(datestr)); 135 } catch (final ParseException e) { 136 // intentionally do nothing 137 } 138 139 final String grp; 140 final String user; 141 final StringTokenizer t = new StringTokenizer(owner, ","); 142 switch (t.countTokens()) { 143 case 1: 144 grp = null; 145 user = t.nextToken(); 146 break; 147 case 2: 148 grp = t.nextToken(); 149 user = t.nextToken(); 150 break; 151 default: 152 grp = null; 153 user = null; 154 } 155 156 if (name.lastIndexOf(".DIR") != -1) { 157 f.setType(FTPFile.DIRECTORY_TYPE); 158 } else { 159 f.setType(FTPFile.FILE_TYPE); 160 } 161 // set FTPFile name 162 // Check also for versions to be returned or not 163 if (!isVersioning()) { 164 name = name.substring(0, name.lastIndexOf(';')); 165 } 166 f.setName(name); 167 // size is retreived in blocks and needs to be put in bytes 168 // for us humans and added to the FTPFile array 169 final long sizeInBytes = Long.parseLong(size) * longBlock; 170 f.setSize(sizeInBytes); 171 172 f.setGroup(grp); 173 f.setUser(user); 174 // set group and owner 175 176 // Set file permission. 177 // VMS has (SYSTEM,OWNER,GROUP,WORLD) users that can contain 178 // R (read) W (write) E (execute) D (delete) 179 180 // iterate for OWNER GROUP WORLD permissions 181 for (int access = 0; access < 3; access++) { 182 final String permission = permissions[access]; 183 184 f.setPermission(access, FTPFile.READ_PERMISSION, permission.indexOf('R') >= 0); 185 f.setPermission(access, FTPFile.WRITE_PERMISSION, permission.indexOf('W') >= 0); 186 f.setPermission(access, FTPFile.EXECUTE_PERMISSION, permission.indexOf('E') >= 0); 187 } 188 189 return f; 190 } 191 return null; 192 } 193 194 // DEPRECATED METHODS - for API compatibility only - DO NOT USE 195 196 /** 197 * Reads the next entry using the supplied BufferedReader object up to whatever delimits one entry from the next. This parser cannot use the default 198 * implementation of simply calling BufferedReader.readLine(), because one entry may span multiple lines. 199 * 200 * @param reader The BufferedReader object from which entries are to be read. 201 * 202 * @return A string representing the next ftp entry or null if none found. 203 * @throws IOException thrown on any IO Error reading from the reader. 204 */ 205 @Override 206 public String readNextEntry(final BufferedReader reader) throws IOException { 207 String line = reader.readLine(); 208 final StringBuilder entry = new StringBuilder(); 209 while (line != null) { 210 if (line.startsWith("Directory") || line.startsWith("Total")) { 211 line = reader.readLine(); 212 continue; 213 } 214 215 entry.append(line); 216 if (line.trim().endsWith(")")) { 217 break; 218 } 219 line = reader.readLine(); 220 } 221 return entry.length() == 0 ? null : entry.toString(); 222 } 223 224}