From 063284837c8c366e5502b1b0264b8eb807b61732 Mon Sep 17 00:00:00 2001 From: Joe Robinson Date: Wed, 27 Oct 2010 14:21:09 +0100 Subject: Basic upload functionality to predifined location, with basic file browser --- .../net/ftp/parser/CompositeFileEntryParser.java | 72 +++ .../parser/ConfigurableFTPFileEntryParserImpl.java | 116 +++++ .../parser/DefaultFTPFileEntryParserFactory.java | 247 ++++++++++ .../ftp/parser/EnterpriseUnixFTPEntryParser.java | 166 +++++++ .../net/ftp/parser/FTPFileEntryParserFactory.java | 68 +++ .../commons/net/ftp/parser/FTPTimestampParser.java | 52 +++ .../net/ftp/parser/FTPTimestampParserImpl.java | 301 +++++++++++++ .../commons/net/ftp/parser/MVSFTPEntryParser.java | 495 +++++++++++++++++++++ .../commons/net/ftp/parser/NTFTPEntryParser.java | 147 ++++++ .../net/ftp/parser/NetwareFTPEntryParser.java | 176 ++++++++ .../commons/net/ftp/parser/OS2FTPEntryParser.java | 147 ++++++ .../net/ftp/parser/OS400FTPEntryParser.java | 158 +++++++ .../ftp/parser/ParserInitializationException.java | 65 +++ .../ftp/parser/RegexFTPFileEntryParserImpl.java | 155 +++++++ .../commons/net/ftp/parser/UnixFTPEntryParser.java | 295 ++++++++++++ .../commons/net/ftp/parser/VMSFTPEntryParser.java | 288 ++++++++++++ .../ftp/parser/VMSVersioningFTPEntryParser.java | 183 ++++++++ 17 files changed, 3131 insertions(+) create mode 100644 src/org/apache/commons/net/ftp/parser/CompositeFileEntryParser.java create mode 100644 src/org/apache/commons/net/ftp/parser/ConfigurableFTPFileEntryParserImpl.java create mode 100644 src/org/apache/commons/net/ftp/parser/DefaultFTPFileEntryParserFactory.java create mode 100644 src/org/apache/commons/net/ftp/parser/EnterpriseUnixFTPEntryParser.java create mode 100644 src/org/apache/commons/net/ftp/parser/FTPFileEntryParserFactory.java create mode 100644 src/org/apache/commons/net/ftp/parser/FTPTimestampParser.java create mode 100644 src/org/apache/commons/net/ftp/parser/FTPTimestampParserImpl.java create mode 100644 src/org/apache/commons/net/ftp/parser/MVSFTPEntryParser.java create mode 100644 src/org/apache/commons/net/ftp/parser/NTFTPEntryParser.java create mode 100644 src/org/apache/commons/net/ftp/parser/NetwareFTPEntryParser.java create mode 100644 src/org/apache/commons/net/ftp/parser/OS2FTPEntryParser.java create mode 100644 src/org/apache/commons/net/ftp/parser/OS400FTPEntryParser.java create mode 100644 src/org/apache/commons/net/ftp/parser/ParserInitializationException.java create mode 100644 src/org/apache/commons/net/ftp/parser/RegexFTPFileEntryParserImpl.java create mode 100644 src/org/apache/commons/net/ftp/parser/UnixFTPEntryParser.java create mode 100644 src/org/apache/commons/net/ftp/parser/VMSFTPEntryParser.java create mode 100644 src/org/apache/commons/net/ftp/parser/VMSVersioningFTPEntryParser.java (limited to 'src/org/apache/commons/net/ftp/parser') diff --git a/src/org/apache/commons/net/ftp/parser/CompositeFileEntryParser.java b/src/org/apache/commons/net/ftp/parser/CompositeFileEntryParser.java new file mode 100644 index 0000000..4977b3f --- /dev/null +++ b/src/org/apache/commons/net/ftp/parser/CompositeFileEntryParser.java @@ -0,0 +1,72 @@ +/* + * 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.FTPFile; +import org.apache.commons.net.ftp.FTPFileEntryParser; +import org.apache.commons.net.ftp.FTPFileEntryParserImpl; + +/** + * This implementation allows to pack some FileEntryParsers together + * and handle the case where to returned dirstyle isnt clearly defined. + * The matching parser will be cached. + * If the cached parser wont match due to the server changed the dirstyle, + * a new matching parser will be searched. + * + * @author Mario Ivankovits + */ +public class CompositeFileEntryParser extends FTPFileEntryParserImpl +{ + private final FTPFileEntryParser[] ftpFileEntryParsers; + private FTPFileEntryParser cachedFtpFileEntryParser; + + public CompositeFileEntryParser(FTPFileEntryParser[] ftpFileEntryParsers) + { + super(); + + this.cachedFtpFileEntryParser = null; + this.ftpFileEntryParsers = ftpFileEntryParsers; + } + + public FTPFile parseFTPEntry(String listEntry) + { + if (cachedFtpFileEntryParser != null) + { + FTPFile matched = cachedFtpFileEntryParser.parseFTPEntry(listEntry); + if (matched != null) + { + return matched; + } + } + else + { + for (int iterParser=0; iterParser < ftpFileEntryParsers.length; iterParser++) + { + FTPFileEntryParser ftpFileEntryParser = ftpFileEntryParsers[iterParser]; + + FTPFile matched = ftpFileEntryParser.parseFTPEntry(listEntry); + if (matched != null) + { + cachedFtpFileEntryParser = ftpFileEntryParser; + return matched; + } + } + } + return null; + } +} diff --git a/src/org/apache/commons/net/ftp/parser/ConfigurableFTPFileEntryParserImpl.java b/src/org/apache/commons/net/ftp/parser/ConfigurableFTPFileEntryParserImpl.java new file mode 100644 index 0000000..a3e832f --- /dev/null +++ b/src/org/apache/commons/net/ftp/parser/ConfigurableFTPFileEntryParserImpl.java @@ -0,0 +1,116 @@ +/* + * 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.Calendar; + +import org.apache.commons.net.ftp.Configurable; +import org.apache.commons.net.ftp.FTPClientConfig; + + +/** + *

+ * 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. + *

+ * @since 1.4 + */ +/** + * To change the template for this generated type comment go to + * Window - Preferences - Java - Code Style - Code Templates - Comments + */ +public abstract class ConfigurableFTPFileEntryParserImpl +extends RegexFTPFileEntryParserImpl +implements Configurable +{ + + private FTPTimestampParser timestampParser; + + /** + * Only constructor for this absract class. + * @param regex Regular expression used main parsing of the + * file listing. + */ + public ConfigurableFTPFileEntryParserImpl(String regex) + { + super(regex); + this.timestampParser = new FTPTimestampParserImpl(); + } + + /** + * This method is called by the concrete parsers to delegate + * timestamp parsing to the timestamp parser. + *

+ * @param timestampStr the timestamp string pulled from the + * file listing by the regular expression parser, to be submitted + * to the 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. + *

+ * @param config the configuration to be used to configure this parser. + * If it is null, a default configuration defined by + * each concrete subclass is used instead. + */ + public void configure(FTPClientConfig config) + { + if (this.timestampParser instanceof Configurable) { + FTPClientConfig defaultCfg = getDefaultConfiguration(); + if (config != null) { + if (null == config.getDefaultDateFormatStr()) { + config.setDefaultDateFormatStr(defaultCfg.getDefaultDateFormatStr()); + } + if (null == config.getRecentDateFormatStr()) { + config.setRecentDateFormatStr(defaultCfg.getRecentDateFormatStr()); + } + ((Configurable)this.timestampParser).configure(config); + } else { + ((Configurable)this.timestampParser).configure(defaultCfg); + } + } + } + + /** + * Each concrete subclass must define this member to create + * a default configuration to be used when that subclass is + * instantiated without a {@link FTPClientConfig FTPClientConfig} + * parameter being specified. + * @return the default configuration for the subclass. + */ + protected abstract FTPClientConfig getDefaultConfiguration(); +} diff --git a/src/org/apache/commons/net/ftp/parser/DefaultFTPFileEntryParserFactory.java b/src/org/apache/commons/net/ftp/parser/DefaultFTPFileEntryParserFactory.java new file mode 100644 index 0000000..c071a8e --- /dev/null +++ b/src/org/apache/commons/net/ftp/parser/DefaultFTPFileEntryParserFactory.java @@ -0,0 +1,247 @@ +/* + * 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.Configurable; +import org.apache.commons.net.ftp.FTPClientConfig; +import org.apache.commons.net.ftp.FTPFileEntryParser; + + +/** + * This is the default implementation of the + * FTPFileEntryParserFactory interface. This is the + * implementation that will be used by + * org.apache.commons.net.ftp.FTPClient.listFiles() + * if no other implementation has been specified. + * + * @see org.apache.commons.net.ftp.FTPClient#listFiles + * @see org.apache.commons.net.ftp.FTPClient#setParserFactory + */ +public class DefaultFTPFileEntryParserFactory + implements FTPFileEntryParserFactory +{ + private FTPClientConfig config = null; + + /** + * This default implementation of the FTPFileEntryParserFactory + * interface works according to the following logic: + * First it attempts to interpret the supplied key as a fully + * qualified classname of a class implementing the + * FTPFileEntryParser interface. If that succeeds, a parser + * object of this class is instantiated and is returned; + * otherwise it attempts to interpret the key as an identirier + * commonly used by the FTP SYST command to identify systems. + *

+ * If 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; + } + + /** + *

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. + *

+ * @param config A {@link FTPClientConfig FTPClientConfig} + * used to configure the parser created + * + * @return the @link FTPFileEntryParser FTPFileEntryParser} so created. + * @exception ParserInitializationException + * Thrown on any exception in instantiation + * @since 1.4 + */ + public FTPFileEntryParser createFileEntryParser(FTPClientConfig config) + throws ParserInitializationException + { + this.config = config; + String key = config.getServerSystemKey(); + return createFileEntryParser(key); + } + + + public FTPFileEntryParser createUnixFTPEntryParser() + { + return new UnixFTPEntryParser(); + } + + public FTPFileEntryParser createVMSVersioningFTPEntryParser() + { + return new VMSVersioningFTPEntryParser(); + } + + public FTPFileEntryParser createNetwareFTPEntryParser() { + return new NetwareFTPEntryParser(); + } + + public FTPFileEntryParser createNTFTPEntryParser() + { + if (config != null && FTPClientConfig.SYST_NT.equals( + config.getServerSystemKey())) + { + return new NTFTPEntryParser(); + } else { + return new CompositeFileEntryParser(new FTPFileEntryParser[] + { + new NTFTPEntryParser(), + new UnixFTPEntryParser() + }); + } + } + + public FTPFileEntryParser createOS2FTPEntryParser() + { + return new OS2FTPEntryParser(); + } + + public FTPFileEntryParser createOS400FTPEntryParser() + { + if (config != null && + FTPClientConfig.SYST_OS400.equals(config.getServerSystemKey())) + { + return new OS400FTPEntryParser(); + } else { + return new CompositeFileEntryParser(new FTPFileEntryParser[] + { + new OS400FTPEntryParser(), + new UnixFTPEntryParser() + }); + } + } + + public FTPFileEntryParser createMVSEntryParser() + { + return new MVSFTPEntryParser(); + } + + + +} + diff --git a/src/org/apache/commons/net/ftp/parser/EnterpriseUnixFTPEntryParser.java b/src/org/apache/commons/net/ftp/parser/EnterpriseUnixFTPEntryParser.java new file mode 100644 index 0000000..c7e6da7 --- /dev/null +++ b/src/org/apache/commons/net/ftp/parser/EnterpriseUnixFTPEntryParser.java @@ -0,0 +1,166 @@ +/* + * 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.Calendar; + +import org.apache.commons.net.ftp.FTPFile; + +/** + * Parser for the Connect Enterprise Unix FTP Server From Sterling Commerce. + * Here is a sample of the sort of output line this parser processes: + * "-C--E-----FTP B QUA1I1 18128 41 Aug 12 13:56 QUADTEST" + *

+ * 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 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; + + /** + *

+ * 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. + *

+ * + * @param config A {@link FTPClientConfig FTPClientConfig} + * used to configure the parser created + * + * @return the @link FTPFileEntryParser FTPFileEntryParser} so created. + * @exception ParserInitializationException + * Thrown on any exception in instantiation + * @since 1.4 + */ + public FTPFileEntryParser createFileEntryParser(FTPClientConfig config) + throws ParserInitializationException; + +} diff --git a/src/org/apache/commons/net/ftp/parser/FTPTimestampParser.java b/src/org/apache/commons/net/ftp/parser/FTPTimestampParser.java new file mode 100644 index 0000000..40b46cb --- /dev/null +++ b/src/org/apache/commons/net/ftp/parser/FTPTimestampParser.java @@ -0,0 +1,52 @@ +/* + * 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.Calendar; + +/** + * This interface specifies the concept of parsing an FTP server's + * timestamp. + * @since 1.4 + */ +public interface FTPTimestampParser { + + /** + * the default default date format. + */ + public static final String DEFAULT_SDF = UnixFTPEntryParser.DEFAULT_DATE_FORMAT; + /** + * the default recent date format. + */ + public static final String DEFAULT_RECENT_SDF = UnixFTPEntryParser.DEFAULT_RECENT_DATE_FORMAT; + + /** + * Parses the supplied datestamp parameter. This parameter typically would + * have been pulled from a longer FTP listing via the regular expression + * mechanism + * @param timestampStr - the timestamp portion of the FTP directory listing + * to be parsed + * @return a 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: + *

+ * 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: + *

+ * 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= + * SEQ is the default and used for normal file transfer JES is used to + * interact with the Job Entry Subsystem (JES) similar to a job scheduler + * DB2 is used to interact with a DB2 subsystem + * + * This parser supports SEQ and JES. + * + * + * + * + * + * + */ + + /** + * The sole constructor for a MVSFTPEntryParser object. + * + */ + public MVSFTPEntryParser() { + super(""); // note the regex is set in preParse. + super.configure(null); // configure parser with default configurations + } + + /** + * Parses a line of an z/OS - MVS 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, 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 List preParse(List orig) { + // simply remove the header line. Composite logic will take care of the + // two different types of + // list in short order. + if (orig != null && orig.size() > 0) { + String header = orig.get(0); + if (header.indexOf("Volume") >= 0 && header.indexOf("Dsname") >= 0) { + setType(FILE_LIST_TYPE); + super.setRegex(FILE_LIST_REGEX); + } else if (header.indexOf("Name") >= 0 && header.indexOf("Id") >= 0) { + setType(MEMBER_LIST_TYPE); + super.setRegex(MEMBER_LIST_REGEX); + } else if (header.indexOf("total") == 0) { + setType(UNIX_LIST_TYPE); + unixFTPEntryParser = new UnixFTPEntryParser(); + } else if (header.indexOf("Spool Files") >= 30) { + setType(JES_LEVEL_1_LIST_TYPE); + super.setRegex(JES_LEVEL_1_LIST_REGEX); + } else if (header.indexOf("JOBNAME") == 0 + && header.indexOf("JOBID") > 8) {// header contains JOBNAME JOBID OWNER // STATUS CLASS + setType(JES_LEVEL_2_LIST_TYPE); + super.setRegex(JES_LEVEL_2_LIST_REGEX); + } else { + setType(UNKNOWN_LIST_TYPE); + } + + if (isType != JES_LEVEL_1_LIST_TYPE) { // remove header is necessary + orig.remove(0); + } + } + + return orig; + } + + /** + * Explicitly set the type of listing being processed. + * @param type The listing type. + */ + void setType(int type) { + isType = type; + } + + /* + * @return + */ + @Override + protected FTPClientConfig getDefaultConfiguration() { + return new FTPClientConfig(FTPClientConfig.SYST_MVS, + DEFAULT_DATE_FORMAT, null, null, null, null); + } + +} diff --git a/src/org/apache/commons/net/ftp/parser/NTFTPEntryParser.java b/src/org/apache/commons/net/ftp/parser/NTFTPEntryParser.java new file mode 100644 index 0000000..b6bc75e --- /dev/null +++ b/src/org/apache/commons/net/ftp/parser/NTFTPEntryParser.java @@ -0,0 +1,147 @@ +/* + * 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 of FTPFileEntryParser and FTPFileListParser for NT Systems. + * + * @author Winston Ojeda + * @author Steve Cohen + * @version $Id: NTFTPEntryParser.java 658518 2008-05-21 01:04:30Z sebb $ + * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions) + */ +public class NTFTPEntryParser extends ConfigurableFTPFileEntryParserImpl +{ + + private static final String DEFAULT_DATE_FORMAT + = "MM-dd-yy hh:mma"; //11-09-01 12:30PM + + + /** + * this is the regular expression used by this parser. + */ + private static final String REGEX = + "(\\S+)\\s+(\\S+)\\s+" + + "(?:()|([0-9]+))\\s+" + + "(\\S.*)"; + + /** + * The sole constructor for an NTFTPEntryParser 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 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 ("

".equals(dirString)) + { + f.setType(FTPFile.DIRECTORY_TYPE); + f.setSize(0); + } + else + { + f.setType(FTPFile.FILE_TYPE); + if (null != size) + { + f.setSize(Long.parseLong(size)); + } + } + 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 + public FTPClientConfig getDefaultConfiguration() { + return new FTPClientConfig( + FTPClientConfig.SYST_NT, + DEFAULT_DATE_FORMAT, + null, null, null, null); + } + +} diff --git a/src/org/apache/commons/net/ftp/parser/NetwareFTPEntryParser.java b/src/org/apache/commons/net/ftp/parser/NetwareFTPEntryParser.java new file mode 100644 index 0000000..3cbea82 --- /dev/null +++ b/src/org/apache/commons/net/ftp/parser/NetwareFTPEntryParser.java @@ -0,0 +1,176 @@ +/* + * 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 of FTPFileEntryParser and FTPFileListParser for Netware Systems. Note that some of the proprietary + * extensions for Novell-specific operations are not supported. See + * http://www.novell.com/documentation/nw65/index.html?page=/documentation/nw65/ftp_enu/data/fbhbgcfa.html + * for more details. + * + * @author Rory Winston + * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions) + * @version $Id: NetwareFTPEntryParser.java 658520 2008-05-21 01:14:11Z sebb $ + * @since 1.5 + */ +public class NetwareFTPEntryParser extends ConfigurableFTPFileEntryParserImpl { + + /** + * Default date format is e.g. Feb 22 2006 + */ + private static final String DEFAULT_DATE_FORMAT = "MMM dd yyyy"; + + /** + * Default recent date format is e.g. Feb 22 17:32 + */ + private static final String DEFAULT_RECENT_DATE_FORMAT = "MMM dd HH:mm"; + + /** + * this is the regular expression used by this parser. + * Example: d [-W---F--] SCION_VOL2 512 Apr 13 23:12 VOL2 + */ + private static final String REGEX = "(d|-){1}\\s+" // Directory/file flag + + "\\[(.*)\\]\\s+" // Attributes + + "(\\S+)\\s+" + "(\\d+)\\s+" // Owner and size + + "(\\S+\\s+\\S+\\s+((\\d+:\\d+)|(\\d{4})))" // Long/short date format + + "\\s+(.*)"; // Filename (incl. spaces) + + /** + * The default constructor for a NetwareFTPEntryParser 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 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: + *

    + *
  • S - Supervisor; All rights. + *
  • R - Read; Right to open and read or execute. + *
  • W - Write; Right to open and modify. + *
  • C - Create; Right to create; when assigned to a file, allows a deleted file to be recovered. + *
  • E - Erase; Right to delete. + *
  • M - Modify; Right to rename a file and to change attributes. + *
  • F - File Scan; Right to see directory or file listings. + *
  • A - Access Control; Right to modify trustee assignments and the Inherited Rights Mask. + *
+ * + * See here + * for more details + * + * @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 dirString = group(1); + String attrib = group(2); + String user = group(3); + String size = group(4); + String datestr = group(5); + String name = group(9); + + try { + f.setTimestamp(super.parseTimestamp(datestr)); + } catch (ParseException e) { + // intentionally do nothing + } + + //is it a DIR or a file + if (dirString.trim().equals("d")) { + f.setType(FTPFile.DIRECTORY_TYPE); + } else // Should be "-" + { + f.setType(FTPFile.FILE_TYPE); + } + + f.setUser(user); + + //set the name + f.setName(name.trim()); + + //set the size + f.setSize(Long.parseLong(size.trim())); + + // Now set the permissions (or at least a subset thereof - full permissions would probably require + // subclassing FTPFile and adding extra metainformation there) + if (attrib.indexOf("R") != -1) { + f.setPermission(FTPFile.USER_ACCESS, FTPFile.READ_PERMISSION, + true); + } + if (attrib.indexOf("W") != -1) { + f.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION, + true); + } + + 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_NETWARE, + DEFAULT_DATE_FORMAT, DEFAULT_RECENT_DATE_FORMAT, null, null, + null); + } + +} diff --git a/src/org/apache/commons/net/ftp/parser/OS2FTPEntryParser.java b/src/org/apache/commons/net/ftp/parser/OS2FTPEntryParser.java new file mode 100644 index 0000000..dc02ffb --- /dev/null +++ b/src/org/apache/commons/net/ftp/parser/OS2FTPEntryParser.java @@ -0,0 +1,147 @@ +/* + * 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 of FTPFileEntryParser and FTPFileListParser for OS2 Systems. + * + * @author Winston Ojeda + * @author Steve Cohen + * @version $Id: OS2FTPEntryParser.java 658518 2008-05-21 01:04:30Z sebb $ + * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions) + */ +public class OS2FTPEntryParser extends ConfigurableFTPFileEntryParserImpl + +{ + + private static final String DEFAULT_DATE_FORMAT + = "MM-dd-yy HH:mm"; //11-09-01 12:30 + /** + * this is the regular expression used by this parser. + */ + private static final String REGEX = + "\\s*([0-9]+)\\s*" + + "(\\s+|[A-Z]+)\\s*" + + "(DIR|\\s+)\\s*" + + "(\\S+)\\s+(\\S+)\\s+" /* date stuff */ + + "(\\S.*)"; + + /** + * The default constructor for a OS2FTPEntryParser 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 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 + * 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 + */ +public abstract class RegexFTPFileEntryParserImpl extends + FTPFileEntryParserImpl { + /** + * internal pattern the matcher tries to match, representing a file + * entry + */ + private Pattern pattern = null; + + /** + * internal match result used by the parser + */ + private MatchResult result = null; + + /** + * Internal PatternMatcher object used by the parser. It has protected + * scope in case subclasses want to make use of it for their own purposes. + */ + protected Matcher _matcher_ = null; + + /** + * The constructor for a RegexFTPFileEntryParserImpl object. + * + * @param regex The regular expression with which this object is + * initialized. + * + * @exception IllegalArgumentException + * Thrown if the regular expression is unparseable. Should not be seen in + * normal conditions. It it is seen, this is a sign that a subclass has + * been created with a bad regular expression. Since the parser must be + * created before use, this means that any bad parser subclasses created + * from this will bomb very quickly, leading to easy detection. + */ + + public RegexFTPFileEntryParserImpl(String regex) { + super(); + setRegex(regex); + } + + /** + * Convenience method delegates to the internal MatchResult's matches() + * method. + * + * @param s the String to be matched + * @return true if s matches this object's regular expression. + */ + + public boolean matches(String s) { + this.result = null; + _matcher_ = pattern.matcher(s); + if (_matcher_.matches()) { + this.result = _matcher_.toMatchResult(); + } + return null != this.result; + } + + /** + * Convenience method + * + * @return the number of groups() in the internal MatchResult. + */ + + public int getGroupCnt() { + if (this.result == null) { + return 0; + } + return this.result.groupCount(); + } + + /** + * Convenience method delegates to the internal MatchResult's group() + * method. + * + * @param matchnum match group number to be retrieved + * + * @return the content of the 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. + *

+ * For now end users may specify this format only via + * 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. + *

+ * @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 + * 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. + *

+ * @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 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) + { + //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 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 preParse(List original) { + original = super.preParse(original); + HashMap existingEntries = new HashMap(); + ListIterator iter = original.listIterator(); + while (iter.hasNext()) { + String entry = iter.next().trim(); + MatchResult result = null; + _preparse_matcher_ = _preparse_pattern_.matcher(entry); + if (_preparse_matcher_.matches()) { + result = _preparse_matcher_.toMatchResult(); + String name = result.group(1); + String version = result.group(2); + NameVersion nv = new NameVersion(name, version); + NameVersion existing = existingEntries.get(name); + if (null != existing) { + if (nv.versionNumber < existing.versionNumber) { + iter.remove(); // removal removes from original list. + continue; + } + } + existingEntries.put(name, nv); + } + + } + // we've now removed all entries less than with less than the largest + // version number for each name that were listed after the largest. + // we now must remove those with smaller than the largest version number + // for each name that were found before the largest + while (iter.hasPrevious()) { + String entry = iter.previous().trim(); + MatchResult result = null; + _preparse_matcher_ = _preparse_pattern_.matcher(entry); + if (_preparse_matcher_.matches()) { + result = _preparse_matcher_.toMatchResult(); + String name = result.group(1); + String version = result.group(2); + NameVersion nv = new NameVersion(name, version); + NameVersion existing = existingEntries.get(name); + if (null != existing) { + if (nv.versionNumber < existing.versionNumber) { + iter.remove(); // removal removes from original list. + } + } + } + + } + return original; + } + + + @Override + protected boolean isVersioning() { + return true; + } + +} + +/* Emacs configuration + * Local variables: ** + * mode: java ** + * c-basic-offset: 4 ** + * indent-tabs-mode: nil ** + * End: ** + */ -- cgit v1.2.3