diff options
| author | Joe Robinson <joe@lc8n.com> | 2010-10-27 14:21:09 +0100 | 
|---|---|---|
| committer | Joe Robinson <joe@lc8n.com> | 2010-10-27 14:21:09 +0100 | 
| commit | 063284837c8c366e5502b1b0264b8eb807b61732 (patch) | |
| tree | fff3a1e1e5afaa671485b9d990704658276627ac /src/org/apache | |
Basic upload functionality to predifined location, with basic file browser
Diffstat (limited to 'src/org/apache')
57 files changed, 13689 insertions, 0 deletions
diff --git a/src/org/apache/commons/net/DatagramSocketClient.java b/src/org/apache/commons/net/DatagramSocketClient.java new file mode 100644 index 0000000..9199704 --- /dev/null +++ b/src/org/apache/commons/net/DatagramSocketClient.java @@ -0,0 +1,275 @@ +/*
 + * 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;
 +
 +import java.net.DatagramSocket;
 +import java.net.InetAddress;
 +import java.net.SocketException;
 +
 +/***
 + * The DatagramSocketClient provides the basic operations that are required
 + * of client objects accessing datagram sockets.  It is meant to be
 + * subclassed to avoid having to rewrite the same code over and over again
 + * to open a socket, close a socket, set timeouts, etc.  Of special note
 + * is the {@link #setDatagramSocketFactory  setDatagramSocketFactory }
 + * method, which allows you to control the type of DatagramSocket the
 + * DatagramSocketClient creates for network communications.  This is
 + * especially useful for adding things like proxy support as well as better
 + * support for applets.  For
 + * example, you could create a
 + * {@link org.apache.commons.net.DatagramSocketFactory}
 + *  that
 + * requests browser security capabilities before creating a socket.
 + * All classes derived from DatagramSocketClient should use the
 + * {@link #_socketFactory_  _socketFactory_ } member variable to
 + * create DatagramSocket instances rather than instantiating
 + * them by directly invoking a constructor.  By honoring this contract
 + * you guarantee that a user will always be able to provide his own
 + * Socket implementations by substituting his own SocketFactory.
 + * <p>
 + * <p>
 + * @author Daniel F. Savarese
 + * @see DatagramSocketFactory
 + ***/
 +
 +public abstract class DatagramSocketClient
 +{
 +    /***
 +     * The default DatagramSocketFactory shared by all DatagramSocketClient
 +     * instances.
 +     ***/
 +    private static final DatagramSocketFactory __DEFAULT_SOCKET_FACTORY =
 +        new DefaultDatagramSocketFactory();
 +
 +    /*** The timeout to use after opening a socket. ***/
 +    protected int _timeout_;
 +
 +    /*** The datagram socket used for the connection. ***/
 +    protected DatagramSocket _socket_;
 +
 +    /***
 +     * A status variable indicating if the client's socket is currently open.
 +     ***/
 +    protected boolean _isOpen_;
 +
 +    /*** The datagram socket's DatagramSocketFactory. ***/
 +    protected DatagramSocketFactory _socketFactory_;
 +
 +    /***
 +     * Default constructor for DatagramSocketClient.  Initializes
 +     * _socket_ to null, _timeout_ to 0, and _isOpen_ to false.
 +     ***/
 +    public DatagramSocketClient()
 +    {
 +        _socket_ = null;
 +        _timeout_ = 0;
 +        _isOpen_ = false;
 +        _socketFactory_ = __DEFAULT_SOCKET_FACTORY;
 +    }
 +
 +
 +    /***
 +     * Opens a DatagramSocket on the local host at the first available port.
 +     * Also sets the timeout on the socket to the default timeout set
 +     * by {@link #setDefaultTimeout  setDefaultTimeout() }.
 +     * <p>
 +     * _isOpen_ is set to true after calling this method and _socket_
 +     * is set to the newly opened socket.
 +     * <p>
 +     * @exception SocketException If the socket could not be opened or the
 +     *   timeout could not be set.
 +     ***/
 +    public void open() throws SocketException
 +    {
 +        _socket_ = _socketFactory_.createDatagramSocket();
 +        _socket_.setSoTimeout(_timeout_);
 +        _isOpen_ = true;
 +    }
 +
 +
 +    /***
 +     * Opens a DatagramSocket on the local host at a specified port.
 +     * Also sets the timeout on the socket to the default timeout set
 +     * by {@link #setDefaultTimeout  setDefaultTimeout() }.
 +     * <p>
 +     * _isOpen_ is set to true after calling this method and _socket_
 +     * is set to the newly opened socket.
 +     * <p>
 +     * @param port The port to use for the socket.
 +     * @exception SocketException If the socket could not be opened or the
 +     *   timeout could not be set.
 +     ***/
 +    public void open(int port) throws SocketException
 +    {
 +        _socket_ = _socketFactory_.createDatagramSocket(port);
 +        _socket_.setSoTimeout(_timeout_);
 +        _isOpen_ = true;
 +    }
 +
 +
 +    /***
 +     * Opens a DatagramSocket at the specified address on the local host
 +     * at a specified port.
 +     * Also sets the timeout on the socket to the default timeout set
 +     * by {@link #setDefaultTimeout  setDefaultTimeout() }.
 +     * <p>
 +     * _isOpen_ is set to true after calling this method and _socket_
 +     * is set to the newly opened socket.
 +     * <p>
 +     * @param port The port to use for the socket.
 +     * @param laddr  The local address to use.
 +     * @exception SocketException If the socket could not be opened or the
 +     *   timeout could not be set.
 +     ***/
 +    public void open(int port, InetAddress laddr) throws SocketException
 +    {
 +        _socket_ = _socketFactory_.createDatagramSocket(port, laddr);
 +        _socket_.setSoTimeout(_timeout_);
 +        _isOpen_ = true;
 +    }
 +
 +
 +
 +    /***
 +     * Closes the DatagramSocket used for the connection.
 +     * You should call this method after you've finished using the class
 +     * instance and also before you call {@link #open open() }
 +     * again.   _isOpen_ is set to false and  _socket_ is set to null.
 +     * If you call this method when the client socket is not open,
 +     * a NullPointerException is thrown.
 +     ***/
 +    public void close()
 +    {
 +        _socket_.close();
 +        _socket_ = null;
 +        _isOpen_ = false;
 +    }
 +
 +
 +    /***
 +     * Returns true if the client has a currently open socket.
 +     * <p>
 +     * @return True if the client has a curerntly open socket, false otherwise.
 +     ***/
 +    public boolean isOpen()
 +    {
 +        return _isOpen_;
 +    }
 +
 +
 +    /***
 +     * Set the default timeout in milliseconds to use when opening a socket.
 +     * After a call to open, the timeout for the socket is set using this value.
 +     * This method should be used prior to a call to {@link #open open()}
 +     * and should not be confused with {@link #setSoTimeout setSoTimeout()}
 +     * which operates on the currently open socket.  _timeout_ contains
 +     * the new timeout value.
 +     * <p>
 +     * @param timeout  The timeout in milliseconds to use for the datagram socket
 +     *                 connection.
 +     ***/
 +    public void setDefaultTimeout(int timeout)
 +    {
 +        _timeout_ = timeout;
 +    }
 +
 +
 +    /***
 +     * Returns the default timeout in milliseconds that is used when
 +     * opening a socket.
 +     * <p>
 +     * @return The default timeout in milliseconds that is used when
 +     *         opening a socket.
 +     ***/
 +    public int getDefaultTimeout()
 +    {
 +        return _timeout_;
 +    }
 +
 +
 +    /***
 +     * Set the timeout in milliseconds of a currently open connection.
 +     * Only call this method after a connection has been opened
 +     * by {@link #open open()}.
 +     * <p>
 +     * @param timeout  The timeout in milliseconds to use for the currently
 +     *                 open datagram socket connection.
 +     ***/
 +    public void setSoTimeout(int timeout) throws SocketException
 +    {
 +        _socket_.setSoTimeout(timeout);
 +    }
 +
 +
 +    /***
 +     * Returns the timeout in milliseconds of the currently opened socket.
 +     * If you call this method when the client socket is not open,
 +     * a NullPointerException is thrown.
 +     * <p>
 +     * @return The timeout in milliseconds of the currently opened socket.
 +     ***/
 +    public int getSoTimeout() throws SocketException
 +    {
 +        return _socket_.getSoTimeout();
 +    }
 +
 +
 +    /***
 +     * Returns the port number of the open socket on the local host used
 +     * for the connection.  If you call this method when the client socket
 +     * is not open, a NullPointerException is thrown.
 +     * <p>
 +     * @return The port number of the open socket on the local host used
 +     *         for the connection.
 +     ***/
 +    public int getLocalPort()
 +    {
 +        return _socket_.getLocalPort();
 +    }
 +
 +
 +    /***
 +     * Returns the local address to which the client's socket is bound.
 +     * If you call this method when the client socket is not open, a
 +     * NullPointerException is thrown.
 +     * <p>
 +     * @return The local address to which the client's socket is bound.
 +     ***/
 +    public InetAddress getLocalAddress()
 +    {
 +        return _socket_.getLocalAddress();
 +    }
 +
 +
 +    /***
 +     * Sets the DatagramSocketFactory used by the DatagramSocketClient
 +     * to open DatagramSockets.  If the factory value is null, then a default
 +     * factory is used (only do this to reset the factory after having
 +     * previously altered it).
 +     * <p>
 +     * @param factory  The new DatagramSocketFactory the DatagramSocketClient
 +     * should use.
 +     ***/
 +    public void setDatagramSocketFactory(DatagramSocketFactory factory)
 +    {
 +        if (factory == null)
 +            _socketFactory_ = __DEFAULT_SOCKET_FACTORY;
 +        else
 +            _socketFactory_ = factory;
 +    }
 +}
 diff --git a/src/org/apache/commons/net/DatagramSocketFactory.java b/src/org/apache/commons/net/DatagramSocketFactory.java new file mode 100644 index 0000000..c152eb2 --- /dev/null +++ b/src/org/apache/commons/net/DatagramSocketFactory.java @@ -0,0 +1,67 @@ +/*
 + * 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;
 +
 +import java.net.DatagramSocket;
 +import java.net.InetAddress;
 +import java.net.SocketException;
 +
 +/***
 + * The DatagramSocketFactory interface provides a means for the
 + * programmer to control the creation of datagram sockets and
 + * provide his own DatagramSocket implementations for use by all
 + * classes derived from
 + * {@link org.apache.commons.net.DatagramSocketClient}
 + * .
 + * This allows you to provide your own DatagramSocket implementations and
 + * to perform security checks or browser capability requests before
 + * creating a DatagramSocket.
 + * <p>
 + * <p>
 + * @author Daniel F. Savarese
 + ***/
 +
 +public interface DatagramSocketFactory
 +{
 +
 +    /***
 +     * Creates a DatagramSocket on the local host at the first available port.
 +     * <p>
 +     * @exception SocketException If the socket could not be created.
 +     ***/
 +    public DatagramSocket createDatagramSocket() throws SocketException;
 +
 +    /***
 +     * Creates a DatagramSocket on the local host at a specified port.
 +     * <p>
 +     * @param port The port to use for the socket.
 +     * @exception SocketException If the socket could not be created.
 +     ***/
 +    public DatagramSocket createDatagramSocket(int port) throws SocketException;
 +
 +    /***
 +     * Creates a DatagramSocket at the specified address on the local host
 +     * at a specified port.
 +     * <p>
 +     * @param port The port to use for the socket.
 +     * @param laddr  The local address to use.
 +     * @exception SocketException If the socket could not be created.
 +     ***/
 +    public DatagramSocket createDatagramSocket(int port, InetAddress laddr)
 +    throws SocketException;
 +}
 diff --git a/src/org/apache/commons/net/DefaultDatagramSocketFactory.java b/src/org/apache/commons/net/DefaultDatagramSocketFactory.java new file mode 100644 index 0000000..3983fcb --- /dev/null +++ b/src/org/apache/commons/net/DefaultDatagramSocketFactory.java @@ -0,0 +1,75 @@ +/*
 + * 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;
 +
 +import java.net.DatagramSocket;
 +import java.net.InetAddress;
 +import java.net.SocketException;
 +
 +/***
 + * DefaultDatagramSocketFactory implements the DatagramSocketFactory
 + * interface by simply wrapping the java.net.DatagramSocket
 + * constructors.  It is the default DatagramSocketFactory used by
 + * {@link org.apache.commons.net.DatagramSocketClient}
 + *  implementations.
 + * <p>
 + * <p>
 + * @author Daniel F. Savarese
 + * @see DatagramSocketFactory
 + * @see DatagramSocketClient
 + * @see DatagramSocketClient#setDatagramSocketFactory
 + ***/
 +
 +public class DefaultDatagramSocketFactory implements DatagramSocketFactory
 +{
 +
 +    /***
 +     * Creates a DatagramSocket on the local host at the first available port.
 +     * <p>
 +     * @exception SocketException If the socket could not be created.
 +     ***/
 +    public DatagramSocket createDatagramSocket() throws SocketException
 +    {
 +        return new DatagramSocket();
 +    }
 +
 +    /***
 +     * Creates a DatagramSocket on the local host at a specified port.
 +     * <p>
 +     * @param port The port to use for the socket.
 +     * @exception SocketException If the socket could not be created.
 +     ***/
 +    public DatagramSocket createDatagramSocket(int port) throws SocketException
 +    {
 +        return new DatagramSocket(port);
 +    }
 +
 +    /***
 +     * Creates a DatagramSocket at the specified address on the local host
 +     * at a specified port.
 +     * <p>
 +     * @param port The port to use for the socket.
 +     * @param laddr  The local address to use.
 +     * @exception SocketException If the socket could not be created.
 +     ***/
 +    public DatagramSocket createDatagramSocket(int port, InetAddress laddr)
 +    throws SocketException
 +    {
 +        return new DatagramSocket(port, laddr);
 +    }
 +}
 diff --git a/src/org/apache/commons/net/DefaultSocketFactory.java b/src/org/apache/commons/net/DefaultSocketFactory.java new file mode 100644 index 0000000..e809f84 --- /dev/null +++ b/src/org/apache/commons/net/DefaultSocketFactory.java @@ -0,0 +1,165 @@ +/*
 + * 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;
 +
 +import java.io.IOException;
 +import java.net.InetAddress;
 +import java.net.ServerSocket;
 +import java.net.Socket;
 +import java.net.UnknownHostException;
 +
 +import javax.net.SocketFactory;
 +
 +/***
 + * DefaultSocketFactory implements the SocketFactory interface by
 + * simply wrapping the java.net.Socket and java.net.ServerSocket
 + * constructors.  It is the default SocketFactory used by
 + * {@link org.apache.commons.net.SocketClient}
 + * implementations.
 + * <p>
 + * <p>
 + * @author Daniel F. Savarese
 + * @see SocketFactory
 + * @see SocketClient
 + * @see SocketClient#setSocketFactory
 + ***/
 +
 +public class DefaultSocketFactory extends SocketFactory
 +{
 +
 +    /***
 +     * Creates a Socket connected to the given host and port.
 +     * <p>
 +     * @param host The hostname to connect to.
 +     * @param port The port to connect to.
 +     * @return A Socket connected to the given host and port.
 +     * @exception UnknownHostException  If the hostname cannot be resolved.
 +     * @exception IOException If an I/O error occurs while creating the Socket.
 +     ***/
 +    @Override
 +    public Socket createSocket(String host, int port)
 +    throws UnknownHostException, IOException
 +    {
 +        return new Socket(host, port);
 +    }
 +
 +    /***
 +     * Creates a Socket connected to the given host and port.
 +     * <p>
 +     * @param address The address of the host to connect to.
 +     * @param port The port to connect to.
 +     * @return A Socket connected to the given host and port.
 +     * @exception IOException If an I/O error occurs while creating the Socket.
 +     ***/
 +    @Override
 +    public Socket createSocket(InetAddress address, int port)
 +    throws IOException
 +    {
 +        return new Socket(address, port);
 +    }
 +
 +    /***
 +     * Creates a Socket connected to the given host and port and
 +     * originating from the specified local address and port.
 +     * <p>
 +     * @param host The hostname to connect to.
 +     * @param port The port to connect to.
 +     * @param localAddr  The local address to use.
 +     * @param localPort  The local port to use.
 +     * @return A Socket connected to the given host and port.
 +     * @exception UnknownHostException  If the hostname cannot be resolved.
 +     * @exception IOException If an I/O error occurs while creating the Socket.
 +     ***/
 +    @Override
 +    public Socket createSocket(String host, int port,
 +                               InetAddress localAddr, int localPort)
 +    throws UnknownHostException, IOException
 +    {
 +        return new Socket(host, port, localAddr, localPort);
 +    }
 +
 +    /***
 +     * Creates a Socket connected to the given host and port and
 +     * originating from the specified local address and port.
 +     * <p>
 +     * @param address The address of the host to connect to.
 +     * @param port The port to connect to.
 +     * @param localAddr  The local address to use.
 +     * @param localPort  The local port to use.
 +     * @return A Socket connected to the given host and port.
 +     * @exception IOException If an I/O error occurs while creating the Socket.
 +     ***/
 +    @Override
 +    public Socket createSocket(InetAddress address, int port,
 +                               InetAddress localAddr, int localPort)
 +    throws IOException
 +    {
 +        return new Socket(address, port, localAddr, localPort);
 +    }
 +
 +    /***
 +     * Creates a ServerSocket bound to a specified port.  A port
 +     * of 0 will create the ServerSocket on a system-determined free port.
 +     * <p>
 +     * @param port  The port on which to listen, or 0 to use any free port.
 +     * @return A ServerSocket that will listen on a specified port.
 +     * @exception IOException If an I/O error occurs while creating
 +     *                        the ServerSocket.
 +     ***/
 +    public ServerSocket createServerSocket(int port) throws IOException
 +    {
 +        return new ServerSocket(port);
 +    }
 +
 +    /***
 +     * Creates a ServerSocket bound to a specified port with a given
 +     * maximum queue length for incoming connections.  A port of 0 will
 +     * create the ServerSocket on a system-determined free port.
 +     * <p>
 +     * @param port  The port on which to listen, or 0 to use any free port.
 +     * @param backlog  The maximum length of the queue for incoming connections.
 +     * @return A ServerSocket that will listen on a specified port.
 +     * @exception IOException If an I/O error occurs while creating
 +     *                        the ServerSocket.
 +     ***/
 +    public ServerSocket createServerSocket(int port, int backlog)
 +    throws IOException
 +    {
 +        return new ServerSocket(port, backlog);
 +    }
 +
 +    /***
 +     * Creates a ServerSocket bound to a specified port on a given local
 +     * address with a given maximum queue length for incoming connections.
 +     * A port of 0 will
 +     * create the ServerSocket on a system-determined free port.
 +     * <p>
 +     * @param port  The port on which to listen, or 0 to use any free port.
 +     * @param backlog  The maximum length of the queue for incoming connections.
 +     * @param bindAddr  The local address to which the ServerSocket should bind.
 +     * @return A ServerSocket that will listen on a specified port.
 +     * @exception IOException If an I/O error occurs while creating
 +     *                        the ServerSocket.
 +     ***/
 +    public ServerSocket createServerSocket(int port, int backlog,
 +                                           InetAddress bindAddr)
 +    throws IOException
 +    {
 +        return new ServerSocket(port, backlog, bindAddr);
 +    }
 +}
 diff --git a/src/org/apache/commons/net/MalformedServerReplyException.java b/src/org/apache/commons/net/MalformedServerReplyException.java new file mode 100644 index 0000000..3cad5f0 --- /dev/null +++ b/src/org/apache/commons/net/MalformedServerReplyException.java @@ -0,0 +1,55 @@ +/*
 + * 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;
 +
 +import java.io.IOException;
 +
 +/***
 + * This exception is used to indicate that the reply from a server
 + * could not be interpreted.  Most of the NetComponents classes attempt
 + * to be as lenient as possible when receiving server replies.  Many
 + * server implementations deviate from IETF protocol specifications, making
 + * it necessary to be as flexible as possible.  However, there will be
 + * certain situations where it is not possible to continue an operation
 + * because the server reply could not be interpreted in a meaningful manner.
 + * In these cases, a MalformedServerReplyException should be thrown.
 + * <p>
 + * <p>
 + * @author Daniel F. Savarese
 + ***/
 +
 +public class MalformedServerReplyException extends IOException
 +{
 +
 +    /*** Constructs a MalformedServerReplyException with no message ***/
 +    public MalformedServerReplyException()
 +    {
 +        super();
 +    }
 +
 +    /***
 +     * Constructs a MalformedServerReplyException with a specified message.
 +     * <p>
 +     * @param message  The message explaining the reason for the exception.
 +     ***/
 +    public MalformedServerReplyException(String message)
 +    {
 +        super(message);
 +    }
 +
 +}
 diff --git a/src/org/apache/commons/net/PrintCommandListener.java b/src/org/apache/commons/net/PrintCommandListener.java new file mode 100644 index 0000000..d8e7a68 --- /dev/null +++ b/src/org/apache/commons/net/PrintCommandListener.java @@ -0,0 +1,54 @@ +/*
 + * 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;
 +
 +import java.io.PrintWriter;
 +import org.apache.commons.net.ProtocolCommandEvent;
 +import org.apache.commons.net.ProtocolCommandListener;
 +
 +/***
 + * This is a support class for some of the example programs.  It is
 + * a sample implementation of the ProtocolCommandListener interface
 + * which just prints out to a specified stream all command/reply traffic.
 + * <p>
 + *
 + * @since 2.0
 + ***/
 +
 +public class PrintCommandListener implements ProtocolCommandListener
 +{
 +    private PrintWriter __writer;
 +
 +    public PrintCommandListener(PrintWriter writer)
 +    {
 +        __writer = writer;
 +    }
 +
 +    public void protocolCommandSent(ProtocolCommandEvent event)
 +    {
 +        __writer.print(event.getMessage());
 +        __writer.flush();
 +    }
 +
 +    public void protocolReplyReceived(ProtocolCommandEvent event)
 +    {
 +        __writer.print(event.getMessage());
 +        __writer.flush();
 +    }
 +}
 +
 diff --git a/src/org/apache/commons/net/ProtocolCommandEvent.java b/src/org/apache/commons/net/ProtocolCommandEvent.java new file mode 100644 index 0000000..8977c03 --- /dev/null +++ b/src/org/apache/commons/net/ProtocolCommandEvent.java @@ -0,0 +1,146 @@ +/*
 + * 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;
 +import java.util.EventObject;
 +
 +/***
 + * There exists a large class of IETF protocols that work by sending an
 + * ASCII text command and arguments to a server, and then receiving an
 + * ASCII text reply.  For debugging and other purposes, it is extremely
 + * useful to log or keep track of the contents of the protocol messages.
 + * The ProtocolCommandEvent class coupled with the
 + * {@link org.apache.commons.net.ProtocolCommandListener}
 + *  interface facilitate this process.
 + * <p>
 + * <p>
 + * @see ProtocolCommandListener
 + * @see ProtocolCommandSupport
 + * @author Daniel F. Savarese
 + ***/
 +
 +public class ProtocolCommandEvent extends EventObject
 +{
 +    private int __replyCode;
 +    private boolean __isCommand;
 +    private String __message, __command;
 +
 +    /***
 +     * Creates a ProtocolCommandEvent signalling a command was sent to
 +     * the server.  ProtocolCommandEvents created with this constructor
 +     * should only be sent after a command has been sent, but before the
 +     * reply has been received.
 +     * <p>
 +     * @param source  The source of the event.
 +     * @param command The string representation of the command type sent, not
 +     *      including the arguments (e.g., "STAT" or "GET").
 +     * @param message The entire command string verbatim as sent to the server,
 +     *        including all arguments.
 +     ***/
 +    public ProtocolCommandEvent(Object source, String command, String message)
 +    {
 +        super(source);
 +        __replyCode = 0;
 +        __message = message;
 +        __isCommand = true;
 +        __command = command;
 +    }
 +
 +
 +    /***
 +     * Creates a ProtocolCommandEvent signalling a reply to a command was
 +     * received.  ProtocolCommandEvents created with this constructor
 +     * should only be sent after a complete command reply has been received
 +     * fromt a server.
 +     * <p>
 +     * @param source  The source of the event.
 +     * @param replyCode The integer code indicating the natureof the reply.
 +     *   This will be the protocol integer value for protocols
 +     *   that use integer reply codes, or the reply class constant
 +     *   corresponding to the reply for protocols like POP3 that use
 +     *   strings like OK rather than integer codes (i.e., POP3Repy.OK).
 +     * @param message The entire reply as received from the server.
 +     ***/
 +    public ProtocolCommandEvent(Object source, int replyCode, String message)
 +    {
 +        super(source);
 +        __replyCode = replyCode;
 +        __message = message;
 +        __isCommand = false;
 +        __command = null;
 +    }
 +
 +    /***
 +     * Returns the string representation of the command type sent (e.g., "STAT"
 +     * or "GET").  If the ProtocolCommandEvent is a reply event, then null
 +     * is returned.
 +     * <p>
 +     * @return The string representation of the command type sent, or null
 +     *         if this is a reply event.
 +     ***/
 +    public String getCommand()
 +    {
 +        return __command;
 +    }
 +
 +
 +    /***
 +     * Returns the reply code of the received server reply.  Undefined if
 +     * this is not a reply event.
 +     * <p>
 +     * @return The reply code of the received server reply.  Undefined if
 +     *         not a reply event.
 +     ***/
 +    public int getReplyCode()
 +    {
 +        return __replyCode;
 +    }
 +
 +    /***
 +     * Returns true if the ProtocolCommandEvent was generated as a result
 +     * of sending a command.
 +     * <p>
 +     * @return true If the ProtocolCommandEvent was generated as a result
 +     * of sending a command.  False otherwise.
 +     ***/
 +    public boolean isCommand()
 +    {
 +        return __isCommand;
 +    }
 +
 +    /***
 +     * Returns true if the ProtocolCommandEvent was generated as a result
 +     * of receiving a reply.
 +     * <p>
 +     * @return true If the ProtocolCommandEvent was generated as a result
 +     * of receiving a reply.  False otherwise.
 +     ***/
 +    public boolean isReply()
 +    {
 +        return !isCommand();
 +    }
 +
 +    /***
 +     * Returns the entire message sent to or received from the server.
 +     * <p>
 +     * @return The entire message sent to or received from the server.
 +     ***/
 +    public String getMessage()
 +    {
 +        return __message;
 +    }
 +}
 diff --git a/src/org/apache/commons/net/ProtocolCommandListener.java b/src/org/apache/commons/net/ProtocolCommandListener.java new file mode 100644 index 0000000..8089926 --- /dev/null +++ b/src/org/apache/commons/net/ProtocolCommandListener.java @@ -0,0 +1,59 @@ +/*
 + * 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;
 +import java.util.EventListener;
 +
 +/***
 + * There exists a large class of IETF protocols that work by sending an
 + * ASCII text command and arguments to a server, and then receiving an
 + * ASCII text reply.  For debugging and other purposes, it is extremely
 + * useful to log or keep track of the contents of the protocol messages.
 + * The ProtocolCommandListener interface coupled with the
 + * {@link ProtocolCommandEvent} class facilitate this process.
 + * <p>
 + * To receive ProtocolCommandEvents, you merely implement the
 + * ProtocolCommandListener interface and register the class as a listener
 + * with a ProtocolCommandEvent source such as
 + * {@link org.apache.commons.net.ftp.FTPClient}.
 + * <p>
 + * <p>
 + * @see ProtocolCommandEvent
 + * @see ProtocolCommandSupport
 + * @author Daniel F. Savarese
 + ***/
 +
 +public interface ProtocolCommandListener extends EventListener
 +{
 +
 +    /***
 +     * This method is invoked by a ProtocolCommandEvent source after
 +     * sending a protocol command to a server.
 +     * <p>
 +     * @param event The ProtocolCommandEvent fired.
 +     ***/
 +    public void protocolCommandSent(ProtocolCommandEvent event);
 +
 +    /***
 +     * This method is invoked by a ProtocolCommandEvent source after
 +     * receiving a reply from a server.
 +     * <p>
 +     * @param event The ProtocolCommandEvent fired.
 +     ***/
 +    public void protocolReplyReceived(ProtocolCommandEvent event);
 +
 +}
 diff --git a/src/org/apache/commons/net/ProtocolCommandSupport.java b/src/org/apache/commons/net/ProtocolCommandSupport.java new file mode 100644 index 0000000..1a51fb6 --- /dev/null +++ b/src/org/apache/commons/net/ProtocolCommandSupport.java @@ -0,0 +1,134 @@ +/*
 + * 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;
 +
 +import java.io.Serializable;
 +import java.util.EventListener;
 +
 +import org.apache.commons.net.util.ListenerList;
 +
 +/***
 + * ProtocolCommandSupport is a convenience class for managing a list of
 + * ProtocolCommandListeners and firing ProtocolCommandEvents.  You can
 + * simply delegate ProtocolCommandEvent firing and listener
 + * registering/unregistering tasks to this class.
 + * <p>
 + * <p>
 + * @see ProtocolCommandEvent
 + * @see ProtocolCommandListener
 + * @author Daniel F. Savarese
 + ***/
 +
 +public class ProtocolCommandSupport implements Serializable
 +{
 +    private Object __source;
 +    private ListenerList __listeners;
 +
 +    /***
 +     * Creates a ProtocolCommandSupport instant using the indicated source
 +     * as the source of fired ProtocolCommandEvents.
 +     * <p>
 +     * @param source  The source to use for all generated ProtocolCommandEvents.
 +     ***/
 +    public ProtocolCommandSupport(Object source)
 +    {
 +        __listeners = new ListenerList();
 +        __source = source;
 +    }
 +
 +
 +    /***
 +     * Fires a ProtocolCommandEvent signalling the sending of a command to all
 +     * registered listeners, invoking their
 +     * {@link org.apache.commons.net.ProtocolCommandListener#protocolCommandSent protocolCommandSent() }
 +     *  methods.
 +     * <p>
 +     * @param command The string representation of the command type sent, not
 +     *      including the arguments (e.g., "STAT" or "GET").
 +     * @param message The entire command string verbatim as sent to the server,
 +     *        including all arguments.
 +     ***/
 +    public void fireCommandSent(String command, String message)
 +    {
 +        ProtocolCommandEvent event;
 +
 +        event = new ProtocolCommandEvent(__source, command, message);
 +
 +        for (EventListener listener : __listeners)
 +        {
 +           ((ProtocolCommandListener)listener).protocolCommandSent(event);
 +        }
 +    }
 +
 +    /***
 +     * Fires a ProtocolCommandEvent signalling the reception of a command reply
 +     * to all registered listeners, invoking their
 +     * {@link org.apache.commons.net.ProtocolCommandListener#protocolReplyReceived protocolReplyReceived() }
 +     *  methods.
 +     * <p>
 +     * @param replyCode The integer code indicating the natureof the reply.
 +     *   This will be the protocol integer value for protocols
 +     *   that use integer reply codes, or the reply class constant
 +     *   corresponding to the reply for protocols like POP3 that use
 +     *   strings like OK rather than integer codes (i.e., POP3Repy.OK).
 +     * @param message The entire reply as received from the server.
 +     ***/
 +    public void fireReplyReceived(int replyCode, String message)
 +    {
 +        ProtocolCommandEvent event;
 +        event = new ProtocolCommandEvent(__source, replyCode, message);
 +
 +        for (EventListener listener : __listeners)
 +        {
 +            ((ProtocolCommandListener)listener).protocolReplyReceived(event);
 +        }
 +    }
 +
 +    /***
 +     * Adds a ProtocolCommandListener.
 +     * <p>
 +     * @param listener  The ProtocolCommandListener to add.
 +     ***/
 +    public void addProtocolCommandListener(ProtocolCommandListener listener)
 +    {
 +        __listeners.addListener(listener);
 +    }
 +
 +    /***
 +     * Removes a ProtocolCommandListener.
 +     * <p>
 +     * @param listener  The ProtocolCommandListener to remove.
 +     ***/
 +    public void removeProtocolCommandListener(ProtocolCommandListener listener)
 +    {
 +        __listeners.removeListener(listener);
 +    }
 +
 +
 +    /***
 +     * Returns the number of ProtocolCommandListeners currently registered.
 +     * <p>
 +     * @return The number of ProtocolCommandListeners currently registered.
 +     ***/
 +    public int getListenerCount()
 +    {
 +        return __listeners.getListenerCount();
 +    }
 +
 +}
 +
 diff --git a/src/org/apache/commons/net/SocketClient.java b/src/org/apache/commons/net/SocketClient.java new file mode 100644 index 0000000..a5a8ead --- /dev/null +++ b/src/org/apache/commons/net/SocketClient.java @@ -0,0 +1,586 @@ +/*
 + * 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;
 +
 +import java.io.IOException;
 +import java.io.InputStream;
 +import java.io.OutputStream;
 +import java.net.InetAddress;
 +import java.net.InetSocketAddress;
 +import java.net.Socket;
 +import java.net.SocketException;
 +import java.net.UnknownHostException;
 +
 +import javax.net.ServerSocketFactory;
 +import javax.net.SocketFactory;
 +
 +
 +/**
 + * The SocketClient provides the basic operations that are required of
 + * client objects accessing sockets.  It is meant to be
 + * subclassed to avoid having to rewrite the same code over and over again
 + * to open a socket, close a socket, set timeouts, etc.  Of special note
 + * is the {@link #setSocketFactory  setSocketFactory }
 + * method, which allows you to control the type of Socket the SocketClient
 + * creates for initiating network connections.  This is especially useful
 + * for adding SSL or proxy support as well as better support for applets.  For
 + * example, you could create a
 + * {@link org.apache.commons.net.SocketFactory} that
 + * requests browser security capabilities before creating a socket.
 + * All classes derived from SocketClient should use the
 + * {@link #_socketFactory_  _socketFactory_ } member variable to
 + * create Socket and ServerSocket instances rather than instanting
 + * them by directly invoking a constructor.  By honoring this contract
 + * you guarantee that a user will always be able to provide his own
 + * Socket implementations by substituting his own SocketFactory.
 + * @author Daniel F. Savarese
 + * @see SocketFactory
 + */
 +public abstract class SocketClient
 +{
 +    /**
 +     * The end of line character sequence used by most IETF protocols.  That
 +     * is a carriage return followed by a newline: "\r\n"
 +     */
 +    public static final String NETASCII_EOL = "\r\n";
 +
 +    /** The default SocketFactory shared by all SocketClient instances. */
 +    private static final SocketFactory __DEFAULT_SOCKET_FACTORY =
 +            SocketFactory.getDefault();
 +    
 +    private static final ServerSocketFactory __DEFAULT_SERVER_SOCKET_FACTORY = 
 +            ServerSocketFactory.getDefault();
 +
 +    /** The timeout to use after opening a socket. */
 +    protected int _timeout_;
 +
 +    /** The socket used for the connection. */
 +    protected Socket _socket_;
 +
 +    /** The default port the client should connect to. */
 +    protected int _defaultPort_;
 +
 +    /** The socket's InputStream. */
 +    protected InputStream _input_;
 +
 +    /** The socket's OutputStream. */
 +    protected OutputStream _output_;
 +
 +    /** The socket's SocketFactory. */
 +    protected SocketFactory _socketFactory_;
 +    
 +    /** The socket's ServerSocket Factory. */
 +    protected ServerSocketFactory _serverSocketFactory_;
 +    
 +    /** The socket's connect timeout (0 = infinite timeout) */
 +    private static final int DEFAULT_CONNECT_TIMEOUT = 0;
 +    protected int connectTimeout = DEFAULT_CONNECT_TIMEOUT;
 +
 +    /**
 +     * Default constructor for SocketClient.  Initializes
 +     * _socket_ to null, _timeout_ to 0, _defaultPort to 0,
 +     * _isConnected_ to false, and _socketFactory_ to a shared instance of
 +     * {@link org.apache.commons.net.DefaultSocketFactory}.
 +     */
 +    public SocketClient()
 +    {
 +        _socket_ = null;
 +        _input_ = null;
 +        _output_ = null;
 +        _timeout_ = 0;
 +        _defaultPort_ = 0;
 +        _socketFactory_ = __DEFAULT_SOCKET_FACTORY;
 +        _serverSocketFactory_ = __DEFAULT_SERVER_SOCKET_FACTORY;
 +    }
 +
 +
 +    /**
 +     * Because there are so many connect() methods, the _connectAction_()
 +     * method is provided as a means of performing some action immediately
 +     * after establishing a connection, rather than reimplementing all
 +     * of the connect() methods.  The last action performed by every
 +     * connect() method after opening a socket is to call this method.
 +     * <p>
 +     * This method sets the timeout on the just opened socket to the default
 +     * timeout set by {@link #setDefaultTimeout  setDefaultTimeout() },
 +     * sets _input_ and _output_ to the socket's InputStream and OutputStream
 +     * respectively, and sets _isConnected_ to true.
 +     * <p>
 +     * Subclasses overriding this method should start by calling
 +     * <code> super._connectAction_() </code> first to ensure the
 +     * initialization of the aforementioned protected variables.
 +     */
 +    protected void _connectAction_() throws IOException
 +    {
 +        _socket_.setSoTimeout(_timeout_);
 +        _input_ = _socket_.getInputStream();
 +        _output_ = _socket_.getOutputStream();
 +    }
 +
 +
 +    /**
 +     * Opens a Socket connected to a remote host at the specified port and
 +     * originating from the current host at a system assigned port.
 +     * Before returning, {@link #_connectAction_  _connectAction_() }
 +     * is called to perform connection initialization actions.
 +     * <p>
 +     * @param host  The remote host.
 +     * @param port  The port to connect to on the remote host.
 +     * @exception SocketException If the socket timeout could not be set.
 +     * @exception IOException If the socket could not be opened.  In most
 +     *  cases you will only want to catch IOException since SocketException is
 +     *  derived from it.
 +     */
 +    public void connect(InetAddress host, int port)
 +    throws SocketException, IOException
 +    {
 +        _socket_ = _socketFactory_.createSocket();
 +        _socket_.connect(new InetSocketAddress(host, port), connectTimeout);
 +
 +        _connectAction_();
 +    }
 +
 +    /**
 +     * Opens a Socket connected to a remote host at the specified port and
 +     * originating from the current host at a system assigned port.
 +     * Before returning, {@link #_connectAction_  _connectAction_() }
 +     * is called to perform connection initialization actions.
 +     * <p>
 +     * @param hostname  The name of the remote host.
 +     * @param port  The port to connect to on the remote host.
 +     * @exception SocketException If the socket timeout could not be set.
 +     * @exception IOException If the socket could not be opened.  In most
 +     *  cases you will only want to catch IOException since SocketException is
 +     *  derived from it.
 +     * @exception UnknownHostException If the hostname cannot be resolved.
 +     */
 +    public void connect(String hostname, int port)
 +    throws SocketException, IOException
 +    {
 +        _socket_= _socketFactory_.createSocket();
 +        _socket_.connect(new InetSocketAddress(hostname, port), connectTimeout);
 +        
 +        _connectAction_();
 +    }
 +
 +
 +    /**
 +     * Opens a Socket connected to a remote host at the specified port and
 +     * originating from the specified local address and port.
 +     * Before returning, {@link #_connectAction_  _connectAction_() }
 +     * is called to perform connection initialization actions.
 +     * <p>
 +     * @param host  The remote host.
 +     * @param port  The port to connect to on the remote host.
 +     * @param localAddr  The local address to use.
 +     * @param localPort  The local port to use.
 +     * @exception SocketException If the socket timeout could not be set.
 +     * @exception IOException If the socket could not be opened.  In most
 +     *  cases you will only want to catch IOException since SocketException is
 +     *  derived from it.
 +     */
 +    public void connect(InetAddress host, int port,
 +                        InetAddress localAddr, int localPort)
 +    throws SocketException, IOException
 +    {
 +        _socket_ = _socketFactory_.createSocket();
 +        _socket_.bind(new InetSocketAddress(localAddr, localPort));
 +        _socket_.connect(new InetSocketAddress(host, port), connectTimeout);
 +        
 +        _connectAction_();
 +    }
 +
 +
 +    /**
 +     * Opens a Socket connected to a remote host at the specified port and
 +     * originating from the specified local address and port.
 +     * Before returning, {@link #_connectAction_  _connectAction_() }
 +     * is called to perform connection initialization actions.
 +     * <p>
 +     * @param hostname  The name of the remote host.
 +     * @param port  The port to connect to on the remote host.
 +     * @param localAddr  The local address to use.
 +     * @param localPort  The local port to use.
 +     * @exception SocketException If the socket timeout could not be set.
 +     * @exception IOException If the socket could not be opened.  In most
 +     *  cases you will only want to catch IOException since SocketException is
 +     *  derived from it.
 +     * @exception UnknownHostException If the hostname cannot be resolved.
 +     */
 +    public void connect(String hostname, int port,
 +                        InetAddress localAddr, int localPort)
 +    throws SocketException, IOException
 +    {
 +        _socket_ =
 +            _socketFactory_.createSocket(hostname, port, localAddr, localPort);
 +        _connectAction_();
 +    }
 +
 +
 +    /**
 +     * Opens a Socket connected to a remote host at the current default port
 +     * and originating from the current host at a system assigned port.
 +     * Before returning, {@link #_connectAction_  _connectAction_() }
 +     * is called to perform connection initialization actions.
 +     * <p>
 +     * @param host  The remote host.
 +     * @exception SocketException If the socket timeout could not be set.
 +     * @exception IOException If the socket could not be opened.  In most
 +     *  cases you will only want to catch IOException since SocketException is
 +     *  derived from it.
 +     */
 +    public void connect(InetAddress host) throws SocketException, IOException
 +    {
 +        connect(host, _defaultPort_);
 +    }
 +
 +
 +    /**
 +     * Opens a Socket connected to a remote host at the current default
 +     * port and originating from the current host at a system assigned port.
 +     * Before returning, {@link #_connectAction_  _connectAction_() }
 +     * is called to perform connection initialization actions.
 +     * <p>
 +     * @param hostname  The name of the remote host.
 +     * @exception SocketException If the socket timeout could not be set.
 +     * @exception IOException If the socket could not be opened.  In most
 +     *  cases you will only want to catch IOException since SocketException is
 +     *  derived from it.
 +     * @exception UnknownHostException If the hostname cannot be resolved.
 +     */
 +    public void connect(String hostname) throws SocketException, IOException
 +    {
 +        connect(hostname, _defaultPort_);
 +    }
 +
 +
 +    /**
 +     * Disconnects the socket connection.
 +     * You should call this method after you've finished using the class
 +     * instance and also before you call
 +     * {@link #connect connect() }
 +     * again.  _isConnected_ is set to false, _socket_ is set to null,
 +     * _input_ is set to null, and _output_ is set to null.
 +     * <p>
 +     * @exception IOException  If there is an error closing the socket.
 +     */
 +    public void disconnect() throws IOException
 +    {
 +        if (_socket_ != null) _socket_.close();
 +        if (_input_ != null) _input_.close();
 +        if (_output_ != null) _output_.close();
 +        if (_socket_ != null) _socket_ = null;
 +        _input_ = null;
 +        _output_ = null;
 +    }
 +
 +
 +    /**
 +     * Returns true if the client is currently connected to a server.
 +     * <p>
 +     * @return True if the client is currently connected to a server,
 +     *         false otherwise.
 +     */
 +    public boolean isConnected()
 +    {
 +        if (_socket_ == null)
 +            return false;
 +        
 +        return _socket_.isConnected();
 +    }
 +
 +
 +    /**
 +     * Sets the default port the SocketClient should connect to when a port
 +     * is not specified.  The {@link #_defaultPort_  _defaultPort_ }
 +     * variable stores this value.  If never set, the default port is equal
 +     * to zero.
 +     * <p>
 +     * @param port  The default port to set.
 +     */
 +    public void setDefaultPort(int port)
 +    {
 +        _defaultPort_ = port;
 +    }
 +
 +    /**
 +     * Returns the current value of the default port (stored in
 +     * {@link #_defaultPort_  _defaultPort_ }).
 +     * <p>
 +     * @return The current value of the default port.
 +     */
 +    public int getDefaultPort()
 +    {
 +        return _defaultPort_;
 +    }
 +
 +
 +    /**
 +     * Set the default timeout in milliseconds to use when opening a socket.
 +     * This value is only used previous to a call to
 +     * {@link #connect connect()}
 +     * and should not be confused with {@link #setSoTimeout setSoTimeout()}
 +     * which operates on an the currently opened socket.  _timeout_ contains
 +     * the new timeout value.
 +     * <p>
 +     * @param timeout  The timeout in milliseconds to use for the socket
 +     *                 connection.
 +     */
 +    public void setDefaultTimeout(int timeout)
 +    {
 +        _timeout_ = timeout;
 +    }
 +
 +
 +    /**
 +     * Returns the default timeout in milliseconds that is used when
 +     * opening a socket.
 +     * <p>
 +     * @return The default timeout in milliseconds that is used when
 +     *         opening a socket.
 +     */
 +    public int getDefaultTimeout()
 +    {
 +        return _timeout_;
 +    }
 +
 +
 +    /**
 +     * Set the timeout in milliseconds of a currently open connection.
 +     * Only call this method after a connection has been opened
 +     * by {@link #connect connect()}.
 +     * <p>
 +     * @param timeout  The timeout in milliseconds to use for the currently
 +     *                 open socket connection.
 +     * @exception SocketException If the operation fails.
 +     */
 +    public void setSoTimeout(int timeout) throws SocketException
 +    {
 +        _socket_.setSoTimeout(timeout);
 +    }
 +    
 +    
 +    /**
 +     * Set the underlying socket send buffer size.
 +     * <p>
 +     * @param size The size of the buffer in bytes.
 +     * @throws SocketException 
 +     * @since 2.0
 +     */
 +    public void setSendBufferSize(int size) throws SocketException {
 +        _socket_.setSendBufferSize(size);
 +    }
 +    
 +    
 +    /**
 +     * Sets the underlying socket receive buffer size.
 +     * <p>
 +     * @param size The size of the buffer in bytes.
 +     * @throws SocketException 
 +     * @since 2.0
 +     */
 +    public void setReceiveBufferSize(int size) throws SocketException  {
 +        _socket_.setReceiveBufferSize(size);
 +    }
 +
 +
 +    /**
 +     * Returns the timeout in milliseconds of the currently opened socket.
 +     * <p>
 +     * @return The timeout in milliseconds of the currently opened socket.
 +     * @exception SocketException If the operation fails.
 +     */
 +    public int getSoTimeout() throws SocketException
 +    {
 +        return _socket_.getSoTimeout();
 +    }
 +
 +    /**
 +     * Enables or disables the Nagle's algorithm (TCP_NODELAY) on the
 +     * currently opened socket.
 +     * <p>
 +     * @param on  True if Nagle's algorithm is to be enabled, false if not.
 +     * @exception SocketException If the operation fails.
 +     */
 +    public void setTcpNoDelay(boolean on) throws SocketException
 +    {
 +        _socket_.setTcpNoDelay(on);
 +    }
 +
 +
 +    /**
 +     * Returns true if Nagle's algorithm is enabled on the currently opened
 +     * socket.
 +     * <p>
 +     * @return True if Nagle's algorithm is enabled on the currently opened
 +     *        socket, false otherwise.
 +     * @exception SocketException If the operation fails.
 +     */
 +    public boolean getTcpNoDelay() throws SocketException
 +    {
 +        return _socket_.getTcpNoDelay();
 +    }
 +
 +
 +    /**
 +     * Sets the SO_LINGER timeout on the currently opened socket.
 +     * <p>
 +     * @param on  True if linger is to be enabled, false if not.
 +     * @param val The linger timeout (in hundredths of a second?)
 +     * @exception SocketException If the operation fails.
 +     */
 +    public void setSoLinger(boolean on, int val) throws SocketException
 +    {
 +        _socket_.setSoLinger(on, val);
 +    }
 +
 +
 +    /**
 +     * Returns the current SO_LINGER timeout of the currently opened socket.
 +     * <p>
 +     * @return The current SO_LINGER timeout.  If SO_LINGER is disabled returns
 +     *         -1.
 +     * @exception SocketException If the operation fails.
 +     */
 +    public int getSoLinger() throws SocketException
 +    {
 +        return _socket_.getSoLinger();
 +    }
 +
 +
 +    /**
 +     * Returns the port number of the open socket on the local host used
 +     * for the connection.
 +     * <p>
 +     * @return The port number of the open socket on the local host used
 +     *         for the connection.
 +     */
 +    public int getLocalPort()
 +    {
 +        return _socket_.getLocalPort();
 +    }
 +
 +
 +    /**
 +     * Returns the local address to which the client's socket is bound.
 +     * <p>
 +     * @return The local address to which the client's socket is bound.
 +     */
 +    public InetAddress getLocalAddress()
 +    {
 +        return _socket_.getLocalAddress();
 +    }
 +
 +    /**
 +     * Returns the port number of the remote host to which the client is
 +     * connected.
 +     * <p>
 +     * @return The port number of the remote host to which the client is
 +     *         connected.
 +     */
 +    public int getRemotePort()
 +    {
 +        return _socket_.getPort();
 +    }
 +
 +
 +    /**
 +     * @return The remote address to which the client is connected.
 +     */
 +    public InetAddress getRemoteAddress()
 +    {
 +        return _socket_.getInetAddress();
 +    }
 +
 +
 +    /**
 +     * Verifies that the remote end of the given socket is connected to the
 +     * the same host that the SocketClient is currently connected to.  This
 +     * is useful for doing a quick security check when a client needs to
 +     * accept a connection from a server, such as an FTP data connection or
 +     * a BSD R command standard error stream.
 +     * <p>
 +     * @return True if the remote hosts are the same, false if not.
 +     */
 +    public boolean verifyRemote(Socket socket)
 +    {
 +        InetAddress host1, host2;
 +
 +        host1 = socket.getInetAddress();
 +        host2 = getRemoteAddress();
 +
 +        return host1.equals(host2);
 +    }
 +
 +
 +    /**
 +     * Sets the SocketFactory used by the SocketClient to open socket
 +     * connections.  If the factory value is null, then a default
 +     * factory is used (only do this to reset the factory after having
 +     * previously altered it).
 +     * <p>
 +     * @param factory  The new SocketFactory the SocketClient should use.
 +     */
 +    public void setSocketFactory(SocketFactory factory)
 +    {
 +        if (factory == null)
 +            _socketFactory_ = __DEFAULT_SOCKET_FACTORY;
 +        else
 +            _socketFactory_ = factory;
 +    }
 +    
 +    /**
 +     * Sets the ServerSocketFactory used by the SocketClient to open ServerSocket
 +     * connections.  If the factory value is null, then a default
 +     * factory is used (only do this to reset the factory after having
 +     * previously altered it).
 +     * <p>
 +     * @param factory  The new ServerSocketFactory the SocketClient should use.
 +     * @since 2.0
 +     */
 +    public void setServerSocketFactory(ServerSocketFactory factory) {
 +        if (factory == null)
 +            _serverSocketFactory_ = __DEFAULT_SERVER_SOCKET_FACTORY;
 +        else
 +            _serverSocketFactory_ = factory;
 +    }
 +    
 +    /**
 +     * Sets the connection timeout in milliseconds, which will be passed to the {@link Socket} object's
 +     * connect() method. 
 +     * @param connectTimeout The connection timeout to use (in ms)
 +     * @since 2.0
 +     */
 +    public void setConnectTimeout(int connectTimeout) {
 +        this.connectTimeout = connectTimeout;
 +    }
 +    
 +    /**
 +     * Get the underlying socket connection timeout.
 +     * @return
 +     * @since 2.0
 +     */
 +    public int getConnectTimeout() {
 +        return connectTimeout;
 +    }
 +    
 +    
 +    
 +}
 +
 +
 diff --git a/src/org/apache/commons/net/ftp/Configurable.java b/src/org/apache/commons/net/ftp/Configurable.java new file mode 100644 index 0000000..5e8e749 --- /dev/null +++ b/src/org/apache/commons/net/ftp/Configurable.java @@ -0,0 +1,35 @@ +/*
 + * 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;
 +
 +
 +/**
 + * This interface adds the aspect of configurability by means of
 + * a supplied FTPClientConfig object to other classes in the
 + * system, especially listing parsers.
 + */
 +public interface Configurable {
 +
 +    /**
 +     * @param config the object containing the configuration data
 +     * @throws IllegalArgumentException if the elements of the 
 +     * <code>config</code> are somehow inadequate to configure the
 +     * Configurable object.
 +     */
 +    public void configure(FTPClientConfig config);
 +}
 diff --git a/src/org/apache/commons/net/ftp/FTP.java b/src/org/apache/commons/net/ftp/FTP.java new file mode 100644 index 0000000..6e2eff8 --- /dev/null +++ b/src/org/apache/commons/net/ftp/FTP.java @@ -0,0 +1,1513 @@ +/*
 + * 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;
 +import java.io.BufferedReader;
 +import java.io.BufferedWriter;
 +import java.io.IOException;
 +import java.io.InputStreamReader;
 +import java.io.OutputStreamWriter;
 +import java.net.InetAddress;
 +import java.net.Socket;
 +import java.net.SocketException;
 +import java.util.ArrayList;
 +import java.util.Arrays;
 +
 +import org.apache.commons.net.MalformedServerReplyException;
 +import org.apache.commons.net.ProtocolCommandListener;
 +import org.apache.commons.net.ProtocolCommandSupport;
 +import org.apache.commons.net.SocketClient;
 +
 +/***
 + * FTP provides the basic the functionality necessary to implement your
 + * own FTP client.  It extends org.apache.commons.net.SocketClient since
 + * extending TelnetClient was causing unwanted behavior (like connections
 + * that did not time out properly).
 + * <p>
 + * To derive the full benefits of the FTP class requires some knowledge
 + * of the FTP protocol defined in RFC 959.  However, there is no reason
 + * why you should have to use the FTP class.  The
 + * {@link org.apache.commons.net.ftp.FTPClient} class,
 + * derived from FTP,
 + * implements all the functionality required of an FTP client.  The
 + * FTP class is made public to provide access to various FTP constants
 + * and to make it easier for adventurous programmers (or those with
 + * special needs) to interact with the FTP protocol and implement their
 + * own clients.  A set of methods with names corresponding to the FTP
 + * command names are provided to facilitate this interaction.
 + * <p>
 + * You should keep in mind that the FTP server may choose to prematurely
 + * close a connection if the client has been idle for longer than a
 + * given time period (usually 900 seconds).  The FTP class will detect a
 + * premature FTP server connection closing when it receives a
 + * {@link org.apache.commons.net.ftp.FTPReply#SERVICE_NOT_AVAILABLE FTPReply.SERVICE_NOT_AVAILABLE }
 + *  response to a command.
 + * When that occurs, the FTP class method encountering that reply will throw
 + * an {@link org.apache.commons.net.ftp.FTPConnectionClosedException}
 + * .  <code>FTPConectionClosedException</code>
 + * is a subclass of <code> IOException </code> and therefore need not be
 + * caught separately, but if you are going to catch it separately, its
 + * catch block must appear before the more general <code> IOException </code>
 + * catch block.  When you encounter an
 + * {@link org.apache.commons.net.ftp.FTPConnectionClosedException}
 + * , you must disconnect the connection with
 + * {@link #disconnect  disconnect() } to properly clean up the
 + * system resources used by FTP.  Before disconnecting, you may check the
 + * last reply code and text with
 + * {@link #getReplyCode  getReplyCode },
 + * {@link #getReplyString  getReplyString },
 + * and {@link #getReplyStrings  getReplyStrings}.
 + * You may avoid server disconnections while the client is idle by
 + * periodicaly sending NOOP commands to the server.
 + * <p>
 + * Rather than list it separately for each method, we mention here that
 + * every method communicating with the server and throwing an IOException
 + * can also throw a
 + * {@link org.apache.commons.net.MalformedServerReplyException}
 + * , which is a subclass
 + * of IOException.  A MalformedServerReplyException will be thrown when
 + * the reply received from the server deviates enough from the protocol
 + * specification that it cannot be interpreted in a useful manner despite
 + * attempts to be as lenient as possible.
 + * <p>
 + * <p>
 + * @author Daniel F. Savarese
 + * @author Rory Winston 
 + * @author Joseph Hindsley
 + * @see FTPClient
 + * @see FTPConnectionClosedException
 + * @see org.apache.commons.net.MalformedServerReplyException
 + * @version $Id: FTP.java 658520 2008-05-21 01:14:11Z sebb $
 + ***/
 +
 +public class FTP extends SocketClient
 +{
 +    /*** The default FTP data port (20). ***/
 +    public static final int DEFAULT_DATA_PORT = 20;
 +    /*** The default FTP control port (21). ***/
 +    public static final int DEFAULT_PORT = 21;
 +
 +    /***
 +     * A constant used to indicate the file(s) being transfered should
 +     * be treated as ASCII.  This is the default file type.  All constants
 +     * ending in <code>FILE_TYPE</code> are used to indicate file types.
 +     ***/
 +    public static final int ASCII_FILE_TYPE = 0;
 +
 +    /***
 +     * A constant used to indicate the file(s) being transfered should
 +     * be treated as EBCDIC.  Note however that there are several different
 +     * EBCDIC formats.  All constants ending in <code>FILE_TYPE</code>
 +     * are used to indicate file types.
 +     ***/
 +    public static final int EBCDIC_FILE_TYPE = 1;
 +
 +   
 +    /***
 +     * A constant used to indicate the file(s) being transfered should
 +     * be treated as a binary image, i.e., no translations should be
 +     * performed.  All constants ending in <code>FILE_TYPE</code> are used to
 +     * indicate file types.
 +     ***/
 +    public static final int BINARY_FILE_TYPE = 2;
 +
 +    /***
 +     * A constant used to indicate the file(s) being transfered should
 +     * be treated as a local type.  All constants ending in
 +     * <code>FILE_TYPE</code> are used to indicate file types.
 +     ***/
 +    public static final int LOCAL_FILE_TYPE = 3;
 +
 +    /***
 +     * A constant used for text files to indicate a non-print text format.
 +     * This is the default format.
 +     * All constants ending in <code>TEXT_FORMAT</code> are used to indicate
 +     * text formatting for text transfers (both ASCII and EBCDIC).
 +     ***/
 +    public static final int NON_PRINT_TEXT_FORMAT = 4;
 +
 +    /***
 +     * A constant used to indicate a text file contains format vertical format
 +     * control characters.
 +     * All constants ending in <code>TEXT_FORMAT</code> are used to indicate
 +     * text formatting for text transfers (both ASCII and EBCDIC).
 +     ***/
 +    public static final int TELNET_TEXT_FORMAT = 5;
 +
 +    /***
 +     * A constant used to indicate a text file contains ASA vertical format
 +     * control characters.
 +     * All constants ending in <code>TEXT_FORMAT</code> are used to indicate
 +     * text formatting for text transfers (both ASCII and EBCDIC).
 +     ***/
 +    public static final int CARRIAGE_CONTROL_TEXT_FORMAT = 6;
 +
 +    /***
 +     * A constant used to indicate a file is to be treated as a continuous
 +     * sequence of bytes.  This is the default structure.  All constants ending
 +     * in <code>_STRUCTURE</code> are used to indicate file structure for
 +     * file transfers.
 +     ***/
 +    public static final int FILE_STRUCTURE = 7;
 +
 +    /***
 +     * A constant used to indicate a file is to be treated as a sequence
 +     * of records.  All constants ending in <code>_STRUCTURE</code>
 +     * are used to indicate file structure for file transfers.
 +     ***/
 +    public static final int RECORD_STRUCTURE = 8;
 +
 +    /***
 +     * A constant used to indicate a file is to be treated as a set of
 +     * independent indexed pages.  All constants ending in
 +     * <code>_STRUCTURE</code> are used to indicate file structure for file
 +     * transfers.
 +     ***/
 +    public static final int PAGE_STRUCTURE = 9;
 +
 +    /***
 +     * A constant used to indicate a file is to be transfered as a stream
 +     * of bytes.  This is the default transfer mode.  All constants ending
 +     * in <code>TRANSFER_MODE</code> are used to indicate file transfer
 +     * modes.
 +     ***/
 +    public static final int STREAM_TRANSFER_MODE = 10;
 +
 +    /***
 +     * A constant used to indicate a file is to be transfered as a series
 +     * of blocks.  All constants ending in <code>TRANSFER_MODE</code> are used
 +     * to indicate file transfer modes.
 +     ***/
 +    public static final int BLOCK_TRANSFER_MODE = 11;
 +
 +    /***
 +     * A constant used to indicate a file is to be transfered as FTP
 +     * compressed data.  All constants ending in <code>TRANSFER_MODE</code>
 +     * are used to indicate file transfer modes.
 +     ***/
 +    public static final int COMPRESSED_TRANSFER_MODE = 12;
 +
 +    // We have to ensure that the protocol communication is in ASCII
 +    // but we use ISO-8859-1 just in case 8-bit characters cross
 +    // the wire.
 +    /**
 +     * The default character encoding used for communicating over an
 +     * FTP control connection.  The default encoding is an
 +     * ASCII-compatible encoding.  Some FTP servers expect other
 +     * encodings.  You can change the encoding used by an FTP instance
 +     * with {@link #setControlEncoding setControlEncoding}.
 +     */
 +    public static final String DEFAULT_CONTROL_ENCODING = "ISO-8859-1";
 +    private static final String __modes = "AEILNTCFRPSBC";
 +
 +    private StringBuilder __commandBuffer = new StringBuilder();
 +
 +    protected int _replyCode;
 +    protected ArrayList<String> _replyLines;
 +    protected boolean _newReplyString;
 +    protected String _replyString;
 +    protected String _controlEncoding;
 +    
 +    /**
 +     * This is used to signal whether a block of multiline responses beginning
 +     * with xxx must be terminated by the same numeric code xxx
 +     * See section 4.2 of RFX 959 for details. 
 +     */
 +    protected boolean strictMultilineParsing = false;
 +
 +    /**
 +     * Wraps SocketClient._input_ to facilitate the writing of text
 +     * to the FTP control connection.  Do not access the control
 +     * connection via SocketClient._input_.  This member starts
 +     * with a null value, is initialized in {@link #_connectAction_},
 +     * and set to null in {@link #disconnect}.
 +     */
 +    protected BufferedReader _controlInput_;
 +
 +    /**
 +     * Wraps SocketClient._output_ to facilitate the reading of text
 +     * from the FTP control connection.  Do not access the control
 +     * connection via SocketClient._output_.  This member starts
 +     * with a null value, is initialized in {@link #_connectAction_},
 +     * and set to null in {@link #disconnect}.
 +     */
 +    protected BufferedWriter _controlOutput_;
 +
 +    /***
 +     * A ProtocolCommandSupport object used to manage the registering of
 +     * ProtocolCommandListeners and te firing of ProtocolCommandEvents.
 +     ***/
 +    protected ProtocolCommandSupport _commandSupport_;
 +
 +    /***
 +     * The default FTP constructor.  Sets the default port to
 +     * <code>DEFAULT_PORT</code> and initializes internal data structures
 +     * for saving FTP reply information.
 +     ***/
 +    public FTP()
 +    {
 +        super();
 +        setDefaultPort(DEFAULT_PORT);
 +        _replyLines = new ArrayList<String>();
 +        _newReplyString = false;
 +        _replyString = null;
 +        _commandSupport_ = new ProtocolCommandSupport(this);
 +        _controlEncoding = DEFAULT_CONTROL_ENCODING;
 +    }
 +
 +    // The RFC-compliant multiline termination check
 +    private boolean __strictCheck(String line, String code) {
 +        return (!(line.startsWith(code) && line.charAt(3) == ' '));
 +    }
 +    
 +    // The strict check is too strong a condition because of non-conforming ftp
 +    // servers like ftp.funet.fi which sent 226 as the last line of a
 +    // 426 multi-line reply in response to ls /.  We relax the condition to
 +    // test that the line starts with a digit rather than starting with
 +    // the code.
 +    private boolean __lenientCheck(String line) {
 +        return (!(line.length() >= 4 && line.charAt(3) != '-' &&
 +                Character.isDigit(line.charAt(0))));
 +    }
 +    
 +    private void __getReply() throws IOException
 +    {
 +        int length;
 +
 +        _newReplyString = true;
 +        _replyLines.clear();
 +
 +        String line = _controlInput_.readLine();
 +
 +        if (line == null)
 +            throw new FTPConnectionClosedException(
 +                "Connection closed without indication.");
 +
 +        // In case we run into an anomaly we don't want fatal index exceptions
 +        // to be thrown.
 +        length = line.length();
 +        if (length < 3)
 +            throw new MalformedServerReplyException(
 +                "Truncated server reply: " + line);
 +        
 +        String code = null;
 +        try
 +        {
 +            code = line.substring(0, 3);
 +            _replyCode = Integer.parseInt(code);
 +        }
 +        catch (NumberFormatException e)
 +        {
 +            throw new MalformedServerReplyException(
 +                "Could not parse response code.\nServer Reply: " + line);
 +        }
 +
 +        _replyLines.add(line);
 +
 +        // Get extra lines if message continues.
 +        if (length > 3 && line.charAt(3) == '-')
 +        {
 +            do
 +            {
 +                line = _controlInput_.readLine();
 +
 +                if (line == null)
 +                    throw new FTPConnectionClosedException(
 +                        "Connection closed without indication.");
 +
 +                _replyLines.add(line);
 +
 +                // The length() check handles problems that could arise from readLine()
 +                // returning too soon after encountering a naked CR or some other
 +                // anomaly.
 +            }
 +            while ( isStrictMultilineParsing() ? __strictCheck(line, code) : __lenientCheck(line));
 +        }
 +
 +        if (_commandSupport_.getListenerCount() > 0) {
 +            _commandSupport_.fireReplyReceived(_replyCode, getReplyString());
 +        }
 +
 +        if (_replyCode == FTPReply.SERVICE_NOT_AVAILABLE) {
 +            throw new FTPConnectionClosedException("FTP response 421 received.  Server closed connection.");
 +        }
 +    }
 +
 +    /**
 +     * Initiates control connections and gets initial reply.
 +     * Initializes {@link #_controlInput_} and {@link #_controlOutput_}.
 +     */
 +    @Override
 +    protected void _connectAction_() throws IOException
 +    {
 +        super._connectAction_();
 +        _controlInput_ =
 +            new BufferedReader(new InputStreamReader(_socket_.getInputStream(),
 +                                                     getControlEncoding()));
 +        _controlOutput_ =
 +            new BufferedWriter(new OutputStreamWriter(_socket_.getOutputStream(),
 +                                                      getControlEncoding()));
 +        __getReply();
 +        // If we received code 120, we have to fetch completion reply.
 +        if (FTPReply.isPositivePreliminary(_replyCode))
 +            __getReply();
 +    }
 +
 +
 +    /**
 +     * Sets the character encoding used by the FTP control connection.
 +     * Some FTP servers require that commands be issued in a non-ASCII
 +     * encoding like UTF-8 so that filenames with multi-byte character
 +     * representations (e.g, Big 8) can be specified.
 +     *
 +     * @param encoding The new character encoding for the control connection.
 +     */
 +    public void setControlEncoding(String encoding) {
 +        _controlEncoding = encoding;
 +    }
 +
 +
 +    /**
 +     * @return The character encoding used to communicate over the
 +     * control connection.
 +     */
 +    public String getControlEncoding() {
 +        return _controlEncoding;
 +    }
 +
 +
 +    /***
 +     * Adds a ProtocolCommandListener.  Delegates this task to
 +     * {@link #_commandSupport_  _commandSupport_ }.
 +     * <p>
 +     * @param listener  The ProtocolCommandListener to add.
 +     ***/
 +    public void addProtocolCommandListener(ProtocolCommandListener listener)
 +    {
 +        _commandSupport_.addProtocolCommandListener(listener);
 +    }
 +
 +    /***
 +     * Removes a ProtocolCommandListener.  Delegates this task to
 +     * {@link #_commandSupport_  _commandSupport_ }.
 +     * <p>
 +     * @param listener  The ProtocolCommandListener to remove.
 +     ***/
 +    public void removeProtocolCommandListener(ProtocolCommandListener listener)
 +    {
 +        _commandSupport_.removeProtocolCommandListener(listener);
 +    }
 +
 +
 +    /***
 +     * Closes the control connection to the FTP server and sets to null
 +     * some internal data so that the memory may be reclaimed by the
 +     * garbage collector.  The reply text and code information from the
 +     * last command is voided so that the memory it used may be reclaimed.
 +     * Also sets {@link #_controlInput_} and {@link #_controlOutput_} to null.
 +     * <p>
 +     * @exception IOException If an error occurs while disconnecting.
 +     ***/
 +    @Override
 +    public void disconnect() throws IOException
 +    {
 +        super.disconnect();
 +        _controlInput_ = null;
 +        _controlOutput_ = null;
 +        _newReplyString = false;
 +        _replyString = null;
 +    }
 +
 +
 +    /***
 +     * Sends an FTP command to the server, waits for a reply and returns the
 +     * numerical response code.  After invocation, for more detailed
 +     * information, the actual reply text can be accessed by calling
 +     * {@link #getReplyString  getReplyString } or
 +     * {@link #getReplyStrings  getReplyStrings }.
 +     * <p>
 +     * @param command  The text representation of the  FTP command to send.
 +     * @param args The arguments to the FTP command.  If this parameter is
 +     *             set to null, then the command is sent with no argument.
 +     * @return The integer value of the FTP reply code returned by the server
 +     *         in response to the command.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int sendCommand(String command, String args) throws IOException
 +    {
 +        String message;
 +
 +        __commandBuffer.setLength(0);
 +        __commandBuffer.append(command);
 +
 +        if (args != null)
 +        {
 +            __commandBuffer.append(' ');
 +            __commandBuffer.append(args);
 +        }
 +        __commandBuffer.append(SocketClient.NETASCII_EOL);
 +
 +        try{
 +        _controlOutput_.write(message = __commandBuffer.toString());
 +            _controlOutput_.flush();
 +        }
 +        catch (SocketException e)
 +        {
 +            if (!isConnected() || !socketIsConnected(_socket_))
 +            {
 +                throw new FTPConnectionClosedException("Connection unexpectedly closed.");
 +            }
 +            else
 +            {
 +                throw e;
 +            }
 +        }
 +    
 +
 +        if (_commandSupport_.getListenerCount() > 0)
 +            _commandSupport_.fireCommandSent(command, message);
 +
 +        __getReply();
 +        return _replyCode;
 +    }
 +
 +    /**
 +     * Checks if the socket is connected 
 +     *
 +     * @param socket
 +     * @return true if connected
 +     */
 +    private boolean socketIsConnected(Socket socket)
 +    {
 +        if (socket == null)
 +        {
 +            return false;
 +        }
 +
 +        return socket.isConnected();
 +        
 +    }
 +
 +    /***
 +     * Sends an FTP command to the server, waits for a reply and returns the
 +     * numerical response code.  After invocation, for more detailed
 +     * information, the actual reply text can be accessed by calling
 +     * {@link #getReplyString  getReplyString } or
 +     * {@link #getReplyStrings  getReplyStrings }.
 +     * <p>
 +     * @param command  The FTPCommand constant corresponding to the FTP command
 +     *                 to send.
 +     * @param args The arguments to the FTP command.  If this parameter is
 +     *             set to null, then the command is sent with no argument.
 +     * @return The integer value of the FTP reply code returned by the server
 +     *         in response to the command.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int sendCommand(int command, String args) throws IOException
 +    {
 +        return sendCommand(FTPCommand._commands[command], args);
 +    }
 +
 +
 +    /***
 +     * Sends an FTP command with no arguments to the server, waits for a
 +     * reply and returns the numerical response code.  After invocation, for
 +     * more detailed information, the actual reply text can be accessed by
 +     * calling {@link #getReplyString  getReplyString } or
 +     * {@link #getReplyStrings  getReplyStrings }.
 +     * <p>
 +     * @param command  The text representation of the  FTP command to send.
 +     * @return The integer value of the FTP reply code returned by the server
 +     *         in response to the command.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int sendCommand(String command) throws IOException
 +    {
 +        return sendCommand(command, null);
 +    }
 +
 +
 +    /***
 +     * Sends an FTP command with no arguments to the server, waits for a
 +     * reply and returns the numerical response code.  After invocation, for
 +     * more detailed information, the actual reply text can be accessed by
 +     * calling {@link #getReplyString  getReplyString } or
 +     * {@link #getReplyStrings  getReplyStrings }.
 +     * <p>
 +     * @param command  The FTPCommand constant corresponding to the FTP command
 +     *                 to send.
 +     * @return The integer value of the FTP reply code returned by the server
 +     *         in response to the command.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int sendCommand(int command) throws IOException
 +    {
 +        return sendCommand(command, null);
 +    }
 +
 +
 +    /***
 +     * Returns the integer value of the reply code of the last FTP reply.
 +     * You will usually only use this method after you connect to the
 +     * FTP server to check that the connection was successful since
 +     * <code> connect </code> is of type void.
 +     * <p>
 +     * @return The integer value of the reply code of the last FTP reply.
 +     ***/
 +    public int getReplyCode()
 +    {
 +        return _replyCode;
 +    }
 +
 +    /***
 +     * Fetches a reply from the FTP server and returns the integer reply
 +     * code.  After calling this method, the actual reply text can be accessed
 +     * from either  calling {@link #getReplyString  getReplyString } or
 +     * {@link #getReplyStrings  getReplyStrings }.  Only use this
 +     * method if you are implementing your own FTP client or if you need to
 +     * fetch a secondary response from the FTP server.
 +     * <p>
 +     * @return The integer value of the reply code of the fetched FTP reply.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while receiving the
 +     *                         server reply.
 +     ***/
 +    public int getReply() throws IOException
 +    {
 +        __getReply();
 +        return _replyCode;
 +    }
 +
 +
 +    /***
 +     * Returns the lines of text from the last FTP server response as an array
 +     * of strings, one entry per line.  The end of line markers of each are
 +     * stripped from each line.
 +     * <p>
 +     * @return The lines of text from the last FTP response as an array.
 +     ***/
 +    public String[] getReplyStrings()
 +    {
 +        String[] lines;
 +        lines = new String[_replyLines.size()];
 +        _replyLines.addAll(Arrays.asList(lines));
 +        return lines;
 +    }
 +
 +    /***
 +     * Returns the entire text of the last FTP server response exactly
 +     * as it was received, including all end of line markers in NETASCII
 +     * format.
 +     * <p>
 +     * @return The entire text from the last FTP response as a String.
 +     ***/
 +    public String getReplyString()
 +    {
 +        StringBuilder buffer;
 +
 +        if (!_newReplyString) {
 +            return _replyString;
 +        }
 +
 +        buffer = new StringBuilder(256);
 +        
 +        for (String line : _replyLines) {
 +                buffer.append(line);
 +                buffer.append(SocketClient.NETASCII_EOL);
 +        }
 +        
 +         _newReplyString = false;
 +
 +        return (_replyString = buffer.toString());
 +    }
 +
 +
 +    /***
 +     * A convenience method to send the FTP USER command to the server,
 +     * receive the reply, and return the reply code.
 +     * <p>
 +     * @param username  The username to login under.
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int user(String username) throws IOException
 +    {
 +        return sendCommand(FTPCommand.USER, username);
 +    }
 +
 +    /**
 +     * A convenience method to send the FTP PASS command to the server,
 +     * receive the reply, and return the reply code.
 +     * @param password The plain text password of the username being logged into.
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     */
 +    public int pass(String password) throws IOException
 +    {
 +        return sendCommand(FTPCommand.PASS, password);
 +    }
 +
 +    /***
 +     * A convenience method to send the FTP ACCT command to the server,
 +     * receive the reply, and return the reply code.
 +     * <p>
 +     * @param account  The account name to access.
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int acct(String account) throws IOException
 +    {
 +        return sendCommand(FTPCommand.ACCT, account);
 +    }
 +
 +
 +    /***
 +     * A convenience method to send the FTP ABOR command to the server,
 +     * receive the reply, and return the reply code.
 +     * <p>
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int abor() throws IOException
 +    {
 +        return sendCommand(FTPCommand.ABOR);
 +    }
 +
 +    /***
 +     * A convenience method to send the FTP CWD command to the server,
 +     * receive the reply, and return the reply code.
 +     * <p>
 +     * @param directory The new working directory.
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int cwd(String directory) throws IOException
 +    {
 +        return sendCommand(FTPCommand.CWD, directory);
 +    }
 +
 +    /***
 +     * A convenience method to send the FTP CDUP command to the server,
 +     * receive the reply, and return the reply code.
 +     * <p>
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int cdup() throws IOException
 +    {
 +        return sendCommand(FTPCommand.CDUP);
 +    }
 +
 +    /***
 +     * A convenience method to send the FTP QUIT command to the server,
 +     * receive the reply, and return the reply code.
 +     * <p>
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int quit() throws IOException
 +    {
 +        return sendCommand(FTPCommand.QUIT);
 +    }
 +
 +    /***
 +     * A convenience method to send the FTP REIN command to the server,
 +     * receive the reply, and return the reply code.
 +     * <p>
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int rein() throws IOException
 +    {
 +        return sendCommand(FTPCommand.REIN);
 +    }
 +
 +    /***
 +     * A convenience method to send the FTP SMNT command to the server,
 +     * receive the reply, and return the reply code.
 +     * <p>
 +     * @param dir  The directory name.
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int smnt(String dir) throws IOException
 +    {
 +        return sendCommand(FTPCommand.SMNT, dir);
 +    }
 +
 +    /***
 +     * A convenience method to send the FTP PORT command to the server,
 +     * receive the reply, and return the reply code.
 +     * <p>
 +     * @param host  The host owning the port.
 +     * @param port  The new port.
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int port(InetAddress host, int port) throws IOException
 +    {
 +        int num;
 +        StringBuffer info = new StringBuffer(24);
 +
 +        info.append(host.getHostAddress().replace('.', ','));
 +        num = port >>> 8;
 +        info.append(',');
 +        info.append(num);
 +        info.append(',');
 +        num = port & 0xff;
 +        info.append(num);
 +
 +        return sendCommand(FTPCommand.PORT, info.toString());
 +    }
 +
 +    /***
 +     * A convenience method to send the FTP PASV command to the server,
 +     * receive the reply, and return the reply code.  Remember, it's up
 +     * to you to interpret the reply string containing the host/port
 +     * information.
 +     * <p>
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int pasv() throws IOException
 +    {
 +        return sendCommand(FTPCommand.PASV);
 +    }
 +
 +    /**
 +     * A convenience method to send the FTP TYPE command for text files
 +     * to the server, receive the reply, and return the reply code.
 +     * @param fileType  The type of the file (one of the <code>FILE_TYPE</code>
 +     *              constants).
 +     * @param formatOrByteSize  The format of the file (one of the
 +     *              <code>_FORMAT</code> constants.  In the case of
 +     *              <code>LOCAL_FILE_TYPE</code>, the byte size.
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     */
 +    public int type(int fileType, int formatOrByteSize) throws IOException
 +    {
 +        StringBuffer arg = new StringBuffer();
 +
 +        arg.append(__modes.charAt(fileType));
 +        arg.append(' ');
 +        if (fileType == LOCAL_FILE_TYPE)
 +            arg.append(formatOrByteSize);
 +        else
 +            arg.append(__modes.charAt(formatOrByteSize));
 +
 +        return sendCommand(FTPCommand.TYPE, arg.toString());
 +    }
 +
 +
 +    /**
 +     * A convenience method to send the FTP TYPE command to the server,
 +     * receive the reply, and return the reply code.
 +     * <p>
 +     * @param fileType  The type of the file (one of the <code>FILE_TYPE</code>
 +     *              constants).
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     */
 +    public int type(int fileType) throws IOException
 +    {
 +        return sendCommand(FTPCommand.TYPE,
 +                           __modes.substring(fileType, fileType + 1));
 +    }
 +
 +    /***
 +     * A convenience method to send the FTP STRU command to the server,
 +     * receive the reply, and return the reply code.
 +     * <p>
 +     * @param structure  The structure of the file (one of the
 +     *         <code>_STRUCTURE</code> constants).
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int stru(int structure) throws IOException
 +    {
 +        return sendCommand(FTPCommand.STRU,
 +                           __modes.substring(structure, structure + 1));
 +    }
 +
 +    /***
 +     * A convenience method to send the FTP MODE command to the server,
 +     * receive the reply, and return the reply code.
 +     * <p>
 +     * @param mode  The transfer mode to use (one of the
 +     *         <code>TRANSFER_MODE</code> constants).
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int mode(int mode) throws IOException
 +    {
 +        return sendCommand(FTPCommand.MODE,
 +                           __modes.substring(mode, mode + 1));
 +    }
 +
 +    /***
 +     * A convenience method to send the FTP RETR command to the server,
 +     * receive the reply, and return the reply code.  Remember, it is up
 +     * to you to manage the data connection.  If you don't need this low
 +     * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
 +     * , which will handle all low level details for you.
 +     * <p>
 +     * @param pathname  The pathname of the file to retrieve.
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int retr(String pathname) throws IOException
 +    {
 +        return sendCommand(FTPCommand.RETR, pathname);
 +    }
 +
 +    /***
 +     * A convenience method to send the FTP STOR command to the server,
 +     * receive the reply, and return the reply code.  Remember, it is up
 +     * to you to manage the data connection.  If you don't need this low
 +     * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
 +     * , which will handle all low level details for you.
 +     * <p>
 +     * @param pathname  The pathname to use for the file when stored at
 +     *                  the remote end of the transfer.
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int stor(String pathname) throws IOException
 +    {
 +        return sendCommand(FTPCommand.STOR, pathname);
 +    }
 +
 +    /***
 +     * A convenience method to send the FTP STOU command to the server,
 +     * receive the reply, and return the reply code.  Remember, it is up
 +     * to you to manage the data connection.  If you don't need this low
 +     * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
 +     * , which will handle all low level details for you.
 +     * <p>
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int stou() throws IOException
 +    {
 +        return sendCommand(FTPCommand.STOU);
 +    }
 +
 +    /***
 +     * A convenience method to send the FTP STOU command to the server,
 +     * receive the reply, and return the reply code.  Remember, it is up
 +     * to you to manage the data connection.  If you don't need this low
 +     * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
 +     * , which will handle all low level details for you.
 +     * @param pathname  The base pathname to use for the file when stored at
 +     *                  the remote end of the transfer.  Some FTP servers
 +     *                  require this.
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     */
 +    public int stou(String pathname) throws IOException
 +    {
 +        return sendCommand(FTPCommand.STOU, pathname);
 +    }
 +
 +    /***
 +     * A convenience method to send the FTP APPE command to the server,
 +     * receive the reply, and return the reply code.  Remember, it is up
 +     * to you to manage the data connection.  If you don't need this low
 +     * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
 +     * , which will handle all low level details for you.
 +     * <p>
 +     * @param pathname  The pathname to use for the file when stored at
 +     *                  the remote end of the transfer.
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int appe(String pathname) throws IOException
 +    {
 +        return sendCommand(FTPCommand.APPE, pathname);
 +    }
 +
 +    /***
 +     * A convenience method to send the FTP ALLO command to the server,
 +     * receive the reply, and return the reply code.
 +     * <p>
 +     * @param bytes The number of bytes to allocate.
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int allo(int bytes) throws IOException
 +    {
 +        return sendCommand(FTPCommand.ALLO, Integer.toString(bytes));
 +    }
 +
 +    /***
 +     * A convenience method to send the FTP ALLO command to the server,
 +     * receive the reply, and return the reply code.
 +     * <p>
 +     * @param bytes The number of bytes to allocate.
 +     * @param recordSize  The size of a record.
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int allo(int bytes, int recordSize) throws IOException
 +    {
 +        return sendCommand(FTPCommand.ALLO, Integer.toString(bytes) + " R " +
 +                           Integer.toString(recordSize));
 +    }
 +
 +    /***
 +     * A convenience method to send the FTP REST command to the server,
 +     * receive the reply, and return the reply code.
 +     * <p>
 +     * @param marker The marker at which to restart a transfer.
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int rest(String marker) throws IOException
 +    {
 +        return sendCommand(FTPCommand.REST, marker);
 +    }
 +    
 +    
 +    /**
 +     * @since 2.0
 +     **/
 +    public int mdtm(String file) throws IOException 
 +    {
 +        return sendCommand(FTPCommand.MDTM, file);
 +    }
 +
 +    /***
 +     * A convenience method to send the FTP RNFR command to the server,
 +     * receive the reply, and return the reply code.
 +     * <p>
 +     * @param pathname The pathname to rename from.
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int rnfr(String pathname) throws IOException
 +    {
 +        return sendCommand(FTPCommand.RNFR, pathname);
 +    }
 +
 +    /***
 +     * A convenience method to send the FTP RNTO command to the server,
 +     * receive the reply, and return the reply code.
 +     * <p>
 +     * @param pathname The pathname to rename to
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int rnto(String pathname) throws IOException
 +    {
 +        return sendCommand(FTPCommand.RNTO, pathname);
 +    }
 +
 +    /***
 +     * A convenience method to send the FTP DELE command to the server,
 +     * receive the reply, and return the reply code.
 +     * <p>
 +     * @param pathname The pathname to delete.
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int dele(String pathname) throws IOException
 +    {
 +        return sendCommand(FTPCommand.DELE, pathname);
 +    }
 +
 +    /***
 +     * A convenience method to send the FTP RMD command to the server,
 +     * receive the reply, and return the reply code.
 +     * <p>
 +     * @param pathname The pathname of the directory to remove.
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int rmd(String pathname) throws IOException
 +    {
 +        return sendCommand(FTPCommand.RMD, pathname);
 +    }
 +
 +    /***
 +     * A convenience method to send the FTP MKD command to the server,
 +     * receive the reply, and return the reply code.
 +     * <p>
 +     * @param pathname The pathname of the new directory to create.
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int mkd(String pathname) throws IOException
 +    {
 +        return sendCommand(FTPCommand.MKD, pathname);
 +    }
 +
 +    /***
 +     * A convenience method to send the FTP PWD command to the server,
 +     * receive the reply, and return the reply code.
 +     * <p>
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int pwd() throws IOException
 +    {
 +        return sendCommand(FTPCommand.PWD);
 +    }
 +
 +    /***
 +     * A convenience method to send the FTP LIST command to the server,
 +     * receive the reply, and return the reply code.  Remember, it is up
 +     * to you to manage the data connection.  If you don't need this low
 +     * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
 +     * , which will handle all low level details for you.
 +     * <p>
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int list() throws IOException
 +    {
 +        return sendCommand(FTPCommand.LIST);
 +    }
 +
 +    /***
 +     * A convenience method to send the FTP LIST command to the server,
 +     * receive the reply, and return the reply code.  Remember, it is up
 +     * to you to manage the data connection.  If you don't need this low
 +     * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
 +     * , which will handle all low level details for you.
 +     * <p>
 +     * @param pathname  The pathname to list.
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int list(String pathname) throws IOException
 +    {
 +        return sendCommand(FTPCommand.LIST, pathname);
 +    }
 +
 +    /***
 +     * A convenience method to send the FTP NLST command to the server,
 +     * receive the reply, and return the reply code.  Remember, it is up
 +     * to you to manage the data connection.  If you don't need this low
 +     * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
 +     * , which will handle all low level details for you.
 +     * <p>
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int nlst() throws IOException
 +    {
 +        return sendCommand(FTPCommand.NLST);
 +    }
 +
 +    /***
 +     * A convenience method to send the FTP NLST command to the server,
 +     * receive the reply, and return the reply code.  Remember, it is up
 +     * to you to manage the data connection.  If you don't need this low
 +     * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
 +     * , which will handle all low level details for you.
 +     * <p>
 +     * @param pathname  The pathname to list.
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int nlst(String pathname) throws IOException
 +    {
 +        return sendCommand(FTPCommand.NLST, pathname);
 +    }
 +
 +    /***
 +     * A convenience method to send the FTP SITE command to the server,
 +     * receive the reply, and return the reply code.
 +     * <p>
 +     * @param parameters  The site parameters to send.
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int site(String parameters) throws IOException
 +    {
 +        return sendCommand(FTPCommand.SITE, parameters);
 +    }
 +
 +    /***
 +     * A convenience method to send the FTP SYST command to the server,
 +     * receive the reply, and return the reply code.
 +     * <p>
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int syst() throws IOException
 +    {
 +        return sendCommand(FTPCommand.SYST);
 +    }
 +
 +    /***
 +     * A convenience method to send the FTP STAT command to the server,
 +     * receive the reply, and return the reply code.
 +     * <p>
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int stat() throws IOException
 +    {
 +        return sendCommand(FTPCommand.STAT);
 +    }
 +
 +    /***
 +     * A convenience method to send the FTP STAT command to the server,
 +     * receive the reply, and return the reply code.
 +     * <p>
 +     * @param pathname  A pathname to list.
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int stat(String pathname) throws IOException
 +    {
 +        return sendCommand(FTPCommand.STAT, pathname);
 +    }
 +
 +    /***
 +     * A convenience method to send the FTP HELP command to the server,
 +     * receive the reply, and return the reply code.
 +     * <p>
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int help() throws IOException
 +    {
 +        return sendCommand(FTPCommand.HELP);
 +    }
 +
 +    /***
 +     * A convenience method to send the FTP HELP command to the server,
 +     * receive the reply, and return the reply code.
 +     * <p>
 +     * @param command  The command name on which to request help.
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int help(String command) throws IOException
 +    {
 +        return sendCommand(FTPCommand.HELP, command);
 +    }
 +
 +    /***
 +     * A convenience method to send the FTP NOOP command to the server,
 +     * receive the reply, and return the reply code.
 +     * <p>
 +     * @return The reply code received from the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending the
 +     *      command or receiving the server reply.
 +     ***/
 +    public int noop() throws IOException
 +    {
 +        return sendCommand(FTPCommand.NOOP);
 +    }
 +
 +    /**
 +     * Return whether strict multiline parsing is enabled, as per RFX 959, section 4.2.
 +     * @return True if strict, false if lenient
 +     * @since 2.0
 +     */
 +    public boolean isStrictMultilineParsing() {
 +        return strictMultilineParsing;
 +    }
 +
 +    /**
 +     * Set strict multiline parsing.
 +     * @param strictMultilineParsing
 +     * @since 2.0
 +     */
 +    public void setStrictMultilineParsing(boolean strictMultilineParsing) {
 +        this.strictMultilineParsing = strictMultilineParsing;
 +    }
 +}
 +
 +/* Emacs configuration
 + * Local variables:        **
 + * mode:             java  **
 + * c-basic-offset:   4     **
 + * indent-tabs-mode: nil   **
 + * End:                    **
 + */
 diff --git a/src/org/apache/commons/net/ftp/FTPClient.java b/src/org/apache/commons/net/ftp/FTPClient.java new file mode 100644 index 0000000..9a2c459 --- /dev/null +++ b/src/org/apache/commons/net/ftp/FTPClient.java @@ -0,0 +1,2447 @@ +/*
 + * 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;
 +import java.io.BufferedInputStream;
 +import java.io.BufferedOutputStream;
 +import java.io.BufferedReader;
 +import java.io.IOException;
 +import java.io.InputStream;
 +import java.io.InputStreamReader;
 +import java.io.OutputStream;
 +import java.net.InetAddress;
 +import java.net.ServerSocket;
 +import java.net.Socket;
 +import java.util.ArrayList;
 +
 +import org.apache.commons.net.MalformedServerReplyException;
 +import org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory;
 +import org.apache.commons.net.ftp.parser.FTPFileEntryParserFactory;
 +import org.apache.commons.net.ftp.parser.ParserInitializationException;
 +import org.apache.commons.net.io.CopyStreamEvent;
 +import org.apache.commons.net.io.CopyStreamException;
 +import org.apache.commons.net.io.FromNetASCIIInputStream;
 +import org.apache.commons.net.io.ToNetASCIIOutputStream;
 +import org.apache.commons.net.io.Util;
 +
 +/***
 + * FTPClient encapsulates all the functionality necessary to store and
 + * retrieve files from an FTP server.  This class takes care of all
 + * low level details of interacting with an FTP server and provides
 + * a convenient higher level interface.  As with all classes derived
 + * from {@link org.apache.commons.net.SocketClient},
 + * you must first connect to the server with
 + * {@link org.apache.commons.net.SocketClient#connect  connect }
 + * before doing anything, and finally
 + * {@link org.apache.commons.net.SocketClient#disconnect  disconnect }
 + * after you're completely finished interacting with the server.
 + * Then you need to check the FTP reply code to see if the connection
 + * was successful.  For example:
 + * <pre>
 + *    boolean error = false;
 + *    try {
 + *      int reply;
 + *      ftp.connect("ftp.foobar.com");
 + *      System.out.println("Connected to " + server + ".");
 + *      System.out.print(ftp.getReplyString());
 + *
 + *      // After connection attempt, you should check the reply code to verify
 + *      // success.
 + *      reply = ftp.getReplyCode();
 + *
 + *      if(!FTPReply.isPositiveCompletion(reply)) {
 + *        ftp.disconnect();
 + *        System.err.println("FTP server refused connection.");
 + *        System.exit(1);
 + *      }
 + *      ... // transfer files
 + *      ftp.logout();
 + *    } catch(IOException e) {
 + *      error = true;
 + *      e.printStackTrace();
 + *    } finally {
 + *      if(ftp.isConnected()) {
 + *        try {
 + *          ftp.disconnect();
 + *        } catch(IOException ioe) {
 + *          // do nothing
 + *        }
 + *      }
 + *      System.exit(error ? 1 : 0);
 + *    }
 + * </pre>
 + * <p>
 + * Immediately after connecting is the only real time you need to check the
 + * reply code (because connect is of type void).  The convention for all the
 + * FTP command methods in FTPClient is such that they either return a
 + * boolean value or some other value.
 + * The boolean methods return true on a successful completion reply from
 + * the FTP server and false on a reply resulting in an error condition or
 + * failure.  The methods returning a value other than boolean return a value
 + * containing the higher level data produced by the FTP command, or null if a
 + * reply resulted in an error condition or failure.  If you want to access
 + * the exact FTP reply code causing a success or failure, you must call
 + * {@link org.apache.commons.net.ftp.FTP#getReplyCode  getReplyCode } after
 + * a success or failure.
 + * <p>
 + * The default settings for FTPClient are for it to use
 + * <code> FTP.ASCII_FILE_TYPE </code>,
 + * <code> FTP.NON_PRINT_TEXT_FORMAT </code>,
 + * <code> FTP.STREAM_TRANSFER_MODE </code>, and
 + * <code> FTP.FILE_STRUCTURE </code>.  The only file types directly supported
 + * are <code> FTP.ASCII_FILE_TYPE </code> and
 + * <code> FTP.BINARY_FILE_TYPE </code>.  Because there are at least 4
 + * different EBCDIC encodings, we have opted not to provide direct support
 + * for EBCDIC.  To transfer EBCDIC and other unsupported file types you
 + * must create your own filter InputStreams and OutputStreams and wrap
 + * them around the streams returned or required by the FTPClient methods.
 + * FTPClient uses the {@link ToNetASCIIOutputStream NetASCII}  
 + * filter streams to provide transparent handling of ASCII files.  We will 
 + * consider incorporating EBCDIC support if there is enough demand.
 + * <p>
 + * <code> FTP.NON_PRINT_TEXT_FORMAT </code>,
 + * <code> FTP.STREAM_TRANSFER_MODE </code>, and
 + * <code> FTP.FILE_STRUCTURE </code> are the only supported formats,
 + * transfer modes, and file structures.
 + * <p>
 + * Because the handling of sockets on different platforms can differ
 + * significantly, the FTPClient automatically issues a new PORT command
 + * prior to every transfer requiring that the server connect to the client's
 + * data port.  This ensures identical problem-free behavior on Windows, Unix,
 + * and Macintosh platforms.  Additionally, it relieves programmers from
 + * having to issue the PORT command themselves and dealing with platform
 + * dependent issues.
 + * <p>
 + * Additionally, for security purposes, all data connections to the
 + * client are verified to ensure that they originated from the intended
 + * party (host and port).  If a data connection is initiated by an unexpected
 + * party, the command will close the socket and throw an IOException.  You
 + * may disable this behavior with
 + * {@link #setRemoteVerificationEnabled setRemoteVerificationEnabled()}.
 + * <p>
 + * You should keep in mind that the FTP server may choose to prematurely
 + * close a connection if the client has been idle for longer than a
 + * given time period (usually 900 seconds).  The FTPClient class will detect a
 + * premature FTP server connection closing when it receives a
 + * {@link org.apache.commons.net.ftp.FTPReply#SERVICE_NOT_AVAILABLE FTPReply.SERVICE_NOT_AVAILABLE }
 + *  response to a command.
 + * When that occurs, the FTP class method encountering that reply will throw
 + * an {@link org.apache.commons.net.ftp.FTPConnectionClosedException}
 + * .
 + * <code>FTPConnectionClosedException</code>
 + * is a subclass of <code> IOException </code> and therefore need not be
 + * caught separately, but if you are going to catch it separately, its
 + * catch block must appear before the more general <code> IOException </code>
 + * catch block.  When you encounter an
 + * {@link org.apache.commons.net.ftp.FTPConnectionClosedException}
 + * , you must disconnect the connection with
 + * {@link #disconnect  disconnect() } to properly clean up the
 + * system resources used by FTPClient.  Before disconnecting, you may check the
 + * last reply code and text with
 + * {@link org.apache.commons.net.ftp.FTP#getReplyCode  getReplyCode },
 + * {@link org.apache.commons.net.ftp.FTP#getReplyString  getReplyString },
 + * and
 + * {@link org.apache.commons.net.ftp.FTP#getReplyStrings  getReplyStrings}.
 + * You may avoid server disconnections while the client is idle by
 + * periodicaly sending NOOP commands to the server.
 + * <p>
 + * Rather than list it separately for each method, we mention here that
 + * every method communicating with the server and throwing an IOException
 + * can also throw a
 + * {@link org.apache.commons.net.MalformedServerReplyException}
 + * , which is a subclass
 + * of IOException.  A MalformedServerReplyException will be thrown when
 + * the reply received from the server deviates enough from the protocol
 + * specification that it cannot be interpreted in a useful manner despite
 + * attempts to be as lenient as possible.
 + * <p>
 + * Listing API Examples
 + * Both paged and unpaged examples of directory listings are available,
 + * as follows:
 + * <p>
 + * Unpaged (whole list) access, using a parser accessible by auto-detect:
 + * <pre>
 + *    FTPClient f=FTPClient();
 + *    f.connect(server);
 + *    f.login(username, password);
 + *    FTPFile[] files = listFiles(directory);
 + * </pre>
 + * <p>
 + * Paged access, using a parser not accessible by auto-detect.  The class
 + * defined in the first parameter of initateListParsing should be derived
 + * from org.apache.commons.net.FTPFileEntryParser:
 + * <pre>
 + *    FTPClient f=FTPClient();
 + *    f.connect(server);
 + *    f.login(username, password);
 + *    FTPListParseEngine engine =
 + *       f.initiateListParsing("com.whatever.YourOwnParser", directory);
 + *
 + *    while (engine.hasNext()) {
 + *       FTPFile[] files = engine.getNext(25);  // "page size" you want
 + *       //do whatever you want with these files, display them, etc.
 + *       //expensive FTPFile objects not created until needed.
 + *    }
 + * </pre>
 + * <p>
 + * Paged access, using a parser accessible by auto-detect:
 + * <pre>
 + *    FTPClient f=FTPClient();
 + *    f.connect(server);
 + *    f.login(username, password);
 + *    FTPListParseEngine engine = f.initiateListParsing(directory);
 + *
 + *    while (engine.hasNext()) {
 + *       FTPFile[] files = engine.getNext(25);  // "page size" you want
 + *       //do whatever you want with these files, display them, etc.
 + *       //expensive FTPFile objects not created until needed.
 + *    }
 + * </pre>
 + * <p>
 + * For examples of using FTPClient on servers whose directory listings 
 + * <ul> 
 + * <li>use languages other than English</li>
 + * <li>use date formats other than the American English "standard" <code>MM d yyyy</code></li>
 + * <li>are in different timezones and you need accurate timestamps for dependency checking 
 + *     as in Ant</li>
 + * </ul>see {@link  FTPClientConfig  FTPClientConfig}.
 + * <p> 
 + * @author Daniel F. Savarese
 + * @author Rory Winston
 + * @see FTP
 + * @see FTPConnectionClosedException
 + * @see FTPFileEntryParser
 + * @see FTPFileEntryParserFactory
 + * @see DefaultFTPFileEntryParserFactory
 + * @see FTPClientConfig
 + * 
 + * @see org.apache.commons.net.MalformedServerReplyException
 + **/
 +public class FTPClient extends FTP
 +implements Configurable
 +{
 +    /***
 +     * A constant indicating the FTP session is expecting all transfers
 +     * to occur between the client (local) and server and that the server
 +     * should connect to the client's data port to initiate a data transfer.
 +     * This is the default data connection mode when and FTPClient instance
 +     * is created.
 +     ***/
 +    public static final int ACTIVE_LOCAL_DATA_CONNECTION_MODE = 0;
 +    /***
 +     * A constant indicating the FTP session is expecting all transfers
 +     * to occur between two remote servers and that the server
 +     * the client is connected to should connect to the other server's
 +     * data port to initiate a data transfer.
 +     ***/
 +    public static final int ACTIVE_REMOTE_DATA_CONNECTION_MODE = 1;
 +    /***
 +     * A constant indicating the FTP session is expecting all transfers
 +     * to occur between the client (local) and server and that the server
 +     * is in passive mode, requiring the client to connect to the
 +     * server's data port to initiate a transfer.
 +     ***/
 +    public static final int PASSIVE_LOCAL_DATA_CONNECTION_MODE = 2;
 +    /***
 +     * A constant indicating the FTP session is expecting all transfers
 +     * to occur between two remote servers and that the server
 +     * the client is connected to is in passive mode, requiring the other
 +     * server to connect to the first server's data port to initiate a data
 +     * transfer.
 +     ***/
 +    public static final int PASSIVE_REMOTE_DATA_CONNECTION_MODE = 3;
 +
 +    private int __dataConnectionMode, __dataTimeout;
 +    private int __passivePort;
 +    private String __passiveHost;
 +    private int __fileType, __fileFormat, __fileStructure, __fileTransferMode;
 +    private boolean __remoteVerificationEnabled;
 +    private long __restartOffset;
 +    private FTPFileEntryParserFactory __parserFactory;
 +    private int __bufferSize;
 +    private boolean __listHiddenFiles;
 +
 +    // __systemName is a cached value that should not be referenced directly
 +    // except when assigned in getSystemName and __initDefaults.
 +    private String __systemName;
 +
 +    // __entryParser is a cached value that should not be referenced directly
 +    // except when assigned in listFiles(String, String) and __initDefaults.
 +    private FTPFileEntryParser __entryParser;
 +    
 +    private FTPClientConfig __configuration;
 +
 +    /** Pattern for PASV mode responses */ 
 +    private static String __parms = "\\d{1,3},\\d{1,3},\\d{1,3},\\d{1,3},\\d{1,3},\\d{1,3}";
 +    private static java.util.regex.Pattern __parms_pat;
 +    static {
 +       __parms_pat = java.util.regex.Pattern.compile(__parms);
 +    }
 +
 +    /***
 +     * Default FTPClient constructor.  Creates a new FTPClient instance
 +     * with the data connection mode set to
 +     * <code> ACTIVE_LOCAL_DATA_CONNECTION_MODE </code>, the file type
 +     * set to <code> FTP.ASCII_FILE_TYPE </code>, the
 +     * file format set to <code> FTP.NON_PRINT_TEXT_FORMAT </code>,
 +     * the file structure set to <code> FTP.FILE_STRUCTURE </code>, and
 +     * the transfer mode set to <code> FTP.STREAM_TRANSFER_MODE </code>.
 +     ***/
 +    public FTPClient()
 +    {
 +        __initDefaults();
 +        __dataTimeout = -1;
 +        __remoteVerificationEnabled = true;
 +        __parserFactory = new DefaultFTPFileEntryParserFactory();
 +        __configuration      = null;
 +        __listHiddenFiles = false;
 +    }
 +
 +
 +    private void __initDefaults()
 +    {
 +        __dataConnectionMode = ACTIVE_LOCAL_DATA_CONNECTION_MODE;
 +        __passiveHost        = null;
 +        __passivePort        = -1;
 +        __fileType           = FTP.ASCII_FILE_TYPE;
 +        __fileStructure      = FTP.FILE_STRUCTURE;
 +        __fileFormat         = FTP.NON_PRINT_TEXT_FORMAT;
 +        __fileTransferMode   = FTP.STREAM_TRANSFER_MODE;
 +        __restartOffset      = 0;
 +        __systemName         = null;
 +        __entryParser        = null;
 +        __bufferSize         = Util.DEFAULT_COPY_BUFFER_SIZE;
 +    }
 +    
 +    private String __parsePathname(String reply)
 +    {
 +        int begin, end;
 +
 +        begin = reply.indexOf('"') + 1;
 +        end = reply.indexOf('"', begin);
 +
 +        return reply.substring(begin, end);
 +    }
 +
 +
 +    private void __parsePassiveModeReply(String reply)
 +    throws MalformedServerReplyException
 +    {
 +        java.util.regex.Matcher m = __parms_pat.matcher(reply);
 +        if (!m.find()) {
 +            throw new MalformedServerReplyException(
 +                "Could not parse passive host information.\nServer Reply: " + reply);
 +        }
 +        reply = m.group();
 +        String parts[] = m.group().split(",");
 +        
 +        __passiveHost = parts[0] + '.' + parts[1] + '.' + parts[2] + '.' + parts[3];
 +
 +        try
 +        {
 +            int oct1 = Integer.parseInt(parts[4]);
 +            int oct2 = Integer.parseInt(parts[5]);
 +            __passivePort = (oct1 << 8) | oct2;
 +        }
 +        catch (NumberFormatException e)
 +        {
 +            throw new MalformedServerReplyException(
 +                "Could not parse passive host information.\nServer Reply: " + reply);
 +        }
 +
 +    }
 +
 +    private boolean __storeFile(int command, String remote, InputStream local)
 +    throws IOException
 +    {
 +        OutputStream output;
 +        Socket socket;
 +
 +        if ((socket = _openDataConnection_(command, remote)) == null)
 +            return false;
 +
 +        output = new BufferedOutputStream(socket.getOutputStream(),
 +                                          getBufferSize()
 +                                          );
 +        if (__fileType == ASCII_FILE_TYPE)
 +            output = new ToNetASCIIOutputStream(output);
 +        // Treat everything else as binary for now
 +        try
 +        {
 +            Util.copyStream(local, output, getBufferSize(),
 +                            CopyStreamEvent.UNKNOWN_STREAM_SIZE, null,
 +                            false);
 +        }
 +        catch (IOException e)
 +        {
 +            try
 +            {
 +                socket.close();
 +            }
 +            catch (IOException f)
 +            {}
 +            throw e;
 +        }
 +        output.close();
 +        socket.close();
 +        return completePendingCommand();
 +    }
 +
 +    private OutputStream __storeFileStream(int command, String remote)
 +    throws IOException
 +    {
 +        OutputStream output;
 +        Socket socket;
 +
 +        if ((socket = _openDataConnection_(command, remote)) == null)
 +            return null;
 +
 +        output = socket.getOutputStream();
 +        if (__fileType == ASCII_FILE_TYPE) {
 +          // We buffer ascii transfers because the buffering has to
 +          // be interposed between ToNetASCIIOutputSream and the underlying
 +          // socket output stream.  We don't buffer binary transfers
 +          // because we don't want to impose a buffering policy on the
 +          // programmer if possible.  Programmers can decide on their
 +          // own if they want to wrap the SocketOutputStream we return
 +          // for file types other than ASCII.
 +          output = new BufferedOutputStream(output,
 +                                            getBufferSize());
 +          output = new ToNetASCIIOutputStream(output);
 +
 +        }
 +        return new org.apache.commons.net.io.SocketOutputStream(socket, output);
 +    }
 +
 +
 +    /**
 +     * Establishes a data connection with the FTP server, returning
 +     * a Socket for the connection if successful.  If a restart
 +     * offset has been set with {@link #setRestartOffset(long)},
 +     * a REST command is issued to the server with the offset as
 +     * an argument before establishing the data connection.  Active
 +     * mode connections also cause a local PORT command to be issued.
 +     * <p>
 +     * @param command  The text representation of the FTP command to send.
 +     * @param arg The arguments to the FTP command.  If this parameter is
 +     *             set to null, then the command is sent with no argument.
 +     * @return A Socket corresponding to the established data connection.
 +     *         Null is returned if an FTP protocol error is reported at
 +     *         any point during the establishment and initialization of
 +     *         the connection.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     */
 +    protected Socket _openDataConnection_(int command, String arg)
 +      throws IOException
 +    {
 +        Socket socket;
 +
 +        if (__dataConnectionMode != ACTIVE_LOCAL_DATA_CONNECTION_MODE &&
 +                __dataConnectionMode != PASSIVE_LOCAL_DATA_CONNECTION_MODE)
 +            return null;
 +
 +        if (__dataConnectionMode == ACTIVE_LOCAL_DATA_CONNECTION_MODE)
 +        {
 +            ServerSocket server;
 +            server = _serverSocketFactory_.createServerSocket(0, 1, getLocalAddress());
 +
 +            if (!FTPReply.isPositiveCompletion(port(getLocalAddress(),
 +                                                    server.getLocalPort())))
 +            {
 +                server.close();
 +                return null;
 +            }
 +
 +            if ((__restartOffset > 0) && !restart(__restartOffset))
 +            {
 +                server.close();
 +                return null;
 +            }
 +
 +            if (!FTPReply.isPositivePreliminary(sendCommand(command, arg)))
 +            {
 +                server.close();
 +                return null;
 +            }
 +
 +            // For now, let's just use the data timeout value for waiting for
 +            // the data connection.  It may be desirable to let this be a
 +            // separately configurable value.  In any case, we really want
 +            // to allow preventing the accept from blocking indefinitely.
 +            if (__dataTimeout >= 0)
 +                server.setSoTimeout(__dataTimeout);
 +            try {
 +                socket = server.accept();
 +            } finally {
 +                server.close();
 +            }
 +        }
 +        else
 +        { // We must be in PASSIVE_LOCAL_DATA_CONNECTION_MODE
 +
 +            if (pasv() != FTPReply.ENTERING_PASSIVE_MODE)
 +                return null;
 +
 +            __parsePassiveModeReply(_replyLines.get(_replyLines.size() - 1));
 +
 +            socket = _socketFactory_.createSocket(__passiveHost, __passivePort);
 +            if ((__restartOffset > 0) && !restart(__restartOffset))
 +            {
 +                socket.close();
 +                return null;
 +            }
 +
 +            if (!FTPReply.isPositivePreliminary(sendCommand(command, arg)))
 +            {
 +                socket.close();
 +                return null;
 +            }
 +        }
 +
 +        if (__remoteVerificationEnabled && !verifyRemote(socket))
 +        {
 +            InetAddress host1, host2;
 +
 +            host1 = socket.getInetAddress();
 +            host2 = getRemoteAddress();
 +
 +            socket.close();
 +
 +            throw new IOException(
 +                "Host attempting data connection " + host1.getHostAddress() +
 +                " is not same as server " + host2.getHostAddress());
 +        }
 +
 +        if (__dataTimeout >= 0)
 +            socket.setSoTimeout(__dataTimeout);
 +
 +        return socket;
 +    }
 +
 +
 +    @Override
 +    protected void _connectAction_() throws IOException
 +    {
 +        super._connectAction_();
 +        __initDefaults();
 +    }
 +
 +
 +    /***
 +     * Sets the timeout in milliseconds to use when reading from the
 +     * data connection.  This timeout will be set immediately after
 +     * opening the data connection.
 +     * <p>
 +     * @param  timeout The default timeout in milliseconds that is used when
 +     *        opening a data connection socket.
 +     ***/
 +    public void setDataTimeout(int timeout)
 +    {
 +        __dataTimeout = timeout;
 +    }
 +
 +    /**
 +     * set the factory used for parser creation to the supplied factory object.
 +     *
 +     * @param parserFactory
 +     *               factory object used to create FTPFileEntryParsers
 +     *
 +     * @see org.apache.commons.net.ftp.parser.FTPFileEntryParserFactory
 +     * @see org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory
 +     */
 +    public void setParserFactory(FTPFileEntryParserFactory parserFactory) {
 +        __parserFactory = parserFactory;
 +    }
 +
 +
 +    /***
 +     * Closes the connection to the FTP server and restores
 +     * connection parameters to the default values.
 +     * <p>
 +     * @exception IOException If an error occurs while disconnecting.
 +     ***/
 +    @Override
 +    public void disconnect() throws IOException
 +    {
 +        super.disconnect();
 +        __initDefaults();
 +    }
 +
 +
 +    /***
 +     * Enable or disable verification that the remote host taking part
 +     * of a data connection is the same as the host to which the control
 +     * connection is attached.  The default is for verification to be
 +     * enabled.  You may set this value at any time, whether the
 +     * FTPClient is currently connected or not.
 +     * <p>
 +     * @param enable True to enable verification, false to disable verification.
 +     ***/
 +    public void setRemoteVerificationEnabled(boolean enable)
 +    {
 +        __remoteVerificationEnabled = enable;
 +    }
 +
 +    /***
 +     * Return whether or not verification of the remote host participating
 +     * in data connections is enabled.  The default behavior is for
 +     * verification to be enabled.
 +     * <p>
 +     * @return True if verification is enabled, false if not.
 +     ***/
 +    public boolean isRemoteVerificationEnabled()
 +    {
 +        return __remoteVerificationEnabled;
 +    }
 +
 +    /***
 +     * Login to the FTP server using the provided username and password.
 +     * <p>
 +     * @param username The username to login under.
 +     * @param password The password to use.
 +     * @return True if successfully completed, false if not.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public boolean login(String username, String password) throws IOException
 +    {
 +        user(username);
 +
 +        if (FTPReply.isPositiveCompletion(_replyCode))
 +            return true;
 +
 +        // If we get here, we either have an error code, or an intermmediate
 +        // reply requesting password.
 +        if (!FTPReply.isPositiveIntermediate(_replyCode))
 +            return false;
 +
 +        return FTPReply.isPositiveCompletion(pass(password));
 +    }
 +
 +
 +    /***
 +     * Login to the FTP server using the provided username, password,
 +     * and account.  If no account is required by the server, only
 +     * the username and password, the account information is not used.
 +     * <p>
 +     * @param username The username to login under.
 +     * @param password The password to use.
 +     * @param account  The account to use.
 +     * @return True if successfully completed, false if not.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public boolean login(String username, String password, String account)
 +    throws IOException
 +    {
 +        user(username);
 +
 +        if (FTPReply.isPositiveCompletion(_replyCode))
 +            return true;
 +
 +        // If we get here, we either have an error code, or an intermmediate
 +        // reply requesting password.
 +        if (!FTPReply.isPositiveIntermediate(_replyCode))
 +            return false;
 +
 +        pass(password);
 +
 +        if (FTPReply.isPositiveCompletion(_replyCode))
 +            return true;
 +
 +        if (!FTPReply.isPositiveIntermediate(_replyCode))
 +            return false;
 +
 +        return FTPReply.isPositiveCompletion(acct(account));
 +    }
 +
 +    /***
 +     * Logout of the FTP server by sending the QUIT command.
 +     * <p>
 +     * @return True if successfully completed, false if not.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public boolean logout() throws IOException
 +    {
 +        return FTPReply.isPositiveCompletion(quit());
 +    }
 +
 +
 +    /***
 +     * Change the current working directory of the FTP session.
 +     * <p>
 +     * @param pathname  The new current working directory.
 +     * @return True if successfully completed, false if not.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public boolean changeWorkingDirectory(String pathname) throws IOException
 +    {
 +        return FTPReply.isPositiveCompletion(cwd(pathname));
 +    }
 +
 +
 +    /***
 +     * Change to the parent directory of the current working directory.
 +     * <p>
 +     * @return True if successfully completed, false if not.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public boolean changeToParentDirectory() throws IOException
 +    {
 +        return FTPReply.isPositiveCompletion(cdup());
 +    }
 +
 +
 +    /***
 +     * Issue the FTP SMNT command.
 +     * <p>
 +     * @param pathname The pathname to mount.
 +     * @return True if successfully completed, false if not.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public boolean structureMount(String pathname) throws IOException
 +    {
 +        return FTPReply.isPositiveCompletion(smnt(pathname));
 +    }
 +
 +    /***
 +     * Reinitialize the FTP session.  Not all FTP servers support this
 +     * command, which issues the FTP REIN command.
 +     * <p>
 +     * @return True if successfully completed, false if not.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    boolean reinitialize() throws IOException
 +    {
 +        rein();
 +
 +        if (FTPReply.isPositiveCompletion(_replyCode) ||
 +                (FTPReply.isPositivePreliminary(_replyCode) &&
 +                 FTPReply.isPositiveCompletion(getReply())))
 +        {
 +
 +            __initDefaults();
 +
 +            return true;
 +        }
 +
 +        return false;
 +    }
 +
 +
 +    /***
 +     * Set the current data connection mode to
 +     * <code>ACTIVE_LOCAL_DATA_CONNECTION_MODE</code>.  No communication
 +     * with the FTP server is conducted, but this causes all future data
 +     * transfers to require the FTP server to connect to the client's
 +     * data port.  Additionally, to accommodate differences between socket
 +     * implementations on different platforms, this method causes the
 +     * client to issue a PORT command before every data transfer.
 +     ***/
 +    public void enterLocalActiveMode()
 +    {
 +        __dataConnectionMode = ACTIVE_LOCAL_DATA_CONNECTION_MODE;
 +        __passiveHost = null;
 +        __passivePort = -1;
 +    }
 +
 +
 +    /***
 +     * Set the current data connection mode to
 +     * <code> PASSIVE_LOCAL_DATA_CONNECTION_MODE </code>.  Use this
 +     * method only for data transfers between the client and server.
 +     * This method causes a PASV command to be issued to the server
 +     * before the opening of every data connection, telling the server to
 +     * open a data port to which the client will connect to conduct
 +     * data transfers.  The FTPClient will stay in
 +     * <code> PASSIVE_LOCAL_DATA_CONNECTION_MODE </code> until the
 +     * mode is changed by calling some other method such as
 +     * {@link #enterLocalActiveMode  enterLocalActiveMode() }
 +     ***/
 +    public void enterLocalPassiveMode()
 +    {
 +        __dataConnectionMode = PASSIVE_LOCAL_DATA_CONNECTION_MODE;
 +        // These will be set when just before a data connection is opened
 +        // in _openDataConnection_()
 +        __passiveHost = null;
 +        __passivePort = -1;
 +    }
 +
 +
 +    /***
 +     * Set the current data connection mode to
 +     * <code> ACTIVE_REMOTE_DATA_CONNECTION </code>.  Use this method only
 +     * for server to server data transfers.  This method issues a PORT
 +     * command to the server, indicating the other server and port to which
 +     * it should connect for data transfers.  You must call this method
 +     * before EVERY server to server transfer attempt.  The FTPClient will
 +     * NOT automatically continue to issue PORT commands.  You also
 +     * must remember to call
 +     * {@link #enterLocalActiveMode  enterLocalActiveMode() } if you
 +     * wish to return to the normal data connection mode.
 +     * <p>
 +     * @param host The passive mode server accepting connections for data
 +     *             transfers.
 +     * @param port The passive mode server's data port.
 +     * @return True if successfully completed, false if not.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public boolean enterRemoteActiveMode(InetAddress host, int port)
 +    throws IOException
 +    {
 +        if (FTPReply.isPositiveCompletion(port(host, port)))
 +        {
 +            __dataConnectionMode = ACTIVE_REMOTE_DATA_CONNECTION_MODE;
 +            __passiveHost = null;
 +            __passivePort = -1;
 +            return true;
 +        }
 +        return false;
 +    }
 +
 +    /***
 +     * Set the current data connection mode to
 +     * <code> PASSIVE_REMOTE_DATA_CONNECTION_MODE </code>.  Use this
 +     * method only for server to server data transfers.
 +     * This method issues a PASV command to the server, telling it to
 +     * open a data port to which the active server will connect to conduct
 +     * data transfers.  You must call this method
 +     * before EVERY server to server transfer attempt.  The FTPClient will
 +     * NOT automatically continue to issue PASV commands.  You also
 +     * must remember to call
 +     * {@link #enterLocalActiveMode  enterLocalActiveMode() } if you
 +     * wish to return to the normal data connection mode.
 +     * <p>
 +     * @return True if successfully completed, false if not.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public boolean enterRemotePassiveMode() throws IOException
 +    {
 +        if (pasv() != FTPReply.ENTERING_PASSIVE_MODE)
 +            return false;
 +
 +        __dataConnectionMode = PASSIVE_REMOTE_DATA_CONNECTION_MODE;
 +        __parsePassiveModeReply(_replyLines.get(0));
 +
 +        return true;
 +    }
 +
 +    /***
 +     * Returns the hostname or IP address (in the form of a string) returned
 +     * by the server when entering passive mode.  If not in passive mode,
 +     * returns null.  This method only returns a valid value AFTER a
 +     * data connection has been opened after a call to
 +     * {@link #enterLocalPassiveMode enterLocalPassiveMode()}.
 +     * This is because FTPClient sends a PASV command to the server only
 +     * just before opening a data connection, and not when you call
 +     * {@link #enterLocalPassiveMode enterLocalPassiveMode()}.
 +     * <p>
 +     * @return The passive host name if in passive mode, otherwise null.
 +     ***/
 +    public String getPassiveHost()
 +    {
 +        return __passiveHost;
 +    }
 +
 +    /***
 +     * If in passive mode, returns the data port of the passive host.
 +     * This method only returns a valid value AFTER a
 +     * data connection has been opened after a call to
 +     * {@link #enterLocalPassiveMode enterLocalPassiveMode()}.
 +     * This is because FTPClient sends a PASV command to the server only
 +     * just before opening a data connection, and not when you call
 +     * {@link #enterLocalPassiveMode enterLocalPassiveMode()}.
 +     * <p>
 +     * @return The data port of the passive server.  If not in passive
 +     *         mode, undefined.
 +     ***/
 +    public int getPassivePort()
 +    {
 +        return __passivePort;
 +    }
 +
 +
 +    /***
 +     * Returns the current data connection mode (one of the
 +     * <code> _DATA_CONNECTION_MODE </code> constants.
 +     * <p>
 +     * @return The current data connection mode (one of the
 +     * <code> _DATA_CONNECTION_MODE </code> constants.
 +     ***/
 +    public int getDataConnectionMode()
 +    {
 +        return __dataConnectionMode;
 +    }
 +
 +
 +    /***
 +     * Sets the file type to be transferred.  This should be one of
 +     * <code> FTP.ASCII_FILE_TYPE </code>, <code> FTP.BINARY_FILE_TYPE</code>,
 +     * etc.  The file type only needs to be set when you want to change the
 +     * type.  After changing it, the new type stays in effect until you change
 +     * it again.  The default file type is <code> FTP.ASCII_FILE_TYPE </code>
 +     * if this method is never called.
 +     * <p>
 +     * @param fileType The <code> _FILE_TYPE </code> constant indcating the
 +     *                 type of file.
 +     * @return True if successfully completed, false if not.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public boolean setFileType(int fileType) throws IOException
 +    {
 +        if (FTPReply.isPositiveCompletion(type(fileType)))
 +        {
 +            __fileType = fileType;
 +            __fileFormat = FTP.NON_PRINT_TEXT_FORMAT;
 +            return true;
 +        }
 +        return false;
 +    }
 +
 +
 +    /***
 +     * Sets the file type to be transferred and the format.  The type should be
 +     * one of  <code> FTP.ASCII_FILE_TYPE </code>,
 +     * <code> FTP.BINARY_FILE_TYPE </code>, etc.  The file type only needs to
 +     * be set when you want to change the type.  After changing it, the new
 +     * type stays in effect until you change it again.  The default file type
 +     * is <code> FTP.ASCII_FILE_TYPE </code> if this method is never called.
 +     * The format should be one of the FTP class <code> TEXT_FORMAT </code>
 +     * constants, or if the type is <code> FTP.LOCAL_FILE_TYPE </code>, the
 +     * format should be the byte size for that type.  The default format
 +     * is <code> FTP.NON_PRINT_TEXT_FORMAT </code> if this method is never
 +     * called.
 +     * <p>
 +     * @param fileType The <code> _FILE_TYPE </code> constant indcating the
 +     *                 type of file.
 +     * @param formatOrByteSize  The format of the file (one of the
 +     *              <code>_FORMAT</code> constants.  In the case of
 +     *              <code>LOCAL_FILE_TYPE</code>, the byte size.
 +     * <p>
 +     * @return True if successfully completed, false if not.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public boolean setFileType(int fileType, int formatOrByteSize)
 +    throws IOException
 +    {
 +        if (FTPReply.isPositiveCompletion(type(fileType, formatOrByteSize)))
 +        {
 +            __fileType = fileType;
 +            __fileFormat = formatOrByteSize;
 +            return true;
 +        }
 +        return false;
 +    }
 +
 +
 +    /***
 +     * Sets the file structure.  The default structure is
 +     * <code> FTP.FILE_STRUCTURE </code> if this method is never called.
 +     * <p>
 +     * @param structure  The structure of the file (one of the FTP class
 +     *         <code>_STRUCTURE</code> constants).
 +     * @return True if successfully completed, false if not.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public boolean setFileStructure(int structure) throws IOException
 +    {
 +        if (FTPReply.isPositiveCompletion(stru(structure)))
 +        {
 +            __fileStructure = structure;
 +            return true;
 +        }
 +        return false;
 +    }
 +
 +
 +    /***
 +     * Sets the transfer mode.  The default transfer mode
 +     * <code> FTP.STREAM_TRANSFER_MODE </code> if this method is never called.
 +     * <p>
 +     * @param mode  The new transfer mode to use (one of the FTP class
 +     *         <code>_TRANSFER_MODE</code> constants).
 +     * @return True if successfully completed, false if not.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public boolean setFileTransferMode(int mode) throws IOException
 +    {
 +        if (FTPReply.isPositiveCompletion(mode(mode)))
 +        {
 +            __fileTransferMode = mode;
 +            return true;
 +        }
 +        return false;
 +    }
 +
 +
 +    /***
 +     * Initiate a server to server file transfer.  This method tells the
 +     * server to which the client is connected to retrieve a given file from
 +     * the other server.
 +     * <p>
 +     * @param filename  The name of the file to retrieve.
 +     * @return True if successfully completed, false if not.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public boolean remoteRetrieve(String filename) throws IOException
 +    {
 +        if (__dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE ||
 +                __dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE)
 +            return FTPReply.isPositivePreliminary(retr(filename));
 +        return false;
 +    }
 +
 +
 +    /***
 +     * Initiate a server to server file transfer.  This method tells the
 +     * server to which the client is connected to store a file on
 +     * the other server using the given filename.  The other server must
 +     * have had a <code> remoteRetrieve </code> issued to it by another
 +     * FTPClient.
 +     * <p>
 +     * @param filename  The name to call the file that is to be stored.
 +     * @return True if successfully completed, false if not.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public boolean remoteStore(String filename) throws IOException
 +    {
 +        if (__dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE ||
 +                __dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE)
 +            return FTPReply.isPositivePreliminary(stor(filename));
 +        return false;
 +    }
 +
 +
 +    /***
 +     * Initiate a server to server file transfer.  This method tells the
 +     * server to which the client is connected to store a file on
 +     * the other server using a unique filename based on the given filename.
 +     * The other server must have had a <code> remoteRetrieve </code> issued
 +     * to it by another FTPClient.
 +     * <p>
 +     * @param filename  The name on which to base the filename of the file
 +     *                  that is to be stored.
 +     * @return True if successfully completed, false if not.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public boolean remoteStoreUnique(String filename) throws IOException
 +    {
 +        if (__dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE ||
 +                __dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE)
 +            return FTPReply.isPositivePreliminary(stou(filename));
 +        return false;
 +    }
 +
 +
 +    /***
 +     * Initiate a server to server file transfer.  This method tells the
 +     * server to which the client is connected to store a file on
 +     * the other server using a unique filename.
 +     * The other server must have had a <code> remoteRetrieve </code> issued
 +     * to it by another FTPClient.  Many FTP servers require that a base
 +     * filename be given from which the unique filename can be derived.  For
 +     * those servers use the other version of <code> remoteStoreUnique</code>
 +     * <p>
 +     * @return True if successfully completed, false if not.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public boolean remoteStoreUnique() throws IOException
 +    {
 +        if (__dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE ||
 +                __dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE)
 +            return FTPReply.isPositivePreliminary(stou());
 +        return false;
 +    }
 +
 +    // For server to server transfers
 +    /***
 +     * Initiate a server to server file transfer.  This method tells the
 +     * server to which the client is connected to append to a given file on
 +     * the other server.  The other server must have had a
 +     * <code> remoteRetrieve </code> issued to it by another FTPClient.
 +     * <p>
 +     * @param filename  The name of the file to be appended to, or if the
 +     *        file does not exist, the name to call the file being stored.
 +     * <p>
 +     * @return True if successfully completed, false if not.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public boolean remoteAppend(String filename) throws IOException
 +    {
 +        if (__dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE ||
 +                __dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE)
 +            return FTPReply.isPositivePreliminary(stor(filename));
 +        return false;
 +    }
 +
 +    /***
 +     * There are a few FTPClient methods that do not complete the
 +     * entire sequence of FTP commands to complete a transaction.  These
 +     * commands require some action by the programmer after the reception
 +     * of a positive intermediate command.  After the programmer's code
 +     * completes its actions, it must call this method to receive
 +     * the completion reply from the server and verify the success of the
 +     * entire transaction.
 +     * <p>
 +     * For example,
 +     * <pre>
 +     * InputStream input;
 +     * OutputStream output;
 +     * input  = new FileInputStream("foobaz.txt");
 +     * output = ftp.storeFileStream("foobar.txt")
 +     * if(!FTPReply.isPositiveIntermediate(ftp.getReplyCode())) {
 +     *     input.close();
 +     *     output.close();
 +     *     ftp.logout();
 +     *     ftp.disconnect();
 +     *     System.err.println("File transfer failed.");
 +     *     System.exit(1);
 +     * }
 +     * Util.copyStream(input, output);
 +     * input.close();
 +     * output.close();
 +     * // Must call completePendingCommand() to finish command.
 +     * if(!ftp.completePendingCommand()) {
 +     *     ftp.logout();
 +     *     ftp.disconnect();
 +     *     System.err.println("File transfer failed.");
 +     *     System.exit(1);
 +     * }
 +     * </pre>
 +     * <p>
 +     * @return True if successfully completed, false if not.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public boolean completePendingCommand() throws IOException
 +    {
 +        return FTPReply.isPositiveCompletion(getReply());
 +    }
 +
 +
 +    /***
 +     * Retrieves a named file from the server and writes it to the given
 +     * OutputStream.  This method does NOT close the given OutputStream.
 +     * If the current file type is ASCII, line separators in the file are
 +     * converted to the local representation.
 +     * <p>
 +     * @param remote  The name of the remote file.
 +     * @param local   The local OutputStream to which to write the file.
 +     * @return True if successfully completed, false if not.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception CopyStreamException  If an I/O error occurs while actually
 +     *      transferring the file.  The CopyStreamException allows you to
 +     *      determine the number of bytes transferred and the IOException
 +     *      causing the error.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public boolean retrieveFile(String remote, OutputStream local)
 +    throws IOException
 +    {
 +        InputStream input;
 +        Socket socket;
 +
 +        if ((socket = _openDataConnection_(FTPCommand.RETR, remote)) == null)
 +            return false;
 +
 +        input = new BufferedInputStream(socket.getInputStream(),
 +                                        getBufferSize());
 +        if (__fileType == ASCII_FILE_TYPE)
 +          input = new FromNetASCIIInputStream(input);
 +        // Treat everything else as binary for now
 +        try
 +        {
 +            Util.copyStream(input, local, getBufferSize(),
 +                            CopyStreamEvent.UNKNOWN_STREAM_SIZE, null,
 +                            false);
 +        }
 +        catch (IOException e)
 +        {
 +            try
 +            {
 +                socket.close();
 +            }
 +            catch (IOException f)
 +            {}
 +            throw e;
 +        }
 +        socket.close();
 +        return completePendingCommand();
 +    }
 +
 +    /***
 +     * Returns an InputStream from which a named file from the server
 +     * can be read.  If the current file type is ASCII, the returned
 +     * InputStream will convert line separators in the file to
 +     * the local representation.  You must close the InputStream when you
 +     * finish reading from it.  The InputStream itself will take care of
 +     * closing the parent data connection socket upon being closed.  To
 +     * finalize the file transfer you must call
 +     * {@link #completePendingCommand  completePendingCommand } and
 +     * check its return value to verify success.
 +     * <p>
 +     * @param remote  The name of the remote file.
 +     * @return An InputStream from which the remote file can be read.  If
 +     *      the data connection cannot be opened (e.g., the file does not
 +     *      exist), null is returned (in which case you may check the reply
 +     *      code to determine the exact reason for failure).
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public InputStream retrieveFileStream(String remote) throws IOException
 +    {
 +        InputStream input;
 +        Socket socket;
 +
 +        if ((socket = _openDataConnection_(FTPCommand.RETR, remote)) == null)
 +            return null;
 +
 +        input = socket.getInputStream();
 +        if (__fileType == ASCII_FILE_TYPE) {
 +          // We buffer ascii transfers because the buffering has to
 +          // be interposed between FromNetASCIIOutputSream and the underlying
 +          // socket input stream.  We don't buffer binary transfers
 +          // because we don't want to impose a buffering policy on the
 +          // programmer if possible.  Programmers can decide on their
 +          // own if they want to wrap the SocketInputStream we return
 +          // for file types other than ASCII.
 +          input = new BufferedInputStream(input,
 +                                          getBufferSize());
 +          input = new FromNetASCIIInputStream(input);
 +        }
 +        return new org.apache.commons.net.io.SocketInputStream(socket, input);
 +    }
 +
 +
 +    /***
 +     * Stores a file on the server using the given name and taking input
 +     * from the given InputStream.  This method does NOT close the given
 +     * InputStream.  If the current file type is ASCII, line separators in
 +     * the file are transparently converted to the NETASCII format (i.e.,
 +     * you should not attempt to create a special InputStream to do this).
 +     * <p>
 +     * @param remote  The name to give the remote file.
 +     * @param local   The local InputStream from which to read the file.
 +     * @return True if successfully completed, false if not.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception CopyStreamException  If an I/O error occurs while actually
 +     *      transferring the file.  The CopyStreamException allows you to
 +     *      determine the number of bytes transferred and the IOException
 +     *      causing the error.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public boolean storeFile(String remote, InputStream local)
 +    throws IOException
 +    {
 +        return __storeFile(FTPCommand.STOR, remote, local);
 +    }
 +
 +
 +    /***
 +     * Returns an OutputStream through which data can be written to store
 +     * a file on the server using the given name.  If the current file type
 +     * is ASCII, the returned OutputStream will convert line separators in
 +     * the file to the NETASCII format  (i.e., you should not attempt to
 +     * create a special OutputStream to do this).  You must close the
 +     * OutputStream when you finish writing to it.  The OutputStream itself
 +     * will take care of closing the parent data connection socket upon being
 +     * closed.  To finalize the file transfer you must call
 +     * {@link #completePendingCommand  completePendingCommand } and
 +     * check its return value to verify success.
 +     * <p>
 +     * @param remote  The name to give the remote file.
 +     * @return An OutputStream through which the remote file can be written.  If
 +     *      the data connection cannot be opened (e.g., the file does not
 +     *      exist), null is returned (in which case you may check the reply
 +     *      code to determine the exact reason for failure).
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public OutputStream storeFileStream(String remote) throws IOException
 +    {
 +        return __storeFileStream(FTPCommand.STOR, remote);
 +    }
 +
 +    /***
 +     * Appends to a file on the server with the given name, taking input
 +     * from the given InputStream.  This method does NOT close the given
 +     * InputStream.  If the current file type is ASCII, line separators in
 +     * the file are transparently converted to the NETASCII format (i.e.,
 +     * you should not attempt to create a special InputStream to do this).
 +     * <p>
 +     * @param remote  The name of the remote file.
 +     * @param local   The local InputStream from which to read the data to
 +     *                be appended to the remote file.
 +     * @return True if successfully completed, false if not.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception CopyStreamException  If an I/O error occurs while actually
 +     *      transferring the file.  The CopyStreamException allows you to
 +     *      determine the number of bytes transferred and the IOException
 +     *      causing the error.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public boolean appendFile(String remote, InputStream local)
 +    throws IOException
 +    {
 +        return __storeFile(FTPCommand.APPE, remote, local);
 +    }
 +
 +    /***
 +     * Returns an OutputStream through which data can be written to append
 +     * to a file on the server with the given name.  If the current file type
 +     * is ASCII, the returned OutputStream will convert line separators in
 +     * the file to the NETASCII format  (i.e., you should not attempt to
 +     * create a special OutputStream to do this).  You must close the
 +     * OutputStream when you finish writing to it.  The OutputStream itself
 +     * will take care of closing the parent data connection socket upon being
 +     * closed.  To finalize the file transfer you must call
 +     * {@link #completePendingCommand  completePendingCommand } and
 +     * check its return value to verify success.
 +     * <p>
 +     * @param remote  The name of the remote file.
 +     * @return An OutputStream through which the remote file can be appended.
 +     *      If the data connection cannot be opened (e.g., the file does not
 +     *      exist), null is returned (in which case you may check the reply
 +     *      code to determine the exact reason for failure).
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public OutputStream appendFileStream(String remote) throws IOException
 +    {
 +        return __storeFileStream(FTPCommand.APPE, remote);
 +    }
 +
 +    /***
 +     * Stores a file on the server using a unique name derived from the
 +     * given name and taking input
 +     * from the given InputStream.  This method does NOT close the given
 +     * InputStream.  If the current file type is ASCII, line separators in
 +     * the file are transparently converted to the NETASCII format (i.e.,
 +     * you should not attempt to create a special InputStream to do this).
 +     * <p>
 +     * @param remote  The name on which to base the unique name given to
 +     *                the remote file.
 +     * @param local   The local InputStream from which to read the file.
 +     * @return True if successfully completed, false if not.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception CopyStreamException  If an I/O error occurs while actually
 +     *      transferring the file.  The CopyStreamException allows you to
 +     *      determine the number of bytes transferred and the IOException
 +     *      causing the error.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public boolean storeUniqueFile(String remote, InputStream local)
 +    throws IOException
 +    {
 +        return __storeFile(FTPCommand.STOU, remote, local);
 +    }
 +
 +
 +    /***
 +     * Returns an OutputStream through which data can be written to store
 +     * a file on the server using a unique name derived from the given name.
 +     * If the current file type
 +     * is ASCII, the returned OutputStream will convert line separators in
 +     * the file to the NETASCII format  (i.e., you should not attempt to
 +     * create a special OutputStream to do this).  You must close the
 +     * OutputStream when you finish writing to it.  The OutputStream itself
 +     * will take care of closing the parent data connection socket upon being
 +     * closed.  To finalize the file transfer you must call
 +     * {@link #completePendingCommand  completePendingCommand } and
 +     * check its return value to verify success.
 +     * <p>
 +     * @param remote  The name on which to base the unique name given to
 +     *                the remote file.
 +     * @return An OutputStream through which the remote file can be written.  If
 +     *      the data connection cannot be opened (e.g., the file does not
 +     *      exist), null is returned (in which case you may check the reply
 +     *      code to determine the exact reason for failure).
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public OutputStream storeUniqueFileStream(String remote) throws IOException
 +    {
 +        return __storeFileStream(FTPCommand.STOU, remote);
 +    }
 +
 +    /**
 +     * Stores a file on the server using a unique name assigned by the
 +     * server and taking input from the given InputStream.  This method does
 +     * NOT close the given
 +     * InputStream.  If the current file type is ASCII, line separators in
 +     * the file are transparently converted to the NETASCII format (i.e.,
 +     * you should not attempt to create a special InputStream to do this).
 +     * <p>
 +     * @param local   The local InputStream from which to read the file.
 +     * @return True if successfully completed, false if not.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception CopyStreamException  If an I/O error occurs while actually
 +     *      transferring the file.  The CopyStreamException allows you to
 +     *      determine the number of bytes transferred and the IOException
 +     *      causing the error.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     */
 +    public boolean storeUniqueFile(InputStream local) throws IOException
 +    {
 +        return __storeFile(FTPCommand.STOU, null, local);
 +    }
 +
 +    /**
 +     * Returns an OutputStream through which data can be written to store
 +     * a file on the server using a unique name assigned by the server.
 +     * If the current file type
 +     * is ASCII, the returned OutputStream will convert line separators in
 +     * the file to the NETASCII format  (i.e., you should not attempt to
 +     * create a special OutputStream to do this).  You must close the
 +     * OutputStream when you finish writing to it.  The OutputStream itself
 +     * will take care of closing the parent data connection socket upon being
 +     * closed.  To finalize the file transfer you must call
 +     * {@link #completePendingCommand  completePendingCommand } and
 +     * check its return value to verify success.
 +     * <p>
 +     * @return An OutputStream through which the remote file can be written.  If
 +     *      the data connection cannot be opened (e.g., the file does not
 +     *      exist), null is returned (in which case you may check the reply
 +     *      code to determine the exact reason for failure).
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     */
 +    public OutputStream storeUniqueFileStream() throws IOException
 +    {
 +        return __storeFileStream(FTPCommand.STOU, null);
 +    }
 +
 +    /***
 +     * Reserve a number of bytes on the server for the next file transfer.
 +     * <p>
 +     * @param bytes  The number of bytes which the server should allocate.
 +     * @return True if successfully completed, false if not.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public boolean allocate(int bytes) throws IOException
 +    {
 +        return FTPReply.isPositiveCompletion(allo(bytes));
 +    }
 +
 +
 +    /**
 +     * Reserve space on the server for the next file transfer.
 +     * <p>
 +     * @param bytes  The number of bytes which the server should allocate.
 +     * @param recordSize  The size of a file record.
 +     * @return True if successfully completed, false if not.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     */
 +    public boolean allocate(int bytes, int recordSize) throws IOException
 +    {
 +        return FTPReply.isPositiveCompletion(allo(bytes, recordSize));
 +    }
 +
 +
 +    /***
 +     * Restart a <code>STREAM_TRANSFER_MODE</code> file transfer starting
 +     * from the given offset.  This will only work on FTP servers supporting
 +     * the REST comand for the stream transfer mode.  However, most FTP
 +     * servers support this.  Any subsequent file transfer will start
 +     * reading or writing the remote file from the indicated offset.
 +     * <p>
 +     * @param offset  The offset into the remote file at which to start the
 +     *           next file transfer.
 +     * @return True if successfully completed, false if not.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    private boolean restart(long offset) throws IOException
 +    {
 +        __restartOffset = 0;
 +        return FTPReply.isPositiveIntermediate(rest(Long.toString(offset)));
 +    }
 +
 +    /***
 +     * Sets the restart offset.  The restart command is sent to the server
 +     * only before sending the file transfer command.  When this is done,
 +     * the restart marker is reset to zero.
 +     * <p>
 +     * @param offset  The offset into the remote file at which to start the
 +     *           next file transfer.  This must be a value greater than or
 +     *           equal to zero.
 +     ***/
 +    public void setRestartOffset(long offset)
 +    {
 +        if (offset >= 0)
 +            __restartOffset = offset;
 +    }
 +
 +    /***
 +     * Fetches the restart offset.
 +     * <p>
 +     * @return offset  The offset into the remote file at which to start the
 +     *           next file transfer.
 +     ***/
 +    public long getRestartOffset()
 +    {
 +        return __restartOffset;
 +    }
 +
 +
 +
 +    /***
 +     * Renames a remote file.
 +     * <p>
 +     * @param from  The name of the remote file to rename.
 +     * @param to    The new name of the remote file.
 +     * @return True if successfully completed, false if not.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public boolean rename(String from, String to) throws IOException
 +    {
 +        if (!FTPReply.isPositiveIntermediate(rnfr(from)))
 +            return false;
 +
 +        return FTPReply.isPositiveCompletion(rnto(to));
 +    }
 +
 +
 +    /***
 +     * Abort a transfer in progress.
 +     * <p>
 +     * @return True if successfully completed, false if not.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public boolean abort() throws IOException
 +    {
 +        return FTPReply.isPositiveCompletion(abor());
 +    }
 +
 +    /***
 +     * Deletes a file on the FTP server.
 +     * <p>
 +     * @param pathname   The pathname of the file to be deleted.
 +     * @return True if successfully completed, false if not.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public boolean deleteFile(String pathname) throws IOException
 +    {
 +        return FTPReply.isPositiveCompletion(dele(pathname));
 +    }
 +
 +
 +    /***
 +     * Removes a directory on the FTP server (if empty).
 +     * <p>
 +     * @param pathname  The pathname of the directory to remove.
 +     * @return True if successfully completed, false if not.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public boolean removeDirectory(String pathname) throws IOException
 +    {
 +        return FTPReply.isPositiveCompletion(rmd(pathname));
 +    }
 +
 +
 +    /***
 +     * Creates a new subdirectory on the FTP server in the current directory
 +     * (if a relative pathname is given) or where specified (if an absolute
 +     * pathname is given).
 +     * <p>
 +     * @param pathname The pathname of the directory to create.
 +     * @return True if successfully completed, false if not.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public boolean makeDirectory(String pathname) throws IOException
 +    {
 +        return FTPReply.isPositiveCompletion(mkd(pathname));
 +    }
 +
 +
 +    /***
 +     * Returns the pathname of the current working directory.
 +     * <p>
 +     * @return The pathname of the current working directory.  If it cannot
 +     *         be obtained, returns null.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public String printWorkingDirectory() throws IOException
 +    {
 +        if (pwd() != FTPReply.PATHNAME_CREATED)
 +            return null;
 +
 +        return __parsePathname(_replyLines.get( _replyLines.size() - 1));
 +    }
 +
 +
 +    /**
 +     * Send a site specific command.
 +     * @param arguments The site specific command and arguments.
 +     * @return True if successfully completed, false if not.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     */
 +    public boolean sendSiteCommand(String arguments) throws IOException
 +    {
 +        return FTPReply.isPositiveCompletion(site(arguments));
 +    }
 +
 +
 +    /***
 +     * Fetches the system type name from the server and returns the string.
 +     * This value is cached for the duration of the connection after the
 +     * first call to this method.  In other words, only the first time
 +     * that you invoke this method will it issue a SYST command to the
 +     * FTP server.  FTPClient will remember the value and return the
 +     * cached value until a call to disconnect.
 +     * <p>
 +     * @return The system type name obtained from the server.  null if the
 +     *       information could not be obtained.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *  command to the server or receiving a reply from the server.
 +     ***/
 +    public String getSystemName() throws IOException
 +    {
 +      //if (syst() == FTPReply.NAME_SYSTEM_TYPE)
 +      // Technically, we should expect a NAME_SYSTEM_TYPE response, but
 +      // in practice FTP servers deviate, so we soften the condition to
 +      // a positive completion.
 +        if (__systemName == null && FTPReply.isPositiveCompletion(syst()))
 +            __systemName = _replyLines.get(_replyLines.size() - 1).substring(4);
 +
 +        return __systemName;
 +    }
 +
 +
 +    /***
 +     * Fetches the system help information from the server and returns the
 +     * full string.
 +     * <p>
 +     * @return The system help string obtained from the server.  null if the
 +     *       information could not be obtained.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *  command to the server or receiving a reply from the server.
 +     ***/
 +    public String listHelp() throws IOException
 +    {
 +        if (FTPReply.isPositiveCompletion(help()))
 +            return getReplyString();
 +        return null;
 +    }
 +
 +
 +    /**
 +     * Fetches the help information for a given command from the server and
 +     * returns the full string.
 +     * @param command The command on which to ask for help.
 +     * @return The command help string obtained from the server.  null if the
 +     *       information could not be obtained.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *  command to the server or receiving a reply from the server.
 +     */
 +    public String listHelp(String command) throws IOException
 +    {
 +        if (FTPReply.isPositiveCompletion(help(command)))
 +            return getReplyString();
 +        return null;
 +    }
 +
 +
 +    /***
 +     * Sends a NOOP command to the FTP server.  This is useful for preventing
 +     * server timeouts.
 +     * <p>
 +     * @return True if successfully completed, false if not.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public boolean sendNoOp() throws IOException
 +    {
 +        return FTPReply.isPositiveCompletion(noop());
 +    }
 +    
 +
 +    /***
 +     * Obtain a list of filenames in a directory (or just the name of a given
 +     * file, which is not particularly useful).  This information is obtained
 +     * through the NLST command.  If the given pathname is a directory and
 +     * contains no files,  a zero length array is returned only
 +     * if the FTP server returned a positive completion code, otherwise
 +     * null is returned (the FTP server returned a 550 error No files found.).
 +     * If the directory is not empty, an array of filenames in the directory is
 +     * returned. If the pathname corresponds
 +     * to a file, only that file will be listed.  The server may or may not
 +     * expand glob expressions.
 +     * <p>
 +     * @param pathname  The file or directory to list.
 +     * @return The list of filenames contained in the given path.  null if
 +     *     the list could not be obtained.  If there are no filenames in
 +     *     the directory, a zero-length array is returned.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public String[] listNames(String pathname) throws IOException
 +    {
 +        String line;
 +        Socket socket;
 +        BufferedReader reader;
 +        ArrayList<String> results;
 +
 +        if ((socket = _openDataConnection_(FTPCommand.NLST, pathname)) == null)
 +            return null;
 +
 +        reader =
 +            new BufferedReader(new InputStreamReader(socket.getInputStream(), getControlEncoding()));
 +
 +        results = new ArrayList<String>();
 +        while ((line = reader.readLine()) != null)
 +            results.add(line);
 +        
 +        reader.close();
 +        socket.close();
 +
 +        if (completePendingCommand())
 +        {
 +            String[] names = new String[ results.size() ];
 +            return results.toArray(names);
 +        }
 +
 +        return null;
 +    }
 +
 +
 +    /***
 +     * Obtain a list of filenames in the current working directory
 +     * This information is obtained through the NLST command.  If the current
 +     * directory contains no files, a zero length array is returned only
 +     * if the FTP server returned a positive completion code, otherwise,
 +     * null is returned (the FTP server returned a 550 error No files found.).
 +     * If the directory is not empty, an array of filenames in the directory is
 +     * returned.
 +     * <p>
 +     * @return The list of filenames contained in the current working
 +     *     directory.  null if the list could not be obtained.
 +     *     If there are no filenames in the directory, a zero-length array
 +     *     is returned.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public String[] listNames() throws IOException
 +    {
 +        return listNames(null);
 +    }
 +
 +
 +
 +    /**
 +     * Using the default system autodetect mechanism, obtain a
 +     * list of file information for the current working directory
 +     * or for just a single file.
 +     * <p>
 +     * This information is obtained through the LIST command.  The contents of
 +     * the returned array is determined by the<code> FTPFileEntryParser </code>
 +     * used.
 +     * <p>
 +     * @param pathname  The file or directory to list.  Since the server may
 +     *                  or may not expand glob expressions, using them here
 +     *                  is not recommended and may well cause this method to
 +     *                  fail.
 +     *
 +     * @return The list of file information contained in the given path in
 +     *         the format determined by the autodetection mechanism
 +     * @exception FTPConnectionClosedException
 +     *                   If the FTP server prematurely closes the connection
 +     *                   as a result of the client being idle or some other
 +     *                   reason causing the server to send FTP reply code 421.
 +     *                   This exception may be caught either as an IOException
 +     *                   or independently as itself.
 +     * @exception IOException
 +     *                   If an I/O error occurs while either sending a
 +     *                   command to the server or receiving a reply
 +     *                   from the server.
 +     * @exception ParserInitializationException
 +     *                   Thrown if the parserKey parameter cannot be
 +     *                   resolved by the selected parser factory.
 +     *                   In the DefaultFTPEntryParserFactory, this will
 +     *                   happen when parserKey is neither
 +     *                   the fully qualified class name of a class
 +     *                   implementing the interface
 +     *                   org.apache.commons.net.ftp.FTPFileEntryParser
 +     *                   nor a string containing one of the recognized keys
 +     *                   mapping to such a parser or if class loader
 +     *                   security issues prevent its being loaded.
 +     * @see org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory
 +     * @see org.apache.commons.net.ftp.parser.FTPFileEntryParserFactory
 +     * @see org.apache.commons.net.ftp.FTPFileEntryParser
 +     */
 +    public FTPFile[] listFiles(String pathname)
 +    throws IOException
 +    {
 +        String key = null;
 +        FTPListParseEngine engine =
 +            initiateListParsing(key, pathname);
 +        return engine.getFiles();
 +
 +    }
 +    /**
 +     * Using the default system autodetect mechanism, obtain a
 +     * list of file information for the current working directory.
 +     * <p>
 +     * This information is obtained through the LIST command.  The contents of
 +     * the returned array is determined by the<code> FTPFileEntryParser </code>
 +     * used.
 +     * <p>
 +     * @return The list of file information contained in the current directory
 +     *         in the format determined by the autodetection mechanism.  
 +     *         <p><b> 
 +     *         NOTE:</b> This array may contain null members if any of the 
 +     *         individual file listings failed to parse.  The caller should 
 +     *         check each entry for null before referencing it.
 +     * @exception FTPConnectionClosedException
 +     *                   If the FTP server prematurely closes the connection
 +     *                   as a result of the client being idle or some other
 +     *                   reason causing the server to send FTP reply code 421.
 +     *                   This exception may be caught either as an IOException
 +     *                   or independently as itself.
 +     * @exception IOException
 +     *                   If an I/O error occurs while either sending a
 +     *                   command to the server or receiving a reply
 +     *                   from the server.
 +     * @exception ParserInitializationException
 +     *                   Thrown if the parserKey parameter cannot be
 +     *                   resolved by the selected parser factory.
 +     *                   In the DefaultFTPEntryParserFactory, this will
 +     *                   happen when parserKey is neither
 +     *                   the fully qualified class name of a class
 +     *                   implementing the interface
 +     *                   org.apache.commons.net.ftp.FTPFileEntryParser
 +     *                   nor a string containing one of the recognized keys
 +     *                   mapping to such a parser or if class loader
 +     *                   security issues prevent its being loaded.
 +     * @see org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory
 +     * @see org.apache.commons.net.ftp.parser.FTPFileEntryParserFactory
 +     * @see org.apache.commons.net.ftp.FTPFileEntryParser
 +     */
 +    public FTPFile[] listFiles()
 +    throws IOException
 +    {
 +        return listFiles((String) null);
 +    }
 +
 +    /**
 +     * Using the default autodetect mechanism, initialize an FTPListParseEngine
 +     * object containing a raw file information for the current working
 +     * directory on the server
 +     * This information is obtained through the LIST command.  This object
 +     * is then capable of being iterated to return a sequence of FTPFile
 +     * objects with information filled in by the
 +     * <code> FTPFileEntryParser </code> used.
 +     * <p>
 +     * This method differs from using the listFiles() methods in that
 +     * expensive FTPFile objects are not created until needed which may be
 +     * an advantage on large lists.
 +     *
 +     * @return A FTPListParseEngine object that holds the raw information and
 +     * is capable of providing parsed FTPFile objects, one for each file
 +     * containing information contained in the given path in the format
 +     * determined by the <code> parser </code> parameter.   Null will be
 +     * returned if a data connection cannot be opened.  If the current working
 +     * directory contains no files, an empty array will be the return.
 +     *
 +     * @exception FTPConnectionClosedException
 +     *                   If the FTP server prematurely closes the connection as a result
 +     *                   of the client being idle or some other reason causing the server
 +     *                   to send FTP reply code 421.  This exception may be caught either
 +     *                   as an IOException or independently as itself.
 +     * @exception IOException
 +     *                   If an I/O error occurs while either sending a
 +     *                   command to the server or receiving a reply from the server.
 +     * @exception ParserInitializationException
 +     *                   Thrown if the autodetect mechanism cannot
 +     *                   resolve the type of system we are connected with.
 +     * @see FTPListParseEngine
 +     */
 +    public FTPListParseEngine initiateListParsing()
 +    throws IOException
 +    {
 +        return initiateListParsing((String) null);
 +    }
 +
 +    /**
 +     * Using the default autodetect mechanism, initialize an FTPListParseEngine
 +     * object containing a raw file information for the supplied directory.
 +     * This information is obtained through the LIST command.  This object
 +     * is then capable of being iterated to return a sequence of FTPFile
 +     * objects with information filled in by the
 +     * <code> FTPFileEntryParser </code> used.
 +     * <p>
 +     * The server may or may not expand glob expressions.  You should avoid
 +     * using glob expressions because the return format for glob listings
 +     * differs from server to server and will likely cause this method to fail.
 +     * <p>
 +     * This method differs from using the listFiles() methods in that
 +     * expensive FTPFile objects are not created until needed which may be
 +     * an advantage on large lists.
 +     * <p>
 +     * <pre>
 +     *    FTPClient f=FTPClient();
 +     *    f.connect(server);
 +     *    f.login(username, password);
 +     *    FTPListParseEngine engine = f.initiateListParsing(directory);
 +     *
 +     *    while (engine.hasNext()) {
 +     *       FTPFile[] files = engine.getNext(25);  // "page size" you want
 +     *       //do whatever you want with these files, display them, etc.
 +     *       //expensive FTPFile objects not created until needed.
 +     *    }
 +     * </pre>
 +     *
 +     * @return A FTPListParseEngine object that holds the raw information and
 +     * is capable of providing parsed FTPFile objects, one for each file
 +     * containing information contained in the given path in the format
 +     * determined by the <code> parser </code> parameter.   Null will be
 +     * returned if a data connection cannot be opened.  If the current working
 +     * directory contains no files, an empty array will be the return.
 +     *
 +     * @exception FTPConnectionClosedException
 +     *                   If the FTP server prematurely closes the connection as a result
 +     *                   of the client being idle or some other reason causing the server
 +     *                   to send FTP reply code 421.  This exception may be caught either
 +     *                   as an IOException or independently as itself.
 +     * @exception IOException
 +     *                   If an I/O error occurs while either sending a
 +     *                   command to the server or receiving a reply from the server.
 +     * @exception ParserInitializationException
 +     *                   Thrown if the autodetect mechanism cannot
 +     *                   resolve the type of system we are connected with.
 +     * @see FTPListParseEngine
 +     */
 +    public FTPListParseEngine initiateListParsing(
 +            String pathname)
 +    throws IOException
 +    {
 +        String key = null;
 +        return initiateListParsing(key, pathname);
 +    }
 +
 +    /**
 +     * Using the supplied parser key, initialize an FTPListParseEngine
 +     * object containing a raw file information for the supplied directory.
 +     * This information is obtained through the LIST command.  This object
 +     * is then capable of being iterated to return a sequence of FTPFile
 +     * objects with information filled in by the
 +     * <code> FTPFileEntryParser </code> used.
 +     * <p>
 +     * The server may or may not expand glob expressions.  You should avoid
 +     * using glob expressions because the return format for glob listings
 +     * differs from server to server and will likely cause this method to fail.
 +     * <p>
 +     * This method differs from using the listFiles() methods in that
 +     * expensive FTPFile objects are not created until needed which may be
 +     * an advantage on large lists.
 +     *
 +     * @param parserKey A string representing a designated code or fully-qualified
 +     * class name of an  <code> FTPFileEntryParser </code> that should be
 +     *               used to parse each server file listing.
 +     *
 +     * @return A FTPListParseEngine object that holds the raw information and
 +     * is capable of providing parsed FTPFile objects, one for each file
 +     * containing information contained in the given path in the format
 +     * determined by the <code> parser </code> parameter.   Null will be
 +     * returned if a data connection cannot be opened.  If the current working
 +     * directory contains no files, an empty array will be the return.
 +     *
 +     * @exception FTPConnectionClosedException
 +     *                   If the FTP server prematurely closes the connection as a result
 +     *                   of the client being idle or some other reason causing the server
 +     *                   to send FTP reply code 421.  This exception may be caught either
 +     *                   as an IOException or independently as itself.
 +     * @exception IOException
 +     *                   If an I/O error occurs while either sending a
 +     *                   command to the server or receiving a reply from the server.
 +     * @exception ParserInitializationException
 +     *                   Thrown if the parserKey parameter cannot be
 +     *                   resolved by the selected parser factory.
 +     *                   In the DefaultFTPEntryParserFactory, this will
 +     *                   happen when parserKey is neither
 +     *                   the fully qualified class name of a class
 +     *                   implementing the interface
 +     *                   org.apache.commons.net.ftp.FTPFileEntryParser
 +     *                   nor a string containing one of the recognized keys
 +     *                   mapping to such a parser or if class loader
 +     *                   security issues prevent its being loaded.
 +     * @see FTPListParseEngine
 +     */
 +    public FTPListParseEngine initiateListParsing(
 +            String parserKey, String pathname)
 +    throws IOException
 +    {
 +        // We cache the value to avoid creation of a new object every
 +        // time a file listing is generated.
 +        if(__entryParser == null) {
 +            if (null != parserKey) {
 +                // if a parser key was supplied in the parameters, 
 +                // use that to create the paraser
 +                __entryParser = 
 +                    __parserFactory.createFileEntryParser(parserKey);
 +                
 +            } else {
 +                // if no parserKey was supplied, check for a configuration
 +                // in the params, and if non-null, use that.
 +                if (null != __configuration) {
 +                    __entryParser = 
 +                        __parserFactory.createFileEntryParser(__configuration);
 +                } else {
 +                    // if a parserKey hasn't been supplied, and a configuration
 +                    // hasn't been supplied, then autodetect by calling
 +                    // the SYST command and use that to choose the parser.
 +                    __entryParser = 
 +                        __parserFactory.createFileEntryParser(getSystemName());
 +                }
 +            }
 +        }
 +
 +        return initiateListParsing(__entryParser, pathname);
 +
 +    }
 +
 +
 +    /**
 +     * private method through which all listFiles() and
 +     * initiateListParsing methods pass once a parser is determined.
 +     *
 +     * @exception FTPConnectionClosedException
 +     *                   If the FTP server prematurely closes the connection as a result
 +     *                   of the client being idle or some other reason causing the server
 +     *                   to send FTP reply code 421.  This exception may be caught either
 +     *                   as an IOException or independently as itself.
 +     * @exception IOException
 +     *                   If an I/O error occurs while either sending a
 +     *                   command to the server or receiving a reply from the server.
 +     * @see FTPListParseEngine
 +     */
 +    private FTPListParseEngine initiateListParsing(
 +            FTPFileEntryParser parser, String pathname)
 +    throws IOException
 +    {
 +        Socket socket;
 +
 +        FTPListParseEngine engine = new FTPListParseEngine(parser);
 +
 +        if ((socket = _openDataConnection_(FTPCommand.LIST, getListArguments(pathname))) == null)
 +        {
 +            return engine;
 +        }
 +
 +
 +        try {
 +            engine.readServerList(socket.getInputStream(), getControlEncoding());
 +        }
 +        finally {
 +            socket.close();
 +        }
 +
 +        completePendingCommand();
 +        return engine;
 +    }
 +
 +    /**
 +     * @since 2.0
 +     */
 +    protected String getListArguments(String pathname) {
 +        if (getListHiddenFiles())
 +        {
 +            StringBuffer sb = new StringBuffer(pathname.length() + 3);
 +            sb.append("-a ");
 +            sb.append(pathname);
 +            return sb.toString();
 +        }
 +        
 +        return pathname;
 +    }
 +
 +
 +    /***
 +     * Issue the FTP STAT command to the server.
 +     * <p>
 +     * @return The status information returned by the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public String getStatus() throws IOException
 +    {
 +        if (FTPReply.isPositiveCompletion(stat()))
 +            return getReplyString();
 +        return null;
 +    }
 +
 +
 +    /***
 +     * Issue the FTP STAT command to the server for a given pathname.  This
 +     * should produce a listing of the file or directory.
 +     * <p>
 +     * @return The status information returned by the server.
 +     * @exception FTPConnectionClosedException
 +     *      If the FTP server prematurely closes the connection as a result
 +     *      of the client being idle or some other reason causing the server
 +     *      to send FTP reply code 421.  This exception may be caught either
 +     *      as an IOException or independently as itself.
 +     * @exception IOException  If an I/O error occurs while either sending a
 +     *      command to the server or receiving a reply from the server.
 +     ***/
 +    public String getStatus(String pathname) throws IOException
 +    {
 +        if (FTPReply.isPositiveCompletion(stat(pathname)))
 +            return getReplyString();
 +        return null;
 +    }
 +    
 +    
 +    /**
 +     * Issue the FTP MDTM command (not supported by all servers to retrieve the last
 +     * modification time of a file. The modification string should be in the 
 +     * ISO 3077 form "YYYYMMDDhhmmss(.xxx)?". The timestamp represented should also be in 
 +     * GMT, but not all FTP servers honour this.
 +     * 
 +     * @param pathname The file path to query.
 +     * @return A string representing the last file modification time in <code>YYYYMMDDhhmmss</code> format.
 +     * @throws IOException if an I/O error occurs.
 +     * @since 2.0
 +     */
 +    public String getModificationTime(String pathname) throws IOException {
 +        if (FTPReply.isPositiveCompletion(mdtm(pathname)))
 +            return getReplyString();
 +        return null;
 +    }
 +
 +
 +    /**
 +     * Set the internal buffer size.
 +     *  
 +     * @param bufSize The size of the buffer
 +     */
 +    public void setBufferSize(int bufSize) {
 +        __bufferSize = bufSize;
 +    }
 +    
 +    /**
 +     * Retrieve the current internal buffer size.
 +     * @return The current buffer size.
 +     */
 +    public int getBufferSize() {
 +        return __bufferSize;
 +    }
 +
 +
 +    /** 
 +     * Implementation of the {@link Configurable Configurable} interface. 
 +     * In the case of this class, configuring merely makes the config object available for the
 +     * factory methods that construct parsers.
 +     * @param config {@link FTPClientConfig FTPClientConfig} object used to 
 +     * provide non-standard configurations to the parser.
 +     * @since 1.4
 +     */
 +    public void configure(FTPClientConfig config) {
 +        this.__configuration = config;
 +    }
 +
 +    /**
 +     * You can set this to true if you would like to get hidden files when {@link #listFiles} too.
 +     * A <code>LIST -a</code> will be issued to the ftp server.
 +     * It depends on your ftp server if you need to call this method, also dont expect to get rid
 +     * of hidden files if you call this method with "false".
 +     * 
 +     * @param listHiddenFiles true if hidden files should be listed 
 +     * @since 2.0
 +     */
 +    public void setListHiddenFiles(boolean listHiddenFiles) {
 +        this.__listHiddenFiles = listHiddenFiles;
 +    }
 +
 +    /**
 +     * @see #setListHiddenFiles(boolean)
 +     * @return the current state
 +     * @since 2.0
 +     */
 +    public boolean getListHiddenFiles() {
 +        return this.__listHiddenFiles;
 +    }
 +}
 +
 +/* Emacs configuration
 + * Local variables:        **
 + * mode:             java  **
 + * c-basic-offset:   4     **
 + * indent-tabs-mode: nil   **
 + * End:                    **
 + */
 diff --git a/src/org/apache/commons/net/ftp/FTPClientConfig.java b/src/org/apache/commons/net/ftp/FTPClientConfig.java new file mode 100644 index 0000000..450eddc --- /dev/null +++ b/src/org/apache/commons/net/ftp/FTPClientConfig.java @@ -0,0 +1,580 @@ +/*
 + * 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;
 +
 +import java.text.DateFormatSymbols;
 +import java.util.Collection;
 +import java.util.Locale;
 +import java.util.Map;
 +import java.util.StringTokenizer;
 +import java.util.TreeMap;
 +
 +/**
 + * <p>
 + * This class implements an alternate means of configuring the
 + * {@link  org.apache.commons.net.ftp.FTPClient  FTPClient} object and
 + * also subordinate objects which it uses.  Any class implementing the 
 + * {@link  org.apache.commons.net.ftp.Configurable  Configurable } 
 + * interface can be configured by this object. 
 + * </p><p>
 + * In particular this class was designed primarily to support configuration
 + * of FTP servers which express file timestamps in formats and languages 
 + * other than those for the US locale, which although it is the most common
 + * is not universal.  Unfortunately, nothing in the FTP spec allows this to 
 + * be determined in an automated way, so manual configuration such as this
 + * is necessary.
 + * </p><p>
 + * This functionality was designed to allow existing clients to work exactly
 + * as before without requiring use of this component.  This component should
 + * only need to be explicitly invoked by the user of this package for problem
 + * cases that previous implementations could not solve.
 + * </p>
 + * <h3>Examples of use of FTPClientConfig</h3>
 + * Use cases:
 + * You are trying to access a server that 
 + * <ul> 
 + * <li>lists files with timestamps that use month names in languages other 
 + * than English</li>
 + * <li>lists files with timestamps that use date formats other 
 + * than the American English "standard" <code>MM dd yyyy</code></li>
 + * <li>is in different timezone and you need accurate timestamps for 
 + * dependency checking as in Ant</li>
 + * </ul>
 + * <p>
 + * Unpaged (whole list) access on a UNIX server that uses French month names
 + * but uses the "standard" <code>MMM d yyyy</code> date formatting
 + * <pre>
 + *    FTPClient f=FTPClient();
 + *    FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX);
 + *    conf.setServerLanguageCode("fr");
 + *    f.configure(conf);
 + *    f.connect(server);
 + *    f.login(username, password);
 + *    FTPFile[] files = listFiles(directory);
 + * </pre>
 + * </p>
 + * <p>
 + * Paged access on a UNIX server that uses Danish month names
 + * and "European" date formatting in Denmark's time zone, when you
 + * are in some other time zone.
 + * <pre>
 + *    FTPClient f=FTPClient();
 + *    FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX);
 + *    conf.setServerLanguageCode("da");
 + *    conf.setDefaultDateFormat("d MMM yyyy");
 + *    conf.setRecentDateFormat("d MMM HH:mm");
 + *    conf.setTimeZoneId("Europe/Copenhagen");
 + *    f.configure(conf);
 + *    f.connect(server);
 + *    f.login(username, password);
 + *    FTPListParseEngine engine =
 + *       f.initiateListParsing("com.whatever.YourOwnParser", directory);
 + *
 + *    while (engine.hasNext()) {
 + *       FTPFile[] files = engine.getNext(25);  // "page size" you want
 + *       //do whatever you want with these files, display them, etc.
 + *       //expensive FTPFile objects not created until needed.
 + *    }
 + * </pre>
 + * </p> 
 + * <p>
 + * Unpaged (whole list) access on a VMS server that uses month names
 + * in a language not {@link #getSupportedLanguageCodes() supported} by the system.
 + * but uses the "standard" <code>MMM d yyyy</code> date formatting
 + * <pre>
 + *    FTPClient f=FTPClient();
 + *    FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_VMS);
 + *    conf.setShortMonthNames(
 + *        "jan|feb|mar|apr|ma\u00ED|j\u00FAn|j\u00FAl|\u00e1g\u00FA|sep|okt|n\u00F3v|des");
 + *    f.configure(conf);
 + *    f.connect(server);
 + *    f.login(username, password);
 + *    FTPFile[] files = listFiles(directory);
 + * </pre>
 + * </p>
 + * <p>
 + * Unpaged (whole list) access on a Windows-NT server in a different time zone.
 + * (Note, since the NT Format uses numeric date formatting, language issues
 + * are irrelevant here).
 + * <pre>
 + *    FTPClient f=FTPClient();
 + *    FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_NT);
 + *    conf.setTimeZoneId("America/Denver");
 + *    f.configure(conf);
 + *    f.connect(server);
 + *    f.login(username, password);
 + *    FTPFile[] files = listFiles(directory);
 + * </pre>
 + * </p>
 + * Unpaged (whole list) access on a Windows-NT server in a different time zone
 + * but which has been configured to use a unix-style listing format.
 + * <pre>
 + *    FTPClient f=FTPClient();
 + *    FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX);
 + *    conf.setTimeZoneId("America/Denver");
 + *    f.configure(conf);
 + *    f.connect(server);
 + *    f.login(username, password);
 + *    FTPFile[] files = listFiles(directory);
 + * </pre>
 + * </p>
 + * @since 1.4
 + * @see org.apache.commons.net.ftp.Configurable
 + * @see org.apache.commons.net.ftp.FTPClient
 + * @see org.apache.commons.net.ftp.parser.FTPTimestampParserImpl#configure(FTPClientConfig)
 + * @see org.apache.commons.net.ftp.parser.ConfigurableFTPFileEntryParserImpl
 + */
 +public class FTPClientConfig
 +{
 +    
 +    /**
 +     * Identifier by which a unix-based ftp server is known throughout
 +     * the commons-net ftp system.
 +     */
 +    public static final String SYST_UNIX  = "UNIX";
 +
 +    /**
 +     * Identifier by which a vms-based ftp server is known throughout
 +     * the commons-net ftp system.
 +     */
 +    public static final String SYST_VMS   = "VMS";
 +    
 +    /**
 +     * Identifier by which a WindowsNT-based ftp server is known throughout
 +     * the commons-net ftp system.
 +     */
 +    public static final String SYST_NT    = "WINDOWS";
 +
 +    /**
 +     * Identifier by which an OS/2-based ftp server is known throughout
 +     * the commons-net ftp system.
 +     */
 +    public static final String SYST_OS2   = "OS/2";
 +
 +    /**
 +     * Identifier by which an OS/400-based ftp server is known throughout
 +     * the commons-net ftp system.
 +     */
 +    public static final String SYST_OS400 = "OS/400";
 +    
 +    /**
 +     * Identifier by which an AS/400-based ftp server is known throughout
 +     * the commons-net ftp system.
 +     */
 +    public static final String SYST_AS400 = "AS/400";
 +    
 +    /**
 +     * Identifier by which an MVS-based ftp server is known throughout
 +     * the commons-net ftp system.
 +     */
 +    public static final String SYST_MVS = "MVS";
 +
 +    /**
 +     * Some servers return an "UNKNOWN Type: L8" message
 +     * in response to the SYST command. We set these to be a Unix-type system.
 +     * This may happen if the ftpd in question was compiled without system
 +     * information.
 +     *
 +     * NET-230 - Updated to be UPPERCASE so that the check done in
 +     * createFileEntryParser will succeed.
 +     *
 +     * @since 1.5
 +     */
 +    public static final String SYST_L8 = "TYPE: L8";
 +    
 +    /**
 +     * Identifier by which an Netware-based ftp server is known throughout
 +     * the commons-net ftp system.
 +     *
 +     * @since 1.5
 +     */
 +    public static final String SYST_NETWARE = "NETWARE";
 +    
 +    private final String serverSystemKey;
 +    private String defaultDateFormatStr = null;
 +    private String recentDateFormatStr = null;
 +    private boolean lenientFutureDates = false;
 +    private String serverLanguageCode = null;
 +    private String shortMonthNames = null;
 +    private String serverTimeZoneId = null;
 +    
 +    
 +    /**
 +     * The main constructor for an FTPClientConfig object
 +     * @param systemKey key representing system type of the  server being 
 +     * connected to. See {@link #getServerSystemKey() serverSystemKey}
 +     */
 +    public FTPClientConfig(String systemKey) {
 +        this.serverSystemKey = systemKey;
 +    }
 +
 +    /**
 +     * Convenience constructor mainly for use in testing.
 +     * Constructs a UNIX configuration. 
 +     */
 +    public FTPClientConfig() {
 +        this(SYST_UNIX);
 +    }
 +
 +    /**
 +     * Constructor which allows setting of all member fields
 +     * @param systemKey key representing system type of the  server being 
 +     * connected to. See 
 +     *  {@link #getServerSystemKey() serverSystemKey}
 +     * @param defaultDateFormatStr See 
 +     *  {@link  #setDefaultDateFormatStr(String)  defaultDateFormatStr}
 +     * @param recentDateFormatStr See
 +     *  {@link  #setRecentDateFormatStr(String)  recentDateFormatStr}
 +     * @param serverLanguageCode See
 +     *  {@link  #setServerLanguageCode(String)  serverLanguageCode}
 +     * @param shortMonthNames See
 +     *  {@link  #setShortMonthNames(String)  shortMonthNames}
 +     * @param serverTimeZoneId See
 +     *  {@link  #setServerTimeZoneId(String)  serverTimeZoneId}
 +     */
 +    public FTPClientConfig(String systemKey,
 +                           String defaultDateFormatStr,
 +                           String recentDateFormatStr,
 +                           String serverLanguageCode,
 +                           String shortMonthNames,
 +                           String serverTimeZoneId)
 +    {
 +        this(systemKey);
 +        this.defaultDateFormatStr = defaultDateFormatStr;
 +        this.recentDateFormatStr = recentDateFormatStr;
 +        this.serverLanguageCode = serverLanguageCode;
 +        this.shortMonthNames = shortMonthNames;
 +        this.serverTimeZoneId = serverTimeZoneId;
 +    }
 +    
 +    private static Map<String, Object> LANGUAGE_CODE_MAP = new TreeMap<String, Object>();
 +    static {
 +        
 +        // if there are other commonly used month name encodings which
 +        // correspond to particular locales, please add them here.
 +        
 +        
 +        
 +        // many locales code short names for months as all three letters
 +        // these we handle simply.
 +        LANGUAGE_CODE_MAP.put("en", Locale.ENGLISH);
 +        LANGUAGE_CODE_MAP.put("de",Locale.GERMAN);
 +        LANGUAGE_CODE_MAP.put("it",Locale.ITALIAN);
 +        LANGUAGE_CODE_MAP.put("es", new Locale("es", "", "")); // spanish
 +        LANGUAGE_CODE_MAP.put("pt", new Locale("pt", "", "")); // portuguese
 +        LANGUAGE_CODE_MAP.put("da", new Locale("da", "", "")); // danish
 +        LANGUAGE_CODE_MAP.put("sv", new Locale("sv", "", "")); // swedish
 +        LANGUAGE_CODE_MAP.put("no", new Locale("no", "", "")); // norwegian
 +        LANGUAGE_CODE_MAP.put("nl", new Locale("nl", "", "")); // dutch
 +        LANGUAGE_CODE_MAP.put("ro", new Locale("ro", "", "")); // romanian
 +        LANGUAGE_CODE_MAP.put("sq", new Locale("sq", "", "")); // albanian
 +        LANGUAGE_CODE_MAP.put("sh", new Locale("sh", "", "")); // serbo-croatian
 +        LANGUAGE_CODE_MAP.put("sk", new Locale("sk", "", "")); // slovak
 +        LANGUAGE_CODE_MAP.put("sl", new Locale("sl", "", "")); // slovenian
 +
 +
 +        // some don't
 +        LANGUAGE_CODE_MAP.put("fr",
 +                "jan|f\u00e9v|mar|avr|mai|jun|jui|ao\u00fb|sep|oct|nov|d\u00e9c");  //french
 +            
 +    }
 +    
 +    /**
 +     * Getter for the serverSystemKey property.  This property
 +     * specifies the general type of server to which the client connects.
 +     * Should be either one of the <code>FTPClientConfig.SYST_*</code> codes
 +     * or else the fully qualified class name of a parser implementing both
 +     * the <code>FTPFileEntryParser</code> and <code>Configurable</code>
 +     * interfaces.
 +     * @return Returns the serverSystemKey property.
 +     */
 +    public String getServerSystemKey() {
 +        return serverSystemKey;
 +    }
 +    
 +    /**
 +     * getter for the {@link  #setDefaultDateFormatStr(String)  defaultDateFormatStr} 
 +     * property.  
 +     * @return Returns the defaultDateFormatStr property.
 +     */
 +    public String getDefaultDateFormatStr() {
 +        return defaultDateFormatStr;
 +    }
 +    
 +    /**
 +     * getter for the {@link  #setRecentDateFormatStr(String)  recentDateFormatStr} property.
 +     * @return Returns the recentDateFormatStr property.
 +     */
 +
 +    public String getRecentDateFormatStr() {
 +        return recentDateFormatStr;
 +    }
 +    
 +    /**
 +     * getter for the {@link  #setServerTimeZoneId(String)  serverTimeZoneId} property.
 +     * @return Returns the serverTimeZoneId property.
 +     */
 +    public String getServerTimeZoneId() {
 +        return serverTimeZoneId;
 +    }
 +    
 +    /**
 +     * <p>
 +     * getter for the {@link  #setShortMonthNames(String)  shortMonthNames} 
 +     * property.  
 +     * </p>
 +     * @return Returns the shortMonthNames.
 +     */
 +    public String getShortMonthNames() {
 +        return shortMonthNames;
 +    }
 +    
 +    /**
 +     * <p>
 +     * getter for the {@link  #setServerLanguageCode(String)  serverLanguageCode} property.
 +     * </p>  
 +     * @return Returns the serverLanguageCode property.
 +     */
 +    public String getServerLanguageCode() {
 +        return serverLanguageCode;
 +    }
 +    
 +    /**
 +     * <p>
 +     * getter for the {@link  #setLenientFutureDates(boolean)  lenientFutureDates} property.
 +     * </p>  
 +     * @return Returns the lenientFutureDates.
 +     * @since 1.5
 +     */
 +    public boolean isLenientFutureDates() {
 +        return lenientFutureDates;
 +    }
 +    /**
 +     * <p>
 +     * setter for the defaultDateFormatStr property.  This property
 +     * specifies the main date format that will be used by a parser configured
 +     * by this configuration to parse file timestamps.  If this is not
 +     * specified, such a parser will use as a default value, the most commonly
 +     * used format which will be in as used in <code>en_US</code> locales.
 +     * </p><p>
 +     * This should be in the format described for 
 +     * <code>java.text.SimpleDateFormat</code>. 
 +     * property.
 +     * </p>
 +     * @param defaultDateFormatStr The defaultDateFormatStr to set.
 +     */
 +    public void setDefaultDateFormatStr(String defaultDateFormatStr) {
 +        this.defaultDateFormatStr = defaultDateFormatStr;
 +    }
 +    
 +    /**
 +     * <p>
 +     * setter for the recentDateFormatStr property.  This property
 +     * specifies a secondary date format that will be used by a parser 
 +     * configured by this configuration to parse file timestamps, typically 
 +     * those less than a year old.  If this is  not specified, such a parser 
 +     * will not attempt to parse using an alternate format.
 +     * </p>
 +     * This is used primarily in unix-based systems.
 +     * </p>
 +     * This should be in the format described for 
 +     * <code>java.text.SimpleDateFormat</code>.
 +     * </p>
 +     * @param recentDateFormatStr The recentDateFormatStr to set.
 +     */
 +    public void setRecentDateFormatStr(String recentDateFormatStr) {
 +        this.recentDateFormatStr = recentDateFormatStr;
 +    }
 +    
 +    /**
 +     * <p>
 +     * setter for the lenientFutureDates property.  This boolean property
 +     * (default: false) only has meaning when a 
 +     * {@link  #setRecentDateFormatStr(String)  recentDateFormatStr} property
 +     * has been set.  In that case, if this property is set true, then the
 +     * parser, when it encounters a listing parseable with the recent date 
 +     * format, will only consider a date to belong to the previous year if
 +     * it is more than one day in the future.  This will allow all 
 +     * out-of-synch situations (whether based on "slop" - i.e. servers simply 
 +     * out of synch with one another or because of time zone differences - 
 +     * but in the latter case it is highly recommended to use the 
 +     * {@link  #setServerTimeZoneId(String)  serverTimeZoneId} property
 +     * instead) to resolve correctly.
 +     * </p><p>
 +     * This is used primarily in unix-based systems.
 +     * </p>
 +     * @param lenientFutureDates set true to compensate for out-of-synch 
 +     * conditions.
 +     */
 +    public void setLenientFutureDates(boolean lenientFutureDates) {
 +        this.lenientFutureDates = lenientFutureDates;
 +    }
 +    /**
 +     * <p>
 +     * setter for the serverTimeZoneId property.  This property
 +     * allows a time zone to be specified corresponding to that known to be 
 +     * used by an FTP server in file listings.  This might be particularly 
 +     * useful to clients such as Ant that try to use these timestamps for 
 +     * dependency checking.
 +     * </p><p>
 +     * This should be one of the identifiers used by 
 +     * <code>java.util.TimeZone</code> to refer to time zones, for example, 
 +     * <code>America/Chicago</code> or <code>Asia/Rangoon</code>.
 +     * </p>
 +     * @param serverTimeZoneId The serverTimeZoneId to set.
 +     */
 +    public void setServerTimeZoneId(String serverTimeZoneId) {
 +        this.serverTimeZoneId = serverTimeZoneId;
 +    }
 +    
 +    /**
 +     * <p>
 +     * setter for the shortMonthNames property.  
 +     * This property allows the user to specify a set of month names
 +     * used by the server that is different from those that may be 
 +     * specified using the {@link  #setServerLanguageCode(String)  serverLanguageCode}
 +     * property.
 +     * </p><p>
 +     * This should be a string containing twelve strings each composed of
 +     * three characters, delimited by pipe (|) characters.  Currently, 
 +     * only 8-bit ASCII characters are known to be supported.  For example,
 +     * a set of month names used by a hypothetical Icelandic FTP server might 
 +     * conceivably be specified as 
 +     * <code>"jan|feb|mar|apr|maí|jún|júl|ágú|sep|okt|nóv|des"</code>.  
 +     * </p>
 +     * @param shortMonthNames The value to set to the shortMonthNames property.
 +     */
 +    public void setShortMonthNames(String shortMonthNames) {
 +        this.shortMonthNames = shortMonthNames;
 +    }
 +    
 +    /**
 +     * <p>
 +     * setter for the serverLanguageCode property.  This property allows
 +     * user to specify a 
 +     * <a href="http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt">
 +     * two-letter ISO-639 language code</a> that will be used to 
 +     * configure the set of month names used by the file timestamp parser.
 +     * If neither this nor the {@link #setShortMonthNames(String) shortMonthNames} 
 +     * is specified, parsing will assume English month names, which may or 
 +     * may not be significant, depending on whether the date format(s) 
 +     * specified via {@link  #setDefaultDateFormatStr(String)  defaultDateFormatStr} 
 +     * and/or {@link  #setRecentDateFormatStr(String)  recentDateFormatStr} are using 
 +     * numeric or alphabetic month names.
 +     * </p>
 +     * <p>If the code supplied is not supported here, <code>en_US</code>
 +     * month names will be used.  We are supporting here those language 
 +     * codes which, when a <code> java.util.Locale</code> is constucted
 +     * using it, and a <code>java.text.SimpleDateFormat</code> is 
 +     * constructed using that Locale, the array returned by the 
 +     * SimpleDateFormat's <code>getShortMonths()</code> method consists
 +     * solely of three 8-bit ASCII character strings.  Additionally, 
 +     * languages which do not meet this requirement are included if a 
 +     * common alternative set of short month names is known to be used.
 +     * This means that users who can tell us of additional such encodings
 +     * may get them added to the list of supported languages by contacting
 +     * the jakarta-commons-net team.
 +     * </p>
 +     * <p><strong>
 +     * Please note that this attribute will NOT be used to determine a 
 +     * locale-based date format for the language.  </strong>  
 +     * Experience has shown that many if not most FTP servers outside the
 +     * United States employ the standard <code>en_US</code> date format 
 +     * orderings of <code>MMM d yyyy</code> and <code>MMM d HH:mm</code> 
 +     * and attempting to deduce this automatically here would cause more
 +     * problems than it would solve.  The date format must be changed 
 +     * via the {@link  #setDefaultDateFormatStr(String)  defaultDateFormatStr} and/or 
 +     * {@link  #setRecentDateFormatStr(String)  recentDateFormatStr} parameters.
 +     * </p>
 +     * @param serverLanguageCode The value to set to the serverLanguageCode property.  
 +     */
 +    public void setServerLanguageCode(String serverLanguageCode) {
 +        this.serverLanguageCode = serverLanguageCode;
 +    }
 +    
 +    /**
 +     * Looks up the supplied language code in the internally maintained table of 
 +     * language codes.  Returns a DateFormatSymbols object configured with 
 +     * short month names corresponding to the code.  If there is no corresponding
 +     * entry in the table, the object returned will be that for 
 +     * <code>Locale.US</code> 
 +     * @param languageCode See {@link  #setServerLanguageCode(String)  serverLanguageCode}
 +     * @return a DateFormatSymbols object configured with short month names 
 +     * corresponding to the supplied code, or with month names for  
 +     * <code>Locale.US</code> if there is no corresponding entry in the internal
 +     * table.
 +     */
 +    public static DateFormatSymbols lookupDateFormatSymbols(String languageCode) 
 +    {
 +        Object lang = LANGUAGE_CODE_MAP.get(languageCode);
 +        if (lang != null) {
 +            if (lang instanceof Locale) {
 +                return new DateFormatSymbols((Locale) lang);
 +            } else if (lang instanceof String){
 +                return getDateFormatSymbols((String) lang);
 +            }
 +        }
 +        return new DateFormatSymbols(Locale.US);
 +    }
 +    
 +    /**
 +     * Returns a DateFormatSymbols object configured with short month names
 +     * as in the supplied string
 +     * @param shortmonths This  should be as described in 
 +     *  {@link  #setShortMonthNames(String)  shortMonthNames}
 +     * @return a DateFormatSymbols object configured with short month names
 +     * as in the supplied string
 +     */
 +    public static DateFormatSymbols getDateFormatSymbols(String shortmonths) 
 +    {
 +        String[] months = splitShortMonthString(shortmonths);
 +        DateFormatSymbols dfs = new DateFormatSymbols(Locale.US);
 +        dfs.setShortMonths(months);
 +        return dfs;
 +    }
 +    
 +    private static String[] splitShortMonthString(String shortmonths) {
 +        StringTokenizer st = new StringTokenizer(shortmonths, "|");
 +        int monthcnt = st.countTokens();
 +        if (12 != monthcnt) {
 +            throw new IllegalArgumentException(
 +                    "expecting a pipe-delimited string containing 12 tokens");
 +        }
 +        String[] months = new String[13];
 +        int pos = 0;
 +        while(st.hasMoreTokens()) {
 +            months[pos++] = st.nextToken();
 +        }
 +        months[pos]="";
 +        return months;
 +    }
 +
 +    /**
 +     * Returns a Collection of all the language codes currently supported
 +     * by this class. See {@link  #setServerLanguageCode(String)  serverLanguageCode}  
 +     * for a functional descrption of language codes within this system. 
 +     *
 +     * @return a Collection of all the language codes currently supported
 +     * by this class
 +     */
 +    public static Collection<String> getSupportedLanguageCodes() {
 +        return LANGUAGE_CODE_MAP.keySet();
 +    }
 +    
 +    
 +}
 diff --git a/src/org/apache/commons/net/ftp/FTPCommand.java b/src/org/apache/commons/net/ftp/FTPCommand.java new file mode 100644 index 0000000..d016dff --- /dev/null +++ b/src/org/apache/commons/net/ftp/FTPCommand.java @@ -0,0 +1,131 @@ +/*
 + * 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;
 +
 +/***
 + * FTPCommand stores a set of constants for FTP command codes.  To interpret
 + * the meaning of the codes, familiarity with RFC 959 is assumed.
 + * The mnemonic constant names are transcriptions from the code descriptions
 + * of RFC 959.  For those who think in terms of the actual FTP commands,
 + * a set of constants such as {@link #USER  USER } are provided
 + * where the constant name is the same as the FTP command.
 + * <p>
 + * <p>
 + * @author Daniel F. Savarese
 + ***/
 +
 +public final class FTPCommand
 +{
 +
 +
 +    public static final int USER = 0;
 +    public static final int PASS = 1;
 +    public static final int ACCT = 2;
 +    public static final int CWD = 3;
 +    public static final int CDUP = 4;
 +    public static final int SMNT = 5;
 +    public static final int REIN = 6;
 +    public static final int QUIT = 7;
 +    public static final int PORT = 8;
 +    public static final int PASV = 9;
 +    public static final int TYPE = 10;
 +    public static final int STRU = 11;
 +    public static final int MODE = 12;
 +    public static final int RETR = 13;
 +    public static final int STOR = 14;
 +    public static final int STOU = 15;
 +    public static final int APPE = 16;
 +    public static final int ALLO = 17;
 +    public static final int REST = 18;
 +    public static final int RNFR = 19;
 +    public static final int RNTO = 20;
 +    public static final int ABOR = 21;
 +    public static final int DELE = 22;
 +    public static final int RMD = 23;
 +    public static final int MKD = 24;
 +    public static final int PWD = 25;
 +    public static final int LIST = 26;
 +    public static final int NLST = 27;
 +    public static final int SITE = 28;
 +    public static final int SYST = 29;
 +    public static final int STAT = 30;
 +    public static final int HELP = 31;
 +    public static final int NOOP = 32;
 +    /** @since 2.0 */
 +    public static final int MDTM = 33;
 +
 +    public static final int USERNAME = USER;
 +    public static final int PASSWORD = PASS;
 +    public static final int ACCOUNT = ACCT;
 +    public static final int CHANGE_WORKING_DIRECTORY = CWD;
 +    public static final int CHANGE_TO_PARENT_DIRECTORY = CDUP;
 +    public static final int STRUCTURE_MOUNT = SMNT;
 +    public static final int REINITIALIZE = REIN;
 +    public static final int LOGOUT = QUIT;
 +    public static final int DATA_PORT = PORT;
 +    public static final int PASSIVE = PASV;
 +    public static final int REPRESENTATION_TYPE = TYPE;
 +    public static final int FILE_STRUCTURE = STRU;
 +    public static final int TRANSFER_MODE = MODE;
 +    public static final int RETRIEVE = RETR;
 +    public static final int STORE = STOR;
 +    public static final int STORE_UNIQUE = STOU;
 +    public static final int APPEND = APPE;
 +    public static final int ALLOCATE = ALLO;
 +    public static final int RESTART = REST;
 +    public static final int RENAME_FROM = RNFR;
 +    public static final int RENAME_TO = RNTO;
 +    public static final int ABORT = ABOR;
 +    public static final int DELETE = DELE;
 +    public static final int REMOVE_DIRECTORY = RMD;
 +    public static final int MAKE_DIRECTORY = MKD;
 +    public static final int PRINT_WORKING_DIRECTORY = PWD;
 +    //  public static final int LIST = LIST;
 +    public static final int NAME_LIST = NLST;
 +    public static final int SITE_PARAMETERS = SITE;
 +    public static final int SYSTEM = SYST;
 +    public static final int STATUS = STAT;
 +    //public static final int HELP = HELP;
 +    //public static final int NOOP = NOOP;
 +    /** @since 2.0 */
 +    public static final int MOD_TIME = MDTM;
 +
 +    // Cannot be instantiated
 +    private FTPCommand()
 +    {}
 +
 +    static final String[] _commands = {
 +                                          "USER", "PASS", "ACCT", "CWD", "CDUP", "SMNT", "REIN", "QUIT", "PORT",
 +                                          "PASV", "TYPE", "STRU", "MODE", "RETR", "STOR", "STOU", "APPE", "ALLO",
 +                                          "REST", "RNFR", "RNTO", "ABOR", "DELE", "RMD", "MKD", "PWD", "LIST",
 +                                          "NLST", "SITE", "SYST", "STAT", "HELP", "NOOP"
 +                                      };
 +
 +    /**
 +     * Retrieve the FTP protocol command string corresponding to a specified
 +     * command code.
 +     * <p>
 +     * @param command The command code.
 +     * @return The FTP protcol command string corresponding to a specified
 +     *         command code.
 +     */
 +    public static final String getCommand(int command)
 +    {
 +        return _commands[command];
 +    }
 +}
 diff --git a/src/org/apache/commons/net/ftp/FTPConnectionClosedException.java b/src/org/apache/commons/net/ftp/FTPConnectionClosedException.java new file mode 100644 index 0000000..3eccbf4 --- /dev/null +++ b/src/org/apache/commons/net/ftp/FTPConnectionClosedException.java @@ -0,0 +1,55 @@ +/*
 + * 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;
 +import java.io.IOException;
 +
 +/***
 + * FTPConnectionClosedException is used to indicate the premature or
 + * unexpected closing of an FTP connection resulting from a
 + * {@link org.apache.commons.net.ftp.FTPReply#SERVICE_NOT_AVAILABLE FTPReply.SERVICE_NOT_AVAILABLE }
 + *  response (FTP reply code 421) to a
 + * failed FTP command.  This exception is derived from IOException and
 + * therefore may be caught either as an IOException or specifically as an
 + * FTPConnectionClosedException.
 + * <p>
 + * <p>
 + * @author Daniel F. Savarese
 + * @see FTP
 + * @see FTPClient
 + ***/
 +
 +public class FTPConnectionClosedException extends IOException
 +{
 +
 +    /*** Constructs a FTPConnectionClosedException with no message ***/
 +    public FTPConnectionClosedException()
 +    {
 +        super();
 +    }
 +
 +    /***
 +     * Constructs a FTPConnectionClosedException with a specified message.
 +     * <p>
 +     * @param message  The message explaining the reason for the exception.
 +     ***/
 +    public FTPConnectionClosedException(String message)
 +    {
 +        super(message);
 +    }
 +
 +}
 diff --git a/src/org/apache/commons/net/ftp/FTPFile.java b/src/org/apache/commons/net/ftp/FTPFile.java new file mode 100644 index 0000000..dd67904 --- /dev/null +++ b/src/org/apache/commons/net/ftp/FTPFile.java @@ -0,0 +1,392 @@ +/*
 + * 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;
 +import java.io.Serializable;
 +import java.util.Calendar;
 +
 +/***
 + * The FTPFile class is used to represent information about files stored
 + * on an FTP server.  Because there is no standard representation for
 + * file information on FTP servers, it may not always be possible to
 + * extract all the information that can be represented by FTPFile, or
 + * it may even be possible to extract more information.  In cases where
 + * more information can be extracted, you will want to subclass FTPFile
 + * and implement your own {@link org.apache.commons.net.ftp.FTPFileListParser}
 + *  to extract the information.
 + * However, most FTP servers return file information in a format that
 + * can be completely parsed by
 + * {@link org.apache.commons.net.ftp.DefaultFTPFileListParser}
 + *  and stored in FTPFile.
 + * <p>
 + * <p>
 + * @author Daniel F. Savarese
 + * @see FTPFileListParser
 + * @see DefaultFTPFileListParser
 + * @see FTPClient#listFiles
 + ***/
 +
 +public class FTPFile implements Serializable
 +{
 +    /** A constant indicating an FTPFile is a file. ***/
 +    public static final int FILE_TYPE = 0;
 +    /** A constant indicating an FTPFile is a directory. ***/
 +    public static final int DIRECTORY_TYPE = 1;
 +    /** A constant indicating an FTPFile is a symbolic link. ***/
 +    public static final int SYMBOLIC_LINK_TYPE = 2;
 +    /** A constant indicating an FTPFile is of unknown type. ***/
 +    public static final int UNKNOWN_TYPE = 3;
 +
 +    /** A constant indicating user access permissions. ***/
 +    public static final int USER_ACCESS = 0;
 +    /** A constant indicating group access permissions. ***/
 +    public static final int GROUP_ACCESS = 1;
 +    /** A constant indicating world access permissions. ***/
 +    public static final int WORLD_ACCESS = 2;
 +
 +    /** A constant indicating file/directory read permission. ***/
 +    public static final int READ_PERMISSION = 0;
 +    /** A constant indicating file/directory write permission. ***/
 +    public static final int WRITE_PERMISSION = 1;
 +    /**
 +     * A constant indicating file execute permission or directory listing
 +     * permission.
 +     ***/
 +    public static final int EXECUTE_PERMISSION = 2;
 +
 +    int _type, _hardLinkCount;
 +    long _size;
 +    String _rawListing, _user, _group, _name, _link;
 +    Calendar _date;
 +    boolean[] _permissions[];
 +
 +    /*** Creates an empty FTPFile. ***/
 +    public FTPFile()
 +    {
 +        _permissions = new boolean[3][3];
 +        _rawListing = null;
 +        _type = UNKNOWN_TYPE;
 +        _hardLinkCount = 0;
 +        _size = 0;
 +        _user = null;
 +        _group = null;
 +        _date = null;
 +        _name = null;
 +    }
 +
 +
 +    /***
 +     * Set the original FTP server raw listing from which the FTPFile was
 +     * created.
 +     * <p>
 +     * @param rawListing  The raw FTP server listing.
 +     ***/
 +    public void setRawListing(String rawListing)
 +    {
 +        _rawListing = rawListing;
 +    }
 +
 +    /***
 +     * Get the original FTP server raw listing used to initialize the FTPFile.
 +     * <p>
 +     * @return The original FTP server raw listing used to initialize the
 +     *         FTPFile.
 +     ***/
 +    public String getRawListing()
 +    {
 +        return _rawListing;
 +    }
 +
 +
 +    /***
 +     * Determine if the file is a directory.
 +     * <p>
 +     * @return True if the file is of type <code>DIRECTORY_TYPE</code>, false if
 +     *         not.
 +     ***/
 +    public boolean isDirectory()
 +    {
 +        return (_type == DIRECTORY_TYPE);
 +    }
 +
 +    /***
 +     * Determine if the file is a regular file.
 +     * <p>
 +     * @return True if the file is of type <code>FILE_TYPE</code>, false if
 +     *         not.
 +     ***/
 +    public boolean isFile()
 +    {
 +        return (_type == FILE_TYPE);
 +    }
 +
 +    /***
 +     * Determine if the file is a symbolic link.
 +     * <p>
 +     * @return True if the file is of type <code>UNKNOWN_TYPE</code>, false if
 +     *         not.
 +     ***/
 +    public boolean isSymbolicLink()
 +    {
 +        return (_type == SYMBOLIC_LINK_TYPE);
 +    }
 +
 +    /***
 +     * Determine if the type of the file is unknown.
 +     * <p>
 +     * @return True if the file is of type <code>UNKNOWN_TYPE</code>, false if
 +     *         not.
 +     ***/
 +    public boolean isUnknown()
 +    {
 +        return (_type == UNKNOWN_TYPE);
 +    }
 +
 +
 +    /***
 +     * Set the type of the file (<code>DIRECTORY_TYPE</code>,
 +     * <code>FILE_TYPE</code>, etc.).
 +     * <p>
 +     * @param type  The integer code representing the type of the file.
 +     ***/
 +    public void setType(int type)
 +    {
 +        _type = type;
 +    }
 +
 +
 +    /***
 +     * Return the type of the file (one of the <code>_TYPE</code> constants),
 +     * e.g., if it is a directory, a regular file, or a symbolic link.
 +     * <p>
 +     * @return The type of the file.
 +     ***/
 +    public int getType()
 +    {
 +        return _type;
 +    }
 +
 +
 +    /***
 +     * Set the name of the file.
 +     * <p>
 +     * @param name  The name of the file.
 +     ***/
 +    public void setName(String name)
 +    {
 +        _name = name;
 +    }
 +
 +    /***
 +     * Return the name of the file.
 +     * <p>
 +     * @return The name of the file.
 +     ***/
 +    public String getName()
 +    {
 +        return _name;
 +    }
 +
 +
 +    /**
 +     * Set the file size in bytes.
 +     * @param size The file size in bytes.
 +     */
 +    public void setSize(long size)
 +    {
 +        _size = size;
 +    }
 +
 +
 +    /***
 +     * Return the file size in bytes.
 +     * <p>
 +     * @return The file size in bytes.
 +     ***/
 +    public long getSize()
 +    {
 +        return _size;
 +    }
 +
 +
 +    /***
 +     * Set the number of hard links to this file.  This is not to be
 +     * confused with symbolic links.
 +     * <p>
 +     * @param links  The number of hard links to this file.
 +     ***/
 +    public void setHardLinkCount(int links)
 +    {
 +        _hardLinkCount = links;
 +    }
 +
 +
 +    /***
 +     * Return the number of hard links to this file.  This is not to be
 +     * confused with symbolic links.
 +     * <p>
 +     * @return The number of hard links to this file.
 +     ***/
 +    public int getHardLinkCount()
 +    {
 +        return _hardLinkCount;
 +    }
 +
 +
 +    /***
 +     * Set the name of the group owning the file.  This may be
 +     * a string representation of the group number.
 +     * <p>
 +     * @param group The name of the group owning the file.
 +     ***/
 +    public void setGroup(String group)
 +    {
 +        _group = group;
 +    }
 +
 +
 +    /***
 +     * Returns the name of the group owning the file.  Sometimes this will be
 +     * a string representation of the group number.
 +     * <p>
 +     * @return The name of the group owning the file.
 +     ***/
 +    public String getGroup()
 +    {
 +        return _group;
 +    }
 +
 +
 +    /***
 +     * Set the name of the user owning the file.  This may be
 +     * a string representation of the user number;
 +     * <p>
 +     * @param user The name of the user owning the file.
 +     ***/
 +    public void setUser(String user)
 +    {
 +        _user = user;
 +    }
 +
 +    /***
 +     * Returns the name of the user owning the file.  Sometimes this will be
 +     * a string representation of the user number.
 +     * <p>
 +     * @return The name of the user owning the file.
 +     ***/
 +    public String getUser()
 +    {
 +        return _user;
 +    }
 +
 +
 +    /***
 +     * If the FTPFile is a symbolic link, use this method to set the name of the
 +     * file being pointed to by the symbolic link.
 +     * <p>
 +     * @param link  The file pointed to by the symbolic link.
 +     ***/
 +    public void setLink(String link)
 +    {
 +        _link = link;
 +    }
 +
 +
 +    /***
 +     * If the FTPFile is a symbolic link, this method returns the name of the
 +     * file being pointed to by the symbolic link.  Otherwise it returns null.
 +     * <p>
 +     * @return The file pointed to by the symbolic link (null if the FTPFile
 +     *         is not a symbolic link).
 +     ***/
 +    public String getLink()
 +    {
 +        return _link;
 +    }
 +
 +
 +    /***
 +     * Set the file timestamp.  This usually the last modification time.
 +     * The parameter is not cloned, so do not alter its value after calling
 +     * this method.
 +     * <p>
 +     * @param date A Calendar instance representing the file timestamp.
 +     ***/
 +    public void setTimestamp(Calendar date)
 +    {
 +        _date = date;
 +    }
 +
 +
 +    /***
 +     * Returns the file timestamp.  This usually the last modification time.
 +     * <p>
 +     * @return A Calendar instance representing the file timestamp.
 +     ***/
 +    public Calendar getTimestamp()
 +    {
 +        return _date;
 +    }
 +
 +
 +    /***
 +     * Set if the given access group (one of the <code> _ACCESS </code>
 +     * constants) has the given access permission (one of the
 +     * <code> _PERMISSION </code> constants) to the file.
 +     * <p>
 +     * @param access The access group (one of the <code> _ACCESS </code>
 +     *               constants)
 +     * @param permission The access permission (one of the
 +     *               <code> _PERMISSION </code> constants)
 +     * @param value  True if permission is allowed, false if not.
 +     ***/
 +    public void setPermission(int access, int permission, boolean value)
 +    {
 +        _permissions[access][permission] = value;
 +    }
 +
 +
 +    /***
 +     * Determines if the given access group (one of the <code> _ACCESS </code>
 +     * constants) has the given access permission (one of the
 +     * <code> _PERMISSION </code> constants) to the file.
 +     * <p>
 +     * @param access The access group (one of the <code> _ACCESS </code>
 +     *               constants)
 +     * @param permission The access permission (one of the
 +     *               <code> _PERMISSION </code> constants)
 +     ***/
 +    public boolean hasPermission(int access, int permission)
 +    {
 +        return _permissions[access][permission];
 +    }
 +
 +
 +    /***
 +     * Returns a string representation of the FTPFile information.  This
 +     * will be the raw FTP server listing that was used to initialize the
 +     * FTPFile instance.
 +     * <p>
 +     * @return A string representation of the FTPFile information.
 +     ***/
 +    @Override
 +    public String toString()
 +    {
 +        return _rawListing;
 +    }
 +
 +}
 diff --git a/src/org/apache/commons/net/ftp/FTPFileEntryParser.java b/src/org/apache/commons/net/ftp/FTPFileEntryParser.java new file mode 100644 index 0000000..8e6d09c --- /dev/null +++ b/src/org/apache/commons/net/ftp/FTPFileEntryParser.java @@ -0,0 +1,152 @@ +/*
 + * 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;
 +import java.io.BufferedReader;
 +import java.io.IOException;
 +import java.util.List;
 +
 +/**
 + * FTPFileEntryParser defines the interface for parsing a single FTP file
 + * listing and converting that information into an
 + * {@link org.apache.commons.net.ftp.FTPFile} instance.
 + * Sometimes you will want to parse unusual listing formats, in which
 + * case you would create your own implementation of FTPFileEntryParser and
 + * if necessary, subclass FTPFile.
 + * <p>
 + * Here are some examples showing how to use one of the classes that
 + * implement this interface.
 + * <p>
 + * The first example shows how to get an <b>iterable</b> list of files in which the
 + * more expensive <code>FTPFile</code> objects are not created until needed.  This
 + * is suitable for paged displays.   It requires that a parser object be created
 + * beforehand: <code>parser</code> is an object (in the package
 + * <code>org.apache.commons.net.ftp.parser</code>)
 + * implementing this inteface.
 + *
 + * <pre>
 + *    FTPClient f=FTPClient();
 + *    f.connect(server);
 + *    f.login(username, password);
 + *    FTPFileList list = f.createFileList(directory, parser);
 + *    FTPFileIterator iter = list.iterator();
 + *
 + *    while (iter.hasNext()) {
 + *       FTPFile[] files = iter.getNext(25);  // "page size" you want
 + *       //do whatever you want with these files, display them, etc.
 + *       //expensive FTPFile objects not created until needed.
 + *    }
 + * </pre>
 + *
 + * The second example uses the revised <code>FTPClient.listFiles()</code>
 + * API to pull the whole list from the subfolder <code>subfolder</code> in
 + * one call, attempting to automatically detect the parser type.  This
 + * method, without a parserKey parameter, indicates that autodection should
 + * be used.
 + *
 + * <pre>
 + *    FTPClient f=FTPClient();
 + *    f.connect(server);
 + *    f.login(username, password);
 + *    FTPFile[] files = f.listFiles("subfolder");
 + * </pre>
 + *
 + * The third example uses the revised <code>FTPClient.listFiles()</code>>
 + * API to pull the whole list from the current working directory in one call,
 + * but specifying by classname the parser to be used.  For this particular
 + * parser class, this approach is necessary since there is no way to
 + * autodetect this server type.
 + *
 + * <pre>
 + *    FTPClient f=FTPClient();
 + *    f.connect(server);
 + *    f.login(username, password);
 + *    FTPFile[] files = f.listFiles(
 + *      "org.apache.commons.net.ftp.parser.EnterpriseUnixFTPFileEntryParser",
 + *      ".");
 + * </pre>
 + *
 + * The fourth example uses the revised <code>FTPClient.listFiles()</code>
 + * API to pull a single file listing in an arbitrary directory in one call,
 + * specifying by KEY the parser to be used, in this case, VMS.
 + *
 + * <pre>
 + *    FTPClient f=FTPClient();
 + *    f.connect(server);
 + *    f.login(username, password);
 + *    FTPFile[] files = f.listFiles("VMS", "subfolder/foo.java");
 + * </pre>
 + *
 + * @author <a href="mailto:scohen@apache.org">Steve Cohen</a>
 + * @version $Id: FTPFileEntryParser.java 636854 2008-03-13 19:55:01Z sebb $
 + * @see org.apache.commons.net.ftp.FTPFile
 + * @see org.apache.commons.net.ftp.FTPClient#createFileList
 + */
 +public interface FTPFileEntryParser
 +{
 +    /**
 +     * Parses a line of an FTP server file listing and converts it into a usable
 +     * format in the form of an <code> FTPFile </code> instance.  If the
 +     * file listing line doesn't describe a file, <code> null </code> should be
 +     * returned, otherwise a <code> FTPFile </code> instance representing the
 +     * files in the directory is returned.
 +     * <p>
 +     * @param listEntry A line of text from the file listing
 +     * @return An FTPFile instance corresponding to the supplied entry
 +     */
 +    FTPFile parseFTPEntry(String listEntry);
 +
 +    /**
 +     * Reads the next entry using the supplied BufferedReader object up to
 +     * whatever delemits one entry from the next.  Implementors must define
 +     * this for the particular ftp system being parsed.  In many but not all
 +     * cases, this can be defined simply by calling BufferedReader.readLine().
 +     *
 +     * @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.
 +     */
 +    String readNextEntry(BufferedReader reader) throws IOException;
 +
 +
 +    /**
 +     * This method is a hook for those implementors (such as
 +     * VMSVersioningFTPEntryParser, and possibly others) which need to
 +     * perform some action upon the FTPFileList after it has been created
 +     * from the server stream, but before any clients see the list.
 +     *
 +     * The default implementation can be a no-op.
 +     *
 +     * @param original Original list after it has been created from the server stream
 +     *
 +     * @return Original list as processed by this method.
 +     */
 +    List<String> preParse(List<String> original);
 +
 +
 +}
 +
 +
 +/* Emacs configuration
 + * Local variables:        **
 + * mode:             java  **
 + * c-basic-offset:   4     **
 + * indent-tabs-mode: nil   **
 + * End:                    **
 + */
 diff --git a/src/org/apache/commons/net/ftp/FTPFileEntryParserImpl.java b/src/org/apache/commons/net/ftp/FTPFileEntryParserImpl.java new file mode 100644 index 0000000..22214fe --- /dev/null +++ b/src/org/apache/commons/net/ftp/FTPFileEntryParserImpl.java @@ -0,0 +1,85 @@ +/*
 + * 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;
 +import java.io.BufferedReader;
 +import java.io.IOException;
 +import java.util.Iterator;
 +import java.util.List;
 +
 +/**
 + * 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.
 + *
 + */
 +public abstract class FTPFileEntryParserImpl
 +    implements FTPFileEntryParser
 +{
 +    /**
 +     * The constructor for a FTPFileEntryParserImpl object.
 +     */
 +    public FTPFileEntryParserImpl()
 +    {
 +    }
 +
 +    /**
 +     * Reads the next entry using the supplied BufferedReader object up to
 +     * whatever delemits one entry from the next.  This default implementation
 +     * simply calls BufferedReader.readLine().
 +     *
 +     * @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 java.io.IOException thrown on any IO Error reading from the reader.
 +     */
 +    public String readNextEntry(BufferedReader reader) throws IOException
 +    {
 +        return reader.readLine();
 +    }
 +    /**
 +     * This method is a hook for those implementors (such as
 +     * VMSVersioningFTPEntryParser, and possibly others) which need to
 +     * perform some action upon the FTPFileList after it has been created
 +     * from the server stream, but before any clients see the list.
 +     *
 +     * This default implementation removes entries that do not parse as files.
 +     *
 +     * @param original Original list after it has been created from the server stream
 +     *
 +     * @return <code>original</code> unmodified.
 +     */
 +     public List<String> preParse(List<String> original) {
 +         Iterator<String> it = original.iterator();
 +         while (it.hasNext()){
 +            String entry = it.next();
 +            if (null == parseFTPEntry(entry)) {
 +                it.remove();
 +            }
 +         }
 +         return original;
 +     }
 +}
 +
 +/* Emacs configuration
 + * Local variables:        **
 + * mode:             java  **
 + * c-basic-offset:   4     **
 + * indent-tabs-mode: nil   **
 + * End:                    **
 + */
 diff --git a/src/org/apache/commons/net/ftp/FTPListParseEngine.java b/src/org/apache/commons/net/ftp/FTPListParseEngine.java new file mode 100644 index 0000000..468bf39 --- /dev/null +++ b/src/org/apache/commons/net/ftp/FTPListParseEngine.java @@ -0,0 +1,290 @@ +/*
 + * 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;
 +
 +import java.io.BufferedReader;
 +import java.io.IOException;
 +import java.io.InputStream;
 +import java.io.InputStreamReader;
 +import java.util.Iterator;
 +import java.util.LinkedList;
 +import java.util.List;
 +import java.util.ListIterator;
 +
 +
 +/**
 + * This class handles the entire process of parsing a listing of
 + * file entries from the server.
 + * <p>
 + * This object defines a two-part parsing mechanism.
 + * <p>
 + * The first part is comprised of reading the raw input into an internal
 + * list of strings.  Every item in this list corresponds to an actual
 + * file.  All extraneous matter emitted by the server will have been
 + * removed by the end of this phase.  This is accomplished in conjunction
 + * with the FTPFileEntryParser associated with this engine, by calling
 + * its methods <code>readNextEntry()</code> - which handles the issue of
 + * what delimits one entry from another, usually but not always a line
 + * feed and <code>preParse()</code> - which handles removal of
 + * extraneous matter such as the preliminary lines of a listing, removal
 + * of duplicates on versioning systems, etc.
 + * <p>
 + * The second part is composed of the actual parsing, again in conjunction
 + * with the particular parser used by this engine.  This is controlled
 + * by an iterator over the internal list of strings.  This may be done
 + * either in block mode, by calling the <code>getNext()</code> and
 + * <code>getPrevious()</code> methods to provide "paged" output of less
 + * than the whole list at one time, or by calling the
 + * <code>getFiles()</code> method to return the entire list.
 + * <p>
 + * Examples:
 + * <p>
 + * Paged access:
 + * <pre>
 + *    FTPClient f=FTPClient();
 + *    f.connect(server);
 + *    f.login(username, password);
 + *    FTPListParseEngine engine = f.initiateListParsing(directory);
 + *
 + *    while (engine.hasNext()) {
 + *       FTPFile[] files = engine.getNext(25);  // "page size" you want
 + *       //do whatever you want with these files, display them, etc.
 + *       //expensive FTPFile objects not created until needed.
 + *    }
 + * </pre>
 + * <p>
 + * For unpaged access, simply use FTPClient.listFiles().  That method
 + * uses this class transparently.
 + * @version $Id: FTPListParseEngine.java 658518 2008-05-21 01:04:30Z sebb $
 + */
 +public class FTPListParseEngine {
 +    private List<String> entries = new LinkedList<String>();
 +    private ListIterator<String> _internalIterator = entries.listIterator();
 +
 +    FTPFileEntryParser parser = null;
 +
 +    public FTPListParseEngine(FTPFileEntryParser parser) {
 +        this.parser = parser;
 +    }
 +
 +    /**
 +     * handle the iniitial reading and preparsing of the list returned by
 +     * the server.  After this method has completed, this object will contain
 +     * a list of unparsed entries (Strings) each referring to a unique file
 +     * on the server.
 +     *
 +     * @param stream input stream provided by the server socket.
 +     *
 +     * @exception IOException
 +     *                   thrown on any failure to read from the sever.
 +     */
 +    public void readServerList(InputStream stream, String encoding)
 +    throws IOException
 +    {
 +        this.entries = new LinkedList<String>();
 +        readStream(stream, encoding);
 +        this.parser.preParse(this.entries);
 +        resetIterator();
 +    }
 +    
 +    /**
 +     * handle the iniitial reading and preparsing of the list returned by
 +     * the server.  After this method has completed, this object will contain
 +     * a list of unparsed entries (Strings) each referring to a unique file
 +     * on the server.
 +     *
 +     * @param stream input stream provided by the server socket.
 +     *
 +     * @exception IOException
 +     *                   thrown on any failure to read from the sever.
 +     *
 +     * @deprecated The version of this method which takes an encoding should be used.
 +    */
 +    public void readServerList(InputStream stream)
 +    throws IOException
 +    {
 +        readServerList(stream, null);
 +    }
 +    
 +
 +
 +    /**
 +     * Internal method for reading the input into the <code>entries</code> list.
 +     * After this method has completed, <code>entries</code> will contain a
 +     * collection of entries (as defined by
 +     * <code>FTPFileEntryParser.readNextEntry()</code>), but this may contain
 +     * various non-entry preliminary lines from the server output, duplicates,
 +     * and other data that will not be part of the final listing.
 +     *
 +     * @param stream The socket stream on which the input will be read.
 +     * @param encoding The encoding to use.
 +     *
 +     * @exception IOException
 +     *                   thrown on any failure to read the stream
 +     */
 +    private void readStream(InputStream stream, String encoding) throws IOException
 +    {
 +        BufferedReader reader;
 +        if (encoding == null)
 +        {
 +            reader = new BufferedReader(new InputStreamReader(stream));
 +        }
 +        else
 +        {
 +            reader = new BufferedReader(new InputStreamReader(stream, encoding));
 +        }
 +        
 +        String line = this.parser.readNextEntry(reader);
 +
 +        while (line != null)
 +        {
 +            this.entries.add(line);
 +            line = this.parser.readNextEntry(reader);
 +        }
 +        reader.close();
 +    }
 +
 +    /**
 +     * Returns an array of at most <code>quantityRequested</code> FTPFile
 +     * objects starting at this object's internal iterator's current position.
 +     * If fewer than <code>quantityRequested</code> such
 +     * elements are available, the returned array will have a length equal
 +     * to the number of entries at and after after the current position.
 +     * If no such entries are found, this array will have a length of 0.
 +     *
 +     * After this method is called this object's internal iterator is advanced
 +     * by a number of positions equal to the size of the array returned.
 +     *
 +     * @param quantityRequested
 +     * the maximum number of entries we want to get.
 +     *
 +     * @return an array of at most <code>quantityRequested</code> FTPFile
 +     * objects starting at the current position of this iterator within its
 +     * list and at least the number of elements which  exist in the list at
 +     * and after its current position.
 +     * <p><b> 
 +     * NOTE:</b> This array may contain null members if any of the 
 +     * individual file listings failed to parse.  The caller should 
 +     * check each entry for null before referencing it.
 +     */
 +    public FTPFile[] getNext(int quantityRequested) {
 +        List<FTPFile> tmpResults = new LinkedList<FTPFile>();
 +        int count = quantityRequested;
 +        while (count > 0 && this._internalIterator.hasNext()) {
 +            String entry = this._internalIterator.next();
 +            FTPFile temp = this.parser.parseFTPEntry(entry);
 +            tmpResults.add(temp);
 +            count--;
 +        }
 +        return tmpResults.toArray(new FTPFile[0]);
 +
 +    }
 +
 +    /**
 +     * Returns an array of at most <code>quantityRequested</code> FTPFile
 +     * objects starting at this object's internal iterator's current position,
 +     * and working back toward the beginning.
 +     *
 +     * If fewer than <code>quantityRequested</code> such
 +     * elements are available, the returned array will have a length equal
 +     * to the number of entries at and after after the current position.
 +     * If no such entries are found, this array will have a length of 0.
 +     *
 +     * After this method is called this object's internal iterator is moved
 +     * back by a number of positions equal to the size of the array returned.
 +     *
 +     * @param quantityRequested
 +     * the maximum number of entries we want to get.
 +     *
 +     * @return an array of at most <code>quantityRequested</code> FTPFile
 +     * objects starting at the current position of this iterator within its
 +     * list and at least the number of elements which  exist in the list at
 +     * and after its current position.  This array will be in the same order
 +     * as the underlying list (not reversed).
 +     * <p><b> 
 +     * NOTE:</b> This array may contain null members if any of the 
 +     * individual file listings failed to parse.  The caller should 
 +     * check each entry for null before referencing it.
 +     */
 +    public FTPFile[] getPrevious(int quantityRequested) {
 +        List<FTPFile> tmpResults = new LinkedList<FTPFile>();
 +        int count = quantityRequested;
 +        while (count > 0 && this._internalIterator.hasPrevious()) {
 +            String entry = this._internalIterator.previous();
 +            FTPFile temp = this.parser.parseFTPEntry(entry);
 +            tmpResults.add(0,temp);
 +            count--;
 +        }
 +        return tmpResults.toArray(new FTPFile[0]);
 +    }
 +
 +    /**
 +     * Returns an array of FTPFile objects containing the whole list of
 +     * files returned by the server as read by this object's parser.
 +     *
 +     * @return an array of FTPFile objects containing the whole list of
 +     *         files returned by the server as read by this object's parser.
 +     * <p><b> 
 +     * NOTE:</b> This array may contain null members if any of the 
 +     * individual file listings failed to parse.  The caller should 
 +     * check each entry for null before referencing it.
 +     * @exception IOException
 +     */
 +    public FTPFile[] getFiles()
 +    throws IOException
 +    {
 +        List<FTPFile> tmpResults = new LinkedList<FTPFile>();
 +        Iterator<String> iter = this.entries.iterator();
 +        while (iter.hasNext()) {
 +            String entry = iter.next();
 +            FTPFile temp = this.parser.parseFTPEntry(entry);
 +            tmpResults.add(temp);
 +        }
 +        return tmpResults.toArray(new FTPFile[0]);
 +
 +    }
 +
 +    /**
 +     * convenience method to allow clients to know whether this object's
 +     * internal iterator's current position is at the end of the list.
 +     *
 +     * @return true if internal iterator is not at end of list, false
 +     * otherwise.
 +     */
 +    public boolean hasNext() {
 +        return _internalIterator.hasNext();
 +    }
 +
 +    /**
 +     * convenience method to allow clients to know whether this object's
 +     * internal iterator's current position is at the beginning of the list.
 +     *
 +     * @return true if internal iterator is not at beginning of list, false
 +     * otherwise.
 +     */
 +    public boolean hasPrevious() {
 +        return _internalIterator.hasPrevious();
 +    }
 +
 +    /**
 +     * resets this object's internal iterator to the beginning of the list.
 +     */
 +    public void resetIterator() {
 +        this._internalIterator = this.entries.listIterator();
 +    }
 +}
 diff --git a/src/org/apache/commons/net/ftp/FTPReply.java b/src/org/apache/commons/net/ftp/FTPReply.java new file mode 100644 index 0000000..6386568 --- /dev/null +++ b/src/org/apache/commons/net/ftp/FTPReply.java @@ -0,0 +1,240 @@ +/*
 + * 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;
 +
 +/***
 + * FTPReply stores a set of constants for FTP reply codes.  To interpret
 + * the meaning of the codes, familiarity with RFC 959 is assumed.
 + * The mnemonic constant names are transcriptions from the code descriptions
 + * of RFC 959.  For those who think in terms of the actual reply code values,
 + * a set of CODE_NUM constants are provided where NUM is the numerical value
 + * of the code.
 + * <p>
 + * <p>
 + * @author Daniel F. Savarese
 + ***/
 +
 +public final class FTPReply
 +{
 +
 +    public static final int CODE_110 = 110;
 +    public static final int CODE_120 = 120;
 +    public static final int CODE_125 = 125;
 +    public static final int CODE_150 = 150;
 +    public static final int CODE_200 = 200;
 +    public static final int CODE_202 = 202;
 +    public static final int CODE_211 = 211;
 +    public static final int CODE_212 = 212;
 +    public static final int CODE_213 = 213;
 +    public static final int CODE_214 = 214;
 +    public static final int CODE_215 = 215;
 +    public static final int CODE_220 = 220;
 +    public static final int CODE_221 = 221;
 +    public static final int CODE_225 = 225;
 +    public static final int CODE_226 = 226;
 +    public static final int CODE_227 = 227;
 +    public static final int CODE_230 = 230;
 +    public static final int CODE_250 = 250;
 +    public static final int CODE_257 = 257;
 +    public static final int CODE_331 = 331;
 +    public static final int CODE_332 = 332;
 +    public static final int CODE_350 = 350;
 +    public static final int CODE_421 = 421;
 +    public static final int CODE_425 = 425;
 +    public static final int CODE_426 = 426;
 +    public static final int CODE_450 = 450;
 +    public static final int CODE_451 = 451;
 +    public static final int CODE_452 = 452;
 +    public static final int CODE_500 = 500;
 +    public static final int CODE_501 = 501;
 +    public static final int CODE_502 = 502;
 +    public static final int CODE_503 = 503;
 +    public static final int CODE_504 = 504;
 +    public static final int CODE_521 = 521;
 +    public static final int CODE_530 = 530;
 +    public static final int CODE_532 = 532;
 +    public static final int CODE_550 = 550;
 +    public static final int CODE_551 = 551;
 +    public static final int CODE_552 = 552;
 +    public static final int CODE_553 = 553;
 +
 +    public static final int RESTART_MARKER = CODE_110;
 +    public static final int SERVICE_NOT_READY = CODE_120;
 +    public static final int DATA_CONNECTION_ALREADY_OPEN = CODE_125;
 +    public static final int FILE_STATUS_OK = CODE_150;
 +    public static final int COMMAND_OK = CODE_200;
 +    public static final int COMMAND_IS_SUPERFLUOUS = CODE_202;
 +    public static final int SYSTEM_STATUS = CODE_211;
 +    public static final int DIRECTORY_STATUS = CODE_212;
 +    public static final int FILE_STATUS = CODE_213;
 +    public static final int HELP_MESSAGE = CODE_214;
 +    public static final int NAME_SYSTEM_TYPE = CODE_215;
 +    public static final int SERVICE_READY = CODE_220;
 +    public static final int SERVICE_CLOSING_CONTROL_CONNECTION = CODE_221;
 +    public static final int DATA_CONNECTION_OPEN = CODE_225;
 +    public static final int CLOSING_DATA_CONNECTION = CODE_226;
 +    public static final int ENTERING_PASSIVE_MODE = CODE_227;
 +    public static final int USER_LOGGED_IN = CODE_230;
 +    public static final int FILE_ACTION_OK = CODE_250;
 +    public static final int PATHNAME_CREATED = CODE_257;
 +    public static final int NEED_PASSWORD = CODE_331;
 +    public static final int NEED_ACCOUNT = CODE_332;
 +    public static final int FILE_ACTION_PENDING = CODE_350;
 +    public static final int SERVICE_NOT_AVAILABLE = CODE_421;
 +    public static final int CANNOT_OPEN_DATA_CONNECTION = CODE_425;
 +    public static final int TRANSFER_ABORTED = CODE_426;
 +    public static final int FILE_ACTION_NOT_TAKEN = CODE_450;
 +    public static final int ACTION_ABORTED = CODE_451;
 +    public static final int INSUFFICIENT_STORAGE = CODE_452;
 +    public static final int UNRECOGNIZED_COMMAND = CODE_500;
 +    public static final int SYNTAX_ERROR_IN_ARGUMENTS = CODE_501;
 +    public static final int COMMAND_NOT_IMPLEMENTED = CODE_502;
 +    public static final int BAD_COMMAND_SEQUENCE = CODE_503;
 +    public static final int COMMAND_NOT_IMPLEMENTED_FOR_PARAMETER = CODE_504;
 +    public static final int NOT_LOGGED_IN = CODE_530;
 +    public static final int NEED_ACCOUNT_FOR_STORING_FILES = CODE_532;
 +    public static final int FILE_UNAVAILABLE = CODE_550;
 +    public static final int PAGE_TYPE_UNKNOWN = CODE_551;
 +    public static final int STORAGE_ALLOCATION_EXCEEDED = CODE_552;
 +    public static final int FILE_NAME_NOT_ALLOWED = CODE_553;
 +    
 +    // FTPS Reply Codes
 +    /** @since 2.0 */
 +    public static final int CODE_234 = 234;
 +    /** @since 2.0 */
 +    public static final int CODE_235 = 235;
 +    /** @since 2.0 */
 +    public static final int CODE_334 = 334;
 +    /** @since 2.0 */
 +    public static final int CODE_335 = 335;
 +    /** @since 2.0 */
 +    public static final int CODE_431 = 431;
 +    /** @since 2.0 */
 +    public static final int CODE_533 = 533;
 +    /** @since 2.0 */
 +    public static final int CODE_534 = 534;
 +    /** @since 2.0 */
 +    public static final int CODE_535 = 535;
 +    /** @since 2.0 */
 +    public static final int CODE_536 = 536;
 +    
 +    /** @since 2.0 */
 +    public static final int SECURITY_DATA_EXCHANGE_COMPLETE = CODE_234;
 +    /** @since 2.0 */
 +    public static final int SECURITY_DATA_EXCHANGE_SUCCESSFULLY = CODE_235;
 +    /** @since 2.0 */
 +    public static final int SECURITY_MECHANISM_IS_OK = CODE_334;
 +    /** @since 2.0 */
 +    public static final int SECURITY_DATA_IS_ACCEPTABLE = CODE_335;
 +    /** @since 2.0 */
 +    public static final int UNAVAILABLE_RESOURCE = CODE_431;
 +    /** @since 2.0 */
 +    public static final int DENIED_FOR_POLICY_REASONS = CODE_533;
 +    /** @since 2.0 */
 +    public static final int REQUEST_DENIED = CODE_534;
 +    /** @since 2.0 */
 +    public static final int FAILED_SECURITY_CHECK = CODE_535;
 +    /** @since 2.0 */
 +    public static final int REQUESTED_PROT_LEVEL_NOT_SUPPORTED = CODE_536;
 +    
 +
 +    // Cannot be instantiated
 +    private FTPReply()
 +    {}
 +
 +    /***
 +     * Determine if a reply code is a positive preliminary response.  All
 +     * codes beginning with a 1 are positive preliminary responses.
 +     * Postitive preliminary responses are used to indicate tentative success.
 +     * No further commands can be issued to the FTP server after a positive
 +     * preliminary response until a follow up response is received from the
 +     * server.
 +     * <p>
 +     * @param reply  The reply code to test.
 +     * @return True if a reply code is a postive preliminary response, false
 +     *         if not.
 +     ***/
 +    public static boolean isPositivePreliminary(int reply)
 +    {
 +        return (reply >= 100 && reply < 200);
 +    }
 +
 +    /***
 +     * Determine if a reply code is a positive completion response.  All
 +     * codes beginning with a 2 are positive completion responses.
 +     * The FTP server will send a positive completion response on the final
 +     * successful completion of a command.
 +     * <p>
 +     * @param reply  The reply code to test.
 +     * @return True if a reply code is a postive completion response, false
 +     *         if not.
 +     ***/
 +    public static boolean isPositiveCompletion(int reply)
 +    {
 +        return (reply >= 200 && reply < 300);
 +    }
 +
 +    /***
 +     * Determine if a reply code is a positive intermediate response.  All
 +     * codes beginning with a 3 are positive intermediate responses.
 +     * The FTP server will send a positive intermediate response on the
 +     * successful completion of one part of a multi-part sequence of
 +     * commands.  For example, after a successful USER command, a positive
 +     * intermediate response will be sent to indicate that the server is
 +     * ready for the PASS command.
 +     * <p>
 +     * @param reply  The reply code to test.
 +     * @return True if a reply code is a postive intermediate response, false
 +     *         if not.
 +     ***/
 +    public static boolean isPositiveIntermediate(int reply)
 +    {
 +        return (reply >= 300 && reply < 400);
 +    }
 +
 +    /***
 +     * Determine if a reply code is a negative transient response.  All
 +     * codes beginning with a 4 are negative transient responses.
 +     * The FTP server will send a negative transient response on the
 +     * failure of a command that can be reattempted with success.
 +     * <p>
 +     * @param reply  The reply code to test.
 +     * @return True if a reply code is a negative transient response, false
 +     *         if not.
 +     ***/
 +    public static boolean isNegativeTransient(int reply)
 +    {
 +        return (reply >= 400 && reply < 500);
 +    }
 +
 +    /***
 +     * Determine if a reply code is a negative permanent response.  All
 +     * codes beginning with a 5 are negative permanent responses.
 +     * The FTP server will send a negative permanent response on the
 +     * failure of a command that cannot be reattempted with success.
 +     * <p>
 +     * @param reply  The reply code to test.
 +     * @return True if a reply code is a negative permanent response, false
 +     *         if not.
 +     ***/
 +    public static boolean isNegativePermanent(int reply)
 +    {
 +        return (reply >= 500 && reply < 600);
 +    }
 +
 +}
 diff --git a/src/org/apache/commons/net/ftp/FTPSClient.java b/src/org/apache/commons/net/ftp/FTPSClient.java new file mode 100644 index 0000000..f809c41 --- /dev/null +++ b/src/org/apache/commons/net/ftp/FTPSClient.java @@ -0,0 +1,533 @@ +/*
 + * 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;
 +
 +import java.io.BufferedReader;
 +import java.io.BufferedWriter;
 +import java.io.IOException;
 +import java.io.InputStreamReader;
 +import java.io.OutputStreamWriter;
 +import java.net.Socket;
 +import java.security.KeyManagementException;
 +import java.security.NoSuchAlgorithmException;
 +
 +import javax.net.ssl.KeyManager;
 +import javax.net.ssl.SSLContext;
 +import javax.net.ssl.SSLException;
 +import javax.net.ssl.SSLServerSocketFactory;
 +import javax.net.ssl.SSLSocket;
 +import javax.net.ssl.SSLSocketFactory;
 +import javax.net.ssl.TrustManager;
 +
 +/**
 + * FTP over SSL processing. If desired, the JVM property -Djavax.net.debug=all can be used to 
 + * see wire-level SSL details.
 + * 
 + * @version $Id: FTPSClient.java 658520 2008-05-21 01:14:11Z sebb $
 + * @since 2.0
 + */
 +public class FTPSClient extends FTPClient {
 +
 +    /** keystore algorithm name. */
 +    public static String KEYSTORE_ALGORITHM;
 +    /** truststore algorithm name. */
 +    public static String TRUSTSTORE_ALGORITHM;
 +    /** provider name. */
 +    public static String PROVIDER;
 +    /** truststore type. */
 +    public static String STORE_TYPE;
 +
 +    /** The value that I can set in PROT command */
 +    private static final String[] PROT_COMMAND_VALUE = {"C","E","S","P"}; 
 +    /** Default PROT Command */
 +    private static final String DEFAULT_PROT = "C";
 +    /** Default protocol name */
 +    private static final String DEFAULT_PROTOCOL = "TLS";
 +
 +    /** The security mode. (True - Implicit Mode / False - Explicit Mode) */
 +    private boolean isImplicit;
 +    /** The use SSL/TLS protocol. */
 +    private String protocol = DEFAULT_PROTOCOL;
 +    /** The AUTH Command value */
 +    private String auth = DEFAULT_PROTOCOL;
 +    /** The context object. */
 +    private SSLContext context;
 +    /** The socket object. */
 +    private Socket planeSocket;
 +    /** The established socket flag. */
 +    private boolean isCreation = true;
 +    /** The use client mode flag. */
 +    private boolean isClientMode = true;
 +    /** The need client auth flag. */
 +    private boolean isNeedClientAuth = false;
 +    /** The want client auth flag. */
 +    private boolean isWantClientAuth = false;
 +    /** The cipher suites */
 +    private String[] suites = null;
 +    /** The protocol versions */
 +    private String[] protocols = null;
 +    
 +    /** The FTPS {@link TrustManager} implementation. */
 +    private TrustManager trustManager = new FTPSTrustManager();
 +    
 +    /** The {@link KeyManager} */
 +    private KeyManager keyManager;
 +
 +    /**
 +     * Constructor for FTPSClient.
 +     * @throws NoSuchAlgorithmException A requested cryptographic algorithm 
 +     * is not available in the environment.
 +     */
 +    public FTPSClient() throws NoSuchAlgorithmException {
 +        this.protocol = DEFAULT_PROTOCOL;
 +        this.isImplicit = false;
 +    }
 +
 +    /**
 +     * Constructor for FTPSClient.
 +     * @param isImplicit The secutiry mode(Implicit/Explicit).
 +     * @throws NoSuchAlgorithmException A requested cryptographic algorithm 
 +     * is not available in the environment.
 +     */
 +    public FTPSClient(boolean isImplicit) throws NoSuchAlgorithmException {
 +        this.protocol = DEFAULT_PROTOCOL;
 +        this.isImplicit = isImplicit;
 +    }
 +
 +    /**
 +     * Constructor for FTPSClient.
 +     * @param protocol the protocol
 +     * @throws NoSuchAlgorithmException A requested cryptographic algorithm 
 +     * is not available in the environment.
 +     */
 +    public FTPSClient(String protocol) throws NoSuchAlgorithmException {
 +        this.protocol = protocol;
 +        this.isImplicit = false;
 +    }
 +
 +    /**
 +     * Constructor for FTPSClient.
 +     * @param protocol the protocol
 +     * @param isImplicit The secutiry mode(Implicit/Explicit).
 +     * @throws NoSuchAlgorithmException A requested cryptographic algorithm 
 +     * is not available in the environment.
 +     */
 +    public FTPSClient(String protocol, boolean isImplicit) 
 +            throws NoSuchAlgorithmException {
 +        this.protocol = protocol;
 +        this.isImplicit = isImplicit;
 +    }
 +
 +
 +    /**
 +     * Set AUTH command use value.
 +     * This processing is done before connected processing.
 +     * @param auth AUTH command use value.
 +     */
 +    public void setAuthValue(String auth) {
 +        this.auth = auth;
 +    }
 +
 +    /**
 +     * Return AUTH command use value.
 +     * @return AUTH command use value.
 +     */
 +    public String getAuthValue() {
 +        return this.auth;
 +    }
 +
 +    
 +    /**
 +     * Because there are so many connect() methods, 
 +     * the _connectAction_() method is provided as a means of performing 
 +     * some action immediately after establishing a connection, 
 +     * rather than reimplementing all of the connect() methods.
 +     * @throws IOException If it throw by _connectAction_.
 +     * @see org.apache.commons.net.SocketClient#_connectAction_()
 +     */
 +    @Override
 +    protected void _connectAction_() throws IOException {
 +        // Implicit mode.
 +        if (isImplicit) sslNegotiation();
 +        super._connectAction_();
 +        // Explicit mode.
 +        if (!isImplicit) {
 +            execAUTH();
 +            sslNegotiation();
 +        }
 +    }
 +
 +    /**
 +     * AUTH command.
 +     * @throws SSLException If it server reply code not equal "234" and "334".
 +     * @throws IOException If an I/O error occurs while either sending 
 +     * the command.
 +     */
 +    private void execAUTH() throws SSLException, IOException {
 +        int replyCode = sendCommand(
 +                FTPSCommand._commands[FTPSCommand.AUTH], auth);
 +        if (FTPReply.SECURITY_MECHANISM_IS_OK == replyCode) {
 +            // replyCode = 334
 +            // I carry out an ADAT command.
 +        } else if (FTPReply.SECURITY_DATA_EXCHANGE_COMPLETE != replyCode) {
 +            throw new SSLException(getReplyString());
 +        }
 +    }
 +
 +    /**
 +     * Performs a lazy init of the SSL context 
 +     * @throws IOException 
 +     */
 +    private void initSslContext() throws IOException {
 +        if(context == null) {
 +            try  {
 +                context = SSLContext.getInstance(protocol);
 +    
 +                context.init(new KeyManager[] { getKeyManager() } , new TrustManager[] { getTrustManager() } , null);
 +            } catch (KeyManagementException e) {
 +                IOException ioe = new IOException("Could not initialize SSL context");
 +                ioe.initCause(e);
 +                throw ioe;
 +            } catch (NoSuchAlgorithmException e) {
 +                IOException ioe = new IOException("Could not initialize SSL context");
 +                ioe.initCause(e);
 +                throw ioe;
 +            }
 +        }
 +    }
 +    
 +    /**
 +     * SSL/TLS negotiation. Acquires an SSL socket of a control 
 +     * connection and carries out handshake processing.
 +     * @throws IOException A handicap breaks out by sever negotiation.
 +     */
 +    private void sslNegotiation() throws IOException {
 +        // Evacuation not ssl socket.
 +        planeSocket = _socket_;
 +        
 +        initSslContext();
 +
 +        SSLSocketFactory ssf = context.getSocketFactory();
 +        String ip = _socket_.getInetAddress().getHostAddress();
 +        int port = _socket_.getPort();
 +        SSLSocket socket = 
 +            (SSLSocket) ssf.createSocket(_socket_, ip, port, true);
 +        socket.setEnableSessionCreation(isCreation);
 +        socket.setUseClientMode(isClientMode);
 +        // server mode
 +        if (!isClientMode) {
 +            socket.setNeedClientAuth(isNeedClientAuth);
 +            socket.setWantClientAuth(isWantClientAuth);
 +        }
 +        if (protocols != null) socket.setEnabledProtocols(protocols);
 +        if (suites != null) socket.setEnabledCipherSuites(suites);
 +
 +        socket.startHandshake();
 +
 +        _socket_ = socket;
 +        _controlInput_ = new BufferedReader(new InputStreamReader(
 +                socket .getInputStream(), getControlEncoding()));
 +        _controlOutput_ = new BufferedWriter(new OutputStreamWriter(
 +                socket.getOutputStream(), getControlEncoding()));
 +    }
 +    
 +    /**
 +     * Get the {@link KeyManager} instance.
 +     * @return The {@link KeyManager} instance
 +     */
 +    private KeyManager getKeyManager() {
 +        return keyManager;
 +    }
 +    
 +    /**
 +    * Set a {@link KeyManager} to use
 +    * 
 +    * @param keyManager The KeyManager implementation to set.
 +    */
 +    public void setKeyManager(KeyManager keyManager) {
 +        this.keyManager = keyManager;
 +    }
 +
 +    /**
 +     * Controls whether new a SSL session may be established by this socket.
 +     * @param isCreation The established socket flag.
 +     */
 +    public void setEnabledSessionCreation(boolean isCreation) {
 +        this.isCreation = isCreation;
 +    }
 +
 +    /**
 +     * Returns true if new SSL sessions may be established by this socket.
 +     * When a socket does not have a ssl socket, This return False.
 +     * @return true - Indicates that sessions may be created;
 +     * this is the default. 
 +     * false - indicates that an existing session must be resumed.
 +     */
 +    public boolean getEnableSessionCreation() {
 +        if (_socket_ instanceof SSLSocket) 
 +            return ((SSLSocket)_socket_).getEnableSessionCreation();
 +        return false;
 +    }
 +
 +    /**
 +     * Configures the socket to require client authentication.
 +     * @param isNeedClientAuth The need client auth flag.
 +     */
 +    public void setNeedClientAuth(boolean isNeedClientAuth) {
 +        this.isNeedClientAuth = isNeedClientAuth;
 +    }
 +
 +    /**
 +     * Returns true if the socket will require client authentication.
 +     * When a socket does not have a ssl socket, This return False.
 +     * @return true - If the server mode socket should request 
 +     * that the client authenticate itself.
 +     */
 +    public boolean getNeedClientAuth() {
 +        if (_socket_ instanceof SSLSocket) 
 +            return ((SSLSocket)_socket_).getNeedClientAuth();
 +        return false;
 +    }
 +
 +    /**
 +     * Configures the socket to request client authentication, 
 +     * but only if such a request is appropriate to the cipher 
 +     * suite negotiated.
 +     * @param isWantClientAuth The want client auth flag.
 +     */
 +    public void setWantClientAuth(boolean isWantClientAuth) {
 +        this.isWantClientAuth = isWantClientAuth;
 +    }
 +
 +    /**
 +     * Returns true if the socket will request client authentication.
 +     * When a socket does not have a ssl socket, This return False.
 +     * @return true - If the server mode socket should request 
 +     * that the client authenticate itself.
 +     */
 +    public boolean getWantClientAuth() {
 +        if (_socket_ instanceof SSLSocket) 
 +            return ((SSLSocket)_socket_).getWantClientAuth();
 +        return false;
 +    }
 +
 +    /**
 +     * Configures the socket to use client (or server) mode in its first 
 +     * handshake.
 +     * @param isClientMode The use client mode flag.
 +     */
 +    public void setUseClientMode(boolean isClientMode) {
 +        this.isClientMode = isClientMode;
 +    }
 +
 +    /**
 +     * Returns true if the socket is set to use client mode 
 +     * in its first handshake.
 +     * When a socket does not have a ssl socket, This return False.
 +     * @return true - If the socket should start its first handshake 
 +     * in "client" mode.
 +     */
 +    public boolean getUseClientMode() {
 +        if (_socket_ instanceof SSLSocket) 
 +            return ((SSLSocket)_socket_).getUseClientMode();
 +        return false;
 +    }
 +
 +    /**
 +     * Controls which particular cipher suites are enabled for use on this 
 +     * connection. I perform setting before a server negotiation.
 +     * @param cipherSuites The cipher suites.
 +     */
 +    public void setEnabledCipherSuites(String[] cipherSuites) {
 +        suites = new String[cipherSuites.length];
 +        System.arraycopy(cipherSuites, 0, suites, 0, cipherSuites.length);
 +    }
 +
 +    /**
 +     * Returns the names of the cipher suites which could be enabled 
 +     * for use on this connection.
 +     * When a socket does not have a ssl socket, This return null.
 +     * @return An array of cipher suite names.
 +     */
 +    public String[] getEnabledCipherSuites() {
 +        if (_socket_ instanceof SSLSocket) 
 +            return ((SSLSocket)_socket_).getEnabledCipherSuites();
 +        return null;
 +    }
 +
 +    /**
 +     * Controls which particular protocol versions are enabled for use on this
 +     * connection. I perform setting before a server negotiation.
 +     * @param protocolVersions The protocol versions.
 +     */
 +    public void setEnabledProtocols(String[] protocolVersions) {
 +        protocols = new String[protocolVersions.length];
 +        System.arraycopy(protocolVersions, 0, protocols, 0, protocolVersions.length);
 +    }
 +
 +    /**
 +     * Returns the names of the protocol versions which are currently 
 +     * enabled for use on this connection.
 +     * When a socket does not have a ssl socket, This return null.
 +     * @return An array of protocols.
 +     */
 +    public String[] getEnabledProtocols() {
 +        if (_socket_ instanceof SSLSocket) 
 +            return ((SSLSocket)_socket_).getEnabledProtocols();
 +        return null;
 +    }
 +
 +    /**
 +     * PBSZ command. pbsz value: 0 to (2^32)-1 decimal integer.
 +     * @param pbsz Protection Buffer Size.
 +     * @throws SSLException If it server reply code not equal "200".
 +     * @throws IOException If an I/O error occurs while either sending 
 +     * the command.
 +     */
 +    public void execPBSZ(long pbsz) throws SSLException, IOException {
 +        if (pbsz < 0 || 4294967295L < pbsz) 
 +            throw new IllegalArgumentException();
 +        if (FTPReply.COMMAND_OK != sendCommand(
 +                FTPSCommand._commands[FTPSCommand.PBSZ],String.valueOf(pbsz)))
 +            throw new SSLException(getReplyString());
 +    }
 +
 +    /**
 +     * PROT command.</br>
 +     * C - Clear</br>
 +     * S - Safe(SSL protocol only)</br>
 +     * E - Confidential(SSL protocol only)</br>
 +     * P - Private
 +     * @param prot Data Channel Protection Level.
 +     * @throws SSLException If it server reply code not equal "200".
 +     * @throws IOException If an I/O error occurs while either sending 
 +     * the command.
 +     */
 +    public void execPROT(String prot) throws SSLException, IOException {
 +        if (prot == null) prot = DEFAULT_PROT;
 +        if (!checkPROTValue(prot)) throw new IllegalArgumentException();
 +        if (FTPReply.COMMAND_OK != sendCommand(
 +                FTPSCommand._commands[FTPSCommand.PROT], prot)) 
 +            throw new SSLException(getReplyString());
 +        if (DEFAULT_PROT.equals(prot)) {
 +            setSocketFactory(null);
 +            setServerSocketFactory(null);
 +        } else {
 +            setSocketFactory(new FTPSSocketFactory(context));
 +
 +            initSslContext();
 +            
 +            SSLServerSocketFactory ssf = context.getServerSocketFactory();
 +
 +            setServerSocketFactory(ssf);
 +        }
 +    }
 +
 +    /**
 +     * I check the value that I can set in PROT Command value.
 +     * @param prot Data Channel Protection Level.
 +     * @return True - A set point is right / False - A set point is not right
 +     */
 +    private boolean checkPROTValue(String prot) {
 +        for (int p = 0; p < PROT_COMMAND_VALUE.length; p++) {
 +            if (PROT_COMMAND_VALUE[p].equals(prot)) return true;
 +        }
 +        return false;
 +    }
 +
 +    /**
 +     * I carry out an ftp command.
 +     * When a CCC command was carried out, I steep socket and SocketFactory 
 +     * in a state of not ssl.
 +     * @parm command ftp command.
 +     * @return server reply.
 +     * @throws IOException If an I/O error occurs while either sending 
 +     * the command.
 +     * @see org.apache.commons.net.ftp.FTP#sendCommand(java.lang.String)
 +     */
 +    @Override
 +    public int sendCommand(String command, String args) throws IOException {
 +        int repCode = super.sendCommand(command, args);
 +        if (FTPSCommand._commands[FTPSCommand.CCC].equals(command)) {
 +            if (FTPReply.COMMAND_OK == repCode) {
 +                    // TODO Check this - is this necessary at all?
 +                _socket_ = planeSocket;
 +                setSocketFactory(null);
 +            } else {
 +                throw new SSLException(getReplyString());
 +            }
 +        }
 +        return repCode;
 +    }
 +
 +    /**
 +     * Returns a socket of the data connection. 
 +     * Wrapped as an {@link SSLSocket}, which carries out handshake processing.
 +     * @pram command The text representation of the FTP command to send.
 +     * @param arg The arguments to the FTP command. 
 +     * If this parameter is set to null, then the command is sent with 
 +     * no argument.
 +     * @return A Socket corresponding to the established data connection. 
 +     * Null is returned if an FTP protocol error is reported at any point 
 +     * during the establishment and initialization of the connection.
 +     * @throws IOException If there is any problem with the connection.
 +     * @see org.apache.commons.net.ftp.FTPClient#_openDataConnection_(java.lang.String, int)
 +     */
 +    @Override
 +    protected Socket _openDataConnection_(int command, String arg)
 +            throws IOException {
 +        Socket socket = super._openDataConnection_(command, arg);
 +        if (socket != null && socket instanceof SSLSocket) {
 +            SSLSocket sslSocket = (SSLSocket)socket;
 +            sslSocket.setUseClientMode(isClientMode);
 +            sslSocket.setEnableSessionCreation(isCreation);
 +            // server mode
 +            if (!isClientMode) {
 +                sslSocket.setNeedClientAuth(isNeedClientAuth);
 +                sslSocket.setWantClientAuth(isWantClientAuth);
 +            }
 +            if (suites != null)
 +                sslSocket.setEnabledCipherSuites(suites);
 +            if (protocols != null)
 +                sslSocket.setEnabledProtocols(protocols);
 +            sslSocket.startHandshake();
 +        }
 +        return socket;
 +    }
 +
 +    /**
 +     * Get the currently configured {@link TrustManager}.
 +     * 
 +     * @return A TrustManager instance.
 +     */
 +    public TrustManager getTrustManager() {
 +        return trustManager;
 +    }
 +
 +    /**
 +     * Override the default {@link TrustManager} to use.
 +     * 
 +     * @param trustManager The TrustManager implementation to set.
 +     */
 +    public void setTrustManager(TrustManager trustManager) {
 +        this.trustManager = trustManager;
 +    }
 +    
 +    
 +    
 +}
 diff --git a/src/org/apache/commons/net/ftp/FTPSCommand.java b/src/org/apache/commons/net/ftp/FTPSCommand.java new file mode 100644 index 0000000..6fd2efa --- /dev/null +++ b/src/org/apache/commons/net/ftp/FTPSCommand.java @@ -0,0 +1,50 @@ +/*
 + * 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;
 +
 +/**
 + * I acquire a command added in FTPS.
 + * @since 2.0
 + */
 +public final class FTPSCommand {
 +    public static final int AUTH = 0;
 +    public static final int ADAT = 1;
 +    public static final int PBSZ = 2;
 +    public static final int PROT = 3;
 +    public static final int CCC = 4;
 +
 +    public static final int AUTHENTICATION_SECURITY_MECHANISM = AUTH;
 +    public static final int AUTHENTICATION_SECURITY_DATA = ADAT;
 +    public static final int PROTECTION_BUFFER_SIZE = PBSZ;
 +    public static final int DATA_CHANNEL_PROTECTION_LEVEL = PROT;
 +    public static final int CLEAR_COMMAND_CHANNEL = CCC;
 +
 +    static final String[] _commands = {"AUTH","ADAT","PBSZ","PROT","CCC"};
 +
 +    /**
 +     * Retrieve the FTPS command string corresponding to a specified
 +     * command code.
 +     * <p>
 +     * @param command The command code.
 +     * @return The FTPS command string corresponding to a specified 
 +     *  command code.
 +     */
 +    public static final String getCommand(int command) {
 +        return _commands[command];
 +    }
 +}
 diff --git a/src/org/apache/commons/net/ftp/FTPSSocketFactory.java b/src/org/apache/commons/net/ftp/FTPSSocketFactory.java new file mode 100644 index 0000000..b7e1186 --- /dev/null +++ b/src/org/apache/commons/net/ftp/FTPSSocketFactory.java @@ -0,0 +1,82 @@ +/*
 + * 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;
 +
 +import java.io.IOException;
 +import java.net.InetAddress;
 +import java.net.ServerSocket;
 +import java.net.Socket;
 +import java.net.UnknownHostException;
 +
 +import javax.net.SocketFactory;
 +import javax.net.ssl.SSLContext;
 +import javax.net.ssl.SSLServerSocket;
 +
 +
 +/**
 + * 
 + * Implementation of org.apache.commons.net.SocketFactory
 + *
 + * @since 2.0
 + */
 +public class FTPSSocketFactory extends SocketFactory {
 +
 +    private SSLContext context;
 +    
 +    public FTPSSocketFactory(SSLContext context) {
 +        this.context = context;
 +    }
 +    
 +    @Override
 +    public Socket createSocket(String address, int port) throws UnknownHostException, IOException {
 +        return this.context.getSocketFactory().createSocket(address, port);
 +    }
 +
 +    @Override
 +    public Socket createSocket(InetAddress address, int port) throws IOException {
 +        return this.context.getSocketFactory().createSocket(address, port);
 +    }
 +
 +    @Override
 +    public Socket createSocket(String address, int port, InetAddress localAddress, int localPort) throws UnknownHostException, IOException {
 +        return this.context.getSocketFactory().createSocket(address, port, localAddress, localPort);
 +    }
 +
 +    @Override
 +    public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
 +        return this.context.getSocketFactory().createSocket(address, port, localAddress, localPort);
 +    }
 +    
 +    public ServerSocket createServerSocket(int port) throws IOException {
 +        return this.init(this.context.getServerSocketFactory().createServerSocket(port));
 +    }
 +
 +    public ServerSocket createServerSocket(int port, int backlog) throws IOException {
 +        return this.init(this.context.getServerSocketFactory().createServerSocket(port, backlog));
 +    }
 +
 +    public ServerSocket createServerSocket(int port, int backlog, InetAddress ifAddress) throws IOException {
 +        return this.init(this.context.getServerSocketFactory().createServerSocket(port, backlog, ifAddress));
 +    }
 +        
 +    public ServerSocket init(ServerSocket socket) throws IOException {
 +        ((SSLServerSocket) socket).setUseClientMode(true);
 +        return socket;
 +    }
 +}
 diff --git a/src/org/apache/commons/net/ftp/FTPSTrustManager.java b/src/org/apache/commons/net/ftp/FTPSTrustManager.java new file mode 100644 index 0000000..d1a2dd7 --- /dev/null +++ b/src/org/apache/commons/net/ftp/FTPSTrustManager.java @@ -0,0 +1,54 @@ +/*
 + * 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;
 +
 +import java.security.cert.CertificateException;
 +import java.security.cert.X509Certificate;
 +
 +import javax.net.ssl.TrustManager;
 +import javax.net.ssl.X509TrustManager;
 +
 +/**
 + * Custom {@link TrustManager} implementation.
 + * 
 + * @version $Id: FTPSTrustManager.java 658520 2008-05-21 01:14:11Z sebb $
 + * @since 2.0
 + */
 +public class FTPSTrustManager implements X509TrustManager
 +{
 +    /**
 +     * No-op
 +     */
 +    public void checkClientTrusted(X509Certificate[] certificates, String authType)
 +    {
 +        return;
 +    }
 +
 +    public void checkServerTrusted(X509Certificate[] certificates, String authType) throws CertificateException
 +    {
 +        for (int i = 0; i < certificates.length; ++i)
 +        {
 +            certificates[i].checkValidity();
 +        }
 +    }
 +
 +    public X509Certificate[] getAcceptedIssuers()
 +    {
 +        return null;
 +    }
 +}
 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 <mario@ops.co.at>
 + */
 +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;
 +
 +
 +/**
 + * <p>
 + * 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.
 + * </p><p>
 + * This class also implements the {@link Configurable Configurable}
 + * interface to allow the parser to be configured from the outside.
 + * </p>
 + * @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.
 +     * <p>
 +     * @param timestampStr the timestamp string pulled from the 
 +     * file listing by the regular expression parser, to be submitted
 +     * to the <code>timestampParser</code> for extracting the timestamp.
 +     * @return a <code>java.util.Calendar</code> 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.
 +     * </p> 
 +     * @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.
 +     * <p/>
 +     * If <code>key</code> is not recognized as a fully qualified
 +     * classname known to the system, this method will then attempt
 +     * to see whether it <b>contains</b> a string identifying one of
 +     * the known parsers.  This comparison is <b>case-insensitive</b>.
 +     * 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.
 +     * <p/>
 +     *
 +     * @param key    should be a fully qualified classname corresponding to
 +     *               a class implementing the FTPFileEntryParser interface<br/>
 +     *               OR<br/>
 +     *               a string containing (case-insensitively) one of the
 +     *               following keywords:
 +     *               <ul>
 +     *               <li>{@link FTPClientConfig#SYST_UNIX UNIX}</li>
 +     *               <li>{@link FTPClientConfig#SYST_NT WINDOWS}</li>
 +     *               <li>{@link FTPClientConfig#SYST_OS2 OS/2}</li>
 +     *               <li>{@link FTPClientConfig#SYST_OS400 OS/400}</li>
 +     *               <li>{@link FTPClientConfig#SYST_VMS VMS}</li>
 +     *               <li>{@link FTPClientConfig#SYST_MVS MVS}</li>
 +     *               <li>{@link FTPClientConfig#SYST_NETWARE}</li>
 +     *               </ul>
 +     * @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;
 +    }
 +    
 +    /**
 +     * <p>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.
 +     * </p><p>
 +     * 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.
 +     * </p>
 +     * @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"
 + * <P><B>
 + * Note: EnterpriseUnixFTPEntryParser can only be instantiated through the
 + * DefaultFTPParserFactory by classname.  It will not be chosen
 + * by the autodetection scheme.
 + * </B>
 + * @version $Id: EnterpriseUnixFTPEntryParser.java 658518 2008-05-21 01:04:30Z sebb $
 + * @author <a href="Winston.Ojeda@qg.com">Winston Ojeda</a>
 + * @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 <b>which</b> 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 <code> FTPFile </code>  instance.  If
 +     * the file listing line doesn't describe a file,  <code> null </code> is
 +     * returned, otherwise a <code> FTPFile </code>  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;
 +    
 +    /**
 +     *<p>
 +     * 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.
 +     * </p><p>
 +     * 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.
 +     * </p>
 +     *
 +     * @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 <code>java.util.Calendar</code> 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 <code>FTPTimestampParser</code> according
 +     * to the following logic:
 +     * <p>
 +     * 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:
 +     * </p><p><ul>
 +     * <li>If a {@link  FTPClientConfig#setShortMonthNames(String) shortMonthString}
 +     * has been supplied in the <code>config</code>, use that to parse  parse timestamps.</li> 
 +     * <li>Otherwise, if a {@link  FTPClientConfig#setServerLanguageCode(String) serverLanguageCode}
 +     * has been supplied in the <code>config</code>, use the month names represented 
 +     * by that {@link  FTPClientConfig#lookupDateFormatSymbols(String) language}
 +     * to parse timestamps.</li>
 +     * <li>otherwise use default English month names</li>
 +     * </ul></p><p>
 +     * 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.  
 +     * </p> 
 +     */
 +    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 <a href="henrik.sorensen@balcab.ch">Henrik Sorensen</a>
 + * @author <a href="jnadler@srcginc.com">Jeff Nadler</a>
 + * @author <a href="wnoto@openfinance.com">William Noto</a>
 + * 
 + * @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|JES|DB2>
 +     * 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 <code> FTPFile </code> instance.
 +     * If the file listing line doesn't describe a file, then
 +     * <code> null </code> is returned. Otherwise a <code> FTPFile </code>
 +     * 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<String> preParse(List<String> 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  <a href="Winston.Ojeda@qg.com">Winston Ojeda</a>
 + * @author <a href="mailto:scohen@apache.org">Steve Cohen</a>
 + * @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+"
 +        + "(?:(<DIR>)|([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
 +     * <code>REGEX</code> 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
 +     * <code>REGEX</code> 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 <code> FTPFile </code> instance.  If the
 +     * file listing line doesn't describe a file, <code> null </code> is
 +     * returned, otherwise a <code> FTPFile </code> instance representing the
 +     * files in the directory is returned.
 +     * <p>
 +     * @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 ("<DIR>".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 
 + * <a href="http://www.novell.com/documentation/nw65/index.html?page=/documentation/nw65/ftp_enu/data/fbhbgcfa.html">http://www.novell.com/documentation/nw65/index.html?page=/documentation/nw65/ftp_enu/data/fbhbgcfa.html</a>
 + * for more details.
 + *
 + * @author <a href="rwinston@apache.org">Rory Winston</a>
 + * @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
 +     * <code>REGEX</code> 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
 +     * <code>REGEX</code> 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 <code> FTPFile </code> instance.  If the
 +     * file listing line doesn't describe a file, <code> null </code> is
 +     * returned, otherwise a <code> FTPFile </code> instance representing the
 +     * files in the directory is returned.
 +     * <p>
 +     * <p>
 +     * Netware file permissions are in the following format:  RWCEAFMS, and are explained as follows:
 +     * <ul>
 +     * <li><b>S</b> - Supervisor; All rights.
 +     * <li><b>R</b> - Read; Right to open and read or execute.
 +     * <li><b>W</b> - Write; Right to open and modify.
 +     * <li><b>C</b> - Create; Right to create; when assigned to a file, allows a deleted file to be recovered.
 +     * <li><b>E</b> - Erase; Right to delete.
 +     * <li><b>M</b> - Modify; Right to rename a file and to change attributes.
 +     * <li><b>F</b> - File Scan; Right to see directory or file listings.
 +     * <li><b>A</b> - Access Control; Right to modify trustee assignments and the Inherited Rights Mask.
 +     * </ul>
 +     * 
 +     * See <a href="http://www.novell.com/documentation/nfap10/index.html?page=/documentation/nfap10/nfaubook/data/abxraws.html">here</a> 
 +     * 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 <a href="Winston.Ojeda@qg.com">Winston Ojeda</a>
 + * @author <a href="mailto:scohen@apache.org">Steve Cohen</a>
 + * @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
 +     * <code>REGEX</code> 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
 +     * <code>REGEX</code> 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 <code> FTPFile </code> instance.  If the
 +     * file listing line doesn't describe a file, <code> null </code> is
 +     * returned, otherwise a <code> FTPFile </code> instance representing the
 +     * files in the directory is returned.
 +     * <p>
 +     * @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
 +     * <code>REGEX</code> 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
 +     * <code>REGEX</code> 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 <scohen@apache.org>
 + */
 +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 <code>matchnum'th<code> 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: 
 +     * <code>"yyyy-MM-dd HH:mm</code>.  
 +     * 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.
 +     * <p>
 +     * For now end users may specify this format only via 
 +     * <code>UnixFTPEntryParser(FTPClientConfig)</code>.
 +     * 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
 +     * <code>REGEX</code> 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
 +     * <code>REGEX</code> 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 <code> FTPFile </code>
 +     * instance.  If the file listing line doesn't describe a file,
 +     * <code> null </code> is returned, otherwise a <code> FTPFile </code>
 +     * instance representing the files in the directory is returned.
 +     * <p>
 +     * @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)",
 + * <P><B>
 + * Note: VMSFTPEntryParser can only be instantiated through the
 + * DefaultFTPParserFactory by classname.  It will not be chosen
 + * by the autodetection scheme.
 + * </B>
 + * <P>
 + *
 + * @author  <a href="Winston.Ojeda@qg.com">Winston Ojeda</a>
 + * @author <a href="mailto:scohen@apache.org">Steve Cohen</a>
 + * @author <a href="sestegra@free.fr">Stephane ESTE-GRACIAS</a>
 + * @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
 +     * <code>REGEX</code> 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
 +     * <code>REGEX</code> 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 <code> FTPFile </code> instances.  If the
 +     * file list contains no files, <code> null </code> should be
 +     * returned, otherwise an array of <code> FTPFile </code> instances
 +     * representing the files in the directory is returned.
 +     * <p>
 +     * @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 <code> FTPFile </code> instance.  If the
 +     * file listing line doesn't describe a file, <code> null </code> is
 +     * returned, otherwise a <code> FTPFile </code> instance representing the
 +     * files in the directory is returned.
 +     * <p>
 +     * @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)",
 + * <P>
 + *
 + * @author  <a href="Winston.Ojeda@qg.com">Winston Ojeda</a>
 + * @author <a href="sestegra@free.fr">Stephane ESTE-GRACIAS</a>
 + * @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
 +     * <code>REGEX</code> 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
 +     * <code>REGEX</code> 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<String> preParse(List<String> original) {
 +        original = super.preParse(original);
 +        HashMap<String, NameVersion> existingEntries = new HashMap<String, NameVersion>();
 +        ListIterator<String> 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:                    **
 + */
 diff --git a/src/org/apache/commons/net/io/CopyStreamAdapter.java b/src/org/apache/commons/net/io/CopyStreamAdapter.java new file mode 100644 index 0000000..0679d23 --- /dev/null +++ b/src/org/apache/commons/net/io/CopyStreamAdapter.java @@ -0,0 +1,122 @@ +/*
 + * 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.io;
 +
 +import java.util.EventListener;
 +
 +import org.apache.commons.net.util.ListenerList;
 +
 +/**
 + * The CopyStreamAdapter will relay CopyStreamEvents to a list of listeners
 + * when either of its bytesTransferred() methods are called.  Its purpose
 + * is to facilitate the notification of the progress of a copy operation
 + * performed by one of the static copyStream() methods in
 + * org.apache.commons.io.Util to multiple listeners.  The static
 + * copyStream() methods invoke the
 + * bytesTransfered(long, int) of a CopyStreamListener for performance
 + * reasons and also because multiple listeners cannot be registered given
 + * that the methods are static.
 + * <p>
 + * <p>
 + * @see CopyStreamEvent
 + * @see CopyStreamListener
 + * @see Util
 + * @author <a href="mailto:savarese@apache.org">Daniel F. Savarese</a>
 + * @version $Id: CopyStreamAdapter.java 489397 2006-12-21 16:28:51Z rwinston $
 + */
 +public class CopyStreamAdapter implements CopyStreamListener
 +{
 +    private ListenerList internalListeners;
 +
 +    /**
 +     * Creates a new copyStreamAdapter.
 +     */
 +    public CopyStreamAdapter()
 +    {
 +        internalListeners = new ListenerList();
 +    }
 +
 +    /**
 +     * This method is invoked by a CopyStreamEvent source after copying
 +     * a block of bytes from a stream.  The CopyStreamEvent will contain
 +     * the total number of bytes transferred so far and the number of bytes
 +     * transferred in the last write.  The CopyStreamAdapater will relay
 +     * the event to all of its registered listeners, listing itself as the
 +     * source of the event.
 +     * @param event The CopyStreamEvent fired by the copying of a block of
 +     *              bytes.
 +     */
 +    public void bytesTransferred(CopyStreamEvent event)
 +    {
 +        bytesTransferred(event.getTotalBytesTransferred(),
 +                         event.getBytesTransferred(),
 +                         event.getStreamSize());
 +    }
 +
 +    /**
 +     * This method is not part of the JavaBeans model and is used by the
 +     * static methods in the org.apache.commons.io.Util class for efficiency.
 +     * It is invoked after a block of bytes to inform the listener of the
 +     * transfer.  The CopyStreamAdapater will create a CopyStreamEvent
 +     * from the arguments and relay the event to all of its registered
 +     * listeners, listing itself as the source of the event.
 +     * @param totalBytesTransferred  The total number of bytes transferred
 +     *         so far by the copy operation.
 +     * @param bytesTransferred  The number of bytes copied by the most recent
 +     *          write.
 +     * @param streamSize The number of bytes in the stream being copied.
 +     *        This may be equal to CopyStreamEvent.UNKNOWN_STREAM_SIZE if
 +     *        the size is unknown.
 +     */
 +    public void bytesTransferred(long totalBytesTransferred,
 +                                 int bytesTransferred, long streamSize)
 +    {
 +        CopyStreamEvent event;
 +
 +        event = new CopyStreamEvent(this,
 +                                    totalBytesTransferred,
 +                                    bytesTransferred,
 +                                    streamSize);
 +
 +        for (EventListener listener : internalListeners)
 +        {
 +            ((CopyStreamListener) (listener)).bytesTransferred(event);
 +        }
 +    }
 +
 +    /**
 +     * Registers a CopyStreamListener to receive CopyStreamEvents.
 +     * Although this method is not declared to be synchronized, it is
 +     * implemented in a thread safe manner.
 +     * @param listener  The CopyStreamlistener to register.
 +     */
 +    public void addCopyStreamListener(CopyStreamListener listener)
 +    {
 +        internalListeners.addListener(listener);
 +    }
 +
 +    /**
 +     * Unregisters a CopyStreamListener.  Although this method is not
 +     * synchronized, it is implemented in a thread safe manner.
 +     * @param listener  The CopyStreamlistener to unregister.
 +     */
 +    public void removeCopyStreamListener(CopyStreamListener listener)
 +    {
 +        internalListeners.removeListener(listener);
 +    }
 +}
 diff --git a/src/org/apache/commons/net/io/CopyStreamEvent.java b/src/org/apache/commons/net/io/CopyStreamEvent.java new file mode 100644 index 0000000..d7d0ec3 --- /dev/null +++ b/src/org/apache/commons/net/io/CopyStreamEvent.java @@ -0,0 +1,98 @@ +/*
 + * 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.io;
 +
 +import java.util.EventObject;
 +
 +/**
 + * A CopyStreamEvent is triggered after every write performed by a
 + * stream copying operation.  The event stores the number of bytes
 + * transferred by the write triggering the event as well as the total
 + * number of bytes transferred so far by the copy operation.
 + * <p>
 + * <p>
 + * @see CopyStreamListener
 + * @see CopyStreamAdapter
 + * @see Util
 + * @author <a href="mailto:savarese@apache.org">Daniel F. Savarese</a>
 + * @version $Id: CopyStreamEvent.java 489397 2006-12-21 16:28:51Z rwinston $
 + */
 +public class CopyStreamEvent extends EventObject
 +{
 +    /**
 +     * Constant used to indicate the stream size is unknown.
 +     */
 +    public static final long UNKNOWN_STREAM_SIZE = -1;
 +
 +    private int bytesTransferred;
 +    private long totalBytesTransferred;
 +    private long streamSize;
 +
 +    /**
 +     * Creates a new CopyStreamEvent instance.
 +     * @param source  The source of the event.
 +     * @param totalBytesTransferred The total number of bytes transferred so
 +     *   far during a copy operation.
 +     * @param bytesTransferred  The number of bytes transferred during the
 +     *        write that triggered the CopyStreamEvent.
 +     * @param streamSize  The number of bytes in the stream being copied.
 +     *          This may be set to <code>UNKNOWN_STREAM_SIZE</code> if the
 +     *          size is unknown.
 +     */
 +    public CopyStreamEvent(Object source, long totalBytesTransferred,
 +                           int bytesTransferred, long streamSize)
 +    {
 +        super(source);
 +        this.bytesTransferred = bytesTransferred;
 +        this.totalBytesTransferred = totalBytesTransferred;
 +        this.streamSize = streamSize;
 +    }
 +
 +    /**
 +     * Returns the number of bytes transferred by the write that triggered
 +     * the event.
 +     * @return The number of bytes transferred by the write that triggered
 +     * the vent.
 +     */
 +    public int getBytesTransferred()
 +    {
 +        return bytesTransferred;
 +    }
 +
 +    /**
 +     * Returns the total number of bytes transferred so far by the copy
 +     * operation.
 +     * @return The total number of bytes transferred so far by the copy
 +     * operation.
 +     */
 +    public long getTotalBytesTransferred()
 +    {
 +        return totalBytesTransferred;
 +    }
 +
 +    /**
 +     * Returns the size of the stream being copied.
 +     * This may be set to <code>UNKNOWN_STREAM_SIZE</code> if the
 +     * size is unknown.
 +     * @return The size of the stream being copied.
 +     */
 +    public long getStreamSize()
 +    {
 +        return streamSize;
 +    }
 +}
 diff --git a/src/org/apache/commons/net/io/CopyStreamException.java b/src/org/apache/commons/net/io/CopyStreamException.java new file mode 100644 index 0000000..8d34145 --- /dev/null +++ b/src/org/apache/commons/net/io/CopyStreamException.java @@ -0,0 +1,71 @@ +/*
 + * 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.io;
 +
 +import java.io.IOException;
 +
 +/**
 + * The CopyStreamException class is thrown by the org.apache.commons.io.Util
 + * copyStream() methods.  It stores the number of bytes confirmed to
 + * have been transferred before an I/O error as well as the IOException
 + * responsible for the failure of a copy operation.
 + * @see Util
 + * @author <a href="mailto:savarese@apache.org">Daniel F. Savarese</a>
 + * @version $Id: CopyStreamException.java 489397 2006-12-21 16:28:51Z rwinston $
 + */
 +public class CopyStreamException extends IOException
 +{
 +    private long totalBytesTransferred;
 +    private IOException ioException;
 +
 +    /**
 +     * Creates a new CopyStreamException instance.
 +     * @param message  A message describing the error.
 +     * @param bytesTransferred  The total number of bytes transferred before
 +     *        an exception was thrown in a copy operation.
 +     * @param exception  The IOException thrown during a copy operation.
 +     */
 +    public CopyStreamException(String message,
 +                               long bytesTransferred,
 +                               IOException exception)
 +    {
 +        super(message);
 +        totalBytesTransferred = bytesTransferred;
 +        ioException = exception;
 +    }
 +
 +    /**
 +     * Returns the total number of bytes confirmed to have
 +     * been transferred by a failed copy operation.
 +     * @return The total number of bytes confirmed to have
 +     * been transferred by a failed copy operation.
 +     */
 +    public long getTotalBytesTransferred()
 +    {
 +        return totalBytesTransferred;
 +    }
 +
 +    /**
 +     * Returns the IOException responsible for the failure of a copy operation.
 +     * @return The IOException responsible for the failure of a copy operation.
 +     */
 +    public IOException getIOException()
 +    {
 +        return ioException;
 +    }
 +}
 diff --git a/src/org/apache/commons/net/io/CopyStreamListener.java b/src/org/apache/commons/net/io/CopyStreamListener.java new file mode 100644 index 0000000..9e97fb9 --- /dev/null +++ b/src/org/apache/commons/net/io/CopyStreamListener.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.io;
 +
 +import java.util.EventListener;
 +
 +/**
 + * The CopyStreamListener class can accept CopyStreamEvents to keep track
 + * of the progress of a stream copying operation.  However, it is currently
 + * not used that way within NetComponents for performance reasons.  Rather
 + * the bytesTransferred(long, int) method is called directly rather than
 + * passing an event to bytesTransferred(CopyStreamEvent), saving the creation
 + * of a CopyStreamEvent instance.  Also, the only place where
 + * CopyStreamListener is currently used within NetComponents is in the
 + * static methods of the uninstantiable org.apache.commons.io.Util class, which
 + * would preclude the use of addCopyStreamListener and
 + * removeCopyStreamListener methods.  However, future additions may use the
 + * JavaBean event model, which is why the hooks have been included from the
 + * beginning.
 + * <p>
 + * <p>
 + * @see CopyStreamEvent
 + * @see CopyStreamAdapter
 + * @see Util
 + * @author <a href="mailto:savarese@apache.org">Daniel F. Savarese</a>
 + * @version $Id: CopyStreamListener.java 489397 2006-12-21 16:28:51Z rwinston $
 + */
 +public interface CopyStreamListener extends EventListener
 +{
 +    /**
 +     * This method is invoked by a CopyStreamEvent source after copying
 +     * a block of bytes from a stream.  The CopyStreamEvent will contain
 +     * the total number of bytes transferred so far and the number of bytes
 +     * transferred in the last write.
 +     * @param event The CopyStreamEvent fired by the copying of a block of
 +     *              bytes.
 +     */
 +    public void bytesTransferred(CopyStreamEvent event);
 +
 +
 +    /**
 +     * This method is not part of the JavaBeans model and is used by the
 +     * static methods in the org.apache.commons.io.Util class for efficiency.
 +     * It is invoked after a block of bytes to inform the listener of the
 +     * transfer.
 +     * @param totalBytesTransferred  The total number of bytes transferred
 +     *         so far by the copy operation.
 +     * @param bytesTransferred  The number of bytes copied by the most recent
 +     *          write.
 +     * @param streamSize The number of bytes in the stream being copied.
 +     *        This may be equal to CopyStreamEvent.UNKNOWN_STREAM_SIZE if
 +     *        the size is unknown.
 +     */
 +    public void bytesTransferred(long totalBytesTransferred,
 +                                 int bytesTransferred,
 +                                 long streamSize);
 +}
 diff --git a/src/org/apache/commons/net/io/DotTerminatedMessageReader.java b/src/org/apache/commons/net/io/DotTerminatedMessageReader.java new file mode 100644 index 0000000..c0f344d --- /dev/null +++ b/src/org/apache/commons/net/io/DotTerminatedMessageReader.java @@ -0,0 +1,280 @@ +/*
 + * 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.io;
 +
 +import java.io.IOException;
 +import java.io.PushbackReader;
 +import java.io.Reader;
 +
 +/**
 + * DotTerminatedMessageReader is a class used to read messages from a
 + * server that are terminated by a single dot followed by a
 + * <CR><LF>
 + * sequence and with double dots appearing at the begining of lines which
 + * do not signal end of message yet start with a dot.  Various Internet
 + * protocols such as NNTP and POP3 produce messages of this type.
 + * <p>
 + * This class handles stripping of the duplicate period at the beginning
 + * of lines starting with a period, converts NETASCII newlines to the
 + * local line separator format, truncates the end of message indicator,
 + * and ensures you cannot read past the end of the message.
 + * @author <a href="mailto:savarese@apache.org">Daniel F. Savarese</a>
 + * @version $Id: DotTerminatedMessageReader.java 636825 2008-03-13 18:34:52Z sebb $
 + */
 +public final class DotTerminatedMessageReader extends Reader
 +{
 +    private static final String LS;
 +    private static final char[] LS_CHARS;
 +
 +    static
 +    {
 +        LS = System.getProperty("line.separator");
 +        LS_CHARS = LS.toCharArray();
 +    }
 +
 +    private boolean atBeginning;
 +    private boolean eof;
 +    private int pos;
 +    private char[] internalBuffer;
 +    private PushbackReader internalReader;
 +
 +    /**
 +     * Creates a DotTerminatedMessageReader that wraps an existing Reader
 +     * input source.
 +     * @param reader  The Reader input source containing the message.
 +     */
 +    public DotTerminatedMessageReader(Reader reader)
 +    {
 +        super(reader);
 +        internalBuffer = new char[LS_CHARS.length + 3];
 +        pos = internalBuffer.length;
 +        // Assumes input is at start of message
 +        atBeginning = true;
 +        eof = false;
 +        internalReader = new PushbackReader(reader);
 +    }
 +
 +    /**
 +     * Reads and returns the next character in the message.  If the end of the
 +     * message has been reached, returns -1.  Note that a call to this method
 +     * may result in multiple reads from the underlying input stream to decode
 +     * the message properly (removing doubled dots and so on).  All of
 +     * this is transparent to the programmer and is only mentioned for
 +     * completeness.
 +     * @return The next character in the message. Returns -1 if the end of the
 +     *          message has been reached.
 +     * @exception IOException If an error occurs while reading the underlying
 +     *            stream.
 +     */
 +    @Override
 +    public int read() throws IOException
 +    {
 +        int ch;
 +
 +        synchronized (lock)
 +        {
 +            if (pos < internalBuffer.length)
 +            {
 +                return internalBuffer[pos++];
 +            }
 +
 +            if (eof)
 +            {
 +                return -1;
 +            }
 +
 +            if ((ch = internalReader.read()) == -1)
 +            {
 +                eof = true;
 +                return -1;
 +            }
 +
 +            if (atBeginning)
 +            {
 +                atBeginning = false;
 +                if (ch == '.')
 +                {
 +                    ch = internalReader.read();
 +
 +                    if (ch != '.')
 +                    {
 +                        // read newline
 +                        eof = true;
 +                        internalReader.read();
 +                        return -1;
 +                    }
 +                    else
 +                    {
 +                        return '.';
 +                    }
 +                }
 +            }
 +
 +            if (ch == '\r')
 +            {
 +                ch = internalReader.read();
 +
 +                if (ch == '\n')
 +                {
 +                    ch = internalReader.read();
 +
 +                    if (ch == '.')
 +                    {
 +                        ch = internalReader.read();
 +
 +                        if (ch != '.')
 +                        {
 +                            // read newline and indicate end of file
 +                            internalReader.read();
 +                            eof = true;
 +                        }
 +                        else
 +                        {
 +                            internalBuffer[--pos] = (char) ch;
 +                        }
 +                    }
 +                    else
 +                    {
 +                        internalReader.unread(ch);
 +                    }
 +
 +                    pos -= LS_CHARS.length;
 +                    System.arraycopy(LS_CHARS, 0, internalBuffer, pos,
 +                                     LS_CHARS.length);
 +                    ch = internalBuffer[pos++];
 +                }
 +                else
 +                {
 +                    internalBuffer[--pos] = (char) ch;
 +                    return '\r';
 +                }
 +            }
 +
 +            return ch;
 +        }
 +    }
 +
 +    /**
 +     * Reads the next characters from the message into an array and
 +     * returns the number of characters read.  Returns -1 if the end of the
 +     * message has been reached.
 +     * @param buffer  The character array in which to store the characters.
 +     * @return The number of characters read. Returns -1 if the
 +     *          end of the message has been reached.
 +     * @exception IOException If an error occurs in reading the underlying
 +     *            stream.
 +     */
 +    @Override
 +    public int read(char[] buffer) throws IOException
 +    {
 +        return read(buffer, 0, buffer.length);
 +    }
 +
 +    /**
 +     * Reads the next characters from the message into an array and
 +     * returns the number of characters read.  Returns -1 if the end of the
 +     * message has been reached.  The characters are stored in the array
 +     * starting from the given offset and up to the length specified.
 +     * @param buffer  The character array in which to store the characters.
 +     * @param offset   The offset into the array at which to start storing
 +     *              characters.
 +     * @param length   The number of characters to read.
 +     * @return The number of characters read. Returns -1 if the
 +     *          end of the message has been reached.
 +     * @exception IOException If an error occurs in reading the underlying
 +     *            stream.
 +     */
 +    @Override
 +    public int read(char[] buffer, int offset, int length) throws IOException
 +    {
 +        int ch, off;
 +        synchronized (lock)
 +        {
 +            if (length < 1)
 +            {
 +                return 0;
 +            }
 +            if ((ch = read()) == -1)
 +            {
 +                return -1;
 +            }
 +            off = offset;
 +
 +            do
 +            {
 +                buffer[offset++] = (char) ch;
 +            }
 +            while (--length > 0 && (ch = read()) != -1);
 +
 +            return (offset - off);
 +        }
 +    }
 +
 +    /**
 +     * Determines if the message is ready to be read.
 +     * @return True if the message is ready to be read, false if not.
 +     * @exception IOException If an error occurs while checking the underlying
 +     *            stream.
 +     */
 +    @Override
 +    public boolean ready() throws IOException
 +    {
 +        synchronized (lock)
 +        {
 +            return (pos < internalBuffer.length || internalReader.ready());
 +        }
 +    }
 +
 +    /**
 +     * Closes the message for reading.  This doesn't actually close the
 +     * underlying stream.  The underlying stream may still be used for
 +     * communicating with the server and therefore is not closed.
 +     * <p>
 +     * If the end of the message has not yet been reached, this method
 +     * will read the remainder of the message until it reaches the end,
 +     * so that the underlying stream may continue to be used properly
 +     * for communicating with the server.  If you do not fully read
 +     * a message, you MUST close it, otherwise your program will likely
 +     * hang or behave improperly.
 +     * @exception IOException  If an error occurs while reading the
 +     *            underlying stream.
 +     */
 +    @Override
 +    public void close() throws IOException
 +    {
 +        synchronized (lock)
 +        {
 +            if (internalReader == null)
 +            {
 +                return;
 +            }
 +
 +            if (!eof)
 +            {
 +                while (read() != -1)
 +                {
 +                    ;
 +                }
 +            }
 +            eof = true;
 +            atBeginning = false;
 +            pos = internalBuffer.length;
 +            internalReader = null;
 +        }
 +    }
 +}
 diff --git a/src/org/apache/commons/net/io/DotTerminatedMessageWriter.java b/src/org/apache/commons/net/io/DotTerminatedMessageWriter.java new file mode 100644 index 0000000..853e42f --- /dev/null +++ b/src/org/apache/commons/net/io/DotTerminatedMessageWriter.java @@ -0,0 +1,215 @@ +/*
 + * 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.io;
 +
 +import java.io.IOException;
 +import java.io.Writer;
 +
 +/***
 + * DotTerminatedMessageWriter is a class used to write messages to a
 + * server that are terminated by a single dot followed by a
 + * <CR><LF>
 + * sequence and with double dots appearing at the begining of lines which
 + * do not signal end of message yet start with a dot.  Various Internet
 + * protocols such as NNTP and POP3 produce messages of this type.
 + * <p>
 + * This class handles the doubling of line-starting periods,
 + * converts single linefeeds to NETASCII newlines, and on closing
 + * will send the final message terminator dot and NETASCII newline
 + * sequence.
 + * <p>
 + * <p>
 + * @author Daniel F. Savarese
 + ***/
 +
 +public final class DotTerminatedMessageWriter extends Writer
 +{
 +    private static final int __NOTHING_SPECIAL_STATE = 0;
 +    private static final int __LAST_WAS_CR_STATE = 1;
 +    private static final int __LAST_WAS_NL_STATE = 2;
 +
 +    private int __state;
 +    private Writer __output;
 +
 +
 +    /***
 +     * Creates a DotTerminatedMessageWriter that wraps an existing Writer
 +     * output destination.
 +     * <p>
 +     * @param output  The Writer output destination to write the message.
 +     ***/
 +    public DotTerminatedMessageWriter(Writer output)
 +    {
 +        super(output);
 +        __output = output;
 +        __state = __NOTHING_SPECIAL_STATE;
 +    }
 +
 +
 +    /***
 +     * Writes a character to the output.  Note that a call to this method
 +     * may result in multiple writes to the underling Writer in order to
 +     * convert naked linefeeds to NETASCII line separators and to double
 +     * line-leading periods.  This is transparent to the programmer and
 +     * is only mentioned for completeness.
 +     * <p>
 +     * @param ch  The character to write.
 +     * @exception IOException  If an error occurs while writing to the
 +     *            underlying output.
 +     ***/
 +    @Override
 +    public void write(int ch) throws IOException
 +    {
 +        synchronized (lock)
 +        {
 +            switch (ch)
 +            {
 +            case '\r':
 +                __state = __LAST_WAS_CR_STATE;
 +                __output.write('\r');
 +                return ;
 +            case '\n':
 +                if (__state != __LAST_WAS_CR_STATE)
 +                    __output.write('\r');
 +                __output.write('\n');
 +                __state = __LAST_WAS_NL_STATE;
 +                return ;
 +            case '.':
 +                // Double the dot at the beginning of a line
 +                if (__state == __LAST_WAS_NL_STATE)
 +                    __output.write('.');
 +                // Fall through
 +            default:
 +                __state = __NOTHING_SPECIAL_STATE;
 +                __output.write(ch);
 +                return ;
 +            }
 +        }
 +    }
 +
 +
 +    /***
 +     * Writes a number of characters from a character array to the output
 +     * starting from a given offset.
 +     * <p>
 +     * @param buffer  The character array to write.
 +     * @param offset  The offset into the array at which to start copying data.
 +     * @param length  The number of characters to write.
 +     * @exception IOException If an error occurs while writing to the underlying
 +     *            output.
 +     ***/
 +    @Override
 +    public void write(char[] buffer, int offset, int length) throws IOException
 +    {
 +        synchronized (lock)
 +        {
 +            while (length-- > 0)
 +                write(buffer[offset++]);
 +        }
 +    }
 +
 +
 +    /***
 +     * Writes a character array to the output.
 +     * <p>
 +     * @param buffer  The character array to write.
 +     * @exception IOException If an error occurs while writing to the underlying
 +     *            output.
 +     ***/
 +    @Override
 +    public void write(char[] buffer) throws IOException
 +    {
 +        write(buffer, 0, buffer.length);
 +    }
 +
 +
 +    /***
 +     * Writes a String to the output.
 +     * <p>
 +     * @param string  The String to write.
 +     * @exception IOException If an error occurs while writing to the underlying
 +     *            output.
 +     ***/
 +    @Override
 +    public void write(String string) throws IOException
 +    {
 +        write(string.toCharArray());
 +    }
 +
 +
 +    /***
 +     * Writes part of a String to the output starting from a given offset.
 +     * <p>
 +     * @param string  The String to write.
 +     * @param offset  The offset into the String at which to start copying data.
 +     * @param length  The number of characters to write.
 +     * @exception IOException If an error occurs while writing to the underlying
 +     *            output.
 +     ***/
 +    @Override
 +    public void write(String string, int offset, int length) throws IOException
 +    {
 +        write(string.toCharArray(), offset, length);
 +    }
 +
 +
 +    /***
 +     * Flushes the underlying output, writing all buffered output.
 +     * <p>
 +     * @exception IOException If an error occurs while writing to the underlying
 +     *            output.
 +     ***/
 +    @Override
 +    public void flush() throws IOException
 +    {
 +        synchronized (lock)
 +        {
 +            __output.flush();
 +        }
 +    }
 +
 +
 +    /***
 +     * Flushes the underlying output, writing all buffered output, but doesn't
 +     * actually close the underlying stream.  The underlying stream may still
 +     * be used for communicating with the server and therefore is not closed.
 +     * <p>
 +     * @exception IOException If an error occurs while writing to the underlying
 +     *            output or closing the Writer.
 +     ***/
 +    @Override
 +    public void close() throws IOException
 +    {
 +        synchronized (lock)
 +        {
 +            if (__output == null)
 +                return ;
 +
 +            if (__state == __LAST_WAS_CR_STATE)
 +                __output.write('\n');
 +            else if (__state != __LAST_WAS_NL_STATE)
 +                __output.write("\r\n");
 +
 +            __output.write(".\r\n");
 +
 +            __output.flush();
 +            __output = null;
 +        }
 +    }
 +
 +}
 diff --git a/src/org/apache/commons/net/io/FromNetASCIIInputStream.java b/src/org/apache/commons/net/io/FromNetASCIIInputStream.java new file mode 100644 index 0000000..76588ab --- /dev/null +++ b/src/org/apache/commons/net/io/FromNetASCIIInputStream.java @@ -0,0 +1,205 @@ +/*
 + * 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.io;
 +
 +import java.io.IOException;
 +import java.io.InputStream;
 +import java.io.PushbackInputStream;
 +
 +/***
 + * This class wraps an input stream, replacing all occurrences
 + * of <CR><LF> (carriage return followed by a linefeed),
 + * which is the NETASCII standard for representing a newline, with the
 + * local line separator representation.  You would use this class to
 + * implement ASCII file transfers requiring conversion from NETASCII.
 + * <p>
 + * <p>
 + * @author Daniel F. Savarese
 + ***/
 +
 +public final class FromNetASCIIInputStream extends PushbackInputStream
 +{
 +    static final boolean _noConversionRequired;
 +    static final String _lineSeparator;
 +    static final byte[] _lineSeparatorBytes;
 +
 +    static {
 +        _lineSeparator = System.getProperty("line.separator");
 +        _noConversionRequired = _lineSeparator.equals("\r\n");
 +        _lineSeparatorBytes = _lineSeparator.getBytes();
 +    }
 +
 +    private int __length = 0;
 +
 +    /***
 +     * Returns true if the NetASCII line separator differs from the system
 +     * line separator, false if they are the same.  This method is useful
 +     * to determine whether or not you need to instantiate a
 +     * FromNetASCIIInputStream object.
 +     * <p>
 +     * @return True if the NETASCII line separator differs from the local
 +     *   system line separator, false if they are the same.
 +     ***/
 +    public static final boolean isConversionRequired()
 +    {
 +        return !_noConversionRequired;
 +    }
 +
 +    /***
 +     * Creates a FromNetASCIIInputStream instance that wraps an existing
 +     * InputStream.
 +     ***/
 +    public FromNetASCIIInputStream(InputStream input)
 +    {
 +        super(input, _lineSeparatorBytes.length + 1);
 +    }
 +
 +
 +    private int __read() throws IOException
 +    {
 +        int ch;
 +
 +        ch = super.read();
 +
 +        if (ch == '\r')
 +        {
 +            ch = super.read();
 +            if (ch == '\n')
 +            {
 +                unread(_lineSeparatorBytes);
 +                ch = super.read();
 +                // This is a kluge for read(byte[], ...) to read the right amount
 +                --__length;
 +            }
 +            else
 +            {
 +                if (ch != -1)
 +                    unread(ch);
 +                return '\r';
 +            }
 +        }
 +
 +        return ch;
 +    }
 +
 +
 +    /***
 +     * Reads and returns the next byte in the stream.  If the end of the
 +     * message has been reached, returns -1.  Note that a call to this method
 +     * may result in multiple reads from the underlying input stream in order
 +     * to convert NETASCII line separators to the local line separator format.
 +     * This is transparent to the programmer and is only mentioned for
 +     * completeness.
 +     * <p>
 +     * @return The next character in the stream. Returns -1 if the end of the
 +     *          stream has been reached.
 +     * @exception IOException If an error occurs while reading the underlying
 +     *            stream.
 +     ***/
 +    @Override
 +    public int read() throws IOException
 +    {
 +        if (_noConversionRequired)
 +            return super.read();
 +
 +        return __read();
 +    }
 +
 +
 +    /***
 +     * Reads the next number of bytes from the stream into an array and
 +     * returns the number of bytes read.  Returns -1 if the end of the
 +     * stream has been reached.
 +     * <p>
 +     * @param buffer  The byte array in which to store the data.
 +     * @return The number of bytes read. Returns -1 if the
 +     *          end of the message has been reached.
 +     * @exception IOException If an error occurs in reading the underlying
 +     *            stream.
 +     ***/
 +    @Override
 +    public int read(byte buffer[]) throws IOException
 +    {
 +        return read(buffer, 0, buffer.length);
 +    }
 +
 +
 +    /***
 +     * Reads the next number of bytes from the stream into an array and returns
 +     * the number of bytes read.  Returns -1 if the end of the
 +     * message has been reached.  The characters are stored in the array
 +     * starting from the given offset and up to the length specified.
 +     * <p>
 +     * @param buffer The byte array in which to store the data.
 +     * @param offset  The offset into the array at which to start storing data.
 +     * @param length   The number of bytes to read.
 +     * @return The number of bytes read. Returns -1 if the
 +     *          end of the stream has been reached.
 +     * @exception IOException If an error occurs while reading the underlying
 +     *            stream.
 +     ***/
 +    @Override
 +    public int read(byte buffer[], int offset, int length) throws IOException
 +    {
 +        int ch, off;
 +
 +        if (length < 1)
 +            return 0;
 +
 +        ch = available();
 +
 +        __length = (length > ch ? ch : length);
 +
 +        // If nothing is available, block to read only one character
 +        if (__length < 1)
 +            __length = 1;
 +
 +        if (_noConversionRequired)
 +            return super.read(buffer, offset, __length);
 +
 +        if ((ch = __read()) == -1)
 +            return -1;
 +
 +        off = offset;
 +
 +        do
 +        {
 +            buffer[offset++] = (byte)ch;
 +        }
 +        while (--__length > 0 && (ch = __read()) != -1);
 +
 +
 +        return (offset - off);
 +    }
 +
 +
 +    // PushbackInputStream in JDK 1.1.3 returns the wrong thing
 +    /***
 +     * Returns the number of bytes that can be read without blocking EXCEPT
 +     * when newline conversions have to be made somewhere within the
 +     * available block of bytes.  In other words, you really should not
 +     * rely on the value returned by this method if you are trying to avoid
 +     * blocking.
 +     ***/
 +    @Override
 +    public int available() throws IOException
 +    {
 +        return (buf.length - pos) + in.available();
 +    }
 +
 +}
 diff --git a/src/org/apache/commons/net/io/FromNetASCIIOutputStream.java b/src/org/apache/commons/net/io/FromNetASCIIOutputStream.java new file mode 100644 index 0000000..c025a1b --- /dev/null +++ b/src/org/apache/commons/net/io/FromNetASCIIOutputStream.java @@ -0,0 +1,174 @@ +/*
 + * 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.io;
 +
 +import java.io.FilterOutputStream;
 +import java.io.IOException;
 +import java.io.OutputStream;
 +
 +/***
 + * This class wraps an output stream, replacing all occurrences
 + * of <CR><LF> (carriage return followed by a linefeed),
 + * which is the NETASCII standard for representing a newline, with the
 + * local line separator representation.  You would use this class to
 + * implement ASCII file transfers requiring conversion from NETASCII.
 + * <p>
 + * Because of the translation process, a call to <code>flush()</code> will
 + * not flush the last byte written if that byte was a carriage
 + * return.  A call to {@link #close  close() }, however, will
 + * flush the carriage return.
 + * <p>
 + * <p>
 + * @author Daniel F. Savarese
 + ***/
 +
 +public final class FromNetASCIIOutputStream extends FilterOutputStream
 +{
 +    private boolean __lastWasCR;
 +
 +    /***
 +     * Creates a FromNetASCIIOutputStream instance that wraps an existing
 +     * OutputStream.
 +     * <p>
 +     * @param output  The OutputStream to wrap.
 +     ***/
 +    public FromNetASCIIOutputStream(OutputStream output)
 +    {
 +        super(output);
 +        __lastWasCR = false;
 +    }
 +
 +
 +    private void __write(int ch) throws IOException
 +    {
 +        switch (ch)
 +        {
 +        case '\r':
 +            __lastWasCR = true;
 +            // Don't write anything.  We need to see if next one is linefeed
 +            break;
 +        case '\n':
 +            if (__lastWasCR)
 +            {
 +                out.write(FromNetASCIIInputStream._lineSeparatorBytes);
 +                __lastWasCR = false;
 +                break;
 +            }
 +            __lastWasCR = false;
 +            out.write('\n');
 +            break;
 +        default:
 +            if (__lastWasCR)
 +            {
 +                out.write('\r');
 +                __lastWasCR = false;
 +            }
 +            out.write(ch);
 +            break;
 +        }
 +    }
 +
 +
 +    /***
 +     * Writes a byte to the stream.    Note that a call to this method
 +     * might not actually write a byte to the underlying stream until a
 +     * subsequent character is written, from which it can be determined if
 +     * a NETASCII line separator was encountered.
 +     * This is transparent to the programmer and is only mentioned for
 +     * completeness.
 +     * <p>
 +     * @param ch The byte to write.
 +     * @exception IOException If an error occurs while writing to the underlying
 +     *            stream.
 +     ***/
 +    @Override
 +    public synchronized void write(int ch)
 +    throws IOException
 +    {
 +        if (FromNetASCIIInputStream._noConversionRequired)
 +        {
 +            out.write(ch);
 +            return ;
 +        }
 +
 +        __write(ch);
 +    }
 +
 +
 +    /***
 +     * Writes a byte array to the stream.
 +     * <p>
 +     * @param buffer  The byte array to write.
 +     * @exception IOException If an error occurs while writing to the underlying
 +     *            stream.
 +     ***/
 +    @Override
 +    public synchronized void write(byte buffer[])
 +    throws IOException
 +    {
 +        write(buffer, 0, buffer.length);
 +    }
 +
 +
 +    /***
 +     * Writes a number of bytes from a byte array to the stream starting from
 +     * a given offset.
 +     * <p>
 +     * @param buffer  The byte array to write.
 +     * @param offset  The offset into the array at which to start copying data.
 +     * @param length  The number of bytes to write.
 +     * @exception IOException If an error occurs while writing to the underlying
 +     *            stream.
 +     ***/
 +    @Override
 +    public synchronized void write(byte buffer[], int offset, int length)
 +    throws IOException
 +    {
 +        if (FromNetASCIIInputStream._noConversionRequired)
 +        {
 +            // FilterOutputStream method is very slow.
 +            //super.write(buffer, offset, length);
 +            out.write(buffer, offset, length);
 +            return ;
 +        }
 +
 +        while (length-- > 0)
 +            __write(buffer[offset++]);
 +    }
 +
 +
 +    /***
 +     * Closes the stream, writing all pending data.
 +     * <p>
 +     * @exception IOException  If an error occurs while closing the stream.
 +     ***/
 +    @Override
 +    public synchronized void close()
 +    throws IOException
 +    {
 +        if (FromNetASCIIInputStream._noConversionRequired)
 +        {
 +            super.close();
 +            return ;
 +        }
 +
 +        if (__lastWasCR)
 +            out.write('\r');
 +        super.close();
 +    }
 +}
 diff --git a/src/org/apache/commons/net/io/SocketInputStream.java b/src/org/apache/commons/net/io/SocketInputStream.java new file mode 100644 index 0000000..673f434 --- /dev/null +++ b/src/org/apache/commons/net/io/SocketInputStream.java @@ -0,0 +1,69 @@ +/*
 + * 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.io;
 +
 +import java.io.FilterInputStream;
 +import java.io.IOException;
 +import java.io.InputStream;
 +import java.net.Socket;
 +
 +/***
 + * This class wraps an input stream, storing a reference to its originating
 + * socket.  When the stream is closed, it will also close the socket
 + * immediately afterward.  This class is useful for situations where you
 + * are dealing with a stream originating from a socket, but do not have
 + * a reference to the socket, and want to make sure it closes when the
 + * stream closes.
 + * <p>
 + * <p>
 + * @author Daniel F. Savarese
 + * @see SocketOutputStream
 + ***/
 +
 +public class SocketInputStream extends FilterInputStream
 +{
 +    private Socket __socket;
 +
 +    /***
 +     * Creates a SocketInputStream instance wrapping an input stream and
 +     * storing a reference to a socket that should be closed on closing
 +     * the stream.
 +     * <p>
 +     * @param socket  The socket to close on closing the stream.
 +     * @param stream  The input stream to wrap.
 +     ***/
 +    public SocketInputStream(Socket socket, InputStream stream)
 +    {
 +        super(stream);
 +        __socket = socket;
 +    }
 +
 +    /***
 +     * Closes the stream and immediately afterward closes the referenced
 +     * socket.
 +     * <p>
 +     * @exception IOException  If there is an error in closing the stream
 +     *                         or socket.
 +     ***/
 +    @Override
 +    public void close() throws IOException
 +    {
 +        super.close();
 +        __socket.close();
 +    }
 +}
 diff --git a/src/org/apache/commons/net/io/SocketOutputStream.java b/src/org/apache/commons/net/io/SocketOutputStream.java new file mode 100644 index 0000000..abd7f5d --- /dev/null +++ b/src/org/apache/commons/net/io/SocketOutputStream.java @@ -0,0 +1,89 @@ +/*
 + * 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.io;
 +
 +import java.io.FilterOutputStream;
 +import java.io.IOException;
 +import java.io.OutputStream;
 +import java.net.Socket;
 +
 +/***
 + * This class wraps an output stream, storing a reference to its originating
 + * socket.  When the stream is closed, it will also close the socket
 + * immediately afterward.  This class is useful for situations where you
 + * are dealing with a stream originating from a socket, but do not have
 + * a reference to the socket, and want to make sure it closes when the
 + * stream closes.
 + * <p>
 + * <p>
 + * @author Daniel F. Savarese
 + * @see SocketInputStream
 + ***/
 +
 +public class SocketOutputStream extends FilterOutputStream
 +{
 +    private Socket __socket;
 +
 +    /***
 +     * Creates a SocketOutputStream instance wrapping an output stream and
 +     * storing a reference to a socket that should be closed on closing
 +     * the stream.
 +     * <p>
 +     * @param socket  The socket to close on closing the stream.
 +     * @param stream  The input stream to wrap.
 +     ***/
 +    public SocketOutputStream(Socket socket, OutputStream stream)
 +    {
 +        super(stream);
 +        __socket = socket;
 +    }
 +
 +
 +    /***
 +     * Writes a number of bytes from a byte array to the stream starting from
 +     * a given offset.  This method bypasses the equivalent method in
 +     * FilterOutputStream because the FilterOutputStream implementation is
 +     * very inefficient.
 +     * <p>
 +     * @param buffer  The byte array to write.
 +     * @param offset  The offset into the array at which to start copying data.
 +     * @param length  The number of bytes to write.
 +     * @exception IOException If an error occurs while writing to the underlying
 +     *            stream.
 +     ***/
 +    @Override
 +    public void write(byte buffer[], int offset, int length) throws IOException
 +    {
 +        out.write(buffer, offset, length);
 +    }
 +
 +
 +    /***
 +     * Closes the stream and immediately afterward closes the referenced
 +     * socket.
 +     * <p>
 +     * @exception IOException  If there is an error in closing the stream
 +     *                         or socket.
 +     ***/
 +    @Override
 +    public void close() throws IOException
 +    {
 +        super.close();
 +        __socket.close();
 +    }
 +}
 diff --git a/src/org/apache/commons/net/io/ToNetASCIIInputStream.java b/src/org/apache/commons/net/io/ToNetASCIIInputStream.java new file mode 100644 index 0000000..55e4735 --- /dev/null +++ b/src/org/apache/commons/net/io/ToNetASCIIInputStream.java @@ -0,0 +1,181 @@ +/*
 + * 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.io;
 +
 +import java.io.FilterInputStream;
 +import java.io.IOException;
 +import java.io.InputStream;
 +
 +/***
 + * This class wraps an input stream, replacing all singly occurring
 + * <LF> (linefeed) characters with <CR><LF> (carriage return
 + * followed by linefeed), which is the NETASCII standard for representing
 + * a newline.
 + * You would use this class to implement ASCII file transfers requiring
 + * conversion to NETASCII.
 + * <p>
 + * <p>
 + * @author Daniel F. Savarese
 + ***/
 +
 +public final class ToNetASCIIInputStream extends FilterInputStream
 +{
 +    private static final int __NOTHING_SPECIAL = 0;
 +    private static final int __LAST_WAS_CR = 1;
 +    private static final int __LAST_WAS_NL = 2;
 +    private int __status;
 +
 +    /***
 +     * Creates a ToNetASCIIInputStream instance that wraps an existing
 +     * InputStream.
 +     * <p>
 +     * @param input  The InputStream to .
 +     ***/
 +    public ToNetASCIIInputStream(InputStream input)
 +    {
 +        super(input);
 +        __status = __NOTHING_SPECIAL;
 +    }
 +
 +
 +    /***
 +     * Reads and returns the next byte in the stream.  If the end of the
 +     * message has been reached, returns -1.
 +     * <p>
 +     * @return The next character in the stream. Returns -1 if the end of the
 +     *          stream has been reached.
 +     * @exception IOException If an error occurs while reading the underlying
 +     *            stream.
 +     ***/
 +    @Override
 +    public int read() throws IOException
 +    {
 +        int ch;
 +
 +        if (__status == __LAST_WAS_NL)
 +        {
 +            __status = __NOTHING_SPECIAL;
 +            return '\n';
 +        }
 +
 +        ch = in.read();
 +
 +        switch (ch)
 +        {
 +        case '\r':
 +            __status = __LAST_WAS_CR;
 +            return '\r';
 +        case '\n':
 +            if (__status != __LAST_WAS_CR)
 +            {
 +                __status = __LAST_WAS_NL;
 +                return '\r';
 +            }
 +            // else fall through
 +        default:
 +            __status = __NOTHING_SPECIAL;
 +            return ch;
 +        }
 +        // statement not reached
 +        //return ch;
 +    }
 +
 +
 +    /***
 +     * Reads the next number of bytes from the stream into an array and
 +     * returns the number of bytes read.  Returns -1 if the end of the
 +     * stream has been reached.
 +     * <p>
 +     * @param buffer  The byte array in which to store the data.
 +     * @return The number of bytes read. Returns -1 if the
 +     *          end of the message has been reached.
 +     * @exception IOException If an error occurs in reading the underlying
 +     *            stream.
 +     ***/
 +    @Override
 +    public int read(byte buffer[]) throws IOException
 +    {
 +        return read(buffer, 0, buffer.length);
 +    }
 +
 +
 +    /***
 +     * Reads the next number of bytes from the stream into an array and returns
 +     * the number of bytes read.  Returns -1 if the end of the
 +     * message has been reached.  The characters are stored in the array
 +     * starting from the given offset and up to the length specified.
 +     * <p>
 +     * @param buffer The byte array in which to store the data.
 +     * @param offset  The offset into the array at which to start storing data.
 +     * @param length   The number of bytes to read.
 +     * @return The number of bytes read. Returns -1 if the
 +     *          end of the stream has been reached.
 +     * @exception IOException If an error occurs while reading the underlying
 +     *            stream.
 +     ***/
 +    @Override
 +    public int read(byte buffer[], int offset, int length) throws IOException
 +    {
 +        int ch, off;
 +
 +        if (length < 1)
 +            return 0;
 +
 +        ch = available();
 +
 +        if (length > ch)
 +            length = ch;
 +
 +        // If nothing is available, block to read only one character
 +        if (length < 1)
 +            length = 1;
 +
 +        if ((ch = read()) == -1)
 +            return -1;
 +
 +        off = offset;
 +
 +        do
 +        {
 +            buffer[offset++] = (byte)ch;
 +        }
 +        while (--length > 0 && (ch = read()) != -1);
 +
 +        return (offset - off);
 +    }
 +
 +    /*** Returns false.  Mark is not supported. ***/
 +    @Override
 +    public boolean markSupported()
 +    {
 +        return false;
 +    }
 +
 +    @Override
 +    public int available() throws IOException
 +    {
 +        int result;
 +
 +        result = in.available();
 +
 +        if (__status == __LAST_WAS_NL)
 +            return (result + 1);
 +
 +        return result;
 +    }
 +}
 diff --git a/src/org/apache/commons/net/io/ToNetASCIIOutputStream.java b/src/org/apache/commons/net/io/ToNetASCIIOutputStream.java new file mode 100644 index 0000000..aeacc98 --- /dev/null +++ b/src/org/apache/commons/net/io/ToNetASCIIOutputStream.java @@ -0,0 +1,119 @@ +/*
 + * 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.io;
 +
 +import java.io.FilterOutputStream;
 +import java.io.IOException;
 +import java.io.OutputStream;
 +
 +/***
 + * This class wraps an output stream, replacing all singly occurring
 + * <LF> (linefeed) characters with <CR><LF> (carriage return
 + * followed by linefeed), which is the NETASCII standard for representing
 + * a newline.
 + * You would use this class to implement ASCII file transfers requiring
 + * conversion to NETASCII.
 + * <p>
 + * <p>
 + * @author Daniel F. Savarese
 + ***/
 +
 +public final class ToNetASCIIOutputStream extends FilterOutputStream
 +{
 +    private boolean __lastWasCR;
 +
 +    /***
 +     * Creates a ToNetASCIIOutputStream instance that wraps an existing
 +     * OutputStream.
 +     * <p>
 +     * @param output  The OutputStream to wrap.
 +     ***/
 +    public ToNetASCIIOutputStream(OutputStream output)
 +    {
 +        super(output);
 +        __lastWasCR = false;
 +    }
 +
 +
 +    /***
 +     * Writes a byte to the stream.    Note that a call to this method
 +     * may result in multiple writes to the underlying input stream in order
 +     * to convert naked newlines to NETASCII line separators.
 +     * This is transparent to the programmer and is only mentioned for
 +     * completeness.
 +     * <p>
 +     * @param ch The byte to write.
 +     * @exception IOException If an error occurs while writing to the underlying
 +     *            stream.
 +     ***/
 +    @Override
 +    public synchronized void write(int ch)
 +    throws IOException
 +    {
 +        switch (ch)
 +        {
 +        case '\r':
 +            __lastWasCR = true;
 +            out.write('\r');
 +            return ;
 +        case '\n':
 +            if (!__lastWasCR)
 +                out.write('\r');
 +            // Fall through
 +        default:
 +            __lastWasCR = false;
 +            out.write(ch);
 +            return ;
 +        }
 +    }
 +
 +
 +    /***
 +     * Writes a byte array to the stream.
 +     * <p>
 +     * @param buffer  The byte array to write.
 +     * @exception IOException If an error occurs while writing to the underlying
 +     *            stream.
 +     ***/
 +    @Override
 +    public synchronized void write(byte buffer[])
 +    throws IOException
 +    {
 +        write(buffer, 0, buffer.length);
 +    }
 +
 +
 +    /***
 +     * Writes a number of bytes from a byte array to the stream starting from
 +     * a given offset.
 +     * <p>
 +     * @param buffer  The byte array to write.
 +     * @param offset  The offset into the array at which to start copying data.
 +     * @param length  The number of bytes to write.
 +     * @exception IOException If an error occurs while writing to the underlying
 +     *            stream.
 +     ***/
 +    @Override
 +    public synchronized void write(byte buffer[], int offset, int length)
 +    throws IOException
 +    {
 +        while (length-- > 0)
 +            write(buffer[offset++]);
 +    }
 +
 +}
 diff --git a/src/org/apache/commons/net/io/Util.java b/src/org/apache/commons/net/io/Util.java new file mode 100644 index 0000000..4e85a93 --- /dev/null +++ b/src/org/apache/commons/net/io/Util.java @@ -0,0 +1,334 @@ +/*
 + * 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.io;
 +
 +import java.io.IOException;
 +import java.io.InputStream;
 +import java.io.OutputStream;
 +import java.io.Reader;
 +import java.io.Writer;
 +
 +/***
 + * The Util class cannot be instantiated and stores short static convenience
 + * methods that are often quite useful.
 + * <p>
 + * <p>
 + * @see CopyStreamException
 + * @see CopyStreamListener
 + * @see CopyStreamAdapter
 + * @author Daniel F. Savarese
 + ***/
 +
 +public final class Util
 +{
 +    /***
 +     * The default buffer size used by {@link #copyStream  copyStream }
 +     * and {@link #copyReader  copyReader }. It's value is 1024.
 +     ***/
 +    public static final int DEFAULT_COPY_BUFFER_SIZE = 1024;
 +
 +    // Cannot be instantiated
 +    private Util()
 +    { }
 +
 +
 +    /***
 +     * Copies the contents of an InputStream to an OutputStream using a
 +     * copy buffer of a given size and notifies the provided
 +     * CopyStreamListener of the progress of the copy operation by calling
 +     * its bytesTransferred(long, int) method after each write to the
 +     * destination.  If you wish to notify more than one listener you should
 +     * use a CopyStreamAdapter as the listener and register the additional
 +     * listeners with the CopyStreamAdapter.
 +     * <p>
 +     * The contents of the InputStream are
 +     * read until the end of the stream is reached, but neither the
 +     * source nor the destination are closed.  You must do this yourself
 +     * outside of the method call.  The number of bytes read/written is
 +     * returned.
 +     * <p>
 +     * @param source  The source InputStream.
 +     * @param dest    The destination OutputStream.
 +     * @param bufferSize  The number of bytes to buffer during the copy.
 +     * @param streamSize  The number of bytes in the stream being copied.
 +     *          Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown.
 +     * @param listener  The CopyStreamListener to notify of progress.  If
 +     *      this parameter is null, notification is not attempted.
 +     * @param flush Whether to flush the output stream after every
 +     *        write.  This is necessary for interactive sessions that rely on
 +     *        buffered streams.  If you don't flush, the data will stay in
 +     *        the stream buffer.
 +     * @exception CopyStreamException  If an error occurs while reading from the
 +     *            source or writing to the destination.  The CopyStreamException
 +     *            will contain the number of bytes confirmed to have been
 +     *            transferred before an
 +     *            IOException occurred, and it will also contain the IOException
 +     *            that caused the error.  These values can be retrieved with
 +     *            the CopyStreamException getTotalBytesTransferred() and
 +     *            getIOException() methods.
 +     ***/
 +    public static final long copyStream(InputStream source, OutputStream dest,
 +                                        int bufferSize, long streamSize,
 +                                        CopyStreamListener listener,
 +                                        boolean flush)
 +    throws CopyStreamException
 +    {
 +        int bytes;
 +        long total;
 +        byte[] buffer;
 +
 +        buffer = new byte[bufferSize];
 +        total = 0;
 +
 +        try
 +        {
 +            while ((bytes = source.read(buffer)) != -1)
 +            {
 +                // Technically, some read(byte[]) methods may return 0 and we cannot
 +                // accept that as an indication of EOF.
 +
 +                if (bytes == 0)
 +                {
 +                    bytes = source.read();
 +                    if (bytes < 0)
 +                        break;
 +                    dest.write(bytes);
 +                    if(flush)
 +                      dest.flush();
 +                    ++total;
 +                    if (listener != null)
 +                        listener.bytesTransferred(total, 1, streamSize);
 +                    continue;
 +                }
 +
 +                dest.write(buffer, 0, bytes);
 +                if(flush)
 +                  dest.flush();
 +                total += bytes;
 +                if (listener != null)
 +                    listener.bytesTransferred(total, bytes, streamSize);
 +            }
 +        }
 +        catch (IOException e)
 +        {
 +            throw new CopyStreamException("IOException caught while copying.",
 +                                          total, e);
 +        }
 +
 +        return total;
 +    }
 +
 +
 +    /***
 +     * Copies the contents of an InputStream to an OutputStream using a
 +     * copy buffer of a given size and notifies the provided
 +     * CopyStreamListener of the progress of the copy operation by calling
 +     * its bytesTransferred(long, int) method after each write to the
 +     * destination.  If you wish to notify more than one listener you should
 +     * use a CopyStreamAdapter as the listener and register the additional
 +     * listeners with the CopyStreamAdapter.
 +     * <p>
 +     * The contents of the InputStream are
 +     * read until the end of the stream is reached, but neither the
 +     * source nor the destination are closed.  You must do this yourself
 +     * outside of the method call.  The number of bytes read/written is
 +     * returned.
 +     * <p>
 +     * @param source  The source InputStream.
 +     * @param dest    The destination OutputStream.
 +     * @param bufferSize  The number of bytes to buffer during the copy.
 +     * @param streamSize  The number of bytes in the stream being copied.
 +     *          Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown.
 +     * @param listener  The CopyStreamListener to notify of progress.  If
 +     *      this parameter is null, notification is not attempted.
 +     * @exception CopyStreamException  If an error occurs while reading from the
 +     *            source or writing to the destination.  The CopyStreamException
 +     *            will contain the number of bytes confirmed to have been
 +     *            transferred before an
 +     *            IOException occurred, and it will also contain the IOException
 +     *            that caused the error.  These values can be retrieved with
 +     *            the CopyStreamException getTotalBytesTransferred() and
 +     *            getIOException() methods.
 +     ***/
 +    public static final long copyStream(InputStream source, OutputStream dest,
 +                                        int bufferSize, long streamSize,
 +                                        CopyStreamListener listener)
 +    throws CopyStreamException
 +    {
 +      return copyStream(source, dest, bufferSize, streamSize, listener,
 +                        true);
 +    }
 +
 +
 +    /***
 +     * Copies the contents of an InputStream to an OutputStream using a
 +     * copy buffer of a given size.  The contents of the InputStream are
 +     * read until the end of the stream is reached, but neither the
 +     * source nor the destination are closed.  You must do this yourself
 +     * outside of the method call.  The number of bytes read/written is
 +     * returned.
 +     * <p>
 +     * @param source  The source InputStream.
 +     * @param dest    The destination OutputStream.
 +     * @return  The number of bytes read/written in the copy operation.
 +     * @exception CopyStreamException  If an error occurs while reading from the
 +     *            source or writing to the destination.  The CopyStreamException
 +     *            will contain the number of bytes confirmed to have been
 +     *            transferred before an
 +     *            IOException occurred, and it will also contain the IOException
 +     *            that caused the error.  These values can be retrieved with
 +     *            the CopyStreamException getTotalBytesTransferred() and
 +     *            getIOException() methods.
 +     ***/
 +    public static final long copyStream(InputStream source, OutputStream dest,
 +                                        int bufferSize)
 +    throws CopyStreamException
 +    {
 +        return copyStream(source, dest, bufferSize,
 +                          CopyStreamEvent.UNKNOWN_STREAM_SIZE, null);
 +    }
 +
 +
 +    /***
 +     * Same as <code> copyStream(source, dest, DEFAULT_COPY_BUFFER_SIZE); </code>
 +     ***/
 +    public static final long copyStream(InputStream source, OutputStream dest)
 +    throws CopyStreamException
 +    {
 +        return copyStream(source, dest, DEFAULT_COPY_BUFFER_SIZE);
 +    }
 +
 +
 +    /***
 +     * Copies the contents of a Reader to a Writer using a
 +     * copy buffer of a given size and notifies the provided
 +     * CopyStreamListener of the progress of the copy operation by calling
 +     * its bytesTransferred(long, int) method after each write to the
 +     * destination.  If you wish to notify more than one listener you should
 +     * use a CopyStreamAdapter as the listener and register the additional
 +     * listeners with the CopyStreamAdapter.
 +     * <p>
 +     * The contents of the Reader are
 +     * read until its end is reached, but neither the source nor the
 +     * destination are closed.  You must do this yourself outside of the
 +     * method call.  The number of characters read/written is returned.
 +     * <p>
 +     * @param source  The source Reader.
 +     * @param dest    The destination writer.
 +     * @param bufferSize  The number of characters to buffer during the copy.
 +     * @param streamSize  The number of characters in the stream being copied.
 +     *          Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown.
 +     * @param listener  The CopyStreamListener to notify of progress.  If
 +     *      this parameter is null, notification is not attempted.
 +     * @return  The number of characters read/written in the copy operation.
 +     * @exception CopyStreamException  If an error occurs while reading from the
 +     *            source or writing to the destination.  The CopyStreamException
 +     *            will contain the number of bytes confirmed to have been
 +     *            transferred before an
 +     *            IOException occurred, and it will also contain the IOException
 +     *            that caused the error.  These values can be retrieved with
 +     *            the CopyStreamException getTotalBytesTransferred() and
 +     *            getIOException() methods.
 +     ***/
 +    public static final long copyReader(Reader source, Writer dest,
 +                                        int bufferSize, long streamSize,
 +                                        CopyStreamListener listener)
 +    throws CopyStreamException
 +    {
 +        int chars;
 +        long total;
 +        char[] buffer;
 +
 +        buffer = new char[bufferSize];
 +        total = 0;
 +
 +        try
 +        {
 +            while ((chars = source.read(buffer)) != -1)
 +            {
 +                // Technically, some read(char[]) methods may return 0 and we cannot
 +                // accept that as an indication of EOF.
 +                if (chars == 0)
 +                {
 +                    chars = source.read();
 +                    if (chars < 0)
 +                        break;
 +                    dest.write(chars);
 +                    dest.flush();
 +                    ++total;
 +                    if (listener != null)
 +                        listener.bytesTransferred(total, chars, streamSize);
 +                    continue;
 +                }
 +
 +                dest.write(buffer, 0, chars);
 +                dest.flush();
 +                total += chars;
 +                if (listener != null)
 +                    listener.bytesTransferred(total, chars, streamSize);
 +            }
 +        }
 +        catch (IOException e)
 +        {
 +            throw new CopyStreamException("IOException caught while copying.",
 +                                          total, e);
 +        }
 +
 +        return total;
 +    }
 +
 +
 +    /***
 +     * Copies the contents of a Reader to a Writer using a
 +     * copy buffer of a given size.  The contents of the Reader are
 +     * read until its end is reached, but neither the source nor the
 +     * destination are closed.  You must do this yourself outside of the
 +     * method call.  The number of characters read/written is returned.
 +     * <p>
 +     * @param source  The source Reader.
 +     * @param dest    The destination writer.
 +     * @param bufferSize  The number of characters to buffer during the copy.
 +     * @return  The number of characters read/written in the copy operation.
 +     * @exception CopyStreamException  If an error occurs while reading from the
 +     *            source or writing to the destination.  The CopyStreamException
 +     *            will contain the number of bytes confirmed to have been
 +     *            transferred before an
 +     *            IOException occurred, and it will also contain the IOException
 +     *            that caused the error.  These values can be retrieved with
 +     *            the CopyStreamException getTotalBytesTransferred() and
 +     *            getIOException() methods.
 +     ***/
 +    public static final long copyReader(Reader source, Writer dest,
 +                                        int bufferSize)
 +    throws CopyStreamException
 +    {
 +        return copyReader(source, dest, bufferSize,
 +                          CopyStreamEvent.UNKNOWN_STREAM_SIZE, null);
 +    }
 +
 +
 +    /***
 +     * Same as <code> copyReader(source, dest, DEFAULT_COPY_BUFFER_SIZE); </code>
 +     ***/
 +    public static final long copyReader(Reader source, Writer dest)
 +    throws CopyStreamException
 +    {
 +        return copyReader(source, dest, DEFAULT_COPY_BUFFER_SIZE);
 +    }
 +
 +}
 diff --git a/src/org/apache/commons/net/util/ListenerList.java b/src/org/apache/commons/net/util/ListenerList.java new file mode 100644 index 0000000..796fb78 --- /dev/null +++ b/src/org/apache/commons/net/util/ListenerList.java @@ -0,0 +1,63 @@ +/*
 + * 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.util;
 +
 +import java.io.Serializable;
 +import java.util.EventListener;
 +import java.util.Iterator;
 +import java.util.concurrent.CopyOnWriteArrayList;
 +
 +/**
 + * @author Daniel F. Savarese
 + */
 +
 +public class ListenerList implements Serializable, Iterable<EventListener>
 +{
 +    private final CopyOnWriteArrayList<EventListener> __listeners;
 +
 +    public ListenerList()
 +    {
 +        __listeners = new CopyOnWriteArrayList<EventListener>();
 +    }
 +
 +    public void addListener(EventListener listener)
 +    {
 +            __listeners.add(listener);
 +    }
 +
 +    public  void removeListener(EventListener listener)
 +    {
 +            __listeners.remove(listener);
 +    }
 +
 +    public int getListenerCount()
 +    {
 +        return __listeners.size();
 +    }
 +    
 +    /**
 +     * Return an {@link Iterator} for the {@link EventListener} instances
 +     * 
 +     * @since 2.0
 +     * TODO Check that this is a good defensive strategy
 +     */
 +    public Iterator<EventListener> iterator() {
 +            return __listeners.iterator();
 +    }
 +
 +}
 diff --git a/src/org/apache/commons/net/util/SubnetUtils.java b/src/org/apache/commons/net/util/SubnetUtils.java new file mode 100644 index 0000000..90cc6e8 --- /dev/null +++ b/src/org/apache/commons/net/util/SubnetUtils.java @@ -0,0 +1,211 @@ +/*
 + * 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.util;
 +
 +import java.util.regex.Matcher;
 +import java.util.regex.Pattern;
 +
 +/**
 + * A class that performs some subnet calculations given a network address and a subnet mask. 
 + * @see http://www.faqs.org/rfcs/rfc1519.html
 + * @author <rwinston@apache.org>
 + * @since 2.0
 + */
 +public class SubnetUtils {
 +
 +    private static final String IP_ADDRESS = "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})";
 +    private static final String SLASH_FORMAT = IP_ADDRESS + "/(\\d{1,3})";
 +    private static final Pattern addressPattern = Pattern.compile(IP_ADDRESS);
 +    private static final Pattern cidrPattern = Pattern.compile(SLASH_FORMAT);
 +    private static final int NBITS = 32;
 +
 +    private int netmask = 0;
 +    private int address = 0;
 +    private int network = 0;
 +    private int broadcast = 0;
 +
 +    /**
 +     * Constructor that takes a CIDR-notation string, e.g. "192.168.0.1/16"
 +     * @param cidrNotation A CIDR-notation string, e.g. "192.168.0.1/16"
 +     */
 +    public SubnetUtils(String cidrNotation) {
 +        calculate(cidrNotation);
 +    }
 +
 +    /**
 +     * Constructor that takes two dotted decimal addresses. 
 +     * @param address An IP address, e.g. "192.168.0.1"
 +     * @param mask A dotted decimal netmask e.g. "255.255.0.0"
 +     */
 +    public SubnetUtils(String address, String mask) {
 +        calculate(toCidrNotation(address, mask));
 +    }
 +
 +    /**
 +     * Convenience container for subnet summary information.
 +     *
 +     */
 +    public final class SubnetInfo {
 +        private SubnetInfo() {}
 +
 +        private int netmask()       { return netmask; }
 +        private int network()       { return network; }
 +        private int address()       { return address; }
 +        private int broadcast()     { return broadcast; }
 +        private int low()           { return network() + 1; }
 +        private int high()          { return broadcast() - 1; }
 +
 +        public boolean isInRange(String address)    { return isInRange(toInteger(address)); }
 +        private boolean isInRange(int address)      { return ((address-low()) <= (high()-low())); }
 +
 +        public String getBroadcastAddress()         { return format(toArray(broadcast())); }
 +        public String getNetworkAddress()           { return format(toArray(network())); }
 +        public String getNetmask()                  { return format(toArray(netmask())); }
 +        public String getAddress()                  { return format(toArray(address())); }
 +        public String getLowAddress()               { return format(toArray(low())); }
 +        public String getHighAddress()              { return format(toArray(high())); }
 +        public int getAddressCount()                { return (broadcast() - low()); }
 +
 +        public int asInteger(String address)        { return toInteger(address); }
 +        
 +        public String getCidrSignature() { 
 +            return toCidrNotation(
 +                    format(toArray(address())), 
 +                    format(toArray(netmask()))
 +            );
 +        }
 +        
 +        public String[] getAllAddresses() { 
 +            String[] addresses = new String[getAddressCount()];
 +            for (int add = low(), j=0; add <= high(); ++add, ++j) {
 +                addresses[j] = format(toArray(add));
 +            }
 +            return addresses;
 +        }
 +    }
 +
 +    /**
 +     * Return a {@link SubnetInfo} instance that contains subnet-specific statistics
 +     * @return
 +     */
 +    public final SubnetInfo getInfo() { return new SubnetInfo(); }
 +
 +    /*
 +     * Initialize the internal fields from the supplied CIDR mask
 +     */
 +    private void calculate(String mask) {
 +        Matcher matcher = cidrPattern.matcher(mask);
 +
 +        if (matcher.matches()) {
 +            address = matchAddress(matcher);
 +
 +            /* Create a binary netmask from the number of bits specification /x */
 +            int cidrPart = rangeCheck(Integer.parseInt(matcher.group(5)), 0, NBITS-1);
 +            for (int j = 0; j < cidrPart; ++j) {
 +                netmask |= (1 << 31-j);
 +            }
 +
 +            /* Calculate base network address */
 +            network = (address & netmask);
 +
 +            /* Calculate broadcast address */
 +            broadcast = network | ~(netmask);
 +        }
 +        else 
 +            throw new IllegalArgumentException("Could not parse [" + mask + "]");
 +    }
 +
 +    /*
 +     * Convert a dotted decimal format address to a packed integer format
 +     */
 +    private int toInteger(String address) {
 +        Matcher matcher = addressPattern.matcher(address);
 +        if (matcher.matches()) {
 +            return matchAddress(matcher);
 +        }
 +        else
 +            throw new IllegalArgumentException("Could not parse [" + address + "]");
 +    }
 +
 +    /*
 +     * Convenience method to extract the components of a dotted decimal address and 
 +     * pack into an integer using a regex match
 +     */
 +    private int matchAddress(Matcher matcher) {
 +        int addr = 0;
 +        for (int i = 1; i <= 4; ++i) { 
 +            int n = (rangeCheck(Integer.parseInt(matcher.group(i)), 0, 255));
 +            addr |= ((n & 0xff) << 8*(4-i));
 +        }
 +        return addr;
 +    }
 +
 +    /*
 +     * Convert a packed integer address into a 4-element array
 +     */
 +    private int[] toArray(int val) {
 +        int ret[] = new int[4];
 +        for (int j = 3; j >= 0; --j)
 +            ret[j] |= ((val >>> 8*(3-j)) & (0xff));
 +        return ret;
 +    }
 +
 +    /*
 +     * Convert a 4-element array into dotted decimal format
 +     */
 +    private String format(int[] octets) {
 +        StringBuilder str = new StringBuilder();
 +        for (int i =0; i < octets.length; ++i){
 +            str.append(octets[i]);
 +            if (i != octets.length - 1) {
 +                str.append("."); 
 +            }
 +        }
 +        return str.toString();
 +    }
 +
 +    /*
 +     * Convenience function to check integer boundaries
 +     */
 +    private int rangeCheck(int value, int begin, int end) {
 +        if (value >= begin && value <= end)
 +            return value;
 +
 +        throw new IllegalArgumentException("Value out of range: [" + value + "]");
 +    }
 +
 +    /*
 +     * Count the number of 1-bits in a 32-bit integer using a divide-and-conquer strategy
 +     * see Hacker's Delight section 5.1 
 +     */
 +    int pop(int x) {
 +        x = x - ((x >>> 1) & 0x55555555); 
 +        x = (x & 0x33333333) + ((x >>> 2) & 0x33333333); 
 +        x = (x + (x >>> 4)) & 0x0F0F0F0F; 
 +        x = x + (x >>> 8); 
 +        x = x + (x >>> 16); 
 +        return x & 0x0000003F; 
 +    } 
 +
 +    /* Convert two dotted decimal addresses to a single xxx.xxx.xxx.xxx/yy format
 +     * by counting the 1-bit population in the mask address. (It may be better to count 
 +     * NBITS-#trailing zeroes for this case)
 +     */
 +    private String toCidrNotation(String addr, String mask) {
 +        return addr + "/" + pop(toInteger(mask));
 +    }
 +}
  | 
