From 01c75a7aa09d0d9fd7a4c61f952aaa93972e7c99 Mon Sep 17 00:00:00 2001
From: Joe Robinson
+ * This abstract class implements the common timestamp parsing
+ * algorithm for all the concrete parsers. Classes derived from
+ * this one will parse file listings via a supplied regular expression
+ * that pulls out the date portion as a separate string which is
+ * passed to the underlying {@link FTPTimestampParser delegate} to
+ * handle parsing of the file timestamp.
+ *
+ * This class also implements the {@link Configurable Configurable}
+ * interface to allow the parser to be configured from the outside.
+ *
+ * @param timestampStr the timestamp string pulled from the
+ * file listing by the regular expression parser, to be submitted
+ * to the Implementation extracts a key from the supplied
+ * {@link FTPClientConfig FTPClientConfig}
+ * parameter and creates an object implementing the
+ * interface FTPFileEntryParser and uses the supplied configuration
+ * to configure it.
+ *
+ * Note that this method will generally not be called in scenarios
+ * that call for autodetection of parser type but rather, for situations
+ * where the user knows that the server uses a non-default configuration
+ * and knows what that configuration is.
+ *
+ * Note: EnterpriseUnixFTPEntryParser can only be instantiated through the
+ * DefaultFTPParserFactory by classname. It will not be chosen
+ * by the autodetection scheme.
+ *
+ * @version $Id: EnterpriseUnixFTPEntryParser.java 658518 2008-05-21 01:04:30Z sebb $
+ * @author Winston Ojeda
+ * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions)
+ * @see org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory
+ */
+public class EnterpriseUnixFTPEntryParser extends RegexFTPFileEntryParserImpl
+{
+
+ /**
+ * months abbreviations looked for by this parser. Also used
+ * to determine which month has been matched by the parser.
+ */
+ private static final String MONTHS =
+ "(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)";
+
+ /**
+ * this is the regular expression used by this parser.
+ */
+ private static final String REGEX =
+ "(([\\-]|[A-Z])([\\-]|[A-Z])([\\-]|[A-Z])([\\-]|[A-Z])([\\-]|[A-Z])"
+ + "([\\-]|[A-Z])([\\-]|[A-Z])([\\-]|[A-Z])([\\-]|[A-Z])([\\-]|[A-Z]))"
+ + "(\\S*)\\s*"
+ + "(\\S+)\\s*"
+ + "(\\S*)\\s*"
+ + "(\\d*)\\s*"
+ + "(\\d*)\\s*"
+ + MONTHS
+ + "\\s*"
+ + "((?:[012]\\d*)|(?:3[01]))\\s*"
+ + "((\\d\\d\\d\\d)|((?:[01]\\d)|(?:2[0123])):([012345]\\d))\\s"
+ + "(\\S*)(\\s*.*)";
+
+ /**
+ * The sole constructor for a EnterpriseUnixFTPEntryParser object.
+ *
+ */
+ public EnterpriseUnixFTPEntryParser()
+ {
+ super(REGEX);
+ }
+
+ /**
+ * Parses a line of a unix FTP server file listing and converts it into a
+ * usable format in the form of an
+ * Implementation should be a method that extracts
+ * a key from the supplied {@link FTPClientConfig FTPClientConfig}
+ * parameter and creates an object implementing the
+ * interface FTPFileEntryParser and uses the supplied configuration
+ * to configure it.
+ *
+ * Note that this method will generally not be called in scenarios
+ * that call for autodetection of parser type but rather, for situations
+ * where the user knows that the server uses a non-default configuration
+ * and knows what that configuration is.
+ *
+ * Set up the {@link FTPClientConfig#setDefaultDateFormatStr(java.lang.String) defaultDateFormat}
+ * and optionally the {@link FTPClientConfig#setRecentDateFormatStr(String) recentDateFormat}
+ * to values supplied in the config based on month names configured as follows:
+ * timestampParser
for extracting the timestamp.
+ * @return a java.util.Calendar
containing results of the
+ * timestamp parse.
+ */
+ public Calendar parseTimestamp(String timestampStr) throws ParseException {
+ return this.timestampParser.parseTimestamp(timestampStr);
+ }
+
+
+ /**
+ * Implementation of the {@link Configurable Configurable}
+ * interface. Configures this parser by delegating to the
+ * underlying Configurable FTPTimestampParser implementation, '
+ * passing it the supplied {@link FTPClientConfig FTPClientConfig}
+ * if that is non-null or a default configuration defined by
+ * each concrete subclass.
+ * key
is not recognized as a fully qualified
+ * classname known to the system, this method will then attempt
+ * to see whether it contains a string identifying one of
+ * the known parsers. This comparison is case-insensitive.
+ * The intent here is where possible, to select as keys strings
+ * which are returned by the SYST command on the systems which
+ * the corresponding parser successfully parses. This enables
+ * this factory to be used in the auto-detection system.
+ *
+ *
+ * @param key should be a fully qualified classname corresponding to
+ * a class implementing the FTPFileEntryParser interface
+ * OR
+ * a string containing (case-insensitively) one of the
+ * following keywords:
+ *
+ *
+ * @return the FTPFileEntryParser corresponding to the supplied key.
+ * @throws ParserInitializationException thrown if for any reason the factory cannot resolve
+ * the supplied key into an FTPFileEntryParser.
+ * @see FTPFileEntryParser
+ */
+ public FTPFileEntryParser createFileEntryParser(String key)
+ {
+ if (key == null)
+ throw new ParserInitializationException("Parser key cannot be null");
+
+ Class> parserClass = null;
+ FTPFileEntryParser parser = null;
+ try
+ {
+ parserClass = Class.forName(key);
+ parser = (FTPFileEntryParser) parserClass.newInstance();
+ }
+ catch (ClassNotFoundException e)
+ {
+ try
+ {
+ String ukey = null;
+ if (null != key)
+ {
+ ukey = key.toUpperCase(java.util.Locale.ENGLISH);
+ }
+ if ((ukey.indexOf(FTPClientConfig.SYST_UNIX) >= 0)
+ || (ukey.indexOf(FTPClientConfig.SYST_L8) >= 0))
+ {
+ parser = createUnixFTPEntryParser();
+ }
+ else if (ukey.indexOf(FTPClientConfig.SYST_VMS) >= 0)
+ {
+ parser = createVMSVersioningFTPEntryParser();
+ }
+ else if (ukey.indexOf(FTPClientConfig.SYST_NT) >= 0)
+ {
+ parser = createNTFTPEntryParser();
+ }
+ else if (ukey.indexOf(FTPClientConfig.SYST_OS2) >= 0)
+ {
+ parser = createOS2FTPEntryParser();
+ }
+ else if (ukey.indexOf(FTPClientConfig.SYST_OS400) >= 0 ||
+ ukey.indexOf(FTPClientConfig.SYST_AS400) >= 0)
+ {
+ parser = createOS400FTPEntryParser();
+ }
+ else if (ukey.indexOf(FTPClientConfig.SYST_MVS) >= 0)
+ {
+ parser = createMVSEntryParser();
+ }
+ else if (ukey.indexOf(FTPClientConfig.SYST_NETWARE) >= 0)
+ {
+ parser = createNetwareFTPEntryParser();
+ }
+ else
+ {
+ throw new ParserInitializationException("Unknown parser type: " + key);
+ }
+ }
+ catch (NoClassDefFoundError nf) {
+ throw new ParserInitializationException("Error initializing parser", nf);
+ }
+
+ }
+ catch (NoClassDefFoundError e)
+ {
+ throw new ParserInitializationException("Error initializing parser", e);
+ }
+ catch (ClassCastException e)
+ {
+ throw new ParserInitializationException(parserClass.getName()
+ + " does not implement the interface "
+ + "org.apache.commons.net.ftp.FTPFileEntryParser.", e);
+ }
+ catch (Throwable e)
+ {
+ throw new ParserInitializationException("Error initializing parser", e);
+ }
+
+ if (parser instanceof Configurable) {
+ ((Configurable)parser).configure(this.config);
+ }
+ return parser;
+ }
+
+ /**
+ * FTPFile
instance. If
+ * the file listing line doesn't describe a file, null
is
+ * returned, otherwise a FTPFile
instance representing the
+ * files in the directory is returned.
+ *
+ * @param entry A line of text from the file listing
+ * @return An FTPFile instance corresponding to the supplied entry
+ */
+ public FTPFile parseFTPEntry(String entry)
+ {
+
+ FTPFile file = new FTPFile();
+ file.setRawListing(entry);
+
+ if (matches(entry))
+ {
+ String usr = group(14);
+ String grp = group(15);
+ String filesize = group(16);
+ String mo = group(17);
+ String da = group(18);
+ String yr = group(20);
+ String hr = group(21);
+ String min = group(22);
+ String name = group(23);
+
+ file.setType(FTPFile.FILE_TYPE);
+ file.setUser(usr);
+ file.setGroup(grp);
+ try
+ {
+ file.setSize(Long.parseLong(filesize));
+ }
+ catch (NumberFormatException e)
+ {
+ // intentionally do nothing
+ }
+
+ Calendar cal = Calendar.getInstance();
+ cal.set(Calendar.MILLISECOND, 0);
+ cal.set(Calendar.SECOND,
+ 0);
+ cal.set(Calendar.MINUTE,
+ 0);
+ cal.set(Calendar.HOUR_OF_DAY,
+ 0);
+ try
+ {
+
+ int pos = MONTHS.indexOf(mo);
+ int month = pos / 4;
+ if (yr != null)
+ {
+ // it's a year
+ cal.set(Calendar.YEAR,
+ Integer.parseInt(yr));
+ }
+ else
+ {
+ // it must be hour/minute or we wouldn't have matched
+ int year = cal.get(Calendar.YEAR);
+
+ // if the month we're reading is greater than now, it must
+ // be last year
+ if (cal.get(Calendar.MONTH) < month)
+ {
+ year--;
+ }
+ cal.set(Calendar.YEAR,
+ year);
+ cal.set(Calendar.HOUR_OF_DAY,
+ Integer.parseInt(hr));
+ cal.set(Calendar.MINUTE,
+ Integer.parseInt(min));
+ }
+ cal.set(Calendar.MONTH,
+ month);
+ cal.set(Calendar.DATE,
+ Integer.parseInt(da));
+ file.setTimestamp(cal);
+ }
+ catch (NumberFormatException e)
+ {
+ // do nothing, date will be uninitialized
+ }
+ file.setName(name);
+
+ return file;
+ }
+ return null;
+ }
+}
diff --git a/src/org/apache/commons/net/ftp/parser/FTPFileEntryParserFactory.java b/src/org/apache/commons/net/ftp/parser/FTPFileEntryParserFactory.java
new file mode 100644
index 0000000..e80ec0d
--- /dev/null
+++ b/src/org/apache/commons/net/ftp/parser/FTPFileEntryParserFactory.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp.parser;
+import org.apache.commons.net.ftp.FTPClientConfig;
+import org.apache.commons.net.ftp.FTPFileEntryParser;
+
+/**
+ * The interface describes a factory for creating FTPFileEntryParsers.
+ * @since 1.2
+ */
+public interface FTPFileEntryParserFactory
+{
+ /**
+ * Implementation should be a method that decodes the
+ * supplied key and creates an object implementing the
+ * interface FTPFileEntryParser.
+ *
+ * @param key A string that somehow identifies an
+ * FTPFileEntryParser to be created.
+ *
+ * @return the FTPFileEntryParser created.
+ * @exception ParserInitializationException
+ * Thrown on any exception in instantiation
+ */
+ public FTPFileEntryParser createFileEntryParser(String key)
+ throws ParserInitializationException;
+
+ /**
+ *java.util.Calendar
object initialized to the date
+ * parsed by the parser
+ * @throws ParseException if none of the parser mechanisms belonging to
+ * the implementor can parse the input.
+ */
+ public Calendar parseTimestamp(String timestampStr) throws ParseException;
+
+}
diff --git a/src/org/apache/commons/net/ftp/parser/FTPTimestampParserImpl.java b/src/org/apache/commons/net/ftp/parser/FTPTimestampParserImpl.java
new file mode 100644
index 0000000..02a0cc8
--- /dev/null
+++ b/src/org/apache/commons/net/ftp/parser/FTPTimestampParserImpl.java
@@ -0,0 +1,301 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp.parser;
+
+import java.text.DateFormatSymbols;
+import java.text.ParseException;
+import java.text.ParsePosition;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+import org.apache.commons.net.ftp.Configurable;
+import org.apache.commons.net.ftp.FTPClientConfig;
+
+/**
+ * Default implementation of the {@link FTPTimestampParser FTPTimestampParser}
+ * interface also implements the {@link org.apache.commons.net.ftp.Configurable Configurable}
+ * interface to allow the parsing to be configured from the outside.
+ *
+ * @see ConfigurableFTPFileEntryParserImpl
+ * @since 1.4
+ */
+public class FTPTimestampParserImpl implements
+ FTPTimestampParser, Configurable
+{
+
+
+ private SimpleDateFormat defaultDateFormat;
+ private SimpleDateFormat recentDateFormat;
+ private boolean lenientFutureDates = false;
+
+
+ /**
+ * The only constructor for this class.
+ */
+ public FTPTimestampParserImpl() {
+ setDefaultDateFormat(DEFAULT_SDF);
+ setRecentDateFormat(DEFAULT_RECENT_SDF);
+ }
+
+ /**
+ * Implements the one {@link FTPTimestampParser#parseTimestamp(String) method}
+ * in the {@link FTPTimestampParser FTPTimestampParser} interface
+ * according to this algorithm:
+ *
+ * If the recentDateFormat member has been defined, try to parse the
+ * supplied string with that. If that parse fails, or if the recentDateFormat
+ * member has not been defined, attempt to parse with the defaultDateFormat
+ * member. If that fails, throw a ParseException.
+ *
+ * This method allows a {@link Calendar} instance to be passed in which represents the
+ * current (system) time.
+ *
+ * @see org.apache.commons.net.ftp.parser.FTPTimestampParser#parseTimestamp(java.lang.String)
+ *
+ * @param timestampStr The timestamp to be parsed
+ */
+ public Calendar parseTimestamp(String timestampStr) throws ParseException {
+ Calendar now = Calendar.getInstance();
+ return parseTimestamp(timestampStr, now);
+ }
+
+ /**
+ * Implements the one {@link FTPTimestampParser#parseTimestamp(String) method}
+ * in the {@link FTPTimestampParser FTPTimestampParser} interface
+ * according to this algorithm:
+ *
+ * If the recentDateFormat member has been defined, try to parse the
+ * supplied string with that. If that parse fails, or if the recentDateFormat
+ * member has not been defined, attempt to parse with the defaultDateFormat
+ * member. If that fails, throw a ParseException.
+ *
+ * @see org.apache.commons.net.ftp.parser.FTPTimestampParser#parseTimestamp(java.lang.String)
+ * @param timestampStr The timestamp to be parsed
+ * @param serverTime The current time for the server
+ * @since 1.5
+ */
+ public Calendar parseTimestamp(String timestampStr, Calendar serverTime) throws ParseException {
+ Calendar now = (Calendar) serverTime.clone();// Copy this, because we may change it
+ now.setTimeZone(this.getServerTimeZone());
+ Calendar working = (Calendar) now.clone();
+ working.setTimeZone(getServerTimeZone());
+ ParsePosition pp = new ParsePosition(0);
+
+ Date parsed = null;
+ if (recentDateFormat != null) {
+ if (lenientFutureDates) {
+ // add a day to "now" so that "slop" doesn't cause a date
+ // slightly in the future to roll back a full year. (Bug 35181)
+ now.add(Calendar.DATE, 1);
+ }
+ parsed = recentDateFormat.parse(timestampStr, pp);
+ }
+ if (parsed != null && pp.getIndex() == timestampStr.length())
+ {
+ working.setTime(parsed);
+ working.set(Calendar.YEAR, now.get(Calendar.YEAR));
+
+ if (working.after(now)) {
+ working.add(Calendar.YEAR, -1);
+ }
+ } else {
+ // Temporarily add the current year to the short date time
+ // to cope with short-date leap year strings.
+ // e.g. Java's DateFormatter will assume that "Feb 29 12:00" refers to
+ // Feb 29 1970 (an invalid date) rather than a potentially valid leap year date.
+ // This is pretty bad hack to work around the deficiencies of the JDK date/time classes.
+ if (recentDateFormat != null) {
+ pp = new ParsePosition(0);
+ int year = now.get(Calendar.YEAR);
+ String timeStampStrPlusYear = timestampStr + " " + year;
+ SimpleDateFormat hackFormatter = new SimpleDateFormat(recentDateFormat.toPattern() + " yyyy",
+ recentDateFormat.getDateFormatSymbols());
+ hackFormatter.setLenient(false);
+ hackFormatter.setTimeZone(recentDateFormat.getTimeZone());
+ parsed = hackFormatter.parse(timeStampStrPlusYear, pp);
+ }
+ if (parsed != null && pp.getIndex() == timestampStr.length() + 5) {
+ working.setTime(parsed);
+ }
+ else {
+ pp = new ParsePosition(0);
+ parsed = defaultDateFormat.parse(timestampStr, pp);
+ // note, length checks are mandatory for us since
+ // SimpleDateFormat methods will succeed if less than
+ // full string is matched. They will also accept,
+ // despite "leniency" setting, a two-digit number as
+ // a valid year (e.g. 22:04 will parse as 22 A.D.)
+ // so could mistakenly confuse an hour with a year,
+ // if we don't insist on full length parsing.
+ if (parsed != null && pp.getIndex() == timestampStr.length()) {
+ working.setTime(parsed);
+ } else {
+ throw new ParseException(
+ "Timestamp could not be parsed with older or recent DateFormat",
+ pp.getIndex());
+ }
+ }
+ }
+ return working;
+ }
+
+ /**
+ * @return Returns the defaultDateFormat.
+ */
+ public SimpleDateFormat getDefaultDateFormat() {
+ return defaultDateFormat;
+ }
+ /**
+ * @return Returns the defaultDateFormat pattern string.
+ */
+ public String getDefaultDateFormatString() {
+ return defaultDateFormat.toPattern();
+ }
+ /**
+ * @param defaultDateFormat The defaultDateFormat to be set.
+ */
+ private void setDefaultDateFormat(String format) {
+ if (format != null) {
+ this.defaultDateFormat = new SimpleDateFormat(format);
+ this.defaultDateFormat.setLenient(false);
+ }
+ }
+ /**
+ * @return Returns the recentDateFormat.
+ */
+ public SimpleDateFormat getRecentDateFormat() {
+ return recentDateFormat;
+ }
+ /**
+ * @return Returns the recentDateFormat.
+ */
+ public String getRecentDateFormatString() {
+ return recentDateFormat.toPattern();
+ }
+ /**
+ * @param recentDateFormat The recentDateFormat to set.
+ */
+ private void setRecentDateFormat(String format) {
+ if (format != null) {
+ this.recentDateFormat = new SimpleDateFormat(format);
+ this.recentDateFormat.setLenient(false);
+ }
+ }
+
+ /**
+ * @return returns an array of 12 strings representing the short
+ * month names used by this parse.
+ */
+ public String[] getShortMonths() {
+ return defaultDateFormat.getDateFormatSymbols().getShortMonths();
+ }
+
+
+ /**
+ * @return Returns the serverTimeZone used by this parser.
+ */
+ public TimeZone getServerTimeZone() {
+ return this.defaultDateFormat.getTimeZone();
+ }
+ /**
+ * sets a TimeZone represented by the supplied ID string into all
+ * of the parsers used by this server.
+ * @param serverTimeZone Time Id java.util.TimeZone id used by
+ * the ftp server. If null the client's local time zone is assumed.
+ */
+ private void setServerTimeZone(String serverTimeZoneId) {
+ TimeZone serverTimeZone = TimeZone.getDefault();
+ if (serverTimeZoneId != null) {
+ serverTimeZone = TimeZone.getTimeZone(serverTimeZoneId);
+ }
+ this.defaultDateFormat.setTimeZone(serverTimeZone);
+ if (this.recentDateFormat != null) {
+ this.recentDateFormat.setTimeZone(serverTimeZone);
+ }
+ }
+
+ /**
+ * Implementation of the {@link Configurable Configurable}
+ * interface. Configures this FTPTimestampParser
according
+ * to the following logic:
+ *
+ *
config
, use that to parse parse timestamps.config
, use the month names represented
+ * by that {@link FTPClientConfig#lookupDateFormatSymbols(String) language}
+ * to parse timestamps.
+ * Finally if a {@link org.apache.commons.net.ftp.FTPClientConfig#setServerTimeZoneId(String) serverTimeZoneId} + * has been supplied via the config, set that into all date formats that have + * been configured. + *
+ */ + public void configure(FTPClientConfig config) { + DateFormatSymbols dfs = null; + + String languageCode = config.getServerLanguageCode(); + String shortmonths = config.getShortMonthNames(); + if (shortmonths != null) { + dfs = FTPClientConfig.getDateFormatSymbols(shortmonths); + } else if (languageCode != null) { + dfs = FTPClientConfig.lookupDateFormatSymbols(languageCode); + } else { + dfs = FTPClientConfig.lookupDateFormatSymbols("en"); + } + + + String recentFormatString = config.getRecentDateFormatStr(); + if (recentFormatString == null) { + this.recentDateFormat = null; + } else { + this.recentDateFormat = new SimpleDateFormat(recentFormatString, dfs); + this.recentDateFormat.setLenient(false); + } + + String defaultFormatString = config.getDefaultDateFormatStr(); + if (defaultFormatString == null) { + throw new IllegalArgumentException("defaultFormatString cannot be null"); + } + this.defaultDateFormat = new SimpleDateFormat(defaultFormatString, dfs); + this.defaultDateFormat.setLenient(false); + + setServerTimeZone(config.getServerTimeZoneId()); + + this.lenientFutureDates = config.isLenientFutureDates(); + } + /** + * @return Returns the lenientFutureDates. + */ + boolean isLenientFutureDates() { + return lenientFutureDates; + } + /** + * @param lenientFutureDates The lenientFutureDates to set. + */ + void setLenientFutureDates(boolean lenientFutureDates) { + this.lenientFutureDates = lenientFutureDates; + } +} diff --git a/src/org/apache/commons/net/ftp/parser/MVSFTPEntryParser.java b/src/org/apache/commons/net/ftp/parser/MVSFTPEntryParser.java new file mode 100644 index 0000000..cd87e6e --- /dev/null +++ b/src/org/apache/commons/net/ftp/parser/MVSFTPEntryParser.java @@ -0,0 +1,495 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.net.ftp.parser; + +import java.text.ParseException; +import java.util.List; + +import org.apache.commons.net.ftp.FTPClientConfig; +import org.apache.commons.net.ftp.FTPFile; + +/** + * Implementation of FTPFileEntryParser and FTPFileListParser for IBM zOS/MVS + * Systems. + * + * @author Henrik Sorensen + * @author Jeff Nadler + * @author William Noto + * + * @version $Id: MVSFTPEntryParser.java 658520 2008-05-21 01:14:11Z sebb $ + * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for + * usage instructions) + */ +public class MVSFTPEntryParser extends ConfigurableFTPFileEntryParserImpl { + + static final int UNKNOWN_LIST_TYPE = -1; + static final int FILE_LIST_TYPE = 0; + static final int MEMBER_LIST_TYPE = 1; + static final int UNIX_LIST_TYPE = 2; + static final int JES_LEVEL_1_LIST_TYPE = 3; + static final int JES_LEVEL_2_LIST_TYPE = 4; + + private int isType = UNKNOWN_LIST_TYPE; + + /** + * Fallback parser for Unix-style listings + */ + private UnixFTPEntryParser unixFTPEntryParser; + + /** + * Dates are ignored for file lists, but are used for member lists where + * possible + */ + static final String DEFAULT_DATE_FORMAT = "yyyy/MM/dd HH:mm"; // 2001/09/18 + // 13:52 + + /** + * Matches these entries: Volume Unit Referred Ext Used Recfm Lrecl BlkSz + * Dsorg Dsname B10142 3390 2006/03/20 2 31 F 80 80 PS MDI.OKL.WORK + * + */ + static final String FILE_LIST_REGEX = "\\S+\\s+" + // volume + // ignored + "\\S+\\s+" + // unit - ignored + "\\S+\\s+" + // access date - ignored + "\\S+\\s+" + // extents -ignored + "\\S+\\s+" + // used - ignored + "[FV]\\S*\\s+" + // recfm - must start with F or V + "\\S+\\s+" + // logical record length -ignored + "\\S+\\s+" + // block size - ignored + "(PS|PO|PO-E)\\s+" + // Dataset organisation. Many exist + // but only support: PS, PO, PO-E + "(\\S+)\\s*"; // Dataset Name (file name) + + /** + * Matches these entries: Name VV.MM Created Changed Size Init Mod Id + * TBSHELF 01.03 2002/09/12 2002/10/11 09:37 11 11 0 KIL001 + */ + static final String MEMBER_LIST_REGEX = "(\\S+)\\s+" + // name + "\\S+\\s+" + // version, modification (ignored) + "\\S+\\s+" + // create date (ignored) + "(\\S+)\\s+" + // modification date + "(\\S+)\\s+" + // modification time + "\\S+\\s+" + // size in lines (ignored) + "\\S+\\s+" + // size in lines at creation(ignored) + "\\S+\\s+" + // lines modified (ignored) + "\\S+\\s*"; // id of user who modified (ignored) + + /** + * Matches these entries, note: no header: IBMUSER1 JOB01906 OUTPUT 3 Spool + * Files 012345678901234567890123456789012345678901234 1 2 3 4 + */ + static final String JES_LEVEL_1_LIST_REGEX = "(\\S+)\\s+" + // job + // name + // ignored + "(\\S+)\\s+" + // job number + "(\\S+)\\s+" + // job status (OUTPUT,INPUT,ACTIVE) + "(\\S+)\\s+" + // number of spool files + "(\\S+)\\s+" + // Text "Spool" ignored + "(\\S+)\\s*" // Text "Files" ignored + ; + + /** + * JES INTERFACE LEVEL 2 parser Matches these entries: JOBNAME JOBID OWNER + * STATUS CLASS IBMUSER1 JOB01906 IBMUSER OUTPUT A RC=0000 3 spool files + * IBMUSER TSU01830 IBMUSER OUTPUT TSU ABEND=522 3 spool files + * 012345678901234567890123456789012345678901234 1 2 3 4 + * 012345678901234567890123456789012345678901234567890 + */ + + static final String JES_LEVEL_2_LIST_REGEX = "(\\S+)\\s+" + // job + // name + // ignored + "(\\S+)\\s+" + // job number + "(\\S+)\\s+" + // owner ignored + "(\\S+)\\s+" + // job status (OUTPUT,INPUT,ACTIVE) ignored + "(\\S+)\\s+" + // job class ignored + "(\\S+).*" // rest ignored + ; + + /* + * --------------------------------------------------------------------- + * Very brief and incomplete description of the zOS/MVS-filesystem. (Note: + * "zOS" is the operating system on the mainframe, and is the new name for + * MVS) + * + * The filesystem on the mainframe does not have hierarchal structure as for + * example the unix filesystem. For a more comprehensive description, please + * refer to the IBM manuals + * + * @LINK: + * http://publibfp.boulder.ibm.com/cgi-bin/bookmgr/BOOKS/dgt2d440/CONTENTS + * + * + * Dataset names ============= + * + * A dataset name consist of a number of qualifiers separated by '.', each + * qualifier can be at most 8 characters, and the total length of a dataset + * can be max 44 characters including the dots. + * + * + * Dataset organisation ==================== + * + * A dataset represents a piece of storage allocated on one or more disks. + * The structure of the storage is described with the field dataset + * organinsation (DSORG). There are a number of dataset organisations, but + * only two are usable for FTP transfer. + * + * DSORG: PS: sequential, or flat file PO: partitioned dataset PO-E: + * extended partitioned dataset + * + * The PS file is just a flat file, as you would find it on the unix file + * system. + * + * The PO and PO-E files, can be compared to a single level directory + * structure. A PO file consist of a number of dataset members, or files if + * you will. It is possible to CD into the file, and to retrieve the + * individual members. + * + * + * Dataset record format ===================== + * + * The physical layout of the dataset is described on the dataset itself. + * There are a number of record formats (RECFM), but just a few is relavant + * for the FTP transfer. + * + * Any one beginning with either F or V can safely used by FTP transfer. All + * others should only be used with great care, so this version will just + * ignore the other record formats. F means a fixed number of records per + * allocated storage, and V means a variable number of records. + * + * + * Other notes =========== + * + * The file system supports automatically backup and retrieval of datasets. + * If a file is backed up, the ftp LIST command will return: ARCIVE Not + * Direct Access Device KJ.IOP998.ERROR.PL.UNITTEST + * + * + * Implementation notes ==================== + * + * Only datasets that have dsorg PS, PO or PO-E and have recfm beginning + * with F or V, is fully parsed. + * + * The following fields in FTPFile is used: FTPFile.Rawlisting: Always set. + * FTPFile.Type: DIRECTORY_TYPE or FILE_TYPE or UNKNOWN FTPFile.Name: name + * FTPFile.Timestamp: change time or null + * + * + * + * Additional information ====================== + * + * The MVS ftp server supports a number of features via the FTP interface. + * The features are controlled with the FTP command quote site filetype= FTPFile
instance.
+ * If the file listing line doesn't describe a file, then
+ * null
is returned. Otherwise a FTPFile
+ * instance representing the file is returned.
+ *
+ * @param entry
+ * A line of text from the file listing
+ * @return An FTPFile instance corresponding to the supplied entry
+ */
+ public FTPFile parseFTPEntry(String entry) {
+ boolean isParsed = false;
+ FTPFile f = new FTPFile();
+
+ if (isType == FILE_LIST_TYPE)
+ isParsed = parseFileList(f, entry);
+ else if (isType == MEMBER_LIST_TYPE) {
+ isParsed = parseMemberList(f, entry);
+ if (!isParsed)
+ isParsed = parseSimpleEntry(f, entry);
+ } else if (isType == UNIX_LIST_TYPE) {
+ isParsed = parseUnixList(f, entry);
+ } else if (isType == JES_LEVEL_1_LIST_TYPE) {
+ isParsed = parseJeslevel1List(f, entry);
+ } else if (isType == JES_LEVEL_2_LIST_TYPE) {
+ isParsed = parseJeslevel2List(f, entry);
+ }
+
+ if (!isParsed)
+ f = null;
+
+ return f;
+ }
+
+ /**
+ * Parse entries representing a dataset list. Only datasets with DSORG PS or
+ * PO or PO-E and with RECFM F* or V* will be parsed.
+ *
+ * Format of ZOS/MVS file list: 1 2 3 4 5 6 7 8 9 10 Volume Unit Referred
+ * Ext Used Recfm Lrecl BlkSz Dsorg Dsname B10142 3390 2006/03/20 2 31 F 80
+ * 80 PS MDI.OKL.WORK ARCIVE Not Direct Access Device
+ * KJ.IOP998.ERROR.PL.UNITTEST B1N231 3390 2006/03/20 1 15 VB 256 27998 PO
+ * PLU B1N231 3390 2006/03/20 1 15 VB 256 27998 PO-E PLB
+ *
+ * ----------------------------------- Group within Regex [1] Volume [2]
+ * Unit [3] Referred [4] Ext: number of extents [5] Used [6] Recfm: Record
+ * format [7] Lrecl: Logical record length [8] BlkSz: Block size [9] Dsorg:
+ * Dataset organisation. Many exists but only support: PS, PO, PO-E [10]
+ * Dsname: Dataset name
+ *
+ * Note: When volume is ARCIVE, it means the dataset is stored somewhere in
+ * a tape archive. These entries is currently not supported by this parser.
+ * A null value is returned.
+ *
+ * @param file
+ * will be updated with Name, Type, Timestamp if parsed.
+ * @param entry zosDirectoryEntry
+ * @return true: entry was parsed, false: entry was not parsed.
+ */
+ private boolean parseFileList(FTPFile file, String entry) {
+ if (matches(entry)) {
+ file.setRawListing(entry);
+ String name = group(2);
+ String dsorg = group(1);
+ file.setName(name);
+
+ // DSORG
+ if ("PS".equals(dsorg)) {
+ file.setType(FTPFile.FILE_TYPE);
+ }
+ else if ("PO".equals(dsorg) || "PO-E".equals(dsorg)) {
+ // regex already ruled out anything other than PO or PO-E
+ file.setType(FTPFile.DIRECTORY_TYPE);
+ }
+ else {
+ return false;
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Parse entries within a partitioned dataset.
+ *
+ * Format of a memberlist within a PDS: 1 2 3 4 5 6 7 8 9 Name VV.MM Created
+ * Changed Size Init Mod Id TBSHELF 01.03 2002/09/12 2002/10/11 09:37 11 11
+ * 0 KIL001 TBTOOL 01.12 2002/09/12 2004/11/26 19:54 51 28 0 KIL001
+ *
+ * ------------------------------------------- [1] Name [2] VV.MM: Version .
+ * modification [3] Created: yyyy / MM / dd [4,5] Changed: yyyy / MM / dd
+ * HH:mm [6] Size: number of lines [7] Init: number of lines when first
+ * created [8] Mod: number of modified lines a last save [9] Id: User id for
+ * last update
+ *
+ *
+ * @param file
+ * will be updated with Name, Type and Timestamp if parsed.
+ * @param entry zosDirectoryEntry
+ * @return true: entry was parsed, false: entry was not parsed.
+ */
+ private boolean parseMemberList(FTPFile file, String entry) {
+ if (matches(entry)) {
+ file.setRawListing(entry);
+ String name = group(1);
+ String datestr = group(2) + " " + group(3);
+ file.setName(name);
+ file.setType(FTPFile.FILE_TYPE);
+ try {
+ file.setTimestamp(super.parseTimestamp(datestr));
+ } catch (ParseException e) {
+ e.printStackTrace();
+ // just ignore parsing errors.
+ // TODO check this is ok
+ return false; // this is a parsing failure too.
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Assigns the name to the first word of the entry. Only to be used from a
+ * safe context, for example from a memberlist, where the regex for some
+ * reason fails. Then just assign the name field of FTPFile.
+ *
+ * @param file
+ * @param entry
+ * @return
+ */
+ private boolean parseSimpleEntry(FTPFile file, String entry) {
+ if (entry != null && entry.length() > 0) {
+ file.setRawListing(entry);
+ String name = entry.split(" ")[0];
+ file.setName(name);
+ file.setType(FTPFile.FILE_TYPE);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Parse the entry as a standard unix file. Using the UnixFTPEntryParser.
+ *
+ * @param file
+ * @param entry
+ * @return true: entry is parsed, false: entry could not be parsed.
+ */
+ private boolean parseUnixList(FTPFile file, String entry) {
+ file = unixFTPEntryParser.parseFTPEntry(entry);
+ if (file == null)
+ return false;
+ return true;
+ }
+
+ /**
+ * Matches these entries, note: no header: [1] [2] [3] [4] [5] IBMUSER1
+ * JOB01906 OUTPUT 3 Spool Files
+ * 012345678901234567890123456789012345678901234 1 2 3 4
+ * ------------------------------------------- Group in regex [1] Job name
+ * [2] Job number [3] Job status (INPUT,ACTIVE,OUTPUT) [4] Number of sysout
+ * files [5] The string "Spool Files"
+ *
+ *
+ * @param file
+ * will be updated with Name, Type and Timestamp if parsed.
+ * @param entry zosDirectoryEntry
+ * @return true: entry was parsed, false: entry was not parsed.
+ */
+ private boolean parseJeslevel1List(FTPFile file, String entry) {
+ if (matches(entry)) {
+ if (group(3).equalsIgnoreCase("OUTPUT")) {
+ file.setRawListing(entry);
+ String name = group(2); /* Job Number, used by GET */
+ file.setName(name);
+ file.setType(FTPFile.FILE_TYPE);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Matches these entries, note: no header: [1] [2] [3] [4] [5] JOBNAME JOBID
+ * OWNER STATUS CLASS IBMUSER1 JOB01906 IBMUSER OUTPUT A RC=0000 3 spool
+ * files IBMUSER TSU01830 IBMUSER OUTPUT TSU ABEND=522 3 spool files
+ * 012345678901234567890123456789012345678901234 1 2 3 4
+ * ------------------------------------------- Group in regex [1] Job name
+ * [2] Job number [3] Owner [4] Job status (INPUT,ACTIVE,OUTPUT) [5] Job
+ * Class [6] The rest
+ *
+ *
+ * @param file
+ * will be updated with Name, Type and Timestamp if parsed.
+ * @param entry zosDirectoryEntry
+ * @return true: entry was parsed, false: entry was not parsed.
+ */
+ private boolean parseJeslevel2List(FTPFile file, String entry) {
+ if (matches(entry)) {
+ if (group(4).equalsIgnoreCase("OUTPUT")) {
+ file.setRawListing(entry);
+ String name = group(2); /* Job Number, used by GET */
+ file.setName(name);
+ file.setType(FTPFile.FILE_TYPE);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * preParse is called as part of the interface. Per definition is is called
+ * before the parsing takes place. Three kind of lists is recognize:
+ * z/OS-MVS File lists z/OS-MVS Member lists unix file lists
+ * @since 2.0
+ */
+ @Override
+ public ListREGEX
is not a valid regular expression.
+ */
+ public NTFTPEntryParser()
+ {
+ this(null);
+ }
+
+ /**
+ * This constructor allows the creation of an NTFTPEntryParser object
+ * with something other than the default configuration.
+ *
+ * @param config The {@link FTPClientConfig configuration} object used to
+ * configure this parser.
+ * @exception IllegalArgumentException
+ * Thrown if the regular expression is unparseable. Should not be seen
+ * under normal conditions. It it is seen, this is a sign that
+ * REGEX
is not a valid regular expression.
+ * @since 1.4
+ */
+ public NTFTPEntryParser(FTPClientConfig config)
+ {
+ super(REGEX);
+ configure(config);
+ }
+
+ /**
+ * Parses a line of an NT FTP server file listing and converts it into a
+ * usable format in the form of an FTPFile
instance. If the
+ * file listing line doesn't describe a file, null
is
+ * returned, otherwise a FTPFile
instance representing the
+ * files in the directory is returned.
+ * + * @param entry A line of text from the file listing + * @return An FTPFile instance corresponding to the supplied entry + */ + public FTPFile parseFTPEntry(String entry) + { + FTPFile f = new FTPFile(); + f.setRawListing(entry); + + if (matches(entry)) + { + String datestr = group(1)+" "+group(2); + String dirString = group(3); + String size = group(4); + String name = group(5); + try + { + f.setTimestamp(super.parseTimestamp(datestr)); + } + catch (ParseException e) + { + // intentionally do nothing + } + + if (null == name || name.equals(".") || name.equals("..")) + { + return (null); + } + f.setName(name); + + + if ("
REGEX
is not a valid regular expression.
+ */
+ public NetwareFTPEntryParser() {
+ this(null);
+ }
+
+ /**
+ * This constructor allows the creation of an NetwareFTPEntryParser object
+ * with something other than the default configuration.
+ *
+ * @param config The {@link FTPClientConfig configuration} object used to
+ * configure this parser.
+ * @exception IllegalArgumentException
+ * Thrown if the regular expression is unparseable. Should not be seen
+ * under normal conditions. It it is seen, this is a sign that
+ * REGEX
is not a valid regular expression.
+ * @since 1.4
+ */
+ public NetwareFTPEntryParser(FTPClientConfig config) {
+ super(REGEX);
+ configure(config);
+ }
+
+ /**
+ * Parses a line of an NetwareFTP server file listing and converts it into a
+ * usable format in the form of an FTPFile
instance. If the
+ * file listing line doesn't describe a file, null
is
+ * returned, otherwise a FTPFile
instance representing the
+ * files in the directory is returned.
+ * + *
+ * Netware file permissions are in the following format: RWCEAFMS, and are explained as follows: + *
REGEX
is not a valid regular expression.
+ */
+ public OS2FTPEntryParser()
+ {
+ this(null);
+ }
+
+ /**
+ * This constructor allows the creation of an OS2FTPEntryParser object
+ * with something other than the default configuration.
+ *
+ * @param config The {@link FTPClientConfig configuration} object used to
+ * configure this parser.
+ * @exception IllegalArgumentException
+ * Thrown if the regular expression is unparseable. Should not be seen
+ * under normal conditions. It it is seen, this is a sign that
+ * REGEX
is not a valid regular expression.
+ * @since 1.4
+ */
+ public OS2FTPEntryParser(FTPClientConfig config)
+ {
+ super(REGEX);
+ configure(config);
+ }
+
+ /**
+ * Parses a line of an OS2 FTP server file listing and converts it into a
+ * usable format in the form of an FTPFile
instance. If the
+ * file listing line doesn't describe a file, null
is
+ * returned, otherwise a FTPFile
instance representing the
+ * files in the directory is returned.
+ *
+ * @param entry A line of text from the file listing
+ * @return An FTPFile instance corresponding to the supplied entry
+ */
+ public FTPFile parseFTPEntry(String entry)
+ {
+
+ FTPFile f = new FTPFile();
+ if (matches(entry))
+ {
+ String size = group(1);
+ String attrib = group(2);
+ String dirString = group(3);
+ String datestr = group(4)+" "+group(5);
+ String name = group(6);
+ try
+ {
+ f.setTimestamp(super.parseTimestamp(datestr));
+ }
+ catch (ParseException e)
+ {
+ // intentionally do nothing
+ }
+
+
+ //is it a DIR or a file
+ if (dirString.trim().equals("DIR") || attrib.trim().equals("DIR"))
+ {
+ f.setType(FTPFile.DIRECTORY_TYPE);
+ }
+ else
+ {
+ f.setType(FTPFile.FILE_TYPE);
+ }
+
+
+ //set the name
+ f.setName(name.trim());
+
+ //set the size
+ f.setSize(Long.parseLong(size.trim()));
+
+ return (f);
+ }
+ return null;
+
+ }
+
+ /**
+ * Defines a default configuration to be used when this class is
+ * instantiated without a {@link FTPClientConfig FTPClientConfig}
+ * parameter being specified.
+ * @return the default configuration for this parser.
+ */
+ @Override
+ protected FTPClientConfig getDefaultConfiguration() {
+ return new FTPClientConfig(
+ FTPClientConfig.SYST_OS2,
+ DEFAULT_DATE_FORMAT,
+ null, null, null, null);
+ }
+
+}
diff --git a/src/org/apache/commons/net/ftp/parser/OS400FTPEntryParser.java b/src/org/apache/commons/net/ftp/parser/OS400FTPEntryParser.java
new file mode 100644
index 0000000..66c370b
--- /dev/null
+++ b/src/org/apache/commons/net/ftp/parser/OS400FTPEntryParser.java
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp.parser;
+
+import java.text.ParseException;
+
+import org.apache.commons.net.ftp.FTPClientConfig;
+import org.apache.commons.net.ftp.FTPFile;
+
+/**
+ * @version $Id: OS400FTPEntryParser.java 658518 2008-05-21 01:04:30Z sebb $
+ */
+
+public class OS400FTPEntryParser extends ConfigurableFTPFileEntryParserImpl
+{
+ private static final String DEFAULT_DATE_FORMAT
+ = "yy/MM/dd HH:mm:ss"; //01/11/09 12:30:24
+
+
+
+ private static final String REGEX =
+ "(\\S+)\\s+" // user
+ + "(\\d+)\\s+" // size
+ + "(\\S+)\\s+(\\S+)\\s+" // date stuff
+ + "(\\*\\S+)\\s+" // *STMF/*DIR
+ + "(\\S+/?)\\s*"; // filename
+
+
+ /**
+ * The default constructor for a OS400FTPEntryParser object.
+ *
+ * @exception IllegalArgumentException
+ * Thrown if the regular expression is unparseable. Should not be seen
+ * under normal conditions. It it is seen, this is a sign that
+ *
+ * For now end users may specify this format only via
+ *
+ * @param entry A line of text from the file listing
+ * @return An FTPFile instance corresponding to the supplied entry
+ */
+ public FTPFile parseFTPEntry(String entry) {
+ FTPFile file = new FTPFile();
+ file.setRawListing(entry);
+ int type;
+ boolean isDevice = false;
+
+ if (matches(entry))
+ {
+ String typeStr = group(1);
+ String hardLinkCount = group(15);
+ String usr = group(16);
+ String grp = group(17);
+ String filesize = group(18);
+ String datestr = group(19) + " " + group(20);
+ String name = group(21);
+ String endtoken = group(22);
+
+ try
+ {
+ file.setTimestamp(super.parseTimestamp(datestr));
+ }
+ catch (ParseException e)
+ {
+ // intentionally do nothing
+ }
+
+
+ // bcdlfmpSs-
+ switch (typeStr.charAt(0))
+ {
+ case 'd':
+ type = FTPFile.DIRECTORY_TYPE;
+ break;
+ case 'e':
+ type = FTPFile.SYMBOLIC_LINK_TYPE;
+ break;
+ case 'l':
+ type = FTPFile.SYMBOLIC_LINK_TYPE;
+ break;
+ case 'b':
+ case 'c':
+ isDevice = true;
+ // break; - fall through
+ case 'f':
+ case '-':
+ type = FTPFile.FILE_TYPE;
+ break;
+ default:
+ type = FTPFile.UNKNOWN_TYPE;
+ }
+
+ file.setType(type);
+
+ int g = 4;
+ for (int access = 0; access < 3; access++, g += 4)
+ {
+ // Use != '-' to avoid having to check for suid and sticky bits
+ file.setPermission(access, FTPFile.READ_PERMISSION,
+ (!group(g).equals("-")));
+ file.setPermission(access, FTPFile.WRITE_PERMISSION,
+ (!group(g + 1).equals("-")));
+
+ String execPerm = group(g + 2);
+ if (!execPerm.equals("-") && !Character.isUpperCase(execPerm.charAt(0)))
+ {
+ file.setPermission(access, FTPFile.EXECUTE_PERMISSION, true);
+ }
+ else
+ {
+ file.setPermission(access, FTPFile.EXECUTE_PERMISSION, false);
+ }
+ }
+
+ if (!isDevice)
+ {
+ try
+ {
+ file.setHardLinkCount(Integer.parseInt(hardLinkCount));
+ }
+ catch (NumberFormatException e)
+ {
+ // intentionally do nothing
+ }
+ }
+
+ file.setUser(usr);
+ file.setGroup(grp);
+
+ try
+ {
+ file.setSize(Long.parseLong(filesize));
+ }
+ catch (NumberFormatException e)
+ {
+ // intentionally do nothing
+ }
+
+ if (null == endtoken)
+ {
+ file.setName(name);
+ }
+ else
+ {
+ // oddball cases like symbolic links, file names
+ // with spaces in them.
+ name += endtoken;
+ if (type == FTPFile.SYMBOLIC_LINK_TYPE)
+ {
+
+ int end = name.indexOf(" -> ");
+ // Give up if no link indicator is present
+ if (end == -1)
+ {
+ file.setName(name);
+ }
+ else
+ {
+ file.setName(name.substring(0, end));
+ file.setLink(name.substring(end + 4));
+ }
+
+ }
+ else
+ {
+ file.setName(name);
+ }
+ }
+ return file;
+ }
+ return null;
+ }
+
+ /**
+ * Defines a default configuration to be used when this class is
+ * instantiated without a {@link FTPClientConfig FTPClientConfig}
+ * parameter being specified.
+ * @return the default configuration for this parser.
+ */
+ @Override
+ protected FTPClientConfig getDefaultConfiguration() {
+ return new FTPClientConfig(
+ FTPClientConfig.SYST_UNIX,
+ DEFAULT_DATE_FORMAT,
+ DEFAULT_RECENT_DATE_FORMAT,
+ null, null, null);
+ }
+
+}
diff --git a/src/org/apache/commons/net/ftp/parser/VMSFTPEntryParser.java b/src/org/apache/commons/net/ftp/parser/VMSFTPEntryParser.java
new file mode 100644
index 0000000..1e55ede
--- /dev/null
+++ b/src/org/apache/commons/net/ftp/parser/VMSFTPEntryParser.java
@@ -0,0 +1,288 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp.parser;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.ParseException;
+import java.util.StringTokenizer;
+
+import org.apache.commons.net.ftp.FTPClientConfig;
+import org.apache.commons.net.ftp.FTPFile;
+import org.apache.commons.net.ftp.FTPListParseEngine;
+
+/**
+ * Implementation FTPFileEntryParser and FTPFileListParser for VMS Systems.
+ * This is a sample of VMS LIST output
+ *
+ * "1-JUN.LIS;1 9/9 2-JUN-1998 07:32:04 [GROUP,OWNER] (RWED,RWED,RWED,RE)",
+ * "1-JUN.LIS;2 9/9 2-JUN-1998 07:32:04 [GROUP,OWNER] (RWED,RWED,RWED,RE)",
+ * "DATA.DIR;1 1/9 2-JUN-1998 07:32:04 [GROUP,OWNER] (RWED,RWED,RWED,RE)",
+ *
+ * Note: VMSFTPEntryParser can only be instantiated through the
+ * DefaultFTPParserFactory by classname. It will not be chosen
+ * by the autodetection scheme.
+ *
+ *
+ *
+ * @author Winston Ojeda
+ * @author Steve Cohen
+ * @author Stephane ESTE-GRACIAS
+ * @version $Id: VMSFTPEntryParser.java 658518 2008-05-21 01:04:30Z sebb $
+ *
+ * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions)
+ * @see org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory
+ */
+public class VMSFTPEntryParser extends ConfigurableFTPFileEntryParserImpl
+{
+
+ private static final String DEFAULT_DATE_FORMAT
+ = "d-MMM-yyyy HH:mm:ss"; //9-NOV-2001 12:30:24
+
+ /**
+ * this is the regular expression used by this parser.
+ */
+ private static final String REGEX =
+ "(.*;[0-9]+)\\s*" //1 file and version
+ + "(\\d+)/\\d+\\s*" //2 size/allocated
+ +"(\\S+)\\s+(\\S+)\\s+" //3+4 date and time
+ + "\\[(([0-9$A-Za-z_]+)|([0-9$A-Za-z_]+),([0-9$a-zA-Z_]+))\\]?\\s*" //5(6,7,8) owner
+ + "\\([a-zA-Z]*,([a-zA-Z]*),([a-zA-Z]*),([a-zA-Z]*)\\)"; //9,10,11 Permissions (O,G,W)
+ // TODO - perhaps restrict permissions to [RWED]* ?
+
+
+
+ /**
+ * Constructor for a VMSFTPEntryParser object.
+ *
+ * @exception IllegalArgumentException
+ * Thrown if the regular expression is unparseable. Should not be seen
+ * under normal conditions. It it is seen, this is a sign that
+ *
+ * @param listStream The InputStream from which the file list should be
+ * read.
+ * @return The list of file information contained in the given path. null
+ * if the list could not be obtained or if there are no files in
+ * the directory.
+ * @exception IOException If an I/O error occurs reading the listStream.
+ ***/
+ public FTPFile[] parseFileList(InputStream listStream) throws IOException {
+ FTPListParseEngine engine = new FTPListParseEngine(this);
+ engine.readServerList(listStream);
+ return engine.getFiles();
+ }
+
+
+
+ /**
+ * Parses a line of a VMS FTP server file listing and converts it into a
+ * usable format in the form of an
+ * @param entry A line of text from the file listing
+ * @return An FTPFile instance corresponding to the supplied entry
+ */
+ public FTPFile parseFTPEntry(String entry)
+ {
+ //one block in VMS equals 512 bytes
+ long longBlock = 512;
+
+ if (matches(entry))
+ {
+ FTPFile f = new FTPFile();
+ f.setRawListing(entry);
+ String name = group(1);
+ String size = group(2);
+ String datestr = group(3)+" "+group(4);
+ String owner = group(5);
+ String permissions[] = new String[3];
+ permissions[0]= group(9);
+ permissions[1]= group(10);
+ permissions[2]= group(11);
+ try
+ {
+ f.setTimestamp(super.parseTimestamp(datestr));
+ }
+ catch (ParseException e)
+ {
+ // intentionally do nothing
+ }
+
+
+ String grp;
+ String user;
+ StringTokenizer t = new StringTokenizer(owner, ",");
+ switch (t.countTokens()) {
+ case 1:
+ grp = null;
+ user = t.nextToken();
+ break;
+ case 2:
+ grp = t.nextToken();
+ user = t.nextToken();
+ break;
+ default:
+ grp = null;
+ user = null;
+ }
+
+ if (name.lastIndexOf(".DIR") != -1)
+ {
+ f.setType(FTPFile.DIRECTORY_TYPE);
+ }
+ else
+ {
+ f.setType(FTPFile.FILE_TYPE);
+ }
+ //set FTPFile name
+ //Check also for versions to be returned or not
+ if (isVersioning())
+ {
+ f.setName(name);
+ }
+ else
+ {
+ name = name.substring(0, name.lastIndexOf(";"));
+ f.setName(name);
+ }
+ //size is retreived in blocks and needs to be put in bytes
+ //for us humans and added to the FTPFile array
+ long sizeInBytes = Long.parseLong(size) * longBlock;
+ f.setSize(sizeInBytes);
+
+ f.setGroup(grp);
+ f.setUser(user);
+ //set group and owner
+
+ //Set file permission.
+ //VMS has (SYSTEM,OWNER,GROUP,WORLD) users that can contain
+ //R (read) W (write) E (execute) D (delete)
+
+ //iterate for OWNER GROUP WORLD permissions
+ for (int access = 0; access < 3; access++)
+ {
+ String permission = permissions[access];
+
+ f.setPermission(access, FTPFile.READ_PERMISSION, permission.indexOf('R')>=0);
+ f.setPermission(access, FTPFile.WRITE_PERMISSION, permission.indexOf('W')>=0);
+ f.setPermission(access, FTPFile.EXECUTE_PERMISSION, permission.indexOf('E')>=0);
+ }
+
+ return f;
+ }
+ return null;
+ }
+
+
+ /**
+ * Reads the next entry using the supplied BufferedReader object up to
+ * whatever delemits one entry from the next. This parser cannot use
+ * the default implementation of simply calling BufferedReader.readLine(),
+ * because one entry may span multiple lines.
+ *
+ * @param reader The BufferedReader object from which entries are to be
+ * read.
+ *
+ * @return A string representing the next ftp entry or null if none found.
+ * @exception IOException thrown on any IO Error reading from the reader.
+ */
+ @Override
+ public String readNextEntry(BufferedReader reader) throws IOException
+ {
+ String line = reader.readLine();
+ StringBuffer entry = new StringBuffer();
+ while (line != null)
+ {
+ if (line.startsWith("Directory") || line.startsWith("Total")) {
+ line = reader.readLine();
+ continue;
+ }
+
+ entry.append(line);
+ if (line.trim().endsWith(")"))
+ {
+ break;
+ }
+ line = reader.readLine();
+ }
+ return (entry.length() == 0 ? null : entry.toString());
+ }
+
+ protected boolean isVersioning() {
+ return false;
+ }
+
+ /**
+ * Defines a default configuration to be used when this class is
+ * instantiated without a {@link FTPClientConfig FTPClientConfig}
+ * parameter being specified.
+ * @return the default configuration for this parser.
+ */
+ @Override
+ protected FTPClientConfig getDefaultConfiguration() {
+ return new FTPClientConfig(
+ FTPClientConfig.SYST_VMS,
+ DEFAULT_DATE_FORMAT,
+ null, null, null, null);
+ }
+
+
+}
+
+/* Emacs configuration
+ * Local variables: **
+ * mode: java **
+ * c-basic-offset: 4 **
+ * indent-tabs-mode: nil **
+ * End: **
+ */
diff --git a/src/org/apache/commons/net/ftp/parser/VMSVersioningFTPEntryParser.java b/src/org/apache/commons/net/ftp/parser/VMSVersioningFTPEntryParser.java
new file mode 100644
index 0000000..cb25709
--- /dev/null
+++ b/src/org/apache/commons/net/ftp/parser/VMSVersioningFTPEntryParser.java
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp.parser;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.regex.MatchResult;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import org.apache.commons.net.ftp.FTPClientConfig;
+
+/**
+ * Special implementation VMSFTPEntryParser with versioning turned on.
+ * This parser removes all duplicates and only leaves the version with the highest
+ * version number for each filename.
+ *
+ * This is a sample of VMS LIST output
+ *
+ * "1-JUN.LIS;1 9/9 2-JUN-1998 07:32:04 [GROUP,OWNER] (RWED,RWED,RWED,RE)",
+ * "1-JUN.LIS;2 9/9 2-JUN-1998 07:32:04 [GROUP,OWNER] (RWED,RWED,RWED,RE)",
+ * "DATA.DIR;1 1/9 2-JUN-1998 07:32:04 [GROUP,OWNER] (RWED,RWED,RWED,RE)",
+ *
+ *
+ * @author Winston Ojeda
+ * @author Stephane ESTE-GRACIAS
+ * @version $Id: VMSVersioningFTPEntryParser.java 636854 2008-03-13 19:55:01Z sebb $
+ *
+ * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions)
+ */
+public class VMSVersioningFTPEntryParser extends VMSFTPEntryParser
+{
+
+ private Matcher _preparse_matcher_;
+ private Pattern _preparse_pattern_;
+ private static final String PRE_PARSE_REGEX =
+ "(.*);([0-9]+)\\s*.*";
+
+ /**
+ * Constructor for a VMSFTPEntryParser object. Sets the versioning member
+ * to the supplied value.
+ *
+ * @exception IllegalArgumentException
+ * Thrown if the regular expression is unparseable. Should not be seen
+ * under normal conditions. It it is seen, this is a sign that
+ * REGEX
is not a valid regular expression.
+ */
+ public OS400FTPEntryParser()
+ {
+ this(null);
+ }
+
+ /**
+ * This constructor allows the creation of an OS400FTPEntryParser object
+ * with something other than the default configuration.
+ *
+ * @param config The {@link FTPClientConfig configuration} object used to
+ * configure this parser.
+ * @exception IllegalArgumentException
+ * Thrown if the regular expression is unparseable. Should not be seen
+ * under normal conditions. It it is seen, this is a sign that
+ * REGEX
is not a valid regular expression.
+ * @since 1.4
+ */
+ public OS400FTPEntryParser(FTPClientConfig config)
+ {
+ super(REGEX);
+ configure(config);
+ }
+
+
+ public FTPFile parseFTPEntry(String entry)
+ {
+
+ FTPFile file = new FTPFile();
+ file.setRawListing(entry);
+ int type;
+
+ if (matches(entry))
+ {
+ String usr = group(1);
+ String filesize = group(2);
+ String datestr = group(3)+" "+group(4);
+ String typeStr = group(5);
+ String name = group(6);
+
+ try
+ {
+ file.setTimestamp(super.parseTimestamp(datestr));
+ }
+ catch (ParseException e)
+ {
+ // intentionally do nothing
+ }
+
+
+ if (typeStr.equalsIgnoreCase("*STMF"))
+ {
+ type = FTPFile.FILE_TYPE;
+ }
+ else if (typeStr.equalsIgnoreCase("*DIR"))
+ {
+ type = FTPFile.DIRECTORY_TYPE;
+ }
+ else
+ {
+ type = FTPFile.UNKNOWN_TYPE;
+ }
+
+ file.setType(type);
+
+ file.setUser(usr);
+
+ try
+ {
+ file.setSize(Long.parseLong(filesize));
+ }
+ catch (NumberFormatException e)
+ {
+ // intentionally do nothing
+ }
+
+ if (name.endsWith("/"))
+ {
+ name = name.substring(0, name.length() - 1);
+ }
+ int pos = name.lastIndexOf('/');
+ if (pos > -1)
+ {
+ name = name.substring(pos + 1);
+ }
+
+ file.setName(name);
+
+ return file;
+ }
+ return null;
+ }
+
+ /**
+ * Defines a default configuration to be used when this class is
+ * instantiated without a {@link FTPClientConfig FTPClientConfig}
+ * parameter being specified.
+ * @return the default configuration for this parser.
+ */
+ @Override
+ protected FTPClientConfig getDefaultConfiguration() {
+ return new FTPClientConfig(
+ FTPClientConfig.SYST_OS400,
+ DEFAULT_DATE_FORMAT,
+ null, null, null, null);
+ }
+
+}
diff --git a/src/org/apache/commons/net/ftp/parser/ParserInitializationException.java b/src/org/apache/commons/net/ftp/parser/ParserInitializationException.java
new file mode 100644
index 0000000..8af9261
--- /dev/null
+++ b/src/org/apache/commons/net/ftp/parser/ParserInitializationException.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp.parser;
+
+/**
+ * This class encapsulates all errors that may be thrown by
+ * the process of an FTPFileEntryParserFactory creating and
+ * instantiating an FTPFileEntryParser.
+ */
+public class ParserInitializationException extends RuntimeException {
+
+ /**
+ * Root exception that caused this to be thrown
+ */
+ private final Throwable rootCause;
+
+ /**
+ * Constucts a ParserInitializationException with just a message
+ *
+ * @param message Exception message
+ */
+ public ParserInitializationException(String message) {
+ super(message);
+ this.rootCause = null;
+ }
+
+ /**
+ * Constucts a ParserInitializationException with a message
+ * and a root cause.
+ *
+ * @param message Exception message
+ * @param rootCause root cause throwable that caused
+ * this to be thrown
+ */
+ public ParserInitializationException(String message, Throwable rootCause) {
+ super(message);
+ this.rootCause = rootCause;
+ }
+
+ /**
+ * returns the root cause of this exception or null
+ * if no root cause was specified.
+ *
+ * @return the root cause of this exception being thrown
+ */
+ public Throwable getRootCause() {
+ return this.rootCause;
+ }
+
+}
diff --git a/src/org/apache/commons/net/ftp/parser/RegexFTPFileEntryParserImpl.java b/src/org/apache/commons/net/ftp/parser/RegexFTPFileEntryParserImpl.java
new file mode 100644
index 0000000..5277444
--- /dev/null
+++ b/src/org/apache/commons/net/ftp/parser/RegexFTPFileEntryParserImpl.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.commons.net.ftp.parser;
+
+import java.util.regex.MatchResult;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import org.apache.commons.net.ftp.FTPFileEntryParserImpl;
+
+/**
+ * This abstract class implements both the older FTPFileListParser and
+ * newer FTPFileEntryParser interfaces with default functionality.
+ * All the classes in the parser subpackage inherit from this.
+ *
+ * This is the base for all regular based FTPFileEntryParser
+ *
+ * @author Steve Cohen matchnum'th
group of the internal
+ * match or null if this method is called without a match having
+ * been made.
+ */
+ public String group(int matchnum) {
+ if (this.result == null) {
+ return null;
+ }
+ return this.result.group(matchnum);
+ }
+
+ /**
+ * For debugging purposes - returns a string shows each match group by
+ * number.
+ *
+ * @return a string shows each match group by number.
+ */
+
+ public String getGroupsAsString() {
+ StringBuffer b = new StringBuffer();
+ for (int i = 1; i <= this.result.groupCount(); i++) {
+ b.append(i).append(") ").append(this.result.group(i)).append(
+ System.getProperty("line.separator"));
+ }
+ return b.toString();
+ }
+
+ /**
+ * Alter the current regular expression being utilised for entry parsing
+ * and create a new {@link Pattern} instance.
+ * @param regex The new regular expression
+ * @return
+ * @since 2.0
+ */
+ public boolean setRegex(String regex) {
+ try {
+ pattern = Pattern.compile(regex);
+ } catch (PatternSyntaxException pse) {
+ throw new IllegalArgumentException("Unparseable regex supplied: "
+ + regex);
+ }
+ return (pattern != null);
+ }
+
+}
diff --git a/src/org/apache/commons/net/ftp/parser/UnixFTPEntryParser.java b/src/org/apache/commons/net/ftp/parser/UnixFTPEntryParser.java
new file mode 100644
index 0000000..2a2a909
--- /dev/null
+++ b/src/org/apache/commons/net/ftp/parser/UnixFTPEntryParser.java
@@ -0,0 +1,295 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp.parser;
+import java.text.ParseException;
+
+import org.apache.commons.net.ftp.FTPClientConfig;
+import org.apache.commons.net.ftp.FTPFile;
+
+/**
+ * Implementation FTPFileEntryParser and FTPFileListParser for standard
+ * Unix Systems.
+ *
+ * This class is based on the logic of Daniel Savarese's
+ * DefaultFTPListParser, but adapted to use regular expressions and to fit the
+ * new FTPFileEntryParser interface.
+ * @version $Id: UnixFTPEntryParser.java 658518 2008-05-21 01:04:30Z sebb $
+ * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions)
+ */
+public class UnixFTPEntryParser extends ConfigurableFTPFileEntryParserImpl
+{
+
+ static final String DEFAULT_DATE_FORMAT
+ = "MMM d yyyy"; //Nov 9 2001
+
+ static final String DEFAULT_RECENT_DATE_FORMAT
+ = "MMM d HH:mm"; //Nov 9 20:06
+
+ static final String NUMERIC_DATE_FORMAT
+ = "yyyy-MM-dd HH:mm"; //2001-11-09 20:06
+
+ /**
+ * Some Linux distributions are now shipping an FTP server which formats
+ * file listing dates in an all-numeric format:
+ *
"yyyy-MM-dd HH:mm
.
+ * This is a very welcome development, and hopefully it will soon become
+ * the standard. However, since it is so new, for now, and possibly
+ * forever, we merely accomodate it, but do not make it the default.
+ * UnixFTPEntryParser(FTPClientConfig)
.
+ * Steve Cohen - 2005-04-17
+ */
+ public static final FTPClientConfig NUMERIC_DATE_CONFIG =
+ new FTPClientConfig(
+ FTPClientConfig.SYST_UNIX,
+ NUMERIC_DATE_FORMAT,
+ null, null, null, null);
+
+ /**
+ * this is the regular expression used by this parser.
+ *
+ * Permissions:
+ * r the file is readable
+ * w the file is writable
+ * x the file is executable
+ * - the indicated permission is not granted
+ * L mandatory locking occurs during access (the set-group-ID bit is
+ * on and the group execution bit is off)
+ * s the set-user-ID or set-group-ID bit is on, and the corresponding
+ * user or group execution bit is also on
+ * S undefined bit-state (the set-user-ID bit is on and the user
+ * execution bit is off)
+ * t the 1000 (octal) bit, or sticky bit, is on [see chmod(1)], and
+ * execution is on
+ * T the 1000 bit is turned on, and execution is off (undefined bit-
+ * state)
+ * e z/OS external link bit
+ */
+ private static final String REGEX =
+ "([bcdelfmpSs-])"
+ +"(((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-])))\\+?\\s+"
+ + "(\\d+)\\s+"
+ + "(?:(\\S+(?:\\s\\S+)*?)\\s+)?" // owner name (optional spaces)
+ + "(?:(\\S+(?:\\s\\S+)*)\\s+)?" // group name (optional spaces)
+ + "(\\d+(?:,\\s*\\d+)?)\\s+"
+
+ /*
+ numeric or standard format date
+ */
+ + "((?:\\d+[-/]\\d+[-/]\\d+)|(?:\\S+\\s+\\S+))\\s+"
+
+ /*
+ year (for non-recent standard format)
+ or time (for numeric or recent standard format
+ */
+ + "(\\d+(?::\\d+)?)\\s+"
+
+ + "(\\S*)(\\s*.*)";
+
+
+ /**
+ * The default constructor for a UnixFTPEntryParser object.
+ *
+ * @exception IllegalArgumentException
+ * Thrown if the regular expression is unparseable. Should not be seen
+ * under normal conditions. It it is seen, this is a sign that
+ * REGEX
is not a valid regular expression.
+ */
+ public UnixFTPEntryParser()
+ {
+ this(null);
+ }
+
+ /**
+ * This constructor allows the creation of a UnixFTPEntryParser object with
+ * something other than the default configuration.
+ *
+ * @param config The {@link FTPClientConfig configuration} object used to
+ * configure this parser.
+ * @exception IllegalArgumentException
+ * Thrown if the regular expression is unparseable. Should not be seen
+ * under normal conditions. It it is seen, this is a sign that
+ * REGEX
is not a valid regular expression.
+ * @since 1.4
+ */
+ public UnixFTPEntryParser(FTPClientConfig config)
+ {
+ super(REGEX);
+ configure(config);
+ }
+
+
+ /**
+ * Parses a line of a unix (standard) FTP server file listing and converts
+ * it into a usable format in the form of an FTPFile
+ * instance. If the file listing line doesn't describe a file,
+ * null
is returned, otherwise a FTPFile
+ * instance representing the files in the directory is returned.
+ * REGEX
is not a valid regular expression.
+ */
+ public VMSFTPEntryParser()
+ {
+ this(null);
+ }
+
+ /**
+ * This constructor allows the creation of a VMSFTPEntryParser object with
+ * something other than the default configuration.
+ *
+ * @param config The {@link FTPClientConfig configuration} object used to
+ * configure this parser.
+ * @exception IllegalArgumentException
+ * Thrown if the regular expression is unparseable. Should not be seen
+ * under normal conditions. It it is seen, this is a sign that
+ * REGEX
is not a valid regular expression.
+ * @since 1.4
+ */
+ public VMSFTPEntryParser(FTPClientConfig config)
+ {
+ super(REGEX);
+ configure(config);
+ }
+
+
+
+ /***
+ * Parses an FTP server file listing and converts it into a usable format
+ * in the form of an array of FTPFile
instances. If the
+ * file list contains no files, null
should be
+ * returned, otherwise an array of FTPFile
instances
+ * representing the files in the directory is returned.
+ * FTPFile
instance. If the
+ * file listing line doesn't describe a file, null
is
+ * returned, otherwise a FTPFile
instance representing the
+ * files in the directory is returned.
+ * REGEX
is not a valid regular expression.
+ */
+ public VMSVersioningFTPEntryParser()
+ {
+ this(null);
+ }
+
+ /**
+ * This constructor allows the creation of a VMSVersioningFTPEntryParser
+ * object with something other than the default configuration.
+ *
+ * @param config The {@link FTPClientConfig configuration} object used to
+ * configure this parser.
+ * @exception IllegalArgumentException
+ * Thrown if the regular expression is unparseable. Should not be seen
+ * under normal conditions. It it is seen, this is a sign that
+ * REGEX
is not a valid regular expression.
+ * @since 1.4
+ */
+ public VMSVersioningFTPEntryParser(FTPClientConfig config)
+ {
+ super();
+ configure(config);
+ try
+ {
+ //_preparse_matcher_ = new Perl5Matcher();
+ _preparse_pattern_ = Pattern.compile(PRE_PARSE_REGEX);
+ }
+ catch (PatternSyntaxException pse)
+ {
+ throw new IllegalArgumentException (
+ "Unparseable regex supplied: " + PRE_PARSE_REGEX);
+ }
+
+ }
+
+
+
+ private static class NameVersion {
+ String name;
+ int versionNumber;
+ NameVersion(String name, String vers) {
+ this.name = name;
+ this.versionNumber = Integer.parseInt(vers);
+ }
+ }
+
+ /**
+ * Implement hook provided for those implementers (such as
+ * VMSVersioningFTPEntryParser, and possibly others) which return
+ * multiple files with the same name to remove the duplicates ..
+ *
+ * @param original Original list
+ *
+ * @return Original list purged of duplicates
+ */
+ @Override
+ public List