parent
							
								
									4f1412b554
								
							
						
					
					
						commit
						0960d5c6b6
					
				| @ -0,0 +1,74 @@ | ||||
| /* | ||||
|  * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
 | ||||
|  * | ||||
|  * Licensed 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 com.arialyy.aria.core.download; | ||||
| 
 | ||||
| import android.text.TextUtils; | ||||
| import android.util.Log; | ||||
| import com.arialyy.aria.core.inf.AbsTaskEntity; | ||||
| 
 | ||||
| /** | ||||
|  * Created by lyy on 2016/12/5. | ||||
|  * https://github.com/AriaLyy/Aria
 | ||||
|  */ | ||||
| public class FtpDownloadTarget extends DownloadTarget { | ||||
|   private final String TAG = "FtpDownloadTarget"; | ||||
| 
 | ||||
|   /** | ||||
|    * @param serverIp ftp服务器地址 | ||||
|    * @param port ftp端口号 | ||||
|    */ | ||||
|   FtpDownloadTarget(String serverIp, String port, String filePath, String targetName) { | ||||
|     this(serverIp + ":" + port + "/" + filePath, targetName); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * @param url url 为 serverIp:port/filePath | ||||
|    */ | ||||
|   private FtpDownloadTarget(String url, String targetName) { | ||||
|     super(url, targetName); | ||||
|     mTaskEntity.downloadType = AbsTaskEntity.FTP; | ||||
|     mTargetName = targetName; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * ftp 用户登录信息 | ||||
|    * | ||||
|    * @param userName ftp用户名 | ||||
|    * @param password ftp用户密码 | ||||
|    */ | ||||
|   public FtpDownloadTarget login(String userName, String password) { | ||||
|     if (TextUtils.isEmpty(userName)) { | ||||
|       Log.e(TAG, "用户名不能为null"); | ||||
|       return this; | ||||
|     } else if (TextUtils.isEmpty(password)) { | ||||
|       Log.e(TAG, "密码不能为null"); | ||||
|       return this; | ||||
|     } | ||||
|     mTaskEntity.userName = userName; | ||||
|     mTaskEntity.userPw = password; | ||||
|     return this; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * 设置下载的文件 | ||||
|    * | ||||
|    * @param filePath ftp服务器上的文件 | ||||
|    */ | ||||
|   public FtpDownloadTarget getFile(String filePath) { | ||||
| 
 | ||||
|     return this; | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1 @@ | ||||
| /build | ||||
| @ -0,0 +1,25 @@ | ||||
| apply plugin: 'com.android.library' | ||||
| 
 | ||||
| android { | ||||
|   compileSdkVersion 23 | ||||
|   buildToolsVersion '25.0.3' | ||||
| 
 | ||||
|   defaultConfig { | ||||
|     minSdkVersion 9 | ||||
|     targetSdkVersion 23 | ||||
|     versionCode 327 | ||||
|     versionName "3.2.7" | ||||
|   } | ||||
|   buildTypes { | ||||
|     release { | ||||
|       minifyEnabled false | ||||
|       proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| dependencies { | ||||
|   compile fileTree(include: ['*.jar'], dir: 'libs') | ||||
|   testCompile 'junit:junit:4.12' | ||||
|   compile 'com.android.support:appcompat-v7:23.1.1' | ||||
| } | ||||
| @ -0,0 +1,25 @@ | ||||
| # Add project specific ProGuard rules here. | ||||
| # By default, the flags in this file are appended to flags specified | ||||
| # in E:\sdk/tools/proguard/proguard-android.txt | ||||
| # You can edit the include path and order by changing the proguardFiles | ||||
| # directive in build.gradle. | ||||
| # | ||||
| # For more details, see | ||||
| #   http://developer.android.com/guide/developing/tools/proguard.html | ||||
| 
 | ||||
| # Add any project specific keep options here: | ||||
| 
 | ||||
| # If your project uses WebView with JS, uncomment the following | ||||
| # and specify the fully qualified class name to the JavaScript interface | ||||
| # class: | ||||
| #-keepclassmembers class fqcn.of.javascript.interface.for.webview { | ||||
| #   public *; | ||||
| #} | ||||
| 
 | ||||
| # Uncomment this to preserve the line number information for | ||||
| # debugging stack traces. | ||||
| #-keepattributes SourceFile,LineNumberTable | ||||
| 
 | ||||
| # If you keep the line number information, uncomment this to | ||||
| # hide the original source file name. | ||||
| #-renamesourcefileattribute SourceFile | ||||
| @ -0,0 +1,24 @@ | ||||
| package com.arialyy.ftpplug; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.support.test.InstrumentationRegistry; | ||||
| import android.support.test.runner.AndroidJUnit4; | ||||
| 
 | ||||
| import org.junit.Test; | ||||
| import org.junit.runner.RunWith; | ||||
| 
 | ||||
| import static org.junit.Assert.*; | ||||
| 
 | ||||
| /** | ||||
|  * Instrumentation test, which will execute on an Android device. | ||||
|  * | ||||
|  * @see <a href="http://d.android.com/tools/testing">Testing documentation</a> | ||||
|  */ | ||||
| @RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest { | ||||
|   @Test public void useAppContext() throws Exception { | ||||
|     // Context of the app under test.
 | ||||
|     Context appContext = InstrumentationRegistry.getTargetContext(); | ||||
| 
 | ||||
|     assertEquals("com.arialyy.ftpplug.test", appContext.getPackageName()); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,12 @@ | ||||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     package="com.arialyy.ftpplug" | ||||
|     > | ||||
| 
 | ||||
|   <application | ||||
|       android:allowBackup="true" | ||||
|       android:supportsRtl="true" | ||||
|       > | ||||
| 
 | ||||
|   </application> | ||||
| 
 | ||||
| </manifest> | ||||
| @ -0,0 +1,8 @@ | ||||
| package com.arialyy.ftpplug; | ||||
| 
 | ||||
| /** | ||||
|  * Created by Aria.Lao on 2017/7/24. | ||||
|  */ | ||||
| 
 | ||||
| public class Test { | ||||
| } | ||||
| @ -0,0 +1,314 @@ | ||||
| /* | ||||
|  * 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; | ||||
| import java.nio.charset.Charset; | ||||
| 
 | ||||
| /*** | ||||
|  * 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. | ||||
|  * | ||||
|  * | ||||
|  * @see DatagramSocketFactory | ||||
|  ***/ | ||||
| 
 | ||||
| public abstract class DatagramSocketClient | ||||
| { | ||||
|     /*** | ||||
|      * The default DatagramSocketFactory shared by all DatagramSocketClient | ||||
|      * instances. | ||||
|      ***/ | ||||
|     private static final DatagramSocketFactory __DEFAULT_SOCKET_FACTORY = | ||||
|         new DefaultDatagramSocketFactory(); | ||||
| 
 | ||||
|     /** | ||||
|      * Charset to use for byte IO. | ||||
|      */ | ||||
|     private Charset charset = Charset.defaultCharset(); | ||||
| 
 | ||||
|     /*** 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. | ||||
|      * | ||||
|      * @throws 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. | ||||
|      * | ||||
|      * @param port The port to use for the socket. | ||||
|      * @throws 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. | ||||
|      * | ||||
|      * @param port The port to use for the socket. | ||||
|      * @param laddr  The local address to use. | ||||
|      * @throws 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. | ||||
|      ***/ | ||||
|     public void close() | ||||
|     { | ||||
|         if (_socket_ != null) { | ||||
|             _socket_.close(); | ||||
|         } | ||||
|         _socket_ = null; | ||||
|         _isOpen_ = false; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /*** | ||||
|      * Returns true if the client has a currently open socket. | ||||
|      * | ||||
|      * @return True if the client has a currently 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. | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @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()}. | ||||
|      * | ||||
|      * @param timeout  The timeout in milliseconds to use for the currently | ||||
|      *                 open datagram socket connection. | ||||
|      * @throws SocketException if an error setting the timeout | ||||
|      ***/ | ||||
|     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. | ||||
|      * | ||||
|      * @return The timeout in milliseconds of the currently opened socket. | ||||
|      * @throws SocketException if an error getting the timeout | ||||
|      ***/ | ||||
|     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. | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @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). | ||||
|      * | ||||
|      * @param factory  The new DatagramSocketFactory the DatagramSocketClient | ||||
|      * should use. | ||||
|      ***/ | ||||
|     public void setDatagramSocketFactory(DatagramSocketFactory factory) | ||||
|     { | ||||
|         if (factory == null) { | ||||
|             _socketFactory_ = __DEFAULT_SOCKET_FACTORY; | ||||
|         } else { | ||||
|             _socketFactory_ = factory; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Gets the charset name. | ||||
|      * | ||||
|      * @return the charset name. | ||||
|      * @since 3.3 | ||||
|      * TODO Will be deprecated once the code requires Java 1.6 as a mininmum | ||||
|      */ | ||||
|     public String getCharsetName() { | ||||
|         return charset.name(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Gets the charset. | ||||
|      * | ||||
|      * @return the charset. | ||||
|      * @since 3.3 | ||||
|      */ | ||||
|     public Charset getCharset() { | ||||
|         return charset; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Sets the charset. | ||||
|      * | ||||
|      * @param charset the charset. | ||||
|      * @since 3.3 | ||||
|      */ | ||||
|     public void setCharset(Charset charset) { | ||||
|         this.charset = charset; | ||||
|     } | ||||
| } | ||||
| @ -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; | ||||
| 
 | ||||
| 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 DatagramSocketClient} | ||||
|  * . | ||||
|  * This allows you to provide your own DatagramSocket implementations and | ||||
|  * to perform security checks or browser capability requests before | ||||
|  * creating a DatagramSocket. | ||||
|  * | ||||
|  * | ||||
|  ***/ | ||||
| 
 | ||||
| public interface DatagramSocketFactory | ||||
| { | ||||
| 
 | ||||
|     /*** | ||||
|      * Creates a DatagramSocket on the local host at the first available port. | ||||
|      * @return the socket | ||||
|      * | ||||
|      * @throws SocketException If the socket could not be created. | ||||
|      ***/ | ||||
|     public DatagramSocket createDatagramSocket() throws SocketException; | ||||
| 
 | ||||
|     /*** | ||||
|      * Creates a DatagramSocket on the local host at a specified port. | ||||
|      * | ||||
|      * @param port The port to use for the socket. | ||||
|      * @return the socket | ||||
|      * @throws 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. | ||||
|      * | ||||
|      * @param port The port to use for the socket. | ||||
|      * @param laddr  The local address to use. | ||||
|      * @return the socket | ||||
|      * @throws SocketException If the socket could not be created. | ||||
|      ***/ | ||||
|     public DatagramSocket createDatagramSocket(int port, InetAddress laddr) | ||||
|     throws SocketException; | ||||
| } | ||||
| @ -0,0 +1,79 @@ | ||||
| /* | ||||
|  * 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 DatagramSocketClient} | ||||
|  *  implementations. | ||||
|  * | ||||
|  * | ||||
|  * @see DatagramSocketFactory | ||||
|  * @see DatagramSocketClient | ||||
|  * @see DatagramSocketClient#setDatagramSocketFactory | ||||
|  ***/ | ||||
| 
 | ||||
| public class DefaultDatagramSocketFactory implements DatagramSocketFactory | ||||
| { | ||||
| 
 | ||||
|     /*** | ||||
|      * Creates a DatagramSocket on the local host at the first available port. | ||||
|      * @return a new DatagramSocket | ||||
|      * @throws SocketException If the socket could not be created. | ||||
|      ***/ | ||||
|     @Override | ||||
|     public DatagramSocket createDatagramSocket() throws SocketException | ||||
|     { | ||||
|         return new DatagramSocket(); | ||||
|     } | ||||
| 
 | ||||
|     /*** | ||||
|      * Creates a DatagramSocket on the local host at a specified port. | ||||
|      * | ||||
|      * @param port The port to use for the socket. | ||||
|      * @return a new DatagramSocket | ||||
|      * @throws SocketException If the socket could not be created. | ||||
|      ***/ | ||||
|     @Override | ||||
|     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. | ||||
|      * | ||||
|      * @param port The port to use for the socket. | ||||
|      * @param laddr  The local address to use. | ||||
|      * @return a new DatagramSocket | ||||
|      * @throws SocketException If the socket could not be created. | ||||
|      ***/ | ||||
|     @Override | ||||
|     public DatagramSocket createDatagramSocket(int port, InetAddress laddr) | ||||
|     throws SocketException | ||||
|     { | ||||
|         return new DatagramSocket(port, laddr); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,230 @@ | ||||
| /* | ||||
|  * 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.InetSocketAddress; | ||||
| import java.net.Proxy; | ||||
| 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 SocketClient} | ||||
|  * implementations. | ||||
|  * | ||||
|  * | ||||
|  * @see SocketFactory | ||||
|  * @see SocketClient | ||||
|  * @see SocketClient#setSocketFactory | ||||
|  ***/ | ||||
| 
 | ||||
| public class DefaultSocketFactory extends SocketFactory | ||||
| { | ||||
|     /** The proxy to use when creating new sockets. */ | ||||
|     private final Proxy connProxy; | ||||
| 
 | ||||
|     /** | ||||
|      * The default constructor. | ||||
|      */ | ||||
|     public DefaultSocketFactory() | ||||
|     { | ||||
|         this(null); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * A constructor for sockets with proxy support. | ||||
|      * | ||||
|      * @param proxy The Proxy to use when creating new Sockets. | ||||
|      * @since 3.2 | ||||
|      */ | ||||
|     public DefaultSocketFactory(Proxy proxy) | ||||
|     { | ||||
|         connProxy = proxy; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Creates an unconnected Socket. | ||||
|      * | ||||
|      * @return A new unconnected Socket. | ||||
|      * @throws IOException If an I/O error occurs while creating the Socket. | ||||
|      * @since 3.2 | ||||
|      */ | ||||
|     @Override | ||||
|     public Socket createSocket() throws IOException | ||||
|     { | ||||
|         if (connProxy != null) | ||||
|         { | ||||
|             return new Socket(connProxy); | ||||
|         } | ||||
|         return new Socket(); | ||||
|     } | ||||
| 
 | ||||
|     /*** | ||||
|      * Creates a Socket connected to the given host and port. | ||||
|      * | ||||
|      * @param host The hostname to connect to. | ||||
|      * @param port The port to connect to. | ||||
|      * @return A Socket connected to the given host and port. | ||||
|      * @throws UnknownHostException  If the hostname cannot be resolved. | ||||
|      * @throws IOException If an I/O error occurs while creating the Socket. | ||||
|      ***/ | ||||
|     @Override | ||||
|     public Socket createSocket(String host, int port) | ||||
|     throws UnknownHostException, IOException | ||||
|     { | ||||
|         if (connProxy != null) | ||||
|         { | ||||
|             Socket s = new Socket(connProxy); | ||||
|             s.connect(new InetSocketAddress(host, port)); | ||||
|             return s; | ||||
|         } | ||||
|         return new Socket(host, port); | ||||
|     } | ||||
| 
 | ||||
|     /*** | ||||
|      * Creates a Socket connected to the given host and port. | ||||
|      * | ||||
|      * @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. | ||||
|      * @throws IOException If an I/O error occurs while creating the Socket. | ||||
|      ***/ | ||||
|     @Override | ||||
|     public Socket createSocket(InetAddress address, int port) | ||||
|     throws IOException | ||||
|     { | ||||
|         if (connProxy != null) | ||||
|         { | ||||
|             Socket s = new Socket(connProxy); | ||||
|             s.connect(new InetSocketAddress(address, port)); | ||||
|             return s; | ||||
|         } | ||||
|         return new Socket(address, port); | ||||
|     } | ||||
| 
 | ||||
|     /*** | ||||
|      * Creates a Socket connected to the given host and port and | ||||
|      * originating from the specified local address and port. | ||||
|      * | ||||
|      * @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. | ||||
|      * @throws UnknownHostException  If the hostname cannot be resolved. | ||||
|      * @throws 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 | ||||
|     { | ||||
|         if (connProxy != null) | ||||
|         { | ||||
|             Socket s = new Socket(connProxy); | ||||
|             s.bind(new InetSocketAddress(localAddr, localPort)); | ||||
|             s.connect(new InetSocketAddress(host, port)); | ||||
|             return s; | ||||
|         } | ||||
|         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. | ||||
|      * | ||||
|      * @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. | ||||
|      * @throws 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 | ||||
|     { | ||||
|         if (connProxy != null) | ||||
|         { | ||||
|             Socket s = new Socket(connProxy); | ||||
|             s.bind(new InetSocketAddress(localAddr, localPort)); | ||||
|             s.connect(new InetSocketAddress(address, port)); | ||||
|             return s; | ||||
|         } | ||||
|         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. | ||||
|      * | ||||
|      * @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. | ||||
|      * @throws 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. | ||||
|      * | ||||
|      * @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. | ||||
|      * @throws 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. | ||||
|      * | ||||
|      * @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. | ||||
|      * @throws 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); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,56 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  * | ||||
|  * | ||||
|  ***/ | ||||
| 
 | ||||
| public class MalformedServerReplyException extends IOException | ||||
| { | ||||
| 
 | ||||
|     private static final long serialVersionUID = 6006765264250543945L; | ||||
| 
 | ||||
|     /*** Constructs a MalformedServerReplyException with no message ***/ | ||||
|     public MalformedServerReplyException() | ||||
|     { | ||||
|         super(); | ||||
|     } | ||||
| 
 | ||||
|     /*** | ||||
|      * Constructs a MalformedServerReplyException with a specified message. | ||||
|      * | ||||
|      * @param message  The message explaining the reason for the exception. | ||||
|      ***/ | ||||
|     public MalformedServerReplyException(String message) | ||||
|     { | ||||
|         super(message); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,202 @@ | ||||
| /* | ||||
|  * 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.PrintStream; | ||||
| import java.io.PrintWriter; | ||||
| 
 | ||||
| /*** | ||||
|  * 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. | ||||
|  * | ||||
|  * @since 2.0 | ||||
|  ***/ | ||||
| 
 | ||||
| public class PrintCommandListener implements ProtocolCommandListener | ||||
| { | ||||
|     private final PrintWriter __writer; | ||||
|     private final boolean __nologin; | ||||
|     private final char __eolMarker; | ||||
|     private final boolean __directionMarker; | ||||
| 
 | ||||
|     /** | ||||
|      * Create the default instance which prints everything. | ||||
|      * | ||||
|      * @param stream where to write the commands and responses | ||||
|      * e.g. System.out | ||||
|      * @since 3.0 | ||||
|      */ | ||||
|     public PrintCommandListener(PrintStream stream) | ||||
|     { | ||||
|         this(new PrintWriter(stream)); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Create an instance which optionally suppresses login command text | ||||
|      * and indicates where the EOL starts with the specified character. | ||||
|      * | ||||
|      * @param stream where to write the commands and responses | ||||
|      * @param suppressLogin if {@code true}, only print command name for login | ||||
|      * | ||||
|      * @since 3.0 | ||||
|      */ | ||||
|     public PrintCommandListener(PrintStream stream, boolean suppressLogin) { | ||||
|         this(new PrintWriter(stream), suppressLogin); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Create an instance which optionally suppresses login command text | ||||
|      * and indicates where the EOL starts with the specified character. | ||||
|      * | ||||
|      * @param stream where to write the commands and responses | ||||
|      * @param suppressLogin if {@code true}, only print command name for login | ||||
|      * @param eolMarker if non-zero, add a marker just before the EOL. | ||||
|      * | ||||
|      * @since 3.0 | ||||
|      */ | ||||
|     public PrintCommandListener(PrintStream stream, boolean suppressLogin, char eolMarker) { | ||||
|         this(new PrintWriter(stream), suppressLogin, eolMarker); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Create an instance which optionally suppresses login command text | ||||
|      * and indicates where the EOL starts with the specified character. | ||||
|      * | ||||
|      * @param stream where to write the commands and responses | ||||
|      * @param suppressLogin if {@code true}, only print command name for login | ||||
|      * @param eolMarker if non-zero, add a marker just before the EOL. | ||||
|      * @param showDirection if {@code true}, add {@code "> "} or {@code "< "} as appropriate to the output | ||||
|      * | ||||
|      * @since 3.0 | ||||
|      */ | ||||
|     public PrintCommandListener(PrintStream stream, boolean suppressLogin, char eolMarker, boolean showDirection) { | ||||
|         this(new PrintWriter(stream), suppressLogin, eolMarker, showDirection); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Create the default instance which prints everything. | ||||
|      * | ||||
|      * @param writer where to write the commands and responses | ||||
|      */ | ||||
|     public PrintCommandListener(PrintWriter writer) | ||||
|     { | ||||
|         this(writer, false); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Create an instance which optionally suppresses login command text. | ||||
|      * | ||||
|      * @param writer where to write the commands and responses | ||||
|      * @param suppressLogin if {@code true}, only print command name for login | ||||
|      * | ||||
|      * @since 3.0 | ||||
|      */ | ||||
|     public PrintCommandListener(PrintWriter writer, boolean suppressLogin) | ||||
|     { | ||||
|         this(writer, suppressLogin, (char) 0); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Create an instance which optionally suppresses login command text | ||||
|      * and indicates where the EOL starts with the specified character. | ||||
|      * | ||||
|      * @param writer where to write the commands and responses | ||||
|      * @param suppressLogin if {@code true}, only print command name for login | ||||
|      * @param eolMarker if non-zero, add a marker just before the EOL. | ||||
|      * | ||||
|      * @since 3.0 | ||||
|      */ | ||||
|     public PrintCommandListener(PrintWriter writer, boolean suppressLogin, char eolMarker) | ||||
|     { | ||||
|         this(writer, suppressLogin, eolMarker, false); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Create an instance which optionally suppresses login command text | ||||
|      * and indicates where the EOL starts with the specified character. | ||||
|      * | ||||
|      * @param writer where to write the commands and responses | ||||
|      * @param suppressLogin if {@code true}, only print command name for login | ||||
|      * @param eolMarker if non-zero, add a marker just before the EOL. | ||||
|      * @param showDirection if {@code true}, add {@code ">} " or {@code "< "} as appropriate to the output | ||||
|      * | ||||
|      * @since 3.0 | ||||
|      */ | ||||
|     public PrintCommandListener(PrintWriter writer, boolean suppressLogin, char eolMarker, boolean showDirection) | ||||
|     { | ||||
|         __writer = writer; | ||||
|         __nologin = suppressLogin; | ||||
|         __eolMarker = eolMarker; | ||||
|         __directionMarker = showDirection; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void protocolCommandSent(ProtocolCommandEvent event) | ||||
|     { | ||||
|         if (__directionMarker) { | ||||
|             __writer.print("> "); | ||||
|         } | ||||
|         if (__nologin) { | ||||
|             String cmd = event.getCommand(); | ||||
|             if ("PASS".equalsIgnoreCase(cmd) || "USER".equalsIgnoreCase(cmd)) { | ||||
|                 __writer.print(cmd); | ||||
|                 __writer.println(" *******"); // Don't bother with EOL marker for this!
 | ||||
|             } else { | ||||
|                 final String IMAP_LOGIN = "LOGIN"; | ||||
|                 if (IMAP_LOGIN.equalsIgnoreCase(cmd)) { // IMAP
 | ||||
|                     String msg = event.getMessage(); | ||||
|                     msg=msg.substring(0, msg.indexOf(IMAP_LOGIN)+IMAP_LOGIN.length()); | ||||
|                     __writer.print(msg); | ||||
|                     __writer.println(" *******"); // Don't bother with EOL marker for this!
 | ||||
|                 } else { | ||||
|                     __writer.print(getPrintableString(event.getMessage())); | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             __writer.print(getPrintableString(event.getMessage())); | ||||
|         } | ||||
|         __writer.flush(); | ||||
|     } | ||||
| 
 | ||||
|     private String getPrintableString(String msg){ | ||||
|         if (__eolMarker == 0) { | ||||
|             return msg; | ||||
|         } | ||||
|         int pos = msg.indexOf(SocketClient.NETASCII_EOL); | ||||
|         if (pos > 0) { | ||||
|             StringBuilder sb = new StringBuilder(); | ||||
|             sb.append(msg.substring(0,pos)); | ||||
|             sb.append(__eolMarker); | ||||
|             sb.append(msg.substring(pos)); | ||||
|             return sb.toString(); | ||||
|         } | ||||
|         return msg; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void protocolReplyReceived(ProtocolCommandEvent event) | ||||
|     { | ||||
|         if (__directionMarker) { | ||||
|             __writer.print("< "); | ||||
|         } | ||||
|         __writer.print(event.getMessage()); | ||||
|         __writer.flush(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -0,0 +1,148 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  * | ||||
|  * | ||||
|  * @see ProtocolCommandListener | ||||
|  * @see ProtocolCommandSupport | ||||
|  ***/ | ||||
| 
 | ||||
| public class ProtocolCommandEvent extends EventObject | ||||
| { | ||||
|     private static final long serialVersionUID = 403743538418947240L; | ||||
| 
 | ||||
|     private final int __replyCode; | ||||
|     private final boolean __isCommand; | ||||
|     private final 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. | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @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. | ||||
|      * Includes the line terminator. | ||||
|      * | ||||
|      * @return The entire message sent to or received from the server. | ||||
|      ***/ | ||||
|     public String getMessage() | ||||
|     { | ||||
|         return __message; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,58 @@ | ||||
| /* | ||||
|  * 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}. | ||||
|  * | ||||
|  * | ||||
|  * @see ProtocolCommandEvent | ||||
|  * @see ProtocolCommandSupport | ||||
|  ***/ | ||||
| 
 | ||||
| public interface ProtocolCommandListener extends EventListener | ||||
| { | ||||
| 
 | ||||
|     /*** | ||||
|      * This method is invoked by a ProtocolCommandEvent source after | ||||
|      * sending a protocol command to a server. | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @param event The ProtocolCommandEvent fired. | ||||
|      ***/ | ||||
|     public void protocolReplyReceived(ProtocolCommandEvent event); | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,135 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  * | ||||
|  * | ||||
|  * @see ProtocolCommandEvent | ||||
|  * @see ProtocolCommandListener | ||||
|  ***/ | ||||
| 
 | ||||
| public class ProtocolCommandSupport implements Serializable | ||||
| { | ||||
|     private static final long serialVersionUID = -8017692739988399978L; | ||||
| 
 | ||||
|     private final Object __source; | ||||
|     private final ListenerList __listeners; | ||||
| 
 | ||||
|     /*** | ||||
|      * Creates a ProtocolCommandSupport instance using the indicated source | ||||
|      * as the source of ProtocolCommandEvents. | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @param listener  The ProtocolCommandListener to add. | ||||
|      ***/ | ||||
|     public void addProtocolCommandListener(ProtocolCommandListener listener) | ||||
|     { | ||||
|         __listeners.addListener(listener); | ||||
|     } | ||||
| 
 | ||||
|     /*** | ||||
|      * Removes a ProtocolCommandListener. | ||||
|      * | ||||
|      * @param listener  The ProtocolCommandListener to remove. | ||||
|      ***/ | ||||
|     public void removeProtocolCommandListener(ProtocolCommandListener listener) | ||||
|     { | ||||
|         __listeners.removeListener(listener); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /*** | ||||
|      * Returns the number of ProtocolCommandListeners currently registered. | ||||
|      * | ||||
|      * @return The number of ProtocolCommandListeners currently registered. | ||||
|      ***/ | ||||
|     public int getListenerCount() | ||||
|     { | ||||
|         return __listeners.getListenerCount(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| @ -0,0 +1,887 @@ | ||||
| /* | ||||
|  * 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.Closeable; | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
| import java.io.OutputStream; | ||||
| import java.net.InetAddress; | ||||
| import java.net.InetSocketAddress; | ||||
| import java.net.Proxy; | ||||
| import java.net.Socket; | ||||
| import java.net.SocketException; | ||||
| import java.nio.charset.Charset; | ||||
| 
 | ||||
| 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 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 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. | ||||
|  * @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(); | ||||
| 
 | ||||
|     /** The default {@link ServerSocketFactory} */ | ||||
|     private static final ServerSocketFactory __DEFAULT_SERVER_SOCKET_FACTORY = | ||||
|             ServerSocketFactory.getDefault(); | ||||
| 
 | ||||
|     /** | ||||
|      * A ProtocolCommandSupport object used to manage the registering of | ||||
|      * ProtocolCommandListeners and the firing of ProtocolCommandEvents. | ||||
|      */ | ||||
|     private ProtocolCommandSupport __commandSupport; | ||||
| 
 | ||||
|     /** The timeout to use after opening a socket. */ | ||||
|     protected int _timeout_; | ||||
| 
 | ||||
|     /** The socket used for the connection. */ | ||||
|     protected Socket _socket_; | ||||
| 
 | ||||
|     /** The hostname used for the connection (null = no hostname supplied). */ | ||||
|     protected String _hostname_; | ||||
| 
 | ||||
|     /** 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; | ||||
| 
 | ||||
|     /** Hint for SO_RCVBUF size */ | ||||
|     private int receiveBufferSize = -1; | ||||
| 
 | ||||
|     /** Hint for SO_SNDBUF size */ | ||||
|     private int sendBufferSize = -1; | ||||
| 
 | ||||
|     /** The proxy to use when connecting. */ | ||||
|     private Proxy connProxy; | ||||
| 
 | ||||
|     /** | ||||
|      * Charset to use for byte IO. | ||||
|      */ | ||||
|     private Charset charset = Charset.defaultCharset(); | ||||
| 
 | ||||
|     /** | ||||
|      * Default constructor for SocketClient.  Initializes | ||||
|      * _socket_ to null, _timeout_ to 0, _defaultPort to 0, | ||||
|      * _isConnected_ to false, charset to {@code Charset.defaultCharset()} | ||||
|      * and _socketFactory_ to a shared instance of | ||||
|      * {@link org.apache.commons.net.DefaultSocketFactory}. | ||||
|      */ | ||||
|     public SocketClient() | ||||
|     { | ||||
|         _socket_ = null; | ||||
|         _hostname_ = 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. | ||||
|      * @throws IOException (SocketException) if a problem occurs with the socket | ||||
|      */ | ||||
|     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. | ||||
|      * @throws SocketException If the socket timeout could not be set. | ||||
|      * @throws 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 | ||||
|     { | ||||
|         _hostname_ = null; | ||||
|         _connect(host, port, null, -1); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 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. | ||||
|      * @throws SocketException If the socket timeout could not be set. | ||||
|      * @throws IOException If the socket could not be opened.  In most | ||||
|      *  cases you will only want to catch IOException since SocketException is | ||||
|      *  derived from it. | ||||
|      * @throws java.net.UnknownHostException If the hostname cannot be resolved. | ||||
|      */ | ||||
|     public void connect(String hostname, int port) | ||||
|     throws SocketException, IOException | ||||
|     { | ||||
|         _hostname_ = hostname; | ||||
|         _connect(InetAddress.getByName(hostname), port, null, -1); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * 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. | ||||
|      * @throws SocketException If the socket timeout could not be set. | ||||
|      * @throws 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 | ||||
|     { | ||||
|         _hostname_ = null; | ||||
|         _connect(host, port, localAddr, localPort); | ||||
|     } | ||||
| 
 | ||||
|     // helper method to allow code to be shared with connect(String,...) methods
 | ||||
|     private void _connect(InetAddress host, int port, InetAddress localAddr, int localPort) | ||||
|         throws SocketException, IOException | ||||
|     { | ||||
|         _socket_ = _socketFactory_.createSocket(); | ||||
|         if (receiveBufferSize != -1) { | ||||
|             _socket_.setReceiveBufferSize(receiveBufferSize); | ||||
|         } | ||||
|         if (sendBufferSize != -1) { | ||||
|             _socket_.setSendBufferSize(sendBufferSize); | ||||
|         } | ||||
|         if (localAddr != null) { | ||||
|             _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. | ||||
|      * @throws SocketException If the socket timeout could not be set. | ||||
|      * @throws IOException If the socket could not be opened.  In most | ||||
|      *  cases you will only want to catch IOException since SocketException is | ||||
|      *  derived from it. | ||||
|      * @throws java.net.UnknownHostException If the hostname cannot be resolved. | ||||
|      */ | ||||
|     public void connect(String hostname, int port, | ||||
|                         InetAddress localAddr, int localPort) | ||||
|     throws SocketException, IOException | ||||
|     { | ||||
|         _hostname_ = hostname; | ||||
|        _connect(InetAddress.getByName(hostname), port, localAddr, localPort); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * 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. | ||||
|      * @throws SocketException If the socket timeout could not be set. | ||||
|      * @throws 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 | ||||
|     { | ||||
|         _hostname_ = null; | ||||
|         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. | ||||
|      * @throws SocketException If the socket timeout could not be set. | ||||
|      * @throws IOException If the socket could not be opened.  In most | ||||
|      *  cases you will only want to catch IOException since SocketException is | ||||
|      *  derived from it. | ||||
|      * @throws java.net.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> | ||||
|      * @throws IOException  If there is an error closing the socket. | ||||
|      */ | ||||
|     public void disconnect() throws IOException | ||||
|     { | ||||
|         closeQuietly(_socket_); | ||||
|         closeQuietly(_input_); | ||||
|         closeQuietly(_output_); | ||||
|         _socket_ = null; | ||||
|         _hostname_ = null; | ||||
|         _input_ = null; | ||||
|         _output_ = null; | ||||
|     } | ||||
| 
 | ||||
|     private void closeQuietly(Socket socket) { | ||||
|         if (socket != null){ | ||||
|             try { | ||||
|                 socket.close(); | ||||
|             } catch (IOException e) { | ||||
|                 // Ignored
 | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private void closeQuietly(Closeable close){ | ||||
|         if (close != null){ | ||||
|             try { | ||||
|                 close.close(); | ||||
|             } catch (IOException e) { | ||||
|                 // Ignored
 | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     /** | ||||
|      * Returns true if the client is currently connected to a server. | ||||
|      * <p> | ||||
|      * Delegates to {@link Socket#isConnected()} | ||||
|      * @return True if the client is currently connected to a server, | ||||
|      *         false otherwise. | ||||
|      */ | ||||
|     public boolean isConnected() | ||||
|     { | ||||
|         if (_socket_ == null) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         return _socket_.isConnected(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Make various checks on the socket to test if it is available for use. | ||||
|      * Note that the only sure test is to use it, but these checks may help | ||||
|      * in some cases. | ||||
|      * @see <a href="https://issues.apache.org/jira/browse/NET-350">NET-350</a> | ||||
|      * @return {@code true} if the socket appears to be available for use | ||||
|      * @since 3.0 | ||||
|      */ | ||||
|     public boolean isAvailable(){ | ||||
|         if (isConnected()) { | ||||
|             try | ||||
|             { | ||||
|                 if (_socket_.getInetAddress() == null) { | ||||
|                     return false; | ||||
|                 } | ||||
|                 if (_socket_.getPort() == 0) { | ||||
|                     return false; | ||||
|                 } | ||||
|                 if (_socket_.getRemoteSocketAddress() == null) { | ||||
|                     return false; | ||||
|                 } | ||||
|                 if (_socket_.isClosed()) { | ||||
|                     return false; | ||||
|                 } | ||||
|                 /* these aren't exact checks (a Socket can be half-open), | ||||
|                    but since we usually require two-way data transfer, | ||||
|                    we check these here too: */ | ||||
|                 if (_socket_.isInputShutdown()) { | ||||
|                     return false; | ||||
|                 } | ||||
|                 if (_socket_.isOutputShutdown()) { | ||||
|                     return false; | ||||
|                 } | ||||
|                 /* ignore the result, catch exceptions: */ | ||||
|                 _socket_.getInputStream(); | ||||
|                 _socket_.getOutputStream(); | ||||
|             } | ||||
|             catch (IOException ioex) | ||||
|             { | ||||
|                 return false; | ||||
|             } | ||||
|             return true; | ||||
|         } else { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 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> | ||||
|      * To set the initial timeout, use {@link #setDefaultTimeout(int)} instead. | ||||
|      * | ||||
|      * @param timeout  The timeout in milliseconds to use for the currently | ||||
|      *                 open socket connection. | ||||
|      * @throws SocketException If the operation fails. | ||||
|      * @throws NullPointerException if the socket is not currently open | ||||
|      */ | ||||
|     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 never thrown, but subclasses might want to do so | ||||
|      * @since 2.0 | ||||
|      */ | ||||
|     public void setSendBufferSize(int size) throws SocketException { | ||||
|         sendBufferSize = size; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the current sendBuffer size | ||||
|      * @return the size, or -1 if not initialised | ||||
|      * @since 3.0 | ||||
|      */ | ||||
|     protected int getSendBufferSize(){ | ||||
|         return sendBufferSize; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Sets the underlying socket receive buffer size. | ||||
|      * <p> | ||||
|      * @param size The size of the buffer in bytes. | ||||
|      * @throws SocketException never (but subclasses may wish to do so) | ||||
|      * @since 2.0 | ||||
|      */ | ||||
|     public void setReceiveBufferSize(int size) throws SocketException  { | ||||
|         receiveBufferSize = size; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the current receivedBuffer size | ||||
|      * @return the size, or -1 if not initialised | ||||
|      * @since 3.0 | ||||
|      */ | ||||
|     protected int getReceiveBufferSize(){ | ||||
|         return receiveBufferSize; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns the timeout in milliseconds of the currently opened socket. | ||||
|      * <p> | ||||
|      * @return The timeout in milliseconds of the currently opened socket. | ||||
|      * @throws SocketException If the operation fails. | ||||
|      * @throws NullPointerException if the socket is not currently open | ||||
|      */ | ||||
|     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. | ||||
|      * @throws SocketException If the operation fails. | ||||
|      * @throws NullPointerException if the socket is not currently open | ||||
|      */ | ||||
|     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. | ||||
|      * @throws SocketException If the operation fails. | ||||
|      * @throws NullPointerException if the socket is not currently open | ||||
|      */ | ||||
|     public boolean getTcpNoDelay() throws SocketException | ||||
|     { | ||||
|         return _socket_.getTcpNoDelay(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Sets the SO_KEEPALIVE flag on the currently opened socket. | ||||
|      * | ||||
|      * From the Javadocs, the default keepalive time is 2 hours (although this is | ||||
|      * implementation  dependent). It looks as though the Windows WSA sockets implementation | ||||
|      * allows a specific keepalive value to be set, although this seems not to be the case on | ||||
|      * other systems. | ||||
|      * @param  keepAlive If true, keepAlive is turned on | ||||
|      * @throws SocketException if there is a problem with the socket | ||||
|      * @throws NullPointerException if the socket is not currently open | ||||
|      * @since 2.2 | ||||
|      */ | ||||
|     public void setKeepAlive(boolean keepAlive) throws SocketException { | ||||
|         _socket_.setKeepAlive(keepAlive); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns the current value of the SO_KEEPALIVE flag on the currently opened socket. | ||||
|      * Delegates to {@link Socket#getKeepAlive()} | ||||
|      * @return True if SO_KEEPALIVE is enabled. | ||||
|      * @throws SocketException if there is a problem with the socket | ||||
|      * @throws NullPointerException if the socket is not currently open | ||||
|      * @since 2.2 | ||||
|      */ | ||||
|     public boolean getKeepAlive() throws SocketException { | ||||
|         return _socket_.getKeepAlive(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 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?) | ||||
|      * @throws SocketException If the operation fails. | ||||
|      * @throws NullPointerException if the socket is not currently open | ||||
|      */ | ||||
|     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. | ||||
|      * @throws SocketException If the operation fails. | ||||
|      * @throws NullPointerException if the socket is not currently open | ||||
|      */ | ||||
|     public int getSoLinger() throws SocketException | ||||
|     { | ||||
|         return _socket_.getSoLinger(); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * Returns the port number of the open socket on the local host used | ||||
|      * for the connection. | ||||
|      * Delegates to {@link Socket#getLocalPort()} | ||||
|      * <p> | ||||
|      * @return The port number of the open socket on the local host used | ||||
|      *         for the connection. | ||||
|      * @throws NullPointerException if the socket is not currently open | ||||
|      */ | ||||
|     public int getLocalPort() | ||||
|     { | ||||
|         return _socket_.getLocalPort(); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * Returns the local address  to which the client's socket is bound. | ||||
|      * Delegates to {@link Socket#getLocalAddress()} | ||||
|      * <p> | ||||
|      * @return The local address to which the client's socket is bound. | ||||
|      * @throws NullPointerException if the socket is not currently open | ||||
|      */ | ||||
|     public InetAddress getLocalAddress() | ||||
|     { | ||||
|         return _socket_.getLocalAddress(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns the port number of the remote host to which the client is | ||||
|      * connected. | ||||
|      * Delegates to {@link Socket#getPort()} | ||||
|      * <p> | ||||
|      * @return The port number of the remote host to which the client is | ||||
|      *         connected. | ||||
|      * @throws NullPointerException if the socket is not currently open | ||||
|      */ | ||||
|     public int getRemotePort() | ||||
|     { | ||||
|         return _socket_.getPort(); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * @return The remote address to which the client is connected. | ||||
|      * Delegates to {@link Socket#getInetAddress()} | ||||
|      * @throws NullPointerException if the socket is not currently open | ||||
|      */ | ||||
|     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> | ||||
|      * @param socket the item to check against | ||||
|      * @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). | ||||
|      * Any proxy setting is discarded. | ||||
|      * <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; | ||||
|         } | ||||
|         // re-setting the socket factory makes the proxy setting useless,
 | ||||
|         // so set the field to null so that getProxy() doesn't return a
 | ||||
|         // Proxy that we're actually not using.
 | ||||
|         connProxy = null; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 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 timeout (in ms) | ||||
|      * @since 2.0 | ||||
|      */ | ||||
|     public int getConnectTimeout() { | ||||
|         return connectTimeout; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the underlying {@link ServerSocketFactory} | ||||
|      * @return The server socket factory | ||||
|      * @since 2.2 | ||||
|      */ | ||||
|     public ServerSocketFactory getServerSocketFactory() { | ||||
|         return _serverSocketFactory_; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * Adds a ProtocolCommandListener. | ||||
|      * | ||||
|      * @param listener  The ProtocolCommandListener to add. | ||||
|      * @since 3.0 | ||||
|      */ | ||||
|     public void addProtocolCommandListener(ProtocolCommandListener listener) { | ||||
|         getCommandSupport().addProtocolCommandListener(listener); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Removes a ProtocolCommandListener. | ||||
|      * | ||||
|      * @param listener  The ProtocolCommandListener to remove. | ||||
|      * @since 3.0 | ||||
|      */ | ||||
|     public void removeProtocolCommandListener(ProtocolCommandListener listener) { | ||||
|         getCommandSupport().removeProtocolCommandListener(listener); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * If there are any listeners, send them the reply details. | ||||
|      * | ||||
|      * @param replyCode the code extracted from the reply | ||||
|      * @param reply the full reply text | ||||
|      * @since 3.0 | ||||
|      */ | ||||
|     protected void fireReplyReceived(int replyCode, String reply) { | ||||
|         if (getCommandSupport().getListenerCount() > 0) { | ||||
|             getCommandSupport().fireReplyReceived(replyCode, reply); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * If there are any listeners, send them the command details. | ||||
|      * | ||||
|      * @param command the command name | ||||
|      * @param message the complete message, including command name | ||||
|      * @since 3.0 | ||||
|      */ | ||||
|     protected void fireCommandSent(String command, String message) { | ||||
|         if (getCommandSupport().getListenerCount() > 0) { | ||||
|             getCommandSupport().fireCommandSent(command, message); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Create the CommandSupport instance if required | ||||
|      */ | ||||
|     protected void createCommandSupport(){ | ||||
|         __commandSupport = new ProtocolCommandSupport(this); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Subclasses can override this if they need to provide their own | ||||
|      * instance field for backwards compatibilty. | ||||
|      * | ||||
|      * @return the CommandSupport instance, may be {@code null} | ||||
|      * @since 3.0 | ||||
|      */ | ||||
|     protected ProtocolCommandSupport getCommandSupport() { | ||||
|         return __commandSupport; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Sets the proxy for use with all the connections. | ||||
|      * The proxy is used for connections established after the | ||||
|      * call to this method. | ||||
|      * | ||||
|      * @param proxy the new proxy for connections. | ||||
|      * @since 3.2 | ||||
|      */ | ||||
|     public void setProxy(Proxy proxy) { | ||||
|         setSocketFactory(new DefaultSocketFactory(proxy)); | ||||
|         connProxy = proxy; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Gets the proxy for use with all the connections. | ||||
|      * @return the current proxy for connections. | ||||
|      */ | ||||
|     public Proxy getProxy() { | ||||
|         return connProxy; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Gets the charset name. | ||||
|      * | ||||
|      * @return the charset. | ||||
|      * @since 3.3 | ||||
|      * @deprecated Since the code now requires Java 1.6 as a mininmum | ||||
|      */ | ||||
|     @Deprecated | ||||
|     public String getCharsetName() { | ||||
|         return charset.name(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Gets the charset. | ||||
|      * | ||||
|      * @return the charset. | ||||
|      * @since 3.3 | ||||
|      */ | ||||
|     public Charset getCharset() { | ||||
|         return charset; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Sets the charset. | ||||
|      * | ||||
|      * @param charset the charset. | ||||
|      * @since 3.3 | ||||
|      */ | ||||
|     public void setCharset(Charset charset) { | ||||
|         this.charset = charset; | ||||
|     } | ||||
| 
 | ||||
|     /* | ||||
|      *  N.B. Fields cannot be pulled up into a super-class without breaking binary compatibility, | ||||
|      *  so the abstract method is needed to pass the instance to the methods which were moved here. | ||||
|      */ | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| @ -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); | ||||
| } | ||||
									
										
											File diff suppressed because it is too large
											Load Diff
										
									
								
							
						
									
										
											File diff suppressed because it is too large
											Load Diff
										
									
								
							
						| @ -0,0 +1,709 @@ | ||||
| /* | ||||
|  * 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  FTPClient  FTPClient} object and | ||||
|  * also subordinate objects which it uses.  Any class implementing the | ||||
|  * {@link  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> | ||||
|  * 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> | ||||
|  * 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> | ||||
|  * 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> | ||||
|  * 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> | ||||
|  * | ||||
|  * @since 1.4 | ||||
|  * @see Configurable | ||||
|  * @see 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 for alternate UNIX parser; same as {@link #SYST_UNIX} but leading spaces are | ||||
|      * trimmed from file names. This is to maintain backwards compatibility with | ||||
|      * the original behaviour of the parser which ignored multiple spaces between the date | ||||
|      * and the start of the file name. | ||||
|      * @since 3.4 | ||||
|      */ | ||||
|     public static final String SYST_UNIX_TRIM_LEADING  = "UNIX_LTRIM"; | ||||
| 
 | ||||
|     /** | ||||
|      * 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"; | ||||
| 
 | ||||
|     /** | ||||
|      * Identifier by which a Mac pre OS-X -based ftp server is known throughout | ||||
|      * the commons-net ftp system. | ||||
|      * | ||||
|      * @since 3.1 | ||||
|      */ | ||||
|     // Full string is "MACOS Peter's Server"; the substring below should be enough
 | ||||
|     public static final String SYST_MACOS_PETER  = "MACOS PETER"; // NET-436
 | ||||
| 
 | ||||
|     private final String serverSystemKey; | ||||
|     private String defaultDateFormatStr = null; | ||||
|     private String recentDateFormatStr = null; | ||||
|     private boolean lenientFutureDates = true; // NET-407
 | ||||
|     private String serverLanguageCode = null; | ||||
|     private String shortMonthNames = null; | ||||
|     private String serverTimeZoneId = null; | ||||
|     private boolean saveUnparseableEntries = false; | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * The main constructor for an FTPClientConfig object | ||||
|      * @param systemKey key representing system type of the  server being | ||||
|      * connected to. See {@link #getServerSystemKey() serverSystemKey} | ||||
|      * If set to the empty string, then FTPClient uses the system type returned by the server. | ||||
|      * However this is not recommended for general use; | ||||
|      * the correct system type should be set if it is known. | ||||
|      */ | ||||
|     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 the format string 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} | ||||
|      *  @since 3.6 | ||||
|      */ | ||||
|     public FTPClientConfig(String systemKey, | ||||
|                            String defaultDateFormatStr, | ||||
|                            String recentDateFormatStr) | ||||
|     { | ||||
|         this(systemKey); | ||||
|         this.defaultDateFormatStr = defaultDateFormatStr; | ||||
|         this.recentDateFormatStr = recentDateFormatStr; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Constructor which allows setting of most 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; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 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} | ||||
|      * @param lenientFutureDates See | ||||
|      * {@link  #setLenientFutureDates(boolean)  lenientFutureDates} | ||||
|      * @param saveUnparseableEntries See | ||||
|      * {@link  #setUnparseableEntries(boolean)  saveUnparseableEntries} | ||||
|      */ | ||||
|     public FTPClientConfig(String systemKey, | ||||
|                            String defaultDateFormatStr, | ||||
|                            String recentDateFormatStr, | ||||
|                            String serverLanguageCode, | ||||
|                            String shortMonthNames, | ||||
|                            String serverTimeZoneId, | ||||
|                            boolean lenientFutureDates, | ||||
|                            boolean saveUnparseableEntries) | ||||
|     { | ||||
|         this(systemKey); | ||||
|         this.defaultDateFormatStr = defaultDateFormatStr; | ||||
|         this.lenientFutureDates = lenientFutureDates; | ||||
|         this.recentDateFormatStr = recentDateFormatStr; | ||||
|         this.saveUnparseableEntries = saveUnparseableEntries; | ||||
|         this.serverLanguageCode = serverLanguageCode; | ||||
|         this.shortMonthNames = shortMonthNames; | ||||
|         this.serverTimeZoneId = serverTimeZoneId; | ||||
|     } | ||||
| 
 | ||||
|     // Copy constructor, intended for use by FTPClient only
 | ||||
|     FTPClientConfig(String systemKey, FTPClientConfig config) { | ||||
|         this.serverSystemKey = systemKey; | ||||
|         this.defaultDateFormatStr = config.defaultDateFormatStr; | ||||
|         this.lenientFutureDates = config.lenientFutureDates; | ||||
|         this.recentDateFormatStr = config.recentDateFormatStr; | ||||
|         this.saveUnparseableEntries = config.saveUnparseableEntries; | ||||
|         this.serverLanguageCode = config.serverLanguageCode; | ||||
|         this.serverTimeZoneId = config.serverTimeZoneId; | ||||
|         this.shortMonthNames = config.shortMonthNames; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Copy constructor | ||||
|      * @param config source | ||||
|      * @since 3.6 | ||||
|      */ | ||||
|     public FTPClientConfig(FTPClientConfig config) { | ||||
|         this.serverSystemKey = config.serverSystemKey; | ||||
|         this.defaultDateFormatStr = config.defaultDateFormatStr; | ||||
|         this.lenientFutureDates = config.lenientFutureDates; | ||||
|         this.recentDateFormatStr = config.recentDateFormatStr; | ||||
|         this.saveUnparseableEntries = config.saveUnparseableEntries; | ||||
|         this.serverLanguageCode = config.serverLanguageCode; | ||||
|         this.serverTimeZoneId = config.serverTimeZoneId; | ||||
|         this.shortMonthNames = config.shortMonthNames; | ||||
|     } | ||||
| 
 | ||||
|     private static final 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> | ||||
|      * <p> | ||||
|      * This is used primarily in unix-based systems. | ||||
|      * </p> | ||||
|      * <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 Apache 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(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Allow list parsing methods to create basic FTPFile entries if parsing fails. | ||||
|      * <p> | ||||
|      * In this case, the FTPFile will contain only the unparsed entry {@link FTPFile#getRawListing()} | ||||
|      * and {@link FTPFile#isValid()} will return {@code false} | ||||
|      * @param saveUnparseable if true, then create FTPFile entries if parsing fails | ||||
|      * @since 3.4 | ||||
|      */ | ||||
|     public void setUnparseableEntries(boolean saveUnparseable) { | ||||
|         this.saveUnparseableEntries = saveUnparseable; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @return true if list parsing should return FTPFile entries even for unparseable response lines | ||||
|      * <p> | ||||
|      * If true, the FTPFile for any unparseable entries will contain only the unparsed entry | ||||
|      * {@link FTPFile#getRawListing()} and {@link FTPFile#isValid()} will return {@code false} | ||||
|      * @since 3.4 | ||||
|      */ | ||||
|     public boolean getUnparseableEntries() { | ||||
|         return this.saveUnparseableEntries; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -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; | ||||
| 
 | ||||
| /** | ||||
| * @since 3.3 | ||||
|  */ | ||||
| public enum FTPCmd { | ||||
|     ABOR, | ||||
|     ACCT, | ||||
|     ALLO, | ||||
|     APPE, | ||||
|     CDUP, | ||||
|     CWD, | ||||
|     DELE, | ||||
|     EPRT, | ||||
|     EPSV, | ||||
|     FEAT, | ||||
|     HELP, | ||||
|     LIST, | ||||
|     MDTM, | ||||
|     MFMT, | ||||
|     MKD, | ||||
|     MLSD, | ||||
|     MLST, | ||||
|     MODE, | ||||
|     NLST, | ||||
|     NOOP, | ||||
|     PASS, | ||||
|     PASV, | ||||
|     PORT, | ||||
|     PWD, | ||||
|     QUIT, | ||||
|     REIN, | ||||
|     REST, | ||||
|     RETR, | ||||
|     RMD, | ||||
|     RNFR, | ||||
|     RNTO, | ||||
|     SITE, | ||||
|     SMNT, | ||||
|     STAT, | ||||
|     STOR, | ||||
|     STOU, | ||||
|     STRU, | ||||
|     SYST, | ||||
|     TYPE, | ||||
|     USER, | ||||
|     ; | ||||
| 
 | ||||
|     // Aliases
 | ||||
| 
 | ||||
|     public static final FTPCmd ABORT = ABOR; | ||||
|     public static final FTPCmd ACCOUNT = ACCT; | ||||
|     public static final FTPCmd ALLOCATE = ALLO; | ||||
|     public static final FTPCmd APPEND = APPE; | ||||
|     public static final FTPCmd CHANGE_TO_PARENT_DIRECTORY = CDUP; | ||||
|     public static final FTPCmd CHANGE_WORKING_DIRECTORY = CWD; | ||||
|     public static final FTPCmd DATA_PORT = PORT; | ||||
|     public static final FTPCmd DELETE = DELE; | ||||
|     public static final FTPCmd FEATURES = FEAT; | ||||
|     public static final FTPCmd FILE_STRUCTURE = STRU; | ||||
|     public static final FTPCmd GET_MOD_TIME = MDTM; | ||||
|     public static final FTPCmd LOGOUT = QUIT; | ||||
|     public static final FTPCmd MAKE_DIRECTORY = MKD; | ||||
|     public static final FTPCmd MOD_TIME = MDTM; | ||||
|     public static final FTPCmd NAME_LIST = NLST; | ||||
|     public static final FTPCmd PASSIVE = PASV; | ||||
|     public static final FTPCmd PASSWORD = PASS; | ||||
|     public static final FTPCmd PRINT_WORKING_DIRECTORY = PWD; | ||||
|     public static final FTPCmd REINITIALIZE = REIN; | ||||
|     public static final FTPCmd REMOVE_DIRECTORY = RMD; | ||||
|     public static final FTPCmd RENAME_FROM = RNFR; | ||||
|     public static final FTPCmd RENAME_TO = RNTO; | ||||
|     public static final FTPCmd REPRESENTATION_TYPE = TYPE; | ||||
|     public static final FTPCmd RESTART = REST; | ||||
|     public static final FTPCmd RETRIEVE = RETR; | ||||
|     public static final FTPCmd SET_MOD_TIME = MFMT; | ||||
|     public static final FTPCmd SITE_PARAMETERS = SITE; | ||||
|     public static final FTPCmd STATUS = STAT; | ||||
|     public static final FTPCmd STORE = STOR; | ||||
|     public static final FTPCmd STORE_UNIQUE = STOU; | ||||
|     public static final FTPCmd STRUCTURE_MOUNT = SMNT; | ||||
|     public static final FTPCmd SYSTEM = SYST; | ||||
|     public static final FTPCmd TRANSFER_MODE = MODE; | ||||
|     public static final FTPCmd USERNAME = USER; | ||||
| 
 | ||||
|     /** | ||||
|      * Retrieve the FTP protocol command string corresponding to a specified | ||||
|      * command code. | ||||
|      * | ||||
|      * @return The FTP protcol command string corresponding to a specified | ||||
|      *         command code. | ||||
|      */ | ||||
|     public final String getCommand() | ||||
|     { | ||||
|         return this.name(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,171 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  * | ||||
|  * @deprecated use {@link FTPCmd} instead | ||||
|  */ | ||||
| @Deprecated | ||||
| 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; | ||||
|     /** @since 2.2 */ | ||||
|     public static final int FEAT = 34; | ||||
|     /** @since 2.2 */ | ||||
|     public static final int MFMT = 35; | ||||
|     /** @since 2.2 */ | ||||
|     public static final int EPSV = 36; | ||||
|     /** @since 2.2 */ | ||||
|     public static final int EPRT = 37; | ||||
| 
 | ||||
|     /** | ||||
|      *  Machine parseable list for a directory | ||||
|      * @since 3.0 | ||||
|      */ | ||||
|     public static final int MLSD = 38; | ||||
| 
 | ||||
|     /** | ||||
|      * Machine parseable list for a single file | ||||
|      * @since 3.0 | ||||
|      */ | ||||
|     public static final int MLST = 39; | ||||
| 
 | ||||
|     // Must agree with final entry above; used to check array size
 | ||||
|     private static final int LAST = MLST; | ||||
| 
 | ||||
|     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; | ||||
| 
 | ||||
|     /** @since 2.2 */ | ||||
|     public static final int FEATURES = FEAT; | ||||
|     /** @since 2.2 */ | ||||
|     public static final int GET_MOD_TIME = MDTM; | ||||
|     /** @since 2.2 */ | ||||
|     public static final int SET_MOD_TIME = MFMT; | ||||
| 
 | ||||
|     // Cannot be instantiated
 | ||||
|     private FTPCommand() | ||||
|     {} | ||||
| 
 | ||||
|     private 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", "MDTM", "FEAT", "MFMT", | ||||
|                                           "EPSV", "EPRT", "MLSD", "MLST" }; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     // default access needed for Unit test
 | ||||
|     static void checkArray(){ | ||||
|         int expectedLength = LAST+1; | ||||
|         if (_commands.length != expectedLength) { | ||||
|             throw new RuntimeException("Incorrect _commands array. Should have length " | ||||
|                     +expectedLength+" found "+_commands.length); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Retrieve the FTP protocol command string corresponding to a specified | ||||
|      * command code. | ||||
|      * | ||||
|      * @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]; | ||||
|     } | ||||
| } | ||||
| @ -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 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. | ||||
|  * | ||||
|  * @see FTP | ||||
|  * @see FTPClient | ||||
|  ***/ | ||||
| 
 | ||||
| public class FTPConnectionClosedException extends IOException | ||||
| { | ||||
| 
 | ||||
|     private static final long serialVersionUID = 3500547241659379952L; | ||||
| 
 | ||||
|     /*** Constructs a FTPConnectionClosedException with no message ***/ | ||||
|     public FTPConnectionClosedException() | ||||
|     { | ||||
|         super(); | ||||
|     } | ||||
| 
 | ||||
|     /*** | ||||
|      * Constructs a FTPConnectionClosedException with a specified message. | ||||
|      * | ||||
|      * @param message  The message explaining the reason for the exception. | ||||
|      ***/ | ||||
|     public FTPConnectionClosedException(String message) | ||||
|     { | ||||
|         super(message); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,536 @@ | ||||
| /* | ||||
|  * 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; | ||||
| import java.util.Date; | ||||
| import java.util.Formatter; | ||||
| import java.util.TimeZone; | ||||
| 
 | ||||
| /*** | ||||
|  * The FTPFile class is used to represent information about files stored | ||||
|  * on an FTP server. | ||||
|  * | ||||
|  * @see FTPFileEntryParser | ||||
|  * @see FTPClient#listFiles | ||||
|  ***/ | ||||
| 
 | ||||
| public class FTPFile implements Serializable | ||||
| { | ||||
|     private static final long serialVersionUID = 9010790363003271996L; | ||||
| 
 | ||||
|     /** 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; | ||||
| 
 | ||||
|     private int _type, _hardLinkCount; | ||||
|     private long _size; | ||||
|     private String _rawListing, _user, _group, _name, _link; | ||||
|     private Calendar _date; | ||||
|     // If this is null, then list entry parsing failed
 | ||||
|     private final boolean[] _permissions[]; // e.g. _permissions[USER_ACCESS][READ_PERMISSION]
 | ||||
| 
 | ||||
|     /*** Creates an empty FTPFile. ***/ | ||||
|     public FTPFile() | ||||
|     { | ||||
|         _permissions = new boolean[3][3]; | ||||
|         _type = UNKNOWN_TYPE; | ||||
|         // init these to values that do not occur in listings
 | ||||
|         // so can distinguish which fields are unset
 | ||||
|         _hardLinkCount = 0; // 0 is invalid as a link count
 | ||||
|         _size = -1; // 0 is valid, so use -1
 | ||||
|         _user = ""; | ||||
|         _group = ""; | ||||
|         _date = null; | ||||
|         _name = null; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Constructor for use by {@link FTPListParseEngine} only. | ||||
|      * Used to create FTPFile entries for failed parses | ||||
|      * @param rawListing line that could not be parsed. | ||||
|      * @since 3.4 | ||||
|      */ | ||||
|     FTPFile(String rawListing) | ||||
|     { | ||||
|         _permissions = null; // flag that entry is invalid
 | ||||
|         _rawListing = rawListing; | ||||
|         _type = UNKNOWN_TYPE; | ||||
|         // init these to values that do not occur in listings
 | ||||
|         // so can distinguish which fields are unset
 | ||||
|         _hardLinkCount = 0; // 0 is invalid as a link count
 | ||||
|         _size = -1; // 0 is valid, so use -1
 | ||||
|         _user = ""; | ||||
|         _group = ""; | ||||
|         _date = null; | ||||
|         _name = null; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /*** | ||||
|      * Set the original FTP server raw listing from which the FTPFile was | ||||
|      * created. | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @return The original FTP server raw listing used to initialize the | ||||
|      *         FTPFile. | ||||
|      ***/ | ||||
|     public String getRawListing() | ||||
|     { | ||||
|         return _rawListing; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /*** | ||||
|      * Determine if the file is a directory. | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @return True if the file is of type <code>UNKNOWN_TYPE</code>, false if | ||||
|      *         not. | ||||
|      ***/ | ||||
|     public boolean isUnknown() | ||||
|     { | ||||
|         return (_type == UNKNOWN_TYPE); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Used to indicate whether an entry is valid or not. | ||||
|      * If the entry is invalid, only the {@link #getRawListing()} method will be useful. | ||||
|      * Other methods may fail. | ||||
|      * | ||||
|      * Used in conjunction with list parsing that preseverves entries that failed to parse. | ||||
|      * @see FTPClientConfig#setUnparseableEntries(boolean) | ||||
|      * @return true if the entry is valid | ||||
|      * @since 3.4 | ||||
|      */ | ||||
|     public boolean isValid() { | ||||
|         return (_permissions != null); | ||||
|     } | ||||
| 
 | ||||
|     /*** | ||||
|      * Set the type of the file (<code>DIRECTORY_TYPE</code>, | ||||
|      * <code>FILE_TYPE</code>, etc.). | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @return The type of the file. | ||||
|      ***/ | ||||
|     public int getType() | ||||
|     { | ||||
|         return _type; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /*** | ||||
|      * Set the name of the file. | ||||
|      * | ||||
|      * @param name  The name of the file. | ||||
|      ***/ | ||||
|     public void setName(String name) | ||||
|     { | ||||
|         _name = name; | ||||
|     } | ||||
| 
 | ||||
|     /*** | ||||
|      * Return the name of the file. | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @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; | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @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. | ||||
|      * @throws ArrayIndexOutOfBoundsException if either of the parameters is out of range | ||||
|      ***/ | ||||
|     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. | ||||
|      * | ||||
|      * @param access The access group (one of the <code> _ACCESS </code> | ||||
|      *               constants) | ||||
|      * @param permission The access permission (one of the | ||||
|      *               <code> _PERMISSION </code> constants) | ||||
|      * @throws ArrayIndexOutOfBoundsException if either of the parameters is out of range | ||||
|      * @return true if {@link #isValid()} is {@code true &&} the associated permission is set; | ||||
|      * {@code false} otherwise. | ||||
|      ***/ | ||||
|     public boolean hasPermission(int access, int permission) | ||||
|     { | ||||
|         if (_permissions == null) { | ||||
|             return false; | ||||
|         } | ||||
|         return _permissions[access][permission]; | ||||
|     } | ||||
| 
 | ||||
|     /*** | ||||
|      * Returns a string representation of the FTPFile information. | ||||
|      * | ||||
|      * @return A string representation of the FTPFile information. | ||||
|      */ | ||||
|     @Override | ||||
|     public String toString() | ||||
|     { | ||||
|         return getRawListing(); | ||||
|     } | ||||
| 
 | ||||
|     /*** | ||||
|      * Returns a string representation of the FTPFile information. | ||||
|      * This currently mimics the Unix listing format. | ||||
|      * This method uses the timezone of the Calendar entry, which is | ||||
|      * the server time zone (if one was provided) otherwise it is | ||||
|      * the local time zone. | ||||
|      * <p> | ||||
|      * Note: if the instance is not valid {@link #isValid()}, no useful | ||||
|      * information can be returned. In this case, use {@link #getRawListing()} | ||||
|      * instead. | ||||
|      * | ||||
|      * @return A string representation of the FTPFile information. | ||||
|      * @since 3.0 | ||||
|      */ | ||||
|     public String toFormattedString() | ||||
|     { | ||||
|         return toFormattedString(null); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns a string representation of the FTPFile information. | ||||
|      * This currently mimics the Unix listing format. | ||||
|      * This method allows the Calendar time zone to be overridden. | ||||
|      * <p> | ||||
|      * Note: if the instance is not valid {@link #isValid()}, no useful | ||||
|      * information can be returned. In this case, use {@link #getRawListing()} | ||||
|      * instead. | ||||
|      * @param timezone the timezone to use for displaying the time stamp | ||||
|      * If {@code null}, then use the Calendar entry timezone | ||||
|      * @return A string representation of the FTPFile information. | ||||
|      * @since 3.4 | ||||
|      */ | ||||
|     public String toFormattedString(final String timezone) | ||||
|     { | ||||
| 
 | ||||
|         if (!isValid()) { | ||||
|             return "[Invalid: could not parse file entry]"; | ||||
|         } | ||||
|         StringBuilder sb = new StringBuilder(); | ||||
|         Formatter fmt = new Formatter(sb); | ||||
|         sb.append(formatType()); | ||||
|         sb.append(permissionToString(USER_ACCESS)); | ||||
|         sb.append(permissionToString(GROUP_ACCESS)); | ||||
|         sb.append(permissionToString(WORLD_ACCESS)); | ||||
|         fmt.format(" %4d", Integer.valueOf(getHardLinkCount())); | ||||
|         fmt.format(" %-8s %-8s", getUser(), getGroup()); | ||||
|         fmt.format(" %8d", Long.valueOf(getSize())); | ||||
|         Calendar timestamp = getTimestamp(); | ||||
|         if (timestamp != null) { | ||||
|             if (timezone != null) { | ||||
|                 TimeZone newZone = TimeZone.getTimeZone(timezone); | ||||
|                 if (!newZone.equals(timestamp.getTimeZone())){ | ||||
|                     Date original = timestamp.getTime(); | ||||
|                     Calendar newStamp = Calendar.getInstance(newZone); | ||||
|                     newStamp.setTime(original); | ||||
|                     timestamp = newStamp; | ||||
|                 } | ||||
|             } | ||||
|             fmt.format(" %1$tY-%1$tm-%1$td", timestamp); | ||||
|             // Only display time units if they are present
 | ||||
|             if (timestamp.isSet(Calendar.HOUR_OF_DAY)) { | ||||
|                 fmt.format(" %1$tH", timestamp); | ||||
|                 if (timestamp.isSet(Calendar.MINUTE)) { | ||||
|                     fmt.format(":%1$tM", timestamp); | ||||
|                     if (timestamp.isSet(Calendar.SECOND)) { | ||||
|                         fmt.format(":%1$tS", timestamp); | ||||
|                         if (timestamp.isSet(Calendar.MILLISECOND)) { | ||||
|                             fmt.format(".%1$tL", timestamp); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 fmt.format(" %1$tZ", timestamp); | ||||
|             } | ||||
|         } | ||||
|         sb.append(' '); | ||||
|         sb.append(getName()); | ||||
|         fmt.close(); | ||||
|         return sb.toString(); | ||||
|     } | ||||
| 
 | ||||
|     private char formatType(){ | ||||
|         switch(_type) { | ||||
|             case FILE_TYPE: | ||||
|                 return '-'; | ||||
|             case DIRECTORY_TYPE: | ||||
|                 return 'd'; | ||||
|             case SYMBOLIC_LINK_TYPE: | ||||
|                 return 'l'; | ||||
|             default: | ||||
|                 return '?'; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private String permissionToString(int access ){ | ||||
|         StringBuilder sb = new StringBuilder(); | ||||
|         if (hasPermission(access, READ_PERMISSION)) { | ||||
|             sb.append('r'); | ||||
|         } else { | ||||
|             sb.append('-'); | ||||
|         } | ||||
|         if (hasPermission(access, WRITE_PERMISSION)) { | ||||
|             sb.append('w'); | ||||
|         } else { | ||||
|             sb.append('-'); | ||||
|         } | ||||
|         if (hasPermission(access, EXECUTE_PERMISSION)) { | ||||
|             sb.append('x'); | ||||
|         } else { | ||||
|             sb.append('-'); | ||||
|         } | ||||
|         return sb.toString(); | ||||
|     } | ||||
| } | ||||
| @ -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.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 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 uses the <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 second example uses the <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 third example uses the <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> | ||||
|  * | ||||
|  * For an alternative approach, see the {@link FTPListParseEngine} class
 | ||||
|  * which provides iterative access. | ||||
|  * | ||||
|  * @version $Id: FTPFileEntryParser.java 1747119 2016-06-07 02:22:24Z ggregory $ | ||||
|  * @see FTPFile | ||||
|  * @see FTPClient#listFiles() | ||||
|  */ | ||||
| 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. | ||||
|      * | ||||
|      * @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. | ||||
|      * @throws 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:                    ** | ||||
|  */ | ||||
| @ -0,0 +1,79 @@ | ||||
| /* | ||||
|  * 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; | ||||
| 
 | ||||
| /** | ||||
|  * 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 delimits 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. | ||||
|      * @throws IOException thrown on any IO Error reading from the reader. | ||||
|      */ | ||||
|     @Override | ||||
|     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 does nothing. | ||||
|      * | ||||
|      * @param original Original list after it has been created from the server stream | ||||
|      * | ||||
|      * @return <code>original</code> unmodified. | ||||
|      */ | ||||
|     @Override | ||||
|     public List<String> preParse(List<String> original) { | ||||
|          return original; | ||||
|      } | ||||
| } | ||||
| 
 | ||||
| /* Emacs configuration | ||||
|  * Local variables:        ** | ||||
|  * mode:             java  ** | ||||
|  * c-basic-offset:   4     ** | ||||
|  * indent-tabs-mode: nil   ** | ||||
|  * End:                    ** | ||||
|  */ | ||||
| @ -0,0 +1,33 @@ | ||||
| /* | ||||
|  * 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; | ||||
| 
 | ||||
| /** | ||||
|  * Perform filtering on FTPFile entries. | ||||
|  * @since 2.2 | ||||
|  */ | ||||
| public interface FTPFileFilter { | ||||
|     /** | ||||
|      * Checks if an FTPFile entry should be included or not. | ||||
|      * | ||||
|      * @param file entry to be checked for inclusion. May be <code>null</code>. | ||||
|      * @return <code>true</code> if the file is to be included, <code>false</code> otherwise | ||||
|      */ | ||||
|     public boolean accept(FTPFile file); | ||||
| } | ||||
| @ -0,0 +1,57 @@ | ||||
| /* | ||||
|  * 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; | ||||
| 
 | ||||
| /** | ||||
|  * Implements some simple FTPFileFilter classes. | ||||
|  * @since 2.2 | ||||
|  */ | ||||
| public class FTPFileFilters { | ||||
| 
 | ||||
|     /** | ||||
|      * Accepts all FTPFile entries, including null. | ||||
|      */ | ||||
|     public static final FTPFileFilter ALL = new FTPFileFilter() { | ||||
|         @Override | ||||
|         public boolean accept(FTPFile file) { | ||||
|             return true; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     /** | ||||
|      * Accepts all non-null FTPFile entries. | ||||
|      */ | ||||
|     public static final FTPFileFilter NON_NULL = new FTPFileFilter() { | ||||
|         @Override | ||||
|         public boolean accept(FTPFile file) { | ||||
|             return file != null; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     /** | ||||
|      * Accepts all (non-null) FTPFile directory entries. | ||||
|      */ | ||||
|     public static final FTPFileFilter DIRECTORIES = new FTPFileFilter() { | ||||
|         @Override | ||||
|         public boolean accept(FTPFile file) { | ||||
|             return file != null && file.isDirectory(); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,202 @@ | ||||
| /* | ||||
|  * 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.io.OutputStream; | ||||
| import java.io.Reader; | ||||
| import java.io.UnsupportedEncodingException; | ||||
| import java.net.Inet6Address; | ||||
| import java.net.Socket; | ||||
| import java.net.SocketException; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| 
 | ||||
| import org.apache.commons.net.util.Base64; | ||||
| 
 | ||||
| /** | ||||
|  * Experimental attempt at FTP client that tunnels over an HTTP proxy connection. | ||||
|  * | ||||
|  * @since 2.2 | ||||
|  */ | ||||
| public class FTPHTTPClient extends FTPClient { | ||||
|     private final String proxyHost; | ||||
|     private final int proxyPort; | ||||
|     private final String proxyUsername; | ||||
|     private final String proxyPassword; | ||||
| 
 | ||||
|     private static final byte[] CRLF={'\r', '\n'}; | ||||
|     private final Base64 base64 = new Base64(); | ||||
| 
 | ||||
|     private String tunnelHost; // Save the host when setting up a tunnel (needed for EPSV)
 | ||||
| 
 | ||||
|     public FTPHTTPClient(String proxyHost, int proxyPort, String proxyUser, String proxyPass) { | ||||
|         this.proxyHost = proxyHost; | ||||
|         this.proxyPort = proxyPort; | ||||
|         this.proxyUsername = proxyUser; | ||||
|         this.proxyPassword = proxyPass; | ||||
|         this.tunnelHost = null; | ||||
|     } | ||||
| 
 | ||||
|     public FTPHTTPClient(String proxyHost, int proxyPort) { | ||||
|         this(proxyHost, proxyPort, null, null); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * {@inheritDoc} | ||||
|      * | ||||
|      * @throws IllegalStateException if connection mode is not passive | ||||
|      * @deprecated (3.3) Use {@link FTPClient#_openDataConnection_(FTPCmd, String)} instead | ||||
|      */ | ||||
|     // Kept to maintain binary compatibility
 | ||||
|     // Not strictly necessary, but Clirr complains even though there is a super-impl
 | ||||
|     @Override | ||||
|     @Deprecated | ||||
|     protected Socket _openDataConnection_(int command, String arg) | ||||
|     throws IOException { | ||||
|         return super._openDataConnection_(command, arg); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * {@inheritDoc} | ||||
|      * | ||||
|      * @throws IllegalStateException if connection mode is not passive | ||||
|      * @since 3.1 | ||||
|      */ | ||||
|     @Override | ||||
|     protected Socket _openDataConnection_(String command, String arg) | ||||
|     throws IOException { | ||||
|         //Force local passive mode, active mode not supported by through proxy
 | ||||
|         if (getDataConnectionMode() != PASSIVE_LOCAL_DATA_CONNECTION_MODE) { | ||||
|             throw new IllegalStateException("Only passive connection mode supported"); | ||||
|         } | ||||
| 
 | ||||
|         final boolean isInet6Address = getRemoteAddress() instanceof Inet6Address; | ||||
|         String passiveHost = null; | ||||
| 
 | ||||
|         boolean attemptEPSV = isUseEPSVwithIPv4() || isInet6Address; | ||||
|         if (attemptEPSV && epsv() == FTPReply.ENTERING_EPSV_MODE) { | ||||
|             _parseExtendedPassiveModeReply(_replyLines.get(0)); | ||||
|             passiveHost = this.tunnelHost; | ||||
|         } else { | ||||
|             if (isInet6Address) { | ||||
|                 return null; // Must use EPSV for IPV6
 | ||||
|             } | ||||
|             // If EPSV failed on IPV4, revert to PASV
 | ||||
|             if (pasv() != FTPReply.ENTERING_PASSIVE_MODE) { | ||||
|                 return null; | ||||
|             } | ||||
|             _parsePassiveModeReply(_replyLines.get(0)); | ||||
|             passiveHost = this.getPassiveHost(); | ||||
|         } | ||||
| 
 | ||||
|         Socket socket = _socketFactory_.createSocket(proxyHost, proxyPort); | ||||
|         InputStream is = socket.getInputStream(); | ||||
|         OutputStream os = socket.getOutputStream(); | ||||
|         tunnelHandshake(passiveHost, this.getPassivePort(), is, os); | ||||
|         if ((getRestartOffset() > 0) && !restart(getRestartOffset())) { | ||||
|             socket.close(); | ||||
|             return null; | ||||
|         } | ||||
| 
 | ||||
|         if (!FTPReply.isPositivePreliminary(sendCommand(command, arg))) { | ||||
|             socket.close(); | ||||
|             return null; | ||||
|         } | ||||
| 
 | ||||
|         return socket; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void connect(String host, int port) throws SocketException, IOException { | ||||
| 
 | ||||
|         _socket_ = _socketFactory_.createSocket(proxyHost, proxyPort); | ||||
|         _input_ = _socket_.getInputStream(); | ||||
|         _output_ = _socket_.getOutputStream(); | ||||
|         Reader socketIsReader; | ||||
|         try { | ||||
|             socketIsReader = tunnelHandshake(host, port, _input_, _output_); | ||||
|         } | ||||
|         catch (Exception e) { | ||||
|             IOException ioe = new IOException("Could not connect to " + host+ " using port " + port); | ||||
|             ioe.initCause(e); | ||||
|             throw ioe; | ||||
|         } | ||||
|         super._connectAction_(socketIsReader); | ||||
|     } | ||||
| 
 | ||||
|     private BufferedReader tunnelHandshake(String host, int port, InputStream input, OutputStream output) throws IOException, | ||||
|     UnsupportedEncodingException { | ||||
|         final String connectString = "CONNECT "  + host + ":" + port  + " HTTP/1.1"; | ||||
|         final String hostString = "Host: " + host + ":" + port; | ||||
| 
 | ||||
|         this.tunnelHost = host; | ||||
|         output.write(connectString.getBytes("UTF-8")); // TODO what is the correct encoding?
 | ||||
|         output.write(CRLF); | ||||
|         output.write(hostString.getBytes("UTF-8")); | ||||
|         output.write(CRLF); | ||||
| 
 | ||||
|         if (proxyUsername != null && proxyPassword != null) { | ||||
|             final String auth = proxyUsername + ":" + proxyPassword; | ||||
|             final String header = "Proxy-Authorization: Basic " | ||||
|                 + base64.encodeToString(auth.getBytes("UTF-8")); | ||||
|             output.write(header.getBytes("UTF-8")); | ||||
|         } | ||||
|         output.write(CRLF); | ||||
| 
 | ||||
|         List<String> response = new ArrayList<String>(); | ||||
|         BufferedReader reader = new BufferedReader( | ||||
|                 new InputStreamReader(input, getCharset())); | ||||
| 
 | ||||
|         for (String line = reader.readLine(); line != null | ||||
|         && line.length() > 0; line = reader.readLine()) { | ||||
|             response.add(line); | ||||
|         } | ||||
| 
 | ||||
|         int size = response.size(); | ||||
|         if (size == 0) { | ||||
|             throw new IOException("No response from proxy"); | ||||
|         } | ||||
| 
 | ||||
|         String code = null; | ||||
|         String resp = response.get(0); | ||||
|         if (resp.startsWith("HTTP/") && resp.length() >= 12) { | ||||
|             code = resp.substring(9, 12); | ||||
|         } else { | ||||
|             throw new IOException("Invalid response from proxy: " + resp); | ||||
|         } | ||||
| 
 | ||||
|         if (!"200".equals(code)) { | ||||
|             StringBuilder msg = new StringBuilder(); | ||||
|             msg.append("HTTPTunnelConnector: connection failed\r\n"); | ||||
|             msg.append("Response received from the proxy:\r\n"); | ||||
|             for (String line : response) { | ||||
|                 msg.append(line); | ||||
|                 msg.append("\r\n"); | ||||
|             } | ||||
|             throw new IOException(msg.toString()); | ||||
|         } | ||||
|         return reader; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| @ -0,0 +1,329 @@ | ||||
| /* | ||||
|  * 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.ArrayList; | ||||
| import java.util.Iterator; | ||||
| import java.util.LinkedList; | ||||
| import java.util.List; | ||||
| import java.util.ListIterator; | ||||
| 
 | ||||
| import org.apache.commons.net.util.Charsets; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * 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 1747119 2016-06-07 02:22:24Z ggregory $ | ||||
|  */ | ||||
| public class FTPListParseEngine { | ||||
|     private List<String> entries = new LinkedList<String>(); | ||||
|     private ListIterator<String> _internalIterator = entries.listIterator(); | ||||
| 
 | ||||
|     private final FTPFileEntryParser parser; | ||||
|     // Should invalid files (parse failures) be allowed?
 | ||||
|     private final boolean saveUnparseableEntries; | ||||
| 
 | ||||
|     public FTPListParseEngine(FTPFileEntryParser parser) { | ||||
|         this(parser, null); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Intended for use by FTPClient only | ||||
|      * @since 3.4 | ||||
|      */ | ||||
|     FTPListParseEngine(FTPFileEntryParser parser, FTPClientConfig configuration) { | ||||
|         this.parser = parser; | ||||
|         if (configuration != null) { | ||||
|             this.saveUnparseableEntries = configuration.getUnparseableEntries(); | ||||
|         } else { | ||||
|             this.saveUnparseableEntries = false; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * handle the initial 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. | ||||
|      * @param encoding the encoding to be used for reading the stream | ||||
|      * | ||||
|      * @throws 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(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 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. | ||||
|      * | ||||
|      * @throws IOException | ||||
|      *                   thrown on any failure to read the stream | ||||
|      */ | ||||
|     private void readStream(InputStream stream, String encoding) throws IOException | ||||
|     { | ||||
|         BufferedReader reader = new BufferedReader( | ||||
|                 new InputStreamReader(stream, Charsets.toCharset(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); | ||||
|             if (temp == null && saveUnparseableEntries) { | ||||
|                 temp = new FTPFile(entry); | ||||
|             } | ||||
|             tmpResults.add(temp); | ||||
|             count--; | ||||
|         } | ||||
|         return tmpResults.toArray(new FTPFile[tmpResults.size()]); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 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); | ||||
|             if (temp == null && saveUnparseableEntries) { | ||||
|                 temp = new FTPFile(entry); | ||||
|             } | ||||
|             tmpResults.add(0,temp); | ||||
|             count--; | ||||
|         } | ||||
|         return tmpResults.toArray(new FTPFile[tmpResults.size()]); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 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. | ||||
|      * None of the entries will be null | ||||
|      * @throws IOException - not ever thrown, may be removed in a later release | ||||
|      */ | ||||
|     public FTPFile[] getFiles() | ||||
|     throws IOException // TODO remove; not actually thrown
 | ||||
|     { | ||||
|         return getFiles(FTPFileFilters.NON_NULL); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns an array of FTPFile objects containing the whole list of | ||||
|      * files returned by the server as read by this object's parser. | ||||
|      * The files are filtered before being added to the array. | ||||
|      * | ||||
|      * @param filter FTPFileFilter, must not be <code>null</code>. | ||||
|      * | ||||
|      * @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, or use the | ||||
|      * a filter such as {@link FTPFileFilters#NON_NULL} which does not | ||||
|      * allow null entries. | ||||
|      * @since 2.2 | ||||
|      * @throws IOException - not ever thrown, may be removed in a later release | ||||
|      */ | ||||
|     public FTPFile[] getFiles(FTPFileFilter filter) | ||||
|     throws IOException // TODO remove; not actually thrown
 | ||||
|     { | ||||
|         List<FTPFile> tmpResults = new ArrayList<FTPFile>(); | ||||
|         Iterator<String> iter = this.entries.iterator(); | ||||
|         while (iter.hasNext()) { | ||||
|             String entry = iter.next(); | ||||
|             FTPFile temp = this.parser.parseFTPEntry(entry); | ||||
|             if (temp == null && saveUnparseableEntries) { | ||||
|                 temp = new FTPFile(entry); | ||||
|             } | ||||
|             if (filter.accept(temp)){ | ||||
|                 tmpResults.add(temp); | ||||
|             } | ||||
|         } | ||||
|         return tmpResults.toArray(new FTPFile[tmpResults.size()]); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 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(); | ||||
|     } | ||||
| 
 | ||||
|     // DEPRECATED METHODS - for API compatibility only - DO NOT USE
 | ||||
| 
 | ||||
|     /** | ||||
|      * Do not use. | ||||
|      * @param stream the stream from which to read | ||||
|      * @throws IOException on error | ||||
|      * @deprecated use {@link #readServerList(InputStream, String)} instead | ||||
|     */ | ||||
|     @Deprecated | ||||
|     public void readServerList(InputStream stream) | ||||
|     throws IOException | ||||
|     { | ||||
|         readServerList(stream, null); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,201 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  * <p> | ||||
|  * TODO replace with an enum | ||||
|  ***/ | ||||
| 
 | ||||
| public final class FTPReply | ||||
| { | ||||
| 
 | ||||
|     public static final int RESTART_MARKER = 110; | ||||
|     public static final int SERVICE_NOT_READY = 120; | ||||
|     public static final int DATA_CONNECTION_ALREADY_OPEN = 125; | ||||
|     public static final int FILE_STATUS_OK = 150; | ||||
|     public static final int COMMAND_OK = 200; | ||||
|     public static final int COMMAND_IS_SUPERFLUOUS = 202; | ||||
|     public static final int SYSTEM_STATUS = 211; | ||||
|     public static final int DIRECTORY_STATUS = 212; | ||||
|     public static final int FILE_STATUS = 213; | ||||
|     public static final int HELP_MESSAGE = 214; | ||||
|     public static final int NAME_SYSTEM_TYPE = 215; | ||||
|     public static final int SERVICE_READY = 220; | ||||
|     public static final int SERVICE_CLOSING_CONTROL_CONNECTION = 221; | ||||
|     public static final int DATA_CONNECTION_OPEN = 225; | ||||
|     public static final int CLOSING_DATA_CONNECTION = 226; | ||||
|     public static final int ENTERING_PASSIVE_MODE = 227; | ||||
|     /** @since 2.2 */ | ||||
|     public static final int ENTERING_EPSV_MODE = 229; | ||||
|     public static final int USER_LOGGED_IN = 230; | ||||
|     public static final int FILE_ACTION_OK = 250; | ||||
|     public static final int PATHNAME_CREATED = 257; | ||||
|     public static final int NEED_PASSWORD = 331; | ||||
|     public static final int NEED_ACCOUNT = 332; | ||||
|     public static final int FILE_ACTION_PENDING = 350; | ||||
|     public static final int SERVICE_NOT_AVAILABLE = 421; | ||||
|     public static final int CANNOT_OPEN_DATA_CONNECTION = 425; | ||||
|     public static final int TRANSFER_ABORTED = 426; | ||||
|     public static final int FILE_ACTION_NOT_TAKEN = 450; | ||||
|     public static final int ACTION_ABORTED = 451; | ||||
|     public static final int INSUFFICIENT_STORAGE = 452; | ||||
|     public static final int UNRECOGNIZED_COMMAND = 500; | ||||
|     public static final int SYNTAX_ERROR_IN_ARGUMENTS = 501; | ||||
|     public static final int COMMAND_NOT_IMPLEMENTED = 502; | ||||
|     public static final int BAD_COMMAND_SEQUENCE = 503; | ||||
|     public static final int COMMAND_NOT_IMPLEMENTED_FOR_PARAMETER = 504; | ||||
|     public static final int NOT_LOGGED_IN = 530; | ||||
|     public static final int NEED_ACCOUNT_FOR_STORING_FILES = 532; | ||||
|     public static final int FILE_UNAVAILABLE = 550; | ||||
|     public static final int PAGE_TYPE_UNKNOWN = 551; | ||||
|     public static final int STORAGE_ALLOCATION_EXCEEDED = 552; | ||||
|     public static final int FILE_NAME_NOT_ALLOWED = 553; | ||||
| 
 | ||||
|     // FTPS Reply Codes
 | ||||
| 
 | ||||
|     /** @since 2.0 */ | ||||
|     public static final int SECURITY_DATA_EXCHANGE_COMPLETE = 234; | ||||
|     /** @since 2.0 */ | ||||
|     public static final int SECURITY_DATA_EXCHANGE_SUCCESSFULLY = 235; | ||||
|     /** @since 2.0 */ | ||||
|     public static final int SECURITY_MECHANISM_IS_OK = 334; | ||||
|     /** @since 2.0 */ | ||||
|     public static final int SECURITY_DATA_IS_ACCEPTABLE = 335; | ||||
|     /** @since 2.0 */ | ||||
|     public static final int UNAVAILABLE_RESOURCE = 431; | ||||
|     /** @since 2.2 */ | ||||
|     public static final int BAD_TLS_NEGOTIATION_OR_DATA_ENCRYPTION_REQUIRED = 522; | ||||
|     /** @since 2.0 */ | ||||
|     public static final int DENIED_FOR_POLICY_REASONS = 533; | ||||
|     /** @since 2.0 */ | ||||
|     public static final int REQUEST_DENIED = 534; | ||||
|     /** @since 2.0 */ | ||||
|     public static final int FAILED_SECURITY_CHECK = 535; | ||||
|     /** @since 2.0 */ | ||||
|     public static final int REQUESTED_PROT_LEVEL_NOT_SUPPORTED = 536; | ||||
| 
 | ||||
|     // IPv6 error codes
 | ||||
|     // Note this is also used as an FTPS error code reply
 | ||||
|     /** @since 2.2 */ | ||||
|     public static final int EXTENDED_PORT_FAILURE = 522; | ||||
| 
 | ||||
|     // 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. | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @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); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Determine if a reply code is a protected response. | ||||
|      * @param reply  The reply code to test. | ||||
|      * @return True if a reply code is a protected response, false | ||||
|      *         if not. | ||||
|      * @since 3.0 | ||||
|      */ | ||||
|     public static boolean isProtectedReplyCode(int reply) | ||||
|     { | ||||
|         // actually, only 3 protected reply codes are
 | ||||
|         // defined in RFC 2228: 631, 632 and 633.
 | ||||
|         return (reply >= 600 && reply < 700); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,925 @@ | ||||
| /* | ||||
|  * 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 javax.net.ssl.HostnameVerifier; | ||||
| import javax.net.ssl.KeyManager; | ||||
| import javax.net.ssl.SSLContext; | ||||
| import javax.net.ssl.SSLException; | ||||
| import javax.net.ssl.SSLHandshakeException; | ||||
| import javax.net.ssl.SSLSocket; | ||||
| import javax.net.ssl.SSLSocketFactory; | ||||
| import javax.net.ssl.TrustManager; | ||||
| 
 | ||||
| import org.apache.commons.net.util.Base64; | ||||
| import org.apache.commons.net.util.SSLContextUtils; | ||||
| import org.apache.commons.net.util.SSLSocketUtils; | ||||
| import org.apache.commons.net.util.TrustManagerUtils; | ||||
| 
 | ||||
| /** | ||||
|  * FTP over SSL processing. If desired, the JVM property -Djavax.net.debug=all can be used to | ||||
|  * see wire-level SSL details. | ||||
|  * | ||||
|  * Warning: the hostname is not verified against the certificate by default, use | ||||
|  * {@link #setHostnameVerifier(HostnameVerifier)} or {@link #setEndpointCheckingEnabled(boolean)} | ||||
|  * (on Java 1.7+) to enable verification. Verification is only performed on client mode connections. | ||||
|  * @version $Id: FTPSClient.java 1747829 2016-06-11 00:57:57Z sebb $ | ||||
|  * @since 2.0 | ||||
|  */ | ||||
| public class FTPSClient extends FTPClient { | ||||
| 
 | ||||
| // From http://www.iana.org/assignments/port-numbers
 | ||||
| 
 | ||||
| //    ftps-data   989/tcp    ftp protocol, data, over TLS/SSL
 | ||||
| //    ftps-data   989/udp    ftp protocol, data, over TLS/SSL
 | ||||
| //    ftps        990/tcp    ftp protocol, control, over TLS/SSL
 | ||||
| //    ftps        990/udp    ftp protocol, control, over TLS/SSL
 | ||||
| 
 | ||||
|     public static final int DEFAULT_FTPS_DATA_PORT = 989; | ||||
|     public static final int DEFAULT_FTPS_PORT = 990; | ||||
| 
 | ||||
|     /** The value that I can set in PROT command  (C = Clear, P = Protected) */ | ||||
|     private static final String[] PROT_COMMAND_VALUE = {"C","E","S","P"}; | ||||
|     /** Default PROT Command */ | ||||
|     private static final String DEFAULT_PROT = "C"; | ||||
|     /** Default secure socket protocol name, i.e. TLS */ | ||||
|     private static final String DEFAULT_PROTOCOL = "TLS"; | ||||
| 
 | ||||
|     /** The AUTH (Authentication/Security Mechanism) command. */ | ||||
|     private static final String CMD_AUTH = "AUTH"; | ||||
|     /**  The ADAT (Authentication/Security Data) command. */ | ||||
|     private static final String CMD_ADAT = "ADAT"; | ||||
|     /**  The PROT (Data Channel Protection Level) command. */ | ||||
|     private static final String CMD_PROT = "PROT"; | ||||
|     /**  The PBSZ (Protection Buffer Size) command. */ | ||||
|     private static final String CMD_PBSZ = "PBSZ"; | ||||
|     /**  The MIC (Integrity Protected Command) command. */ | ||||
|     private static final String CMD_MIC = "MIC"; | ||||
|     /**  The CONF (Confidentiality Protected Command) command. */ | ||||
|     private static final String CMD_CONF = "CONF"; | ||||
|     /**  The ENC (Privacy Protected Command) command. */ | ||||
|     private static final String CMD_ENC = "ENC"; | ||||
|     /**  The CCC (Clear Command Channel) command. */ | ||||
|     private static final String CMD_CCC = "CCC"; | ||||
| 
 | ||||
|     /** The security mode. (True - Implicit Mode / False - Explicit Mode) */ | ||||
|     private final boolean isImplicit; | ||||
|     /** The secure socket protocol to be used, e.g. SSL/TLS. */ | ||||
|     private final String protocol; | ||||
|     /** The AUTH Command value */ | ||||
|     private String auth = DEFAULT_PROTOCOL; | ||||
|     /** The context object. */ | ||||
|     private SSLContext context; | ||||
|     /** The socket object. */ | ||||
|     private Socket plainSocket; | ||||
|     /** Controls whether a new SSL session may be established by this socket. Default true. */ | ||||
|     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, default validate only | ||||
|      * {@link TrustManagerUtils#getValidateServerCertificateTrustManager()}. | ||||
|      */ | ||||
|     private TrustManager trustManager = TrustManagerUtils.getValidateServerCertificateTrustManager(); | ||||
| 
 | ||||
|     /** The {@link KeyManager}, default null (i.e. use system default). */ | ||||
|     private KeyManager keyManager = null; | ||||
| 
 | ||||
|     /** The {@link HostnameVerifier} to use post-TLS, default null (i.e. no verification). */ | ||||
|     private HostnameVerifier hostnameVerifier = null; | ||||
| 
 | ||||
|     /** Use Java 1.7+ HTTPS Endpoint Identification Algorithim. */ | ||||
|     private boolean tlsEndpointChecking; | ||||
| 
 | ||||
|     /** | ||||
|      * Constructor for FTPSClient, calls {@link #FTPSClient(String, boolean)}. | ||||
|      * | ||||
|      * Sets protocol to {@link #DEFAULT_PROTOCOL} - i.e. TLS - and security mode to explicit (isImplicit = false) | ||||
|      */ | ||||
|     public FTPSClient() { | ||||
|         this(DEFAULT_PROTOCOL, false); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Constructor for FTPSClient, using {@link #DEFAULT_PROTOCOL} - i.e. TLS | ||||
|      * Calls {@link #FTPSClient(String, boolean)} | ||||
|      * @param isImplicit The security mode (Implicit/Explicit). | ||||
|      */ | ||||
|     public FTPSClient(boolean isImplicit) { | ||||
|         this(DEFAULT_PROTOCOL, isImplicit); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Constructor for FTPSClient, using explict mode, calls {@link #FTPSClient(String, boolean)}. | ||||
|      * | ||||
|      * @param protocol the protocol to use | ||||
|      */ | ||||
|     public FTPSClient(String protocol) { | ||||
|         this(protocol, false); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Constructor for FTPSClient allowing specification of protocol | ||||
|      * and security mode. If isImplicit is true, the port is set to | ||||
|      * {@link #DEFAULT_FTPS_PORT} i.e. 990. | ||||
|      * The default TrustManager is set from {@link TrustManagerUtils#getValidateServerCertificateTrustManager()} | ||||
|      * @param protocol the protocol | ||||
|      * @param isImplicit The security mode(Implicit/Explicit). | ||||
|      */ | ||||
|     public FTPSClient(String protocol, boolean isImplicit) { | ||||
|         super(); | ||||
|         this.protocol = protocol; | ||||
|         this.isImplicit = isImplicit; | ||||
|         if (isImplicit) { | ||||
|             setDefaultPort(DEFAULT_FTPS_PORT); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Constructor for FTPSClient, using {@link #DEFAULT_PROTOCOL} - i.e. TLS | ||||
|      * The default TrustManager is set from {@link TrustManagerUtils#getValidateServerCertificateTrustManager()} | ||||
|      * @param isImplicit The security mode(Implicit/Explicit). | ||||
|      * @param context A pre-configured SSL Context | ||||
|      */ | ||||
|     public FTPSClient(boolean isImplicit, SSLContext context) { | ||||
|         this(DEFAULT_PROTOCOL, isImplicit); | ||||
|         this.context = context; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Constructor for FTPSClient, using {@link #DEFAULT_PROTOCOL} - i.e. TLS | ||||
|      * and isImplicit {@code false} | ||||
|      * Calls {@link #FTPSClient(boolean, SSLContext)} | ||||
|      * @param context A pre-configured SSL Context | ||||
|      */ | ||||
|     public FTPSClient(SSLContext context) { | ||||
|         this(false, context); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * 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. | ||||
|      */ | ||||
|     protected void execAUTH() throws SSLException, IOException { | ||||
|         int replyCode = sendCommand(CMD_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) { | ||||
|             context = SSLContextUtils.createSSLContext(protocol, getKeyManager(), getTrustManager()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * SSL/TLS negotiation. Acquires an SSL socket of a control | ||||
|      * connection and carries out handshake processing. | ||||
|      * @throws IOException If server negotiation fails | ||||
|      */ | ||||
|     protected void sslNegotiation() throws IOException { | ||||
|         plainSocket = _socket_; | ||||
|         initSslContext(); | ||||
| 
 | ||||
|         SSLSocketFactory ssf = context.getSocketFactory(); | ||||
|         String host = (_hostname_ != null) ? _hostname_ : getRemoteAddress().getHostAddress(); | ||||
|         int port = _socket_.getPort(); | ||||
|         SSLSocket socket = | ||||
|             (SSLSocket) ssf.createSocket(_socket_, host, port, false); | ||||
|         socket.setEnableSessionCreation(isCreation); | ||||
|         socket.setUseClientMode(isClientMode); | ||||
| 
 | ||||
|         // client mode
 | ||||
|         if (isClientMode) { | ||||
|             if (tlsEndpointChecking) { | ||||
|                 SSLSocketUtils.enableEndpointNameVerification(socket); | ||||
|             } | ||||
|         } else { // server mode
 | ||||
|             socket.setNeedClientAuth(isNeedClientAuth); | ||||
|             socket.setWantClientAuth(isWantClientAuth); | ||||
|         } | ||||
| 
 | ||||
|         if (protocols != null) { | ||||
|             socket.setEnabledProtocols(protocols); | ||||
|         } | ||||
|         if (suites != null) { | ||||
|             socket.setEnabledCipherSuites(suites); | ||||
|         } | ||||
|         socket.startHandshake(); | ||||
| 
 | ||||
|         // TODO the following setup appears to duplicate that in the super class methods
 | ||||
|         _socket_ = socket; | ||||
|         _controlInput_ = new BufferedReader(new InputStreamReader( | ||||
|                 socket .getInputStream(), getControlEncoding())); | ||||
|         _controlOutput_ = new BufferedWriter(new OutputStreamWriter( | ||||
|                 socket.getOutputStream(), getControlEncoding())); | ||||
| 
 | ||||
|         if (isClientMode) { | ||||
|             if (hostnameVerifier != null && !hostnameVerifier.verify(host, socket.getSession())) { | ||||
|                 throw new SSLHandshakeException("Hostname doesn't match certificate"); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 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. | ||||
|     * @see org.apache.commons.net.util.KeyManagerUtils | ||||
|     */ | ||||
|     public void setKeyManager(KeyManager keyManager) { | ||||
|         this.keyManager = keyManager; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Controls whether a new 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 the underlying {@link Socket} instance is not SSL-enabled (i.e. an | ||||
|      * instance of {@link SSLSocket} with {@link SSLSocket}{@link #getEnableSessionCreation()}) enabled, | ||||
|      * this returns 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 the underlying {@link Socket} is not an {@link SSLSocket} instance, returns 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 the underlying {@link Socket} is not an {@link SSLSocket} instance, returns 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 the underlying {@link Socket} is not an {@link SSLSocket} instance, returns 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. Called before 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 the underlying {@link Socket} is not an {@link SSLSocket} instance, returns null. | ||||
|      * @return An array of cipher suite names, or <code>null</code> | ||||
|      */ | ||||
|     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 the underlying {@link Socket} is not an {@link SSLSocket} instance, returns null. | ||||
|      * @return An array of protocols, or <code>null</code> | ||||
|      */ | ||||
|     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 the server reply code does not equal "200". | ||||
|      * @throws IOException If an I/O error occurs while sending | ||||
|      * the command. | ||||
|      * @see #parsePBSZ(long) | ||||
|      */ | ||||
|     public void execPBSZ(long pbsz) throws SSLException, IOException { | ||||
|         if (pbsz < 0 || 4294967295L < pbsz) { // 32-bit unsigned number
 | ||||
|             throw new IllegalArgumentException(); | ||||
|         } | ||||
|         int status = sendCommand(CMD_PBSZ, String.valueOf(pbsz)); | ||||
|         if (FTPReply.COMMAND_OK != status) { | ||||
|             throw new SSLException(getReplyString()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * PBSZ command. pbsz value: 0 to (2^32)-1 decimal integer. | ||||
|      * Issues the command and parses the response to return the negotiated value. | ||||
|      * | ||||
|      * @param pbsz Protection Buffer Size. | ||||
|      * @throws SSLException If the server reply code does not equal "200". | ||||
|      * @throws IOException If an I/O error occurs while sending | ||||
|      * the command. | ||||
|      * @return the negotiated value. | ||||
|      * @see #execPBSZ(long) | ||||
|      * @since 3.0 | ||||
|      */ | ||||
|     public long parsePBSZ(long pbsz) throws SSLException, IOException { | ||||
|         execPBSZ(pbsz); | ||||
|         long minvalue = pbsz; | ||||
|         String remainder = extractPrefixedData("PBSZ=", getReplyString()); | ||||
|         if (remainder != null) { | ||||
|             long replysz = Long.parseLong(remainder); | ||||
|             if (replysz < minvalue) { | ||||
|                 minvalue = replysz; | ||||
|             } | ||||
|         } | ||||
|         return minvalue; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * PROT command. | ||||
|      * <ul> | ||||
|      * <li>C - Clear</li> | ||||
|      * <li>S - Safe(SSL protocol only)</li> | ||||
|      * <li>E - Confidential(SSL protocol only)</li> | ||||
|      * <li>P - Private</li> | ||||
|      * </ul> | ||||
|      * <b>N.B.</b> the method calls | ||||
|      *  {@link #setSocketFactory(javax.net.SocketFactory)} and | ||||
|      *  {@link #setServerSocketFactory(javax.net.ServerSocketFactory)} | ||||
|      * | ||||
|      * @param prot Data Channel Protection Level, if {@code null}, use {@link #DEFAULT_PROT}. | ||||
|      * @throws SSLException If the server reply code does not equal  {@code 200}. | ||||
|      * @throws IOException If an I/O error occurs while 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(CMD_PROT, prot)) { | ||||
|             throw new SSLException(getReplyString()); | ||||
|         } | ||||
|         if (DEFAULT_PROT.equals(prot)) { | ||||
|             setSocketFactory(null); | ||||
|             setServerSocketFactory(null); | ||||
|         } else { | ||||
|             setSocketFactory(new FTPSSocketFactory(context)); | ||||
|             setServerSocketFactory(new FTPSServerSocketFactory(context)); | ||||
|             initSslContext(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Check the value that can be 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 (String element : PROT_COMMAND_VALUE) | ||||
|         { | ||||
|             if (element.equals(prot)) { | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Send an FTP command. | ||||
|      * A successful CCC (Clear Command Channel) command causes the underlying {@link SSLSocket} | ||||
|      * instance to be assigned to a plain {@link Socket} | ||||
|      * @param command The FTP command. | ||||
|      * @return server reply. | ||||
|      * @throws IOException If an I/O error occurs while sending the command. | ||||
|      * @throws SSLException if a CCC command fails | ||||
|      * @see FTP#sendCommand(String) | ||||
|      */ | ||||
|     // Would like to remove this method, but that will break any existing clients that are using CCC
 | ||||
|     @Override | ||||
|     public int sendCommand(String command, String args) throws IOException { | ||||
|         int repCode = super.sendCommand(command, args); | ||||
|         /* If CCC is issued, restore socket i/o streams to unsecured versions */ | ||||
|         if (CMD_CCC.equals(command)) { | ||||
|             if (FTPReply.COMMAND_OK == repCode) { | ||||
|                 _socket_.close(); | ||||
|                 _socket_ = plainSocket; | ||||
|                 _controlInput_ = new BufferedReader( | ||||
|                     new InputStreamReader( | ||||
|                         _socket_ .getInputStream(), getControlEncoding())); | ||||
|                 _controlOutput_ = new BufferedWriter( | ||||
|                     new OutputStreamWriter( | ||||
|                         _socket_.getOutputStream(), getControlEncoding())); | ||||
|             } else { | ||||
|                 throw new SSLException(getReplyString()); | ||||
|             } | ||||
|         } | ||||
|         return repCode; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns a socket of the data connection. | ||||
|      * Wrapped as an {@link SSLSocket}, which carries out handshake processing. | ||||
|      * @param command The int 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 arguments. | ||||
|      * @return 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 FTPClient#_openDataConnection_(int, String) | ||||
|      * @deprecated (3.3) Use {@link FTPClient#_openDataConnection_(FTPCmd, String)} instead | ||||
|      */ | ||||
|     @Override | ||||
|     // Strictly speaking this is not needed, but it works round a Clirr bug
 | ||||
|     // So rather than invoke the parent code, we do it here
 | ||||
|     @Deprecated | ||||
|     protected Socket _openDataConnection_(int command, String arg) | ||||
|             throws IOException { | ||||
|         return _openDataConnection_(FTPCommand.getCommand(command), arg); | ||||
|     } | ||||
| 
 | ||||
|    /** | ||||
|      * Returns a socket of the data connection. | ||||
|      * Wrapped as an {@link SSLSocket}, which carries out handshake processing. | ||||
|      * @param command The textual 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 arguments. | ||||
|      * @return 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 FTPClient#_openDataConnection_(int, String) | ||||
|      * @since 3.2 | ||||
|      */ | ||||
|     @Override | ||||
|     protected Socket _openDataConnection_(String command, String arg) | ||||
|             throws IOException { | ||||
|         Socket socket = super._openDataConnection_(command, arg); | ||||
|         _prepareDataSocket_(socket); | ||||
|         if (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; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|     * Performs any custom initialization for a newly created SSLSocket (before | ||||
|     * the SSL handshake happens). | ||||
|     * Called by {@link #_openDataConnection_(int, String)} immediately | ||||
|     * after creating the socket. | ||||
|     * The default implementation is a no-op | ||||
|      * @param socket the socket to set up | ||||
|     * @throws IOException on error | ||||
|     * @since 3.1 | ||||
|     */ | ||||
|     protected void _prepareDataSocket_(Socket socket) | ||||
|             throws IOException { | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the currently configured {@link TrustManager}. | ||||
|      * | ||||
|      * @return A TrustManager instance. | ||||
|      */ | ||||
|     public TrustManager getTrustManager() { | ||||
|         return trustManager; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Override the default {@link TrustManager} to use; if set to {@code null}, | ||||
|      * the default TrustManager from the JVM will be used. | ||||
|      * | ||||
|      * @param trustManager The TrustManager implementation to set, may be {@code null} | ||||
|      * @see org.apache.commons.net.util.TrustManagerUtils | ||||
|      */ | ||||
|     public void setTrustManager(TrustManager trustManager) { | ||||
|         this.trustManager = trustManager; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the currently configured {@link HostnameVerifier}. | ||||
|      * The verifier is only used on client mode connections. | ||||
|      * @return A HostnameVerifier instance. | ||||
|      * @since 3.4 | ||||
|      */ | ||||
|     public HostnameVerifier getHostnameVerifier() | ||||
|     { | ||||
|         return hostnameVerifier; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Override the default {@link HostnameVerifier} to use. | ||||
|      * The verifier is only used on client mode connections. | ||||
|      * @param newHostnameVerifier The HostnameVerifier implementation to set or <code>null</code> to disable. | ||||
|      * @since 3.4 | ||||
|      */ | ||||
|     public void setHostnameVerifier(HostnameVerifier newHostnameVerifier) | ||||
|     { | ||||
|         hostnameVerifier = newHostnameVerifier; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Return whether or not endpoint identification using the HTTPS algorithm | ||||
|      * on Java 1.7+ is enabled. The default behaviour is for this to be disabled. | ||||
|      * | ||||
|      * This check is only performed on client mode connections. | ||||
|      * | ||||
|      * @return True if enabled, false if not. | ||||
|      * @since 3.4 | ||||
|      */ | ||||
|     public boolean isEndpointCheckingEnabled() | ||||
|     { | ||||
|         return tlsEndpointChecking; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Automatic endpoint identification checking using the HTTPS algorithm | ||||
|      * is supported on Java 1.7+. The default behaviour is for this to be disabled. | ||||
|      * | ||||
|      * This check is only performed on client mode connections. | ||||
|      * | ||||
|      * @param enable Enable automatic endpoint identification checking using the HTTPS algorithm on Java 1.7+. | ||||
|      * @since 3.4 | ||||
|      */ | ||||
|     public void setEndpointCheckingEnabled(boolean enable) | ||||
|     { | ||||
|         tlsEndpointChecking = enable; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Closes the connection to the FTP server and restores | ||||
|      * connection parameters to the default values. | ||||
|      * <p> | ||||
|      * Calls {@code setSocketFactory(null)} and {@code setServerSocketFactory(null)} | ||||
|      * to reset the factories that may have been changed during the session, | ||||
|      * e.g. by {@link #execPROT(String)} | ||||
|      * @throws IOException If an error occurs while disconnecting. | ||||
|      * @since 3.0 | ||||
|      */ | ||||
|     @Override | ||||
|     public void disconnect() throws IOException | ||||
|     { | ||||
|         super.disconnect(); | ||||
|         if (plainSocket != null) { | ||||
|             plainSocket.close(); | ||||
|         } | ||||
|         setSocketFactory(null); | ||||
|         setServerSocketFactory(null); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Send the AUTH command with the specified mechanism. | ||||
|      * @param mechanism The mechanism name to send with the command. | ||||
|      * @return server reply. | ||||
|      * @throws IOException If an I/O error occurs while sending | ||||
|      * the command. | ||||
|      * @since 3.0 | ||||
|      */ | ||||
|     public int execAUTH(String mechanism) throws IOException | ||||
|     { | ||||
|         return sendCommand(CMD_AUTH, mechanism); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Send the ADAT command with the specified authentication data. | ||||
|      * @param data The data to send with the command. | ||||
|      * @return server reply. | ||||
|      * @throws IOException If an I/O error occurs while sending | ||||
|      * the command. | ||||
|      * @since 3.0 | ||||
|      */ | ||||
|     public int execADAT(byte[] data) throws IOException | ||||
|     { | ||||
|         if (data != null) | ||||
|         { | ||||
|             return sendCommand(CMD_ADAT, Base64.encodeBase64StringUnChunked(data)); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return sendCommand(CMD_ADAT); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Send the CCC command to the server. | ||||
|      * The CCC (Clear Command Channel) command causes the underlying {@link SSLSocket} instance  to be assigned | ||||
|      * to a plain {@link Socket} instances | ||||
|      * @return server reply. | ||||
|      * @throws IOException If an I/O error occurs while sending | ||||
|      * the command. | ||||
|      * @since 3.0 | ||||
|      */ | ||||
|     public int execCCC() throws IOException | ||||
|     { | ||||
|         int repCode = sendCommand(CMD_CCC); | ||||
| // This will be performed by sendCommand(String, String)
 | ||||
| //        if (FTPReply.isPositiveCompletion(repCode)) {
 | ||||
| //            _socket_.close();
 | ||||
| //            _socket_ = plainSocket;
 | ||||
| //            _controlInput_ = new BufferedReader(
 | ||||
| //                new InputStreamReader(
 | ||||
| //                    _socket_.getInputStream(), getControlEncoding()));
 | ||||
| //            _controlOutput_ = new BufferedWriter(
 | ||||
| //                new OutputStreamWriter(
 | ||||
| //                    _socket_.getOutputStream(), getControlEncoding()));
 | ||||
| //        }
 | ||||
|         return repCode; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Send the MIC command with the specified data. | ||||
|      * @param data The data to send with the command. | ||||
|      * @return server reply. | ||||
|      * @throws IOException If an I/O error occurs while sending | ||||
|      * the command. | ||||
|      * @since 3.0 | ||||
|      */ | ||||
|     public int execMIC(byte[] data) throws IOException | ||||
|     { | ||||
|         if (data != null) | ||||
|         { | ||||
|             return sendCommand(CMD_MIC, Base64.encodeBase64StringUnChunked(data)); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return sendCommand(CMD_MIC, ""); // perhaps "=" or just sendCommand(String)?
 | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Send the CONF command with the specified data. | ||||
|      * @param data The data to send with the command. | ||||
|      * @return server reply. | ||||
|      * @throws IOException If an I/O error occurs while sending | ||||
|      * the command. | ||||
|      * @since 3.0 | ||||
|      */ | ||||
|     public int execCONF(byte[] data) throws IOException | ||||
|     { | ||||
|         if (data != null) | ||||
|         { | ||||
|             return sendCommand(CMD_CONF, Base64.encodeBase64StringUnChunked(data)); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return sendCommand(CMD_CONF, ""); // perhaps "=" or just sendCommand(String)?
 | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Send the ENC command with the specified data. | ||||
|      * @param data The data to send with the command. | ||||
|      * @return server reply. | ||||
|      * @throws IOException If an I/O error occurs while sending | ||||
|      * the command. | ||||
|      * @since 3.0 | ||||
|      */ | ||||
|     public int execENC(byte[] data) throws IOException | ||||
|     { | ||||
|         if (data != null) | ||||
|         { | ||||
|             return sendCommand(CMD_ENC, Base64.encodeBase64StringUnChunked(data)); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return sendCommand(CMD_ENC, ""); // perhaps "=" or just sendCommand(String)?
 | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Parses the given ADAT response line and base64-decodes the data. | ||||
|      * @param reply The ADAT reply to parse. | ||||
|      * @return the data in the reply, base64-decoded. | ||||
|      * @since 3.0 | ||||
|      */ | ||||
|     public byte[] parseADATReply(String reply) | ||||
|     { | ||||
|         if (reply == null) { | ||||
|             return null; | ||||
|         } else { | ||||
|             return Base64.decodeBase64(extractPrefixedData("ADAT=", reply)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Extract the data from a reply with a prefix, e.g. PBSZ=1234 => 1234 | ||||
|      * @param prefix the prefix to find | ||||
|      * @param reply where to find the prefix | ||||
|      * @return the remainder of the string after the prefix, or null if the prefix was not present. | ||||
|      */ | ||||
|     private String extractPrefixedData(String prefix, String reply) { | ||||
|         int idx = reply.indexOf(prefix); | ||||
|         if (idx == -1) { | ||||
|             return null; | ||||
|         } | ||||
|         // N.B. Cannot use trim before substring as leading space would affect the offset.
 | ||||
|         return reply.substring(idx+prefix.length()).trim(); | ||||
|     } | ||||
| 
 | ||||
|     // DEPRECATED - for API compatibility only - DO NOT USE
 | ||||
| 
 | ||||
|     /** @deprecated - not used - may be removed in a future release */ | ||||
|     @Deprecated | ||||
|     public static String KEYSTORE_ALGORITHM; | ||||
| 
 | ||||
|     /** @deprecated - not used - may be removed in a future release */ | ||||
|     @Deprecated | ||||
|     public static String TRUSTSTORE_ALGORITHM; | ||||
| 
 | ||||
|     /** @deprecated - not used - may be removed in a future release */ | ||||
|     @Deprecated | ||||
|     public static String PROVIDER; | ||||
| 
 | ||||
|     /** @deprecated - not used - may be removed in a future release */ | ||||
|     @Deprecated | ||||
|     public static String STORE_TYPE; | ||||
| 
 | ||||
| } | ||||
| /* kate: indent-width 4; replace-tabs on; */ | ||||
| @ -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; | ||||
| 
 | ||||
| /** | ||||
|  * FTPS-specific commands. | ||||
|  * @since 2.0 | ||||
|  * @deprecated 3.0 DO NOT USE | ||||
|  */ | ||||
| @Deprecated | ||||
| 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; | ||||
| 
 | ||||
|     private static final String[] _commands = {"AUTH","ADAT","PBSZ","PROT","CCC"}; | ||||
| 
 | ||||
|     /** | ||||
|      * Retrieve the FTPS command string corresponding to a specified | ||||
|      * command code. | ||||
|      * | ||||
|      * @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]; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,74 @@ | ||||
| /* | ||||
|  * 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 javax.net.ServerSocketFactory; | ||||
| import javax.net.ssl.SSLContext; | ||||
| import javax.net.ssl.SSLServerSocket; | ||||
| 
 | ||||
| /** | ||||
|  * Server socket factory for FTPS connections. | ||||
|  * @since 2.2 | ||||
|  */ | ||||
| public class FTPSServerSocketFactory extends ServerSocketFactory { | ||||
| 
 | ||||
|     /** Factory for secure socket factories */ | ||||
|     private final SSLContext context; | ||||
| 
 | ||||
|     public FTPSServerSocketFactory(SSLContext context) { | ||||
|         this.context = context; | ||||
|     } | ||||
| 
 | ||||
|     // Override the default superclass method
 | ||||
|     @Override | ||||
|     public ServerSocket createServerSocket() throws IOException { | ||||
|         return init(this.context.getServerSocketFactory().createServerSocket()); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public ServerSocket createServerSocket(int port) throws IOException { | ||||
|         return init(this.context.getServerSocketFactory().createServerSocket(port)); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public ServerSocket createServerSocket(int port, int backlog) throws IOException { | ||||
|         return init(this.context.getServerSocketFactory().createServerSocket(port, backlog)); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public ServerSocket createServerSocket(int port, int backlog, InetAddress ifAddress) throws IOException { | ||||
|         return init(this.context.getServerSocketFactory().createServerSocket(port, backlog, ifAddress)); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Sets the socket so newly accepted connections will use SSL client mode. | ||||
|      * | ||||
|      * @param socket the SSLServerSocket to initialise | ||||
|      * @return the socket | ||||
|      * @throws ClassCastException if socket is not an instance of SSLServerSocket | ||||
|      */ | ||||
|     public ServerSocket init(ServerSocket socket) { | ||||
|         ((SSLServerSocket) socket).setUseClientMode(true); | ||||
|         return socket; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -0,0 +1,112 @@ | ||||
| /* | ||||
|  * 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.Socket; | ||||
| import java.net.UnknownHostException; | ||||
| 
 | ||||
| import javax.net.SocketFactory; | ||||
| import javax.net.ssl.SSLContext; | ||||
| 
 | ||||
| /** | ||||
|  * | ||||
|  * Implementation of org.apache.commons.net.SocketFactory | ||||
|  * | ||||
|  * @since 2.0 | ||||
|  */ | ||||
| public class FTPSSocketFactory extends SocketFactory { | ||||
| 
 | ||||
|     private final SSLContext context; | ||||
| 
 | ||||
|     public FTPSSocketFactory(SSLContext context) { | ||||
|         this.context = context; | ||||
|     } | ||||
| 
 | ||||
|     // Override the default implementation
 | ||||
|     @Override | ||||
|     public Socket createSocket() throws IOException{ | ||||
|         return this.context.getSocketFactory().createSocket(); | ||||
|     } | ||||
| 
 | ||||
|     @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); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     // DEPRECATED METHODS - for API compatibility only - DO NOT USE
 | ||||
| 
 | ||||
|     /** @param port the port | ||||
|      * @return  the socket | ||||
|      * @throws IOException on error | ||||
|      * @deprecated (2.2) use {@link FTPSServerSocketFactory#createServerSocket(int) instead} */ | ||||
|     @Deprecated | ||||
|     public java.net.ServerSocket createServerSocket(int port) throws IOException { | ||||
|         return this.init(this.context.getServerSocketFactory().createServerSocket(port)); | ||||
|     } | ||||
| 
 | ||||
|     /** @param port  the port | ||||
|      * @param backlog the backlog | ||||
|      * @return  the socket | ||||
|      * @throws IOException  on error | ||||
|      * @deprecated  (2.2) use {@link FTPSServerSocketFactory#createServerSocket(int, int) instead} */ | ||||
|     @Deprecated | ||||
|     public java.net.ServerSocket createServerSocket(int port, int backlog) throws IOException { | ||||
|         return this.init(this.context.getServerSocketFactory().createServerSocket(port, backlog)); | ||||
|     } | ||||
| 
 | ||||
|     /** @param port  the port | ||||
|      * @param backlog the backlog | ||||
|      * @param ifAddress the interface
 | ||||
|      * @return  the socket | ||||
|      * @throws IOException  on error | ||||
|      * @deprecated  (2.2) use {@link FTPSServerSocketFactory#createServerSocket(int, int, InetAddress) instead} */ | ||||
|     @Deprecated | ||||
|     public java.net.ServerSocket createServerSocket(int port, int backlog, InetAddress ifAddress) throws IOException { | ||||
|         return this.init(this.context.getServerSocketFactory().createServerSocket(port, backlog, ifAddress)); | ||||
|     } | ||||
| 
 | ||||
|     /** @param socket the socket | ||||
|      * @return the socket | ||||
|      * @throws IOException  on error | ||||
|      * @deprecated  (2.2) use {@link FTPSServerSocketFactory#init(java.net.ServerSocket)} */ | ||||
|     @Deprecated | ||||
|     public java.net.ServerSocket init(java.net.ServerSocket socket) throws IOException { | ||||
|         ((javax.net.ssl.SSLServerSocket) socket).setUseClientMode(true); | ||||
|         return socket; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,60 @@ | ||||
| /* | ||||
|  * 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.X509TrustManager; | ||||
| 
 | ||||
| /** | ||||
|  * Do not use. | ||||
|  * @since 2.0 | ||||
|  * @deprecated 3.0 use | ||||
|  * {@link org.apache.commons.net.util.TrustManagerUtils#getValidateServerCertificateTrustManager() | ||||
|  *  TrustManagerUtils#getValidateServerCertificateTrustManager()} instead | ||||
|  */ | ||||
| @Deprecated | ||||
| public class FTPSTrustManager implements X509TrustManager | ||||
| { | ||||
|     private static final X509Certificate[] EMPTY_X509CERTIFICATE_ARRAY = new X509Certificate[]{}; | ||||
| 
 | ||||
|     /** | ||||
|      * No-op | ||||
|      */ | ||||
|     @Override | ||||
|     public void checkClientTrusted(X509Certificate[] certificates, String authType) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void checkServerTrusted(X509Certificate[] certificates, String authType) throws CertificateException | ||||
|     { | ||||
|         for (X509Certificate certificate : certificates) | ||||
|         { | ||||
|             certificate.checkValidity(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public X509Certificate[] getAcceptedIssuers() | ||||
|     { | ||||
|         return EMPTY_X509CERTIFICATE_ARRAY; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,21 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  */ | ||||
| 
 | ||||
| /** | ||||
|  * FTP and FTPS support classes | ||||
|  */ | ||||
| package org.apache.commons.net.ftp; | ||||
| @ -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.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. | ||||
|  */ | ||||
| public class CompositeFileEntryParser extends FTPFileEntryParserImpl | ||||
| { | ||||
|     private final FTPFileEntryParser[] ftpFileEntryParsers; | ||||
|     private FTPFileEntryParser cachedFtpFileEntryParser; | ||||
| 
 | ||||
|     public CompositeFileEntryParser(FTPFileEntryParser[] ftpFileEntryParsers) | ||||
|     { | ||||
|         super(); | ||||
| 
 | ||||
|         this.cachedFtpFileEntryParser = null; | ||||
|         this.ftpFileEntryParsers = ftpFileEntryParsers; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public FTPFile parseFTPEntry(String listEntry) | ||||
|     { | ||||
|         if (cachedFtpFileEntryParser != null) | ||||
|         { | ||||
|             FTPFile matched = cachedFtpFileEntryParser.parseFTPEntry(listEntry); | ||||
|             if (matched != null) | ||||
|             { | ||||
|                 return matched; | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             for (FTPFileEntryParser ftpFileEntryParser : ftpFileEntryParsers) | ||||
|             { | ||||
|                 FTPFile matched = ftpFileEntryParser.parseFTPEntry(listEntry); | ||||
|                 if (matched != null) | ||||
|                 { | ||||
|                     cachedFtpFileEntryParser = ftpFileEntryParser; | ||||
|                     return matched; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,128 @@ | ||||
| /* | ||||
|  * 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> | ||||
|  * This class also implements the {@link Configurable Configurable} | ||||
|  * interface to allow the parser to be configured from the outside. | ||||
|  * | ||||
|  * @since 1.4 | ||||
|  */ | ||||
| public abstract class ConfigurableFTPFileEntryParserImpl | ||||
| extends RegexFTPFileEntryParserImpl | ||||
| implements Configurable | ||||
| { | ||||
| 
 | ||||
|     private final FTPTimestampParser timestampParser; | ||||
| 
 | ||||
|     /** | ||||
|      * constructor for this abstract class. | ||||
|      * @param regex  Regular expression used main parsing of the | ||||
|      * file listing. | ||||
|      */ | ||||
|     public ConfigurableFTPFileEntryParserImpl(String regex) | ||||
|     { | ||||
|         super(regex); | ||||
|         this.timestampParser = new FTPTimestampParserImpl(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * constructor for this abstract class. | ||||
|      * @param regex  Regular expression used main parsing of the | ||||
|      * file listing. | ||||
|      * @param flags the flags to apply, see | ||||
|      * {@link java.util.regex.Pattern#compile(String, int) Pattern#compile(String, int)}. Use 0 for none. | ||||
|      * @since 3.4 | ||||
|      */ | ||||
|     public ConfigurableFTPFileEntryParserImpl(String regex, int flags) | ||||
|     { | ||||
|         super(regex, flags); | ||||
|         this.timestampParser = new FTPTimestampParserImpl(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * This method is called by the concrete parsers to delegate | ||||
|      * timestamp parsing to the timestamp parser. | ||||
|      * | ||||
|      * @param timestampStr the timestamp string pulled from the | ||||
|      * file listing by the regular expression parser, to be submitted | ||||
|      * to the <code>timestampParser</code> for extracting the timestamp. | ||||
|      * @return a <code>java.util.Calendar</code> containing results of the | ||||
|      * timestamp parse. | ||||
|      * @throws ParseException on parse error | ||||
|      */ | ||||
|     public Calendar parseTimestamp(String timestampStr) throws ParseException { | ||||
|         return this.timestampParser.parseTimestamp(timestampStr); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * Implementation of the {@link  Configurable  Configurable} | ||||
|      * interface. Configures this parser by delegating to the | ||||
|      * underlying Configurable FTPTimestampParser implementation, ' | ||||
|      * passing it the supplied {@link  FTPClientConfig FTPClientConfig} | ||||
|      * if that is non-null or a default configuration defined by | ||||
|      * each concrete subclass. | ||||
|      * | ||||
|      * @param config the configuration to be used to configure this parser. | ||||
|      * If it is null, a default configuration defined by | ||||
|      * each concrete subclass is used instead. | ||||
|      */ | ||||
|     @Override | ||||
|     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(); | ||||
| } | ||||
| @ -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.util.regex.Pattern; | ||||
| 
 | ||||
| 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 | ||||
| { | ||||
| 
 | ||||
|     // Match a plain Java Identifier
 | ||||
|     private static final String JAVA_IDENTIFIER = "\\p{javaJavaIdentifierStart}(\\p{javaJavaIdentifierPart})*"; | ||||
|     // Match a qualified name, e.g. a.b.c.Name - but don't allow the default package as that would allow "VMS"/"UNIX" etc.
 | ||||
|     private static final String JAVA_QUALIFIED_NAME  = "("+JAVA_IDENTIFIER+"\\.)+"+JAVA_IDENTIFIER; | ||||
|     // Create the pattern, as it will be reused many times
 | ||||
|     private static final Pattern JAVA_QUALIFIED_NAME_PATTERN = Pattern.compile(JAVA_QUALIFIED_NAME); | ||||
| 
 | ||||
|     /** | ||||
|      * 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 (default package is not allowed) 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. | ||||
|      * | ||||
|      * @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_AS400 AS/400}</li> | ||||
|      *               <li>{@link FTPClientConfig#SYST_VMS VMS}</li> | ||||
|      *               <li>{@link FTPClientConfig#SYST_MVS MVS}</li> | ||||
|      *               <li>{@link FTPClientConfig#SYST_NETWARE NETWARE}</li> | ||||
|      *               <li>{@link FTPClientConfig#SYST_L8 TYPE:L8}</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 | ||||
|      */ | ||||
|     @Override | ||||
|     public FTPFileEntryParser createFileEntryParser(String key) | ||||
|     { | ||||
|         if (key == null) { | ||||
|             throw new ParserInitializationException("Parser key cannot be null"); | ||||
|         } | ||||
|         return createFileEntryParser(key, null); | ||||
|     } | ||||
| 
 | ||||
|     // Common method to process both key and config parameters.
 | ||||
|     private FTPFileEntryParser createFileEntryParser(String key, FTPClientConfig config) { | ||||
|         FTPFileEntryParser parser = null; | ||||
| 
 | ||||
|         // Is the key a possible class name?
 | ||||
|         if (JAVA_QUALIFIED_NAME_PATTERN.matcher(key).matches()) { | ||||
|             try | ||||
|             { | ||||
|                 Class<?> parserClass = Class.forName(key); | ||||
|                 try { | ||||
|                     parser = (FTPFileEntryParser) parserClass.newInstance(); | ||||
|                 } catch (ClassCastException e) { | ||||
|                     throw new ParserInitializationException(parserClass.getName() | ||||
|                         + " does not implement the interface " | ||||
|                         + "org.apache.commons.net.ftp.FTPFileEntryParser.", e); | ||||
|                 } catch (Exception e) { | ||||
|                     throw new ParserInitializationException("Error initializing parser", e); | ||||
|                 } catch (ExceptionInInitializerError e) { | ||||
|                     throw new ParserInitializationException("Error initializing parser", e); | ||||
|                 } | ||||
|             } catch (ClassNotFoundException e) { | ||||
|                 // OK, assume it is an alias
 | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (parser == null) { // Now try for aliases
 | ||||
|             String ukey = key.toUpperCase(java.util.Locale.ENGLISH); | ||||
|             if (ukey.indexOf(FTPClientConfig.SYST_UNIX_TRIM_LEADING) >= 0) | ||||
|             { | ||||
|                 parser = new UnixFTPEntryParser(config, true); | ||||
|             } | ||||
|             // must check this after SYST_UNIX_TRIM_LEADING as it is a substring of it
 | ||||
|             else if (ukey.indexOf(FTPClientConfig.SYST_UNIX) >= 0) | ||||
|             { | ||||
|                 parser = new UnixFTPEntryParser(config, false); | ||||
|             } | ||||
|             else if (ukey.indexOf(FTPClientConfig.SYST_VMS) >= 0) | ||||
|             { | ||||
|                 parser = new VMSVersioningFTPEntryParser(config); | ||||
|             } | ||||
|             else if (ukey.indexOf(FTPClientConfig.SYST_NT) >= 0) | ||||
|             { | ||||
|                 parser = createNTFTPEntryParser(config); | ||||
|             } | ||||
|             else if (ukey.indexOf(FTPClientConfig.SYST_OS2) >= 0) | ||||
|             { | ||||
|                 parser = new OS2FTPEntryParser(config); | ||||
|             } | ||||
|             else if (ukey.indexOf(FTPClientConfig.SYST_OS400) >= 0 || | ||||
|                     ukey.indexOf(FTPClientConfig.SYST_AS400) >= 0) | ||||
|             { | ||||
|                 parser = createOS400FTPEntryParser(config); | ||||
|             } | ||||
|             else if (ukey.indexOf(FTPClientConfig.SYST_MVS) >= 0) | ||||
|             { | ||||
|                 parser = new MVSFTPEntryParser(); // Does not currently support config parameter
 | ||||
|             } | ||||
|             else if (ukey.indexOf(FTPClientConfig.SYST_NETWARE) >= 0) | ||||
|             { | ||||
|                 parser = new NetwareFTPEntryParser(config); | ||||
|             } | ||||
|             else if (ukey.indexOf(FTPClientConfig.SYST_MACOS_PETER) >= 0) | ||||
|             { | ||||
|                 parser = new MacOsPeterFTPEntryParser(config); | ||||
|             } | ||||
|             else if (ukey.indexOf(FTPClientConfig.SYST_L8) >= 0) | ||||
|             { | ||||
|                 // L8 normally means Unix, but move it to the end for some L8 systems that aren't.
 | ||||
|                 // This check should be last!
 | ||||
|                 parser = new UnixFTPEntryParser(config); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 throw new ParserInitializationException("Unknown parser type: " + key); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (parser instanceof Configurable) { | ||||
|             ((Configurable)parser).configure(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. | ||||
|      * @throws ParserInitializationException | ||||
|      *                   Thrown on any exception in instantiation | ||||
|      * @throws NullPointerException if {@code config} is {@code null} | ||||
|      * @since 1.4 | ||||
|      */ | ||||
|     @Override | ||||
|     public FTPFileEntryParser createFileEntryParser(FTPClientConfig config) | ||||
|     throws ParserInitializationException | ||||
|     { | ||||
|         String key = config.getServerSystemKey(); | ||||
|         return createFileEntryParser(key, config); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     public FTPFileEntryParser createUnixFTPEntryParser() | ||||
|     { | ||||
|         return new UnixFTPEntryParser(); | ||||
|     } | ||||
| 
 | ||||
|     public FTPFileEntryParser createVMSVersioningFTPEntryParser() | ||||
|     { | ||||
|         return new VMSVersioningFTPEntryParser(); | ||||
|     } | ||||
| 
 | ||||
|     public FTPFileEntryParser createNetwareFTPEntryParser() { | ||||
|         return new NetwareFTPEntryParser(); | ||||
|     } | ||||
| 
 | ||||
|     public FTPFileEntryParser createNTFTPEntryParser() | ||||
|     { | ||||
|         return createNTFTPEntryParser(null); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Creates an NT FTP parser: if the config exists, and the system key equals | ||||
|      * {@link FTPClientConfig.SYST_NT} then a plain {@link NTFTPEntryParser} is used, | ||||
|      * otherwise a composite of {@link NTFTPEntryParser} and {@link UnixFTPEntryParser} is used. | ||||
|      * @param config the config to use, may be {@code null} | ||||
|      * @return the parser | ||||
|      */ | ||||
|     private FTPFileEntryParser createNTFTPEntryParser(FTPClientConfig config) | ||||
|     { | ||||
|         if (config != null && FTPClientConfig.SYST_NT.equals( | ||||
|                 config.getServerSystemKey())) | ||||
|         { | ||||
|             return new NTFTPEntryParser(config); | ||||
|         } else { | ||||
|             // clone the config as it may be changed by the parsers (NET-602)
 | ||||
|             final FTPClientConfig config2 =  (config != null) ? new FTPClientConfig(config) : null; | ||||
|             return new CompositeFileEntryParser(new FTPFileEntryParser[] | ||||
|                    { | ||||
|                        new NTFTPEntryParser(config), | ||||
|                        new UnixFTPEntryParser(config2, | ||||
|                                config2 != null && FTPClientConfig.SYST_UNIX_TRIM_LEADING.equals(config2.getServerSystemKey())) | ||||
|                    }); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|      public FTPFileEntryParser createOS2FTPEntryParser() | ||||
|     { | ||||
|         return new OS2FTPEntryParser(); | ||||
|     } | ||||
| 
 | ||||
|     public FTPFileEntryParser createOS400FTPEntryParser() | ||||
|     { | ||||
|         return createOS400FTPEntryParser(null); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Creates an OS400 FTP parser: if the config exists, and the system key equals | ||||
|      * {@link FTPClientConfig.SYST_OS400} then a plain {@link OS400FTPEntryParser} is used, | ||||
|      * otherwise a composite of {@link OS400FTPEntryParser} and {@link UnixFTPEntryParser} is used. | ||||
|      * @param config the config to use, may be {@code null} | ||||
|      * @return the parser | ||||
|      */ | ||||
|     private FTPFileEntryParser createOS400FTPEntryParser(FTPClientConfig config) | ||||
|         { | ||||
|         if (config != null && | ||||
|                 FTPClientConfig.SYST_OS400.equals(config.getServerSystemKey())) | ||||
|         { | ||||
|             return new OS400FTPEntryParser(config); | ||||
|         } else { | ||||
|             // clone the config as it may be changed by the parsers (NET-602)
 | ||||
|             final FTPClientConfig config2 =  (config != null) ? new FTPClientConfig(config) : null; | ||||
|             return new CompositeFileEntryParser(new FTPFileEntryParser[] | ||||
|                 { | ||||
|                     new OS400FTPEntryParser(config), | ||||
|                     new UnixFTPEntryParser(config2, | ||||
|                             config2 != null && FTPClientConfig.SYST_UNIX_TRIM_LEADING.equals(config2.getServerSystemKey())) | ||||
|                 }); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public FTPFileEntryParser createMVSEntryParser() | ||||
|     { | ||||
|         return new MVSFTPEntryParser(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| @ -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 1741829 2016-05-01 00:24:44Z sebb $ | ||||
|  * @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*" // 12
 | ||||
|         + "(\\S+)\\s*" // 13
 | ||||
|         + "(\\S*)\\s*" // 14 user
 | ||||
|         + "(\\d*)\\s*" // 15 group
 | ||||
|         + "(\\d*)\\s*" // 16 filesize
 | ||||
|         + MONTHS       // 17 month
 | ||||
|         + "\\s*"       // TODO should the space be optional?
 | ||||
|         // TODO \\d* should be \\d? surely ? Otherwise 01111 is allowed
 | ||||
|         + "((?:[012]\\d*)|(?:3[01]))\\s*" // 18 date [012]\d* or 3[01]
 | ||||
|         + "((\\d\\d\\d\\d)|((?:[01]\\d)|(?:2[0123])):([012345]\\d))\\s" | ||||
|         // 20 \d\d\d\d  = year  OR
 | ||||
|         // 21 [01]\d or 2[0123] hour + ':'
 | ||||
|         // 22 [012345]\d = minute
 | ||||
|         + "(\\S*)(\\s*.*)"; // 23 name
 | ||||
| 
 | ||||
|     /** | ||||
|      * 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 | ||||
|      */ | ||||
|     @Override | ||||
|     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); | ||||
| 
 | ||||
|             int pos = MONTHS.indexOf(mo); | ||||
|             int month = pos / 4; | ||||
|             final int missingUnit; // the first missing unit
 | ||||
|             try | ||||
|             { | ||||
| 
 | ||||
|                 if (yr != null) | ||||
|                 { | ||||
|                     // it's a year; there are no hours and minutes
 | ||||
|                     cal.set(Calendar.YEAR, Integer.parseInt(yr)); | ||||
|                     missingUnit = Calendar.HOUR_OF_DAY; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     // it must be  hour/minute or we wouldn't have matched
 | ||||
|                     missingUnit = Calendar.SECOND; | ||||
|                     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.DAY_OF_MONTH, Integer.parseInt(da)); | ||||
|                 cal.clear(missingUnit); | ||||
|                 file.setTimestamp(cal); | ||||
|             } | ||||
|             catch (NumberFormatException e) | ||||
|             { | ||||
|                 // do nothing, date will be uninitialized
 | ||||
|             } | ||||
|             file.setName(name); | ||||
| 
 | ||||
|             return file; | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
| } | ||||
| @ -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. | ||||
|      * @throws 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. | ||||
|      * @throws ParserInitializationException | ||||
|      *                   Thrown on any exception in instantiation | ||||
|      * @since 1.4 | ||||
|      */ | ||||
|     public FTPFileEntryParser createFileEntryParser(FTPClientConfig config) | ||||
|         throws ParserInitializationException; | ||||
| 
 | ||||
| } | ||||
| @ -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; | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,406 @@ | ||||
| /* | ||||
|  * 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 | ||||
| { | ||||
| 
 | ||||
| 
 | ||||
|     /** The date format for all dates, except possibly recent dates. Assumed to include the year. */ | ||||
|     private SimpleDateFormat defaultDateFormat; | ||||
|     /* The index in CALENDAR_UNITS of the smallest time unit in defaultDateFormat */ | ||||
|     private int defaultDateSmallestUnitIndex; | ||||
| 
 | ||||
|     /** The format used for recent dates (which don't have the year). May be null. */ | ||||
|     private SimpleDateFormat recentDateFormat; | ||||
|     /* The index in CALENDAR_UNITS of the smallest time unit in recentDateFormat */ | ||||
|     private int recentDateSmallestUnitIndex; | ||||
| 
 | ||||
|     private boolean lenientFutureDates = false; | ||||
| 
 | ||||
|     /* | ||||
|      * List of units in order of increasing significance. | ||||
|      * This allows the code to clear all units in the Calendar until it | ||||
|      * reaches the least significant unit in the parse string. | ||||
|      * The date formats are analysed to find the least significant | ||||
|      * unit (e.g. Minutes or Milliseconds) and the appropriate index to | ||||
|      * the array is saved. | ||||
|      * This is done by searching the array for the unit specifier, | ||||
|      * and returning the index. When clearing the Calendar units, | ||||
|      * the code loops through the array until the previous entry. | ||||
|      * e.g. for MINUTE it would clear MILLISECOND and SECOND | ||||
|      */ | ||||
|     private static final int[] CALENDAR_UNITS = { | ||||
|         Calendar.MILLISECOND, | ||||
|         Calendar.SECOND, | ||||
|         Calendar.MINUTE, | ||||
|         Calendar.HOUR_OF_DAY, | ||||
|         Calendar.DAY_OF_MONTH, | ||||
|         Calendar.MONTH, | ||||
|         Calendar.YEAR}; | ||||
| 
 | ||||
|     /* | ||||
|      * Return the index to the array representing the least significant | ||||
|      * unit found in the date format. | ||||
|      * Default is 0 (to avoid dropping precision) | ||||
|      */ | ||||
|     private static int getEntry(SimpleDateFormat dateFormat) { | ||||
|         if (dateFormat == null) { | ||||
|             return 0; | ||||
|         } | ||||
|         final String FORMAT_CHARS="SsmHdM"; | ||||
|         final String pattern = dateFormat.toPattern(); | ||||
|         for(char ch : FORMAT_CHARS.toCharArray()) { | ||||
|             if (pattern.indexOf(ch) != -1){ // found the character
 | ||||
|                 switch(ch) { | ||||
|                 case 'S': | ||||
|                     return indexOf(Calendar.MILLISECOND); | ||||
|                 case 's': | ||||
|                     return indexOf(Calendar.SECOND); | ||||
|                 case 'm': | ||||
|                     return indexOf(Calendar.MINUTE); | ||||
|                 case 'H': | ||||
|                     return indexOf(Calendar.HOUR_OF_DAY); | ||||
|                 case 'd': | ||||
|                     return indexOf(Calendar.DAY_OF_MONTH); | ||||
|                 case 'M': | ||||
|                     return indexOf(Calendar.MONTH); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     /* | ||||
|      * Find the entry in the CALENDAR_UNITS array. | ||||
|      */ | ||||
|     private static int indexOf(int calendarUnit) { | ||||
|         int i; | ||||
|         for(i = 0; i <CALENDAR_UNITS.length; i++) { | ||||
|             if (calendarUnit == CALENDAR_UNITS[i]) { | ||||
|                 return i; | ||||
|             } | ||||
|         } | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     /* | ||||
|      * Sets the Calendar precision (used by FTPFile#toFormattedDate) by clearing | ||||
|      * the immediately preceeding unit (if any). | ||||
|      * Unfortunately the clear(int) method results in setting all other units. | ||||
|      */ | ||||
|     private static void setPrecision(int index, Calendar working) { | ||||
|         if (index <= 0) { // e.g. MILLISECONDS
 | ||||
|             return; | ||||
|         } | ||||
|         final int field = CALENDAR_UNITS[index-1]; | ||||
|         // Just in case the analysis is wrong, stop clearing if
 | ||||
|         // field value is not the default.
 | ||||
|         final int value = working.get(field); | ||||
|         if (value != 0) { // don't reset if it has a value
 | ||||
| //            new Throwable("Unexpected value "+value).printStackTrace(); // DEBUG
 | ||||
|         } else { | ||||
|             working.clear(field); // reset just the required field
 | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * The only constructor for this class. | ||||
|      */ | ||||
|     public FTPTimestampParserImpl() { | ||||
|         setDefaultDateFormat(DEFAULT_SDF, null); | ||||
|         setRecentDateFormat(DEFAULT_RECENT_SDF, null); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 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 assumes that the server time is the same as the local time. | ||||
|      * | ||||
|      * @see FTPTimestampParserImpl#parseTimestamp(String, Calendar) | ||||
|      * | ||||
|      * @param timestampStr The timestamp to be parsed | ||||
|      * @return a Calendar with the parsed timestamp | ||||
|      */ | ||||
|     @Override | ||||
|     public Calendar parseTimestamp(String timestampStr) throws ParseException { | ||||
|         Calendar now = Calendar.getInstance(); | ||||
|         return parseTimestamp(timestampStr, now); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 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 FTPTimestampParser#parseTimestamp(String) | ||||
|      * @param timestampStr The timestamp to be parsed | ||||
|      * @param serverTime The current time for the server | ||||
|      * @return the calendar | ||||
|      * @throws ParseException if timestamp cannot be parsed | ||||
|      * @since 1.5 | ||||
|      */ | ||||
|     public Calendar parseTimestamp(String timestampStr, Calendar serverTime) throws ParseException { | ||||
|         Calendar working = (Calendar) serverTime.clone(); | ||||
|         working.setTimeZone(getServerTimeZone()); // is this needed?
 | ||||
| 
 | ||||
|         Date parsed = null; | ||||
| 
 | ||||
|         if (recentDateFormat != null) { | ||||
|             Calendar now = (Calendar) serverTime.clone();// Copy this, because we may change it
 | ||||
|             now.setTimeZone(this.getServerTimeZone()); | ||||
|             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 => NET-83)
 | ||||
|                 now.add(Calendar.DAY_OF_MONTH, 1); | ||||
|             } | ||||
|             // The Java SimpleDateFormat class uses the epoch year 1970 if not present in the input
 | ||||
|             // As 1970 was not a leap year, it cannot parse "Feb 29" correctly.
 | ||||
|             // Java 1.5+ returns Mar 1 1970
 | ||||
|             // Temporarily add the current year to the short date time
 | ||||
|             // to cope with short-date leap year strings.
 | ||||
|             // Since Feb 29 is more that 6 months from the end of the year, this should be OK for
 | ||||
|             // all instances of short dates which are +- 6 months from current date.
 | ||||
|             // TODO this won't always work for systems that use short dates +0/-12months
 | ||||
|             // e.g. if today is Jan 1 2001 and the short date is Feb 29
 | ||||
|             String year = Integer.toString(now.get(Calendar.YEAR)); | ||||
|             String timeStampStrPlusYear = timestampStr + " " + year; | ||||
|             SimpleDateFormat hackFormatter = new SimpleDateFormat(recentDateFormat.toPattern() + " yyyy", | ||||
|                     recentDateFormat.getDateFormatSymbols()); | ||||
|             hackFormatter.setLenient(false); | ||||
|             hackFormatter.setTimeZone(recentDateFormat.getTimeZone()); | ||||
|             ParsePosition pp = new ParsePosition(0); | ||||
|             parsed = hackFormatter.parse(timeStampStrPlusYear, pp); | ||||
|             // Check if we parsed the full string, if so it must have been a short date originally
 | ||||
|             if (parsed != null && pp.getIndex() == timeStampStrPlusYear.length()) { | ||||
|                 working.setTime(parsed); | ||||
|                 if (working.after(now)) { // must have been last year instead
 | ||||
|                     working.add(Calendar.YEAR, -1); | ||||
|                 } | ||||
|                 setPrecision(recentDateSmallestUnitIndex, working); | ||||
|                 return working; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         ParsePosition 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 '"+timestampStr+"' could not be parsed using a server time of " | ||||
|                         +serverTime.getTime().toString(), | ||||
|                     pp.getErrorIndex()); | ||||
|         } | ||||
|         setPrecision(defaultDateSmallestUnitIndex, working); | ||||
|         return working; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @return Returns the defaultDateFormat. | ||||
|      */ | ||||
|     public SimpleDateFormat getDefaultDateFormat() { | ||||
|         return defaultDateFormat; | ||||
|     } | ||||
|     /** | ||||
|      * @return Returns the defaultDateFormat pattern string. | ||||
|      */ | ||||
|     public String getDefaultDateFormatString() { | ||||
|         return defaultDateFormat.toPattern(); | ||||
|     } | ||||
|     /** | ||||
|      * @param format The defaultDateFormat to be set. | ||||
|      * @param dfs the symbols to use (may be null) | ||||
|      */ | ||||
|     private void setDefaultDateFormat(String format, DateFormatSymbols dfs) { | ||||
|         if (format != null) { | ||||
|             if (dfs != null) { | ||||
|                 this.defaultDateFormat = new SimpleDateFormat(format, dfs); | ||||
|             } else { | ||||
|                 this.defaultDateFormat = new SimpleDateFormat(format); | ||||
|             } | ||||
|             this.defaultDateFormat.setLenient(false); | ||||
|         } else { | ||||
|             this.defaultDateFormat = null; | ||||
|         } | ||||
|         this.defaultDateSmallestUnitIndex = getEntry(this.defaultDateFormat); | ||||
|     } | ||||
|     /** | ||||
|      * @return Returns the recentDateFormat. | ||||
|      */ | ||||
|     public SimpleDateFormat getRecentDateFormat() { | ||||
|         return recentDateFormat; | ||||
|     } | ||||
|     /** | ||||
|      * @return Returns the recentDateFormat. | ||||
|      */ | ||||
|     public String getRecentDateFormatString() { | ||||
|         return recentDateFormat.toPattern(); | ||||
|     } | ||||
|     /** | ||||
|      * @param format The recentDateFormat to set. | ||||
|      * @param dfs the symbols to use (may be null) | ||||
|      */ | ||||
|     private void setRecentDateFormat(String format, DateFormatSymbols dfs) { | ||||
|         if (format != null) { | ||||
|             if (dfs != null) { | ||||
|                 this.recentDateFormat = new SimpleDateFormat(format, dfs); | ||||
|             } else { | ||||
|                 this.recentDateFormat = new SimpleDateFormat(format); | ||||
|             } | ||||
|             this.recentDateFormat.setLenient(false); | ||||
|         } else { | ||||
|             this.recentDateFormat = null; | ||||
|         } | ||||
|         this.recentDateSmallestUnitIndex = getEntry(this.recentDateFormat); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @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(String) defaultDateFormat} | ||||
|      * and optionally the {@link  FTPClientConfig#setRecentDateFormatStr(String) recentDateFormat} | ||||
|      * to values supplied in the config based on month names configured as follows: | ||||
|      * </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> | ||||
|      * 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> | ||||
|      */ | ||||
|     @Override | ||||
|     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(); | ||||
|         setRecentDateFormat(recentFormatString, dfs); | ||||
| 
 | ||||
|         String defaultFormatString = config.getDefaultDateFormatStr(); | ||||
|         if (defaultFormatString == null) { | ||||
|             throw new IllegalArgumentException("defaultFormatString cannot be null"); | ||||
|         } | ||||
|         setDefaultDateFormat(defaultFormatString, dfs); | ||||
| 
 | ||||
|         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; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,269 @@ | ||||
| /* | ||||
|  * 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.ParsePosition; | ||||
| import java.text.SimpleDateFormat; | ||||
| import java.util.Calendar; | ||||
| import java.util.Date; | ||||
| import java.util.GregorianCalendar; | ||||
| import java.util.HashMap; | ||||
| import java.util.Locale; | ||||
| import java.util.TimeZone; | ||||
| 
 | ||||
| import org.apache.commons.net.ftp.FTPFile; | ||||
| import org.apache.commons.net.ftp.FTPFileEntryParserImpl; | ||||
| 
 | ||||
| /** | ||||
|  * Parser class for MSLT and MLSD replies. See RFC 3659. | ||||
|  * <p> | ||||
|  * Format is as follows: | ||||
|  * <pre> | ||||
|  * entry            = [ facts ] SP pathname | ||||
|  * facts            = 1*( fact ";" ) | ||||
|  * fact             = factname "=" value | ||||
|  * factname         = "Size" / "Modify" / "Create" / | ||||
|  *                    "Type" / "Unique" / "Perm" / | ||||
|  *                    "Lang" / "Media-Type" / "CharSet" / | ||||
|  * os-depend-fact / local-fact | ||||
|  * os-depend-fact   = {IANA assigned OS name} "." token | ||||
|  * local-fact       = "X." token | ||||
|  * value            = *SCHAR | ||||
|  * | ||||
|  * Sample os-depend-fact: | ||||
|  * UNIX.group=0;UNIX.mode=0755;UNIX.owner=0; | ||||
|  * </pre> | ||||
|  * A single control response entry (MLST) is returned with a leading space; | ||||
|  * multiple (data) entries are returned without any leading spaces. | ||||
|  * The parser requires that the leading space from the MLST entry is removed. | ||||
|  * MLSD entries can begin with a single space if there are no facts. | ||||
|  * | ||||
|  * @since 3.0 | ||||
|  */ | ||||
| public class MLSxEntryParser extends FTPFileEntryParserImpl | ||||
| { | ||||
|     // This class is immutable, so a single instance can be shared.
 | ||||
|     private static final MLSxEntryParser PARSER = new MLSxEntryParser(); | ||||
| 
 | ||||
|     private static final HashMap<String, Integer> TYPE_TO_INT = new HashMap<String, Integer>(); | ||||
|     static { | ||||
|         TYPE_TO_INT.put("file", Integer.valueOf(FTPFile.FILE_TYPE)); | ||||
|         TYPE_TO_INT.put("cdir", Integer.valueOf(FTPFile.DIRECTORY_TYPE)); // listed directory
 | ||||
|         TYPE_TO_INT.put("pdir", Integer.valueOf(FTPFile.DIRECTORY_TYPE)); // a parent dir
 | ||||
|         TYPE_TO_INT.put("dir", Integer.valueOf(FTPFile.DIRECTORY_TYPE)); // dir or sub-dir
 | ||||
|     } | ||||
| 
 | ||||
|     private static int UNIX_GROUPS[] = { // Groups in order of mode digits
 | ||||
|         FTPFile.USER_ACCESS, | ||||
|         FTPFile.GROUP_ACCESS, | ||||
|         FTPFile.WORLD_ACCESS, | ||||
|     }; | ||||
| 
 | ||||
|     private static int UNIX_PERMS[][] = { // perm bits, broken down by octal int value
 | ||||
| /* 0 */  {}, | ||||
| /* 1 */  {FTPFile.EXECUTE_PERMISSION}, | ||||
| /* 2 */  {FTPFile.WRITE_PERMISSION}, | ||||
| /* 3 */  {FTPFile.EXECUTE_PERMISSION, FTPFile.WRITE_PERMISSION}, | ||||
| /* 4 */  {FTPFile.READ_PERMISSION}, | ||||
| /* 5 */  {FTPFile.READ_PERMISSION, FTPFile.EXECUTE_PERMISSION}, | ||||
| /* 6 */  {FTPFile.READ_PERMISSION, FTPFile.WRITE_PERMISSION}, | ||||
| /* 7 */  {FTPFile.READ_PERMISSION, FTPFile.WRITE_PERMISSION, FTPFile.EXECUTE_PERMISSION}, | ||||
|     }; | ||||
| 
 | ||||
|     /** | ||||
|      * Create the parser for MSLT and MSLD listing entries | ||||
|      * This class is immutable, so one can use {@link #getInstance()} instead. | ||||
|      */ | ||||
|     public MLSxEntryParser() | ||||
|     { | ||||
|         super(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public FTPFile parseFTPEntry(String entry) { | ||||
|         if (entry.startsWith(" ")) {// leading space means no facts are present
 | ||||
|             if (entry.length() > 1) { // is there a path name?
 | ||||
|                 FTPFile file = new FTPFile(); | ||||
|                 file.setRawListing(entry); | ||||
|                 file.setName(entry.substring(1)); | ||||
|                 return file; | ||||
|             } else { | ||||
|                 return null; // Invalid - no pathname
 | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
|         String parts[] = entry.split(" ",2); // Path may contain space
 | ||||
|         if (parts.length != 2 || parts[1].length() == 0) { | ||||
|             return null; // no space found or no file name
 | ||||
|         } | ||||
|         final String factList = parts[0]; | ||||
|         if (!factList.endsWith(";")) { | ||||
|             return null; | ||||
|         } | ||||
|         FTPFile file = new FTPFile(); | ||||
|         file.setRawListing(entry); | ||||
|         file.setName(parts[1]); | ||||
|         String[] facts = factList.split(";"); | ||||
|         boolean hasUnixMode = parts[0].toLowerCase(Locale.ENGLISH).contains("unix.mode="); | ||||
|         for(String fact : facts) { | ||||
|             String []factparts = fact.split("=", -1); // Don't drop empty values
 | ||||
| // Sample missing permission
 | ||||
| // drwx------   2 mirror   mirror       4096 Mar 13  2010 subversion
 | ||||
| // modify=20100313224553;perm=;type=dir;unique=811U282598;UNIX.group=500;UNIX.mode=0700;UNIX.owner=500; subversion
 | ||||
|             if (factparts.length != 2) { | ||||
|                 return null; // invalid - there was no "=" sign
 | ||||
|             } | ||||
|             String factname = factparts[0].toLowerCase(Locale.ENGLISH); | ||||
|             String factvalue = factparts[1]; | ||||
|             if (factvalue.length() == 0) { | ||||
|                 continue; // nothing to see here
 | ||||
|             } | ||||
|             String valueLowerCase = factvalue.toLowerCase(Locale.ENGLISH); | ||||
|             if ("size".equals(factname)) { | ||||
|                 file.setSize(Long.parseLong(factvalue)); | ||||
|             } | ||||
|             else if ("sizd".equals(factname)) { // Directory size
 | ||||
|                 file.setSize(Long.parseLong(factvalue)); | ||||
|             } | ||||
|             else if ("modify".equals(factname)) { | ||||
|                 final Calendar parsed = parseGMTdateTime(factvalue); | ||||
|                 if (parsed == null) { | ||||
|                     return null; | ||||
|                 } | ||||
|                 file.setTimestamp(parsed); | ||||
|             } | ||||
|             else if ("type".equals(factname)) { | ||||
|                     Integer intType = TYPE_TO_INT.get(valueLowerCase); | ||||
|                     if (intType == null) { | ||||
|                         file.setType(FTPFile.UNKNOWN_TYPE); | ||||
|                     } else { | ||||
|                         file.setType(intType.intValue()); | ||||
|                     } | ||||
|             } | ||||
|             else if (factname.startsWith("unix.")) { | ||||
|                 String unixfact = factname.substring("unix.".length()).toLowerCase(Locale.ENGLISH); | ||||
|                 if ("group".equals(unixfact)){ | ||||
|                     file.setGroup(factvalue); | ||||
|                 } else if ("owner".equals(unixfact)){ | ||||
|                     file.setUser(factvalue); | ||||
|                 } else if ("mode".equals(unixfact)){ // e.g. 0[1]755
 | ||||
|                     int off = factvalue.length()-3; // only parse last 3 digits
 | ||||
|                     for(int i=0; i < 3; i++){ | ||||
|                         int ch = factvalue.charAt(off+i)-'0'; | ||||
|                         if (ch >= 0 && ch <= 7) { // Check it's valid octal
 | ||||
|                             for(int p : UNIX_PERMS[ch]) { | ||||
|                                 file.setPermission(UNIX_GROUPS[i], p, true); | ||||
|                             } | ||||
|                         } else { | ||||
|                             // TODO should this cause failure, or can it be reported somehow?
 | ||||
|                         } | ||||
|                     } // digits
 | ||||
|                 } // mode
 | ||||
|             } // unix.
 | ||||
|             else if (!hasUnixMode && "perm".equals(factname)) { // skip if we have the UNIX.mode
 | ||||
|                 doUnixPerms(file, valueLowerCase); | ||||
|             } // process "perm"
 | ||||
|         } // each fact
 | ||||
|         return file; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Parse a GMT time stamp of the form YYYYMMDDHHMMSS[.sss] | ||||
|      * | ||||
|      * @param timestamp the date-time to parse | ||||
|      * @return a Calendar entry, may be {@code null} | ||||
|      * @since 3.4 | ||||
|      */ | ||||
|     public static Calendar parseGMTdateTime(String timestamp) { | ||||
|         final SimpleDateFormat sdf; | ||||
|         final boolean hasMillis; | ||||
|         if (timestamp.contains(".")){ | ||||
|             sdf = new SimpleDateFormat("yyyyMMddHHmmss.SSS"); | ||||
|             hasMillis = true; | ||||
|         } else { | ||||
|             sdf = new SimpleDateFormat("yyyyMMddHHmmss"); | ||||
|             hasMillis = false; | ||||
|         } | ||||
|         TimeZone GMT = TimeZone.getTimeZone("GMT"); | ||||
|         // both timezones need to be set for the parse to work OK
 | ||||
|         sdf.setTimeZone(GMT); | ||||
|         GregorianCalendar gc = new GregorianCalendar(GMT); | ||||
|         ParsePosition pos = new ParsePosition(0); | ||||
|         sdf.setLenient(false); // We want to parse the whole string
 | ||||
|         final Date parsed = sdf.parse(timestamp, pos); | ||||
|         if (pos.getIndex()  != timestamp.length()) { | ||||
|             return null; // did not fully parse the input
 | ||||
|         } | ||||
|         gc.setTime(parsed); | ||||
|         if (!hasMillis) { | ||||
|             gc.clear(Calendar.MILLISECOND); // flag up missing ms units
 | ||||
|         } | ||||
|         return gc; | ||||
|     } | ||||
| 
 | ||||
|     //              perm-fact    = "Perm" "=" *pvals
 | ||||
|     //              pvals        = "a" / "c" / "d" / "e" / "f" /
 | ||||
|     //                             "l" / "m" / "p" / "r" / "w"
 | ||||
|     private void doUnixPerms(FTPFile file, String valueLowerCase) { | ||||
|         for(char c : valueLowerCase.toCharArray()) { | ||||
|             // TODO these are mostly just guesses at present
 | ||||
|             switch (c) { | ||||
|                 case 'a':     // (file) may APPEnd
 | ||||
|                     file.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION, true); | ||||
|                     break; | ||||
|                 case 'c':     // (dir) files may be created in the dir
 | ||||
|                     file.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION, true); | ||||
|                     break; | ||||
|                 case 'd':     // deletable
 | ||||
|                     file.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION, true); | ||||
|                     break; | ||||
|                 case 'e':     // (dir) can change to this dir
 | ||||
|                     file.setPermission(FTPFile.USER_ACCESS, FTPFile.READ_PERMISSION, true); | ||||
|                     break; | ||||
|                 case 'f':     // (file) renamable
 | ||||
|                     // ?? file.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION, true);
 | ||||
|                     break; | ||||
|                 case 'l':     // (dir) can be listed
 | ||||
|                     file.setPermission(FTPFile.USER_ACCESS, FTPFile.EXECUTE_PERMISSION, true); | ||||
|                     break; | ||||
|                 case 'm':     // (dir) can create directory here
 | ||||
|                     file.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION, true); | ||||
|                     break; | ||||
|                 case 'p':     // (dir) entries may be deleted
 | ||||
|                     file.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION, true); | ||||
|                     break; | ||||
|                 case 'r':     // (files) file may be RETRieved
 | ||||
|                     file.setPermission(FTPFile.USER_ACCESS, FTPFile.READ_PERMISSION, true); | ||||
|                     break; | ||||
|                 case 'w':     // (files) file may be STORed
 | ||||
|                     file.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION, true); | ||||
|                     break; | ||||
|                 default: | ||||
|                     break; | ||||
|                     // ignore unexpected flag for now.
 | ||||
|             } // switch
 | ||||
|         } // each char
 | ||||
|     } | ||||
| 
 | ||||
|     public static FTPFile parseEntry(String entry) { | ||||
|         return PARSER.parseFTPEntry(entry); | ||||
|     } | ||||
| 
 | ||||
|     public static  MLSxEntryParser getInstance() { | ||||
|         return PARSER; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,554 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  * | ||||
|  * @version $Id: MVSFTPEntryParser.java 1752660 2016-07-14 13:25:39Z 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: | ||||
|      * <pre> | ||||
|      *  Volume Unit    Referred Ext Used Recfm Lrecl BlkSz Dsorg Dsname | ||||
|      *  B10142 3390   2006/03/20  2   31  F       80    80  PS   MDI.OKL.WORK | ||||
|      * </pre> | ||||
|      */ | ||||
|     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: | ||||
|      * <pre> | ||||
|      *   Name      VV.MM   Created       Changed      Size  Init   Mod   Id | ||||
|      *   TBSHELF   01.03 2002/09/12 2002/10/11 09:37    11    11     0 KIL001 | ||||
|      * </pre> | ||||
|      */ | ||||
|     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: | ||||
|      * <pre> | ||||
|      *   IBMUSER1  JOB01906  OUTPUT    3 Spool Files | ||||
|      *   012345678901234567890123456789012345678901234 | ||||
|      *             1         2         3         4 | ||||
|      * </pre> | ||||
|      */ | ||||
|     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: | ||||
|      * <pre> | ||||
|      * 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 | ||||
|      * </pre> | ||||
|      * Sample output from FTP session: | ||||
|      * <pre> | ||||
|      * ftp> quote site filetype=jes | ||||
|      * 200 SITE command was accepted | ||||
|      * ftp> ls | ||||
|      * 200 Port request OK. | ||||
|      * 125 List started OK for JESJOBNAME=IBMUSER*, JESSTATUS=ALL and JESOWNER=IBMUSER | ||||
|      * 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 | ||||
|      * 250 List completed successfully. | ||||
|      * ftp> ls job01906 | ||||
|      * 200 Port request OK. | ||||
|      * 125 List started OK for JESJOBNAME=IBMUSER*, JESSTATUS=ALL and JESOWNER=IBMUSER | ||||
|      * JOBNAME  JOBID    OWNER    STATUS CLASS | ||||
|      * IBMUSER1 JOB01906 IBMUSER  OUTPUT A        RC=0000 | ||||
|      * -------- | ||||
|      * ID  STEPNAME PROCSTEP C DDNAME   BYTE-COUNT | ||||
|      * 001 JES2              A JESMSGLG       858 | ||||
|      * 002 JES2              A JESJCL         128 | ||||
|      * 003 JES2              A JESYSMSG       443 | ||||
|      * 3 spool files | ||||
|      * 250 List completed successfully. | ||||
|      * </pre> | ||||
|      */ | ||||
| 
 | ||||
|     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 | ||||
|      */ | ||||
|     @Override | ||||
|     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: | ||||
|      * <pre> | ||||
|      *    0         1        2          3        4     5     6      7    8 | ||||
|      *   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 | ||||
|      * </pre> | ||||
|      * | ||||
|      * @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 true if the entry string is non-null and non-empty | ||||
|      */ | ||||
|     private boolean parseSimpleEntry(FTPFile file, String entry) { | ||||
|         if (entry != null && entry.trim().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: | ||||
|      * <pre> | ||||
|      * [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" | ||||
|      *</pre> | ||||
|      * | ||||
|      * @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: | ||||
|      * <pre> | ||||
|      * [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 | ||||
|      * </pre> | ||||
|      * | ||||
|      * @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); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,278 @@ | ||||
| /* | ||||
|  * 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 pre MacOS-X Systems. | ||||
|  * | ||||
|  * @version $Id: MacOsPeterFTPEntryParser.java 1752660 2016-07-14 13:25:39Z sebb $ | ||||
|  * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions) | ||||
|  * @since 3.1 | ||||
|  */ | ||||
| public class MacOsPeterFTPEntryParser 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
 | ||||
| 
 | ||||
|     /** | ||||
|      * 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-])" // type (1)
 | ||||
|         + "(((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-])))\\+?\\s+" // permission
 | ||||
|         + "(" | ||||
|         +   "(folder\\s+)" | ||||
|         + "|" | ||||
|         +   "((\\d+)\\s+(\\d+)\\s+)" // resource size & data size
 | ||||
|         + ")" | ||||
|         + "(\\d+)\\s+"              // size
 | ||||
|         /* | ||||
|          * numeric or standard format date: | ||||
|          *   yyyy-mm-dd (expecting hh:mm to follow) | ||||
|          *   MMM [d]d | ||||
|          *   [d]d MMM | ||||
|          *   N.B. use non-space for MMM to allow for languages such as German which use | ||||
|          *   diacritics (e.g. umlaut) in some abbreviations. | ||||
|         */ | ||||
|         + "((?:\\d+[-/]\\d+[-/]\\d+)|(?:\\S{3}\\s+\\d{1,2})|(?:\\d{1,2}\\s+\\S{3}))\\s+" | ||||
|         /* | ||||
|            year (for non-recent standard format) - yyyy | ||||
|            or time (for numeric or recent standard format) [h]h:mm | ||||
|         */ | ||||
|         + "(\\d+(?::\\d+)?)\\s+" | ||||
| 
 | ||||
|         + "(\\S*)(\\s*.*)"; // the rest
 | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * The default constructor for a UnixFTPEntryParser object. | ||||
|      * | ||||
|      * @throws 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 MacOsPeterFTPEntryParser() | ||||
|     { | ||||
|         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. | ||||
|      * @throws 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 MacOsPeterFTPEntryParser(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. | ||||
|      * | ||||
|      * @param entry A line of text from the file listing | ||||
|      * @return An FTPFile instance corresponding to the supplied entry | ||||
|      */ | ||||
|     @Override | ||||
|     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 = "0"; | ||||
|             String usr = null; | ||||
|             String grp = null; | ||||
|             String filesize = group(20); | ||||
|             String datestr = group(21) + " " + group(22); | ||||
|             String name = group(23); | ||||
|             String endtoken = group(24); | ||||
| 
 | ||||
|             try | ||||
|             { | ||||
|                 file.setTimestamp(super.parseTimestamp(datestr)); | ||||
|             } | ||||
|             catch (ParseException e) | ||||
|             { | ||||
|                  // intentionally do nothing
 | ||||
|             } | ||||
| 
 | ||||
|             // A 'whiteout' file is an ARTIFICIAL entry in any of several types of
 | ||||
|             // 'translucent' filesystems, of which a 'union' filesystem is one.
 | ||||
| 
 | ||||
|             // bcdelfmpSs-
 | ||||
|             switch (typeStr.charAt(0)) | ||||
|             { | ||||
|             case 'd': | ||||
|                 type = FTPFile.DIRECTORY_TYPE; | ||||
|                 break; | ||||
|             case 'e': // NET-39 => z/OS external link
 | ||||
|                 type = FTPFile.SYMBOLIC_LINK_TYPE; | ||||
|                 break; | ||||
|             case 'l': | ||||
|                 type = FTPFile.SYMBOLIC_LINK_TYPE; | ||||
|                 break; | ||||
|             case 'b': | ||||
|             case 'c': | ||||
|                 isDevice = true; | ||||
|                 type = FTPFile.FILE_TYPE; // TODO change this if DEVICE_TYPE implemented
 | ||||
|                 break; | ||||
|             case 'f': | ||||
|             case '-': | ||||
|                 type = FTPFile.FILE_TYPE; | ||||
|                 break; | ||||
|             default: // e.g. ? and w = whiteout
 | ||||
|                 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); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,167 @@ | ||||
| /* | ||||
|  * 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.regex.Pattern; | ||||
| 
 | ||||
| import org.apache.commons.net.ftp.Configurable; | ||||
| import org.apache.commons.net.ftp.FTPClientConfig; | ||||
| import org.apache.commons.net.ftp.FTPFile; | ||||
| 
 | ||||
| /** | ||||
|  * Implementation of FTPFileEntryParser and FTPFileListParser for NT Systems. | ||||
|  * | ||||
|  * @version $Id: NTFTPEntryParser.java 1752660 2016-07-14 13:25:39Z 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
 | ||||
| 
 | ||||
|     private static final String DEFAULT_DATE_FORMAT2 | ||||
|         = "MM-dd-yy kk:mm"; //11-09-01 18:30
 | ||||
| 
 | ||||
|     private final FTPTimestampParser timestampParser; | ||||
| 
 | ||||
|     /** | ||||
|      * this is the regular expression used by this parser. | ||||
|      */ | ||||
|     private static final String REGEX = | ||||
|         "(\\S+)\\s+(\\S+)\\s+"          // MM-dd-yy whitespace hh:mma|kk:mm; swallow trailing spaces
 | ||||
|         + "(?:(<DIR>)|([0-9]+))\\s+"    // <DIR> or ddddd; swallow trailing spaces
 | ||||
|         + "(\\S.*)";                    // First non-space followed by rest of line (name)
 | ||||
| 
 | ||||
|     /** | ||||
|      * The sole constructor for an NTFTPEntryParser object. | ||||
|      * | ||||
|      * @throws 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. | ||||
|      * @throws 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, Pattern.DOTALL); | ||||
|         configure(config); | ||||
|         FTPClientConfig config2 = new FTPClientConfig( | ||||
|                 FTPClientConfig.SYST_NT, | ||||
|                 DEFAULT_DATE_FORMAT2, | ||||
|                 null); | ||||
|         config2.setDefaultDateFormatStr(DEFAULT_DATE_FORMAT2); | ||||
|         this.timestampParser = new FTPTimestampParserImpl(); | ||||
|         ((Configurable)this.timestampParser).configure(config2); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 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. | ||||
|      * | ||||
|      * @param entry A line of text from the file listing | ||||
|      * @return An FTPFile instance corresponding to the supplied entry | ||||
|      */ | ||||
|     @Override | ||||
|     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) | ||||
|             { | ||||
|                 // parsing fails, try the other date format
 | ||||
|                 try | ||||
|                 { | ||||
|                     f.setTimestamp(timestampParser.parseTimestamp(datestr)); | ||||
|                 } | ||||
|                 catch (ParseException e2) | ||||
|                 { | ||||
|                     // 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); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,177 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  * | ||||
|  * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions) | ||||
|  * @version $Id: NetwareFTPEntryParser.java 1752660 2016-07-14 13:25:39Z 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
 | ||||
|             + "\\[([-A-Z]+)\\]\\s+"                         // Attributes RWCEAFMS or -
 | ||||
|             + "(\\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. | ||||
|      * | ||||
|      * @throws 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. | ||||
|      * @throws 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> | ||||
|      * 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 | ||||
|      */ | ||||
|     @Override | ||||
|     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); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -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.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. | ||||
|  * | ||||
|  * @version $Id: OS2FTPEntryParser.java 1752660 2016-07-14 13:25:39Z 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. | ||||
|      * | ||||
|      * @throws 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. | ||||
|      * @throws 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. | ||||
|      * | ||||
|      * @param entry A line of text from the file listing | ||||
|      * @return An FTPFile instance corresponding to the supplied entry | ||||
|      */ | ||||
|     @Override | ||||
|     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); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,435 @@ | ||||
| /* | ||||
|  * 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.File; | ||||
| import java.text.ParseException; | ||||
| 
 | ||||
| import org.apache.commons.net.ftp.FTPClientConfig; | ||||
| import org.apache.commons.net.ftp.FTPFile; | ||||
| 
 | ||||
| /** | ||||
|  * @version $Id: OS400FTPEntryParser.java 1752660 2016-07-14 13:25:39Z sebb $ | ||||
|  * <pre> | ||||
|  * Example *FILE/*MEM FTP entries, when the current | ||||
|  * working directory is a file of file system QSYS: | ||||
|  * ------------------------------------------------ | ||||
|  * | ||||
|  * $ cwd /qsys.lib/rpgunit.lib/rpgunitc1.file | ||||
|  *   250-NAMEFMT set to 1. | ||||
|  *   250 "/QSYS.LIB/RPGUNIT.LIB/RPGUNITC1.FILE" is current directory. | ||||
|  * $ dir | ||||
|  *   227 Entering Passive Mode (10,200,36,33,40,249). | ||||
|  *   125 List started. | ||||
|  *   QPGMR          135168 22.06.13 13:18:19 *FILE | ||||
|  *   QPGMR                                   *MEM       MKCMD.MBR | ||||
|  *   QPGMR                                   *MEM       RUCALLTST.MBR | ||||
|  *   QPGMR                                   *MEM       RUCMDHLP.MBR | ||||
|  *   QPGMR                                   *MEM       RUCRTTST.MBR | ||||
|  *   250 List completed. | ||||
|  * | ||||
|  * | ||||
|  * Example *FILE entry of an OS/400 save file: | ||||
|  * --------------------------------------------------- | ||||
|  * | ||||
|  * $ cwd /qsys.lib/rpgunit.lib | ||||
|  *   250 "/QSYS.LIB/RPGUNIT.LIB" is current library. | ||||
|  * $ dir rpgunit.file | ||||
|  *   227 Entering Passive Mode (10,200,36,33,188,106). | ||||
|  *   125 List started. | ||||
|  *   QPGMR        16347136 29.06.13 15:45:09 *FILE      RPGUNIT.SAVF | ||||
|  *   250 List completed. | ||||
|  * | ||||
|  * | ||||
|  * Example *STMF/*DIR FTP entries, when the | ||||
|  * current working directory is in file system "root": | ||||
|  * --------------------------------------------------- | ||||
|  * | ||||
|  * $ cwd /home/raddatz | ||||
|  *   250 "/home/raddatz" is current directory. | ||||
|  * $ dir test* | ||||
|  *   227 Entering Passive Mode (10,200,36,33,200,189). | ||||
|  *   125 List started. | ||||
|  *   RADDATZ           200 21.05.11 12:31:18 *STMF      TEST_RG_02_CRLF.properties | ||||
|  *   RADDATZ           187 08.05.11 12:31:40 *STMF      TEST_RG_02_LF.properties | ||||
|  *   RADDATZ           187 08.05.11 12:31:52 *STMF      TEST_RG_02_CR.properties | ||||
|  *   RADDATZ          8192 04.07.13 09:04:14 *DIR       testDir1/ | ||||
|  *   RADDATZ          8192 04.07.13 09:04:17 *DIR       testDir2/ | ||||
|  *   250 List completed. | ||||
|  * | ||||
|  * | ||||
|  * Example 1, using ANT to list specific members of a file: | ||||
|  * -------------------------------------------------------- | ||||
|  * | ||||
|  *      <echo/> | ||||
|  *      <echo>Listing members of a file:</echo> | ||||
|  * | ||||
|  *      <ftp action="list" | ||||
|  *           server="${ftp.server}" | ||||
|  *           userid="${ftp.user}" | ||||
|  *           password="${ftp.password}" | ||||
|  *           binary="false" | ||||
|  *           verbose="true" | ||||
|  *           remotedir="/QSYS.LIB/RPGUNIT.LIB/RPGUNITY1.FILE" | ||||
|  *           systemTypeKey="OS/400" | ||||
|  *           listing="ftp-listing.txt" | ||||
|  *           > | ||||
|  *          <fileset dir="./i5-downloads-file" casesensitive="false"> | ||||
|  *              <include name="run*.mbr" /> | ||||
|  *          </fileset> | ||||
|  *      </ftp> | ||||
|  * | ||||
|  * Output: | ||||
|  * ------- | ||||
|  * | ||||
|  *   [echo] Listing members of a file: | ||||
|  *    [ftp] listing files | ||||
|  *    [ftp] listing RUN.MBR | ||||
|  *    [ftp] listing RUNNER.MBR | ||||
|  *    [ftp] listing RUNNERBND.MBR | ||||
|  *    [ftp] 3 files listed | ||||
|  * | ||||
|  * | ||||
|  * Example 2, using ANT to list specific members of all files of a library: | ||||
|  * ------------------------------------------------------------------------ | ||||
|  * | ||||
|  *      <echo/> | ||||
|  *      <echo>Listing members of all files of a library:</echo> | ||||
|  * | ||||
|  *      <ftp action="list" | ||||
|  *           server="${ftp.server}" | ||||
|  *           userid="${ftp.user}" | ||||
|  *           password="${ftp.password}" | ||||
|  *           binary="false" | ||||
|  *           verbose="true" | ||||
|  *           remotedir="/QSYS.LIB/RPGUNIT.LIB" | ||||
|  *           systemTypeKey="OS/400" | ||||
|  *           listing="ftp-listing.txt" | ||||
|  *           > | ||||
|  *          <fileset dir="./i5-downloads-lib" casesensitive="false"> | ||||
|  *              <include name="**\run*.mbr" /> | ||||
|  *          </fileset> | ||||
|  *      </ftp> | ||||
|  * | ||||
|  * Output: | ||||
|  * ------- | ||||
|  * | ||||
|  *   [echo] Listing members of all files of a library: | ||||
|  *    [ftp] listing files | ||||
|  *    [ftp] listing RPGUNIT1.FILE\RUN.MBR | ||||
|  *    [ftp] listing RPGUNIT1.FILE\RUNRMT.MBR | ||||
|  *    [ftp] listing RPGUNITT1.FILE\RUNT.MBR | ||||
|  *    [ftp] listing RPGUNITY1.FILE\RUN.MBR | ||||
|  *    [ftp] listing RPGUNITY1.FILE\RUNNER.MBR | ||||
|  *    [ftp] listing RPGUNITY1.FILE\RUNNERBND.MBR | ||||
|  *    [ftp] 6 files listed | ||||
|  * | ||||
|  * | ||||
|  * Example 3, using ANT to download specific members of a file: | ||||
|  * ------------------------------------------------------------ | ||||
|  * | ||||
|  *      <echo/> | ||||
|  *      <echo>Downloading members of a file:</echo> | ||||
|  * | ||||
|  *      <ftp action="get" | ||||
|  *           server="${ftp.server}" | ||||
|  *           userid="${ftp.user}" | ||||
|  *           password="${ftp.password}" | ||||
|  *           binary="false" | ||||
|  *           verbose="true" | ||||
|  *           remotedir="/QSYS.LIB/RPGUNIT.LIB/RPGUNITY1.FILE" | ||||
|  *           systemTypeKey="OS/400" | ||||
|  *           > | ||||
|  *          <fileset dir="./i5-downloads-file" casesensitive="false"> | ||||
|  *              <include name="run*.mbr" /> | ||||
|  *          </fileset> | ||||
|  *      </ftp> | ||||
|  * | ||||
|  * Output: | ||||
|  * ------- | ||||
|  * | ||||
|  *   [echo] Downloading members of a file: | ||||
|  *    [ftp] getting files | ||||
|  *    [ftp] transferring RUN.MBR to C:\workspaces\rdp_080\workspace\ANT - FTP\i5-downloads-file\RUN.MBR | ||||
|  *    [ftp] transferring RUNNER.MBR to C:\workspaces\rdp_080\workspace\ANT - FTP\i5-downloads-file\RUNNER.MBR | ||||
|  *    [ftp] transferring RUNNERBND.MBR to C:\workspaces\rdp_080\workspace\ANT - FTP\i5-downloads-file\RUNNERBND.MBR | ||||
|  *    [ftp] 3 files retrieved | ||||
|  * | ||||
|  * | ||||
|  * Example 4, using ANT to download specific members of all files of a library: | ||||
|  * ---------------------------------------------------------------------------- | ||||
|  * | ||||
|  *      <echo/> | ||||
|  *      <echo>Downloading members of all files of a library:</echo> | ||||
|  * | ||||
|  *      <ftp action="get" | ||||
|  *           server="${ftp.server}" | ||||
|  *           userid="${ftp.user}" | ||||
|  *           password="${ftp.password}" | ||||
|  *           binary="false" | ||||
|  *           verbose="true" | ||||
|  *           remotedir="/QSYS.LIB/RPGUNIT.LIB" | ||||
|  *           systemTypeKey="OS/400" | ||||
|  *           > | ||||
|  *          <fileset dir="./i5-downloads-lib" casesensitive="false"> | ||||
|  *              <include name="**\run*.mbr" /> | ||||
|  *          </fileset> | ||||
|  *      </ftp> | ||||
|  * | ||||
|  * Output: | ||||
|  * ------- | ||||
|  * | ||||
|  *   [echo] Downloading members of all files of a library: | ||||
|  *    [ftp] getting files | ||||
|  *    [ftp] transferring RPGUNIT1.FILE\RUN.MBR to C:\work\rdp_080\space\ANT - FTP\i5-downloads\RPGUNIT1.FILE\RUN.MBR | ||||
|  *    [ftp] transferring RPGUNIT1.FILE\RUNRMT.MBR to C:\work\rdp_080\space\ANT - FTP\i5-downloads\RPGUNIT1.FILE\RUNRMT.MBR | ||||
|  *    [ftp] transferring RPGUNITT1.FILE\RUNT.MBR to C:\work\rdp_080\space\ANT - FTP\i5-downloads\RPGUNITT1.FILE\RUNT.MBR | ||||
|  *    [ftp] transferring RPGUNITY1.FILE\RUN.MBR to C:\work\rdp_080\space\ANT - FTP\i5-downloads\RPGUNITY1.FILE\RUN.MBR | ||||
|  *    [ftp] transferring RPGUNITY1.FILE\RUNNER.MBR to C:\work\rdp_080\space\ANT - FTP\i5-downloads\RPGUNITY1.FILE\RUNNER.MBR | ||||
|  *    [ftp] transferring RPGUNITY1.FILE\RUNNERBND.MBR to C:\work\rdp_080\space\ANT - FTP\i5-downloads\RPGUNITY1.FILE\RUNNERBND.MBR | ||||
|  *    [ftp] 6 files retrieved | ||||
|  * | ||||
|  * | ||||
|  * Example 5, using ANT to download a save file of a library: | ||||
|  * ---------------------------------------------------------- | ||||
|  * | ||||
|  *      <ftp action="get" | ||||
|  *           server="${ftp.server}" | ||||
|  *           userid="${ftp.user}" | ||||
|  *           password="${ftp.password}" | ||||
|  *           binary="true" | ||||
|  *           verbose="true" | ||||
|  *           remotedir="/QSYS.LIB/RPGUNIT.LIB" | ||||
|  *           systemTypeKey="OS/400" | ||||
|  *           > | ||||
|  *        <fileset dir="./i5-downloads-savf" casesensitive="false"> | ||||
|  *            <include name="RPGUNIT.SAVF" /> | ||||
|  *        </fileset> | ||||
|  *      </ftp> | ||||
|  * | ||||
|  * Output: | ||||
|  * ------- | ||||
|  *   [echo] Downloading save file: | ||||
|  *    [ftp] getting files | ||||
|  *    [ftp] transferring RPGUNIT.SAVF to C:\workspaces\rdp_080\workspace\net-Test\i5-downloads-lib\RPGUNIT.SAVF | ||||
|  *    [ftp] 1 files retrieved | ||||
|  * | ||||
|  * </pre> | ||||
|  */ | ||||
| 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, empty for members
 | ||||
|         + "(?:(\\S+)\\s+(\\S+)\\s+)?" // date stuff, empty for members
 | ||||
|         + "(\\*STMF|\\*DIR|\\*FILE|\\*MEM)\\s+"  // *STMF/*DIR/*FILE/*MEM
 | ||||
|         + "(?:(\\S+)\\s*)?";          // filename, missing, when CWD is a *FILE
 | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * The default constructor for a OS400FTPEntryParser object. | ||||
|      * | ||||
|      * @throws 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. | ||||
|      * @throws 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); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     @Override | ||||
|     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 = ""; | ||||
|             if (!isNullOrEmpty(group(3)) || !isNullOrEmpty(group(4))) | ||||
|             { | ||||
|                 datestr = group(3)+" "+group(4); | ||||
|             } | ||||
|             String typeStr = group(5); | ||||
|             String name = group(6); | ||||
| 
 | ||||
|             boolean mustScanForPathSeparator = true; | ||||
| 
 | ||||
|             try | ||||
|             { | ||||
|                 file.setTimestamp(super.parseTimestamp(datestr)); | ||||
|             } | ||||
|             catch (ParseException e) | ||||
|             { | ||||
|                 // intentionally do nothing
 | ||||
|             } | ||||
| 
 | ||||
| 
 | ||||
|             if (typeStr.equalsIgnoreCase("*STMF")) | ||||
|             { | ||||
|                 type = FTPFile.FILE_TYPE; | ||||
|                 if (isNullOrEmpty(filesize) || isNullOrEmpty(name)) | ||||
|                 { | ||||
|                     return null; | ||||
|                 } | ||||
|             } | ||||
|             else if (typeStr.equalsIgnoreCase("*DIR")) | ||||
|             { | ||||
|                 type = FTPFile.DIRECTORY_TYPE; | ||||
|                 if (isNullOrEmpty(filesize) || isNullOrEmpty(name)) | ||||
|                 { | ||||
|                     return null; | ||||
|                 } | ||||
|             } | ||||
|             else if (typeStr.equalsIgnoreCase("*FILE")) | ||||
|             { | ||||
|                 // File, defines the structure of the data (columns of a row)
 | ||||
|                 // but the data is stored in one or more members. Typically a
 | ||||
|                 // source file contains multiple members whereas it is
 | ||||
|                 // recommended (but not enforced) to use one member per data
 | ||||
|                 // file.
 | ||||
|                 // Save files are a special type of files which are used
 | ||||
|                 // to save objects, e.g. for backups.
 | ||||
|                 if (name != null && name.toUpperCase().endsWith(".SAVF")) | ||||
|                 { | ||||
|                     mustScanForPathSeparator = false; | ||||
|                     type = FTPFile.FILE_TYPE; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return null; | ||||
|                 } | ||||
|             } | ||||
|             else if (typeStr.equalsIgnoreCase("*MEM")) | ||||
|             { | ||||
|                 mustScanForPathSeparator = false; | ||||
|                 type = FTPFile.FILE_TYPE; | ||||
| 
 | ||||
|                 if (isNullOrEmpty(name)) | ||||
|                 { | ||||
|                     return null; | ||||
|                 } | ||||
|                 if (!(isNullOrEmpty(filesize) && isNullOrEmpty(datestr))) | ||||
|                 { | ||||
|                     return null; | ||||
|                 } | ||||
| 
 | ||||
|                 // Quick and dirty bug fix to make SelectorUtils work.
 | ||||
|                 // Class SelectorUtils uses 'File.separator' to splitt
 | ||||
|                 // a given path into pieces. But actually it had to
 | ||||
|                 // use the separator of the FTP server, which is a forward
 | ||||
|                 // slash in case of an AS/400.
 | ||||
|                 name = name.replace('/', File.separatorChar); | ||||
|             } | ||||
|             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); | ||||
|             } | ||||
|             if (mustScanForPathSeparator) | ||||
|             { | ||||
|                 int pos = name.lastIndexOf('/'); | ||||
|                 if (pos > -1) | ||||
|                 { | ||||
|                     name = name.substring(pos + 1); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             file.setName(name); | ||||
| 
 | ||||
|             return file; | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * | ||||
|      * @param string String value that is checked for <code>null</code> | ||||
|      * or empty. | ||||
|      * @return <code>true</code> for <code>null</code> or empty values, | ||||
|      * else <code>false</code>. | ||||
|      */ | ||||
|     private boolean isNullOrEmpty(String string) { | ||||
|         if (string == null || string.length() == 0) { | ||||
|             return true; | ||||
|         } | ||||
|         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_OS400, | ||||
|                 DEFAULT_DATE_FORMAT, | ||||
|                 null); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,62 @@ | ||||
| /* | ||||
|  * 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 { | ||||
| 
 | ||||
|     private static final long serialVersionUID = 5563335279583210658L; | ||||
| 
 | ||||
|     /** | ||||
|      * Constucts a ParserInitializationException with just a message | ||||
|      * | ||||
|      * @param message Exception message | ||||
|      */ | ||||
|     public ParserInitializationException(String message) { | ||||
|         super(message); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 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, 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 | ||||
|      * @deprecated use {@link #getCause()} instead | ||||
|      */ | ||||
|     @Deprecated | ||||
|     public Throwable getRootCause() { | ||||
|         return super.getCause(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,202 @@ | ||||
| /* | ||||
|  * 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 class for all regular expression based FTPFileEntryParser classes | ||||
|  */ | ||||
| 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. | ||||
|      * The expression is compiled with flags = 0. | ||||
|      * | ||||
|      * @param regex  The regular expression with which this object is | ||||
|      * initialized. | ||||
|      * | ||||
|      * @throws 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(); | ||||
|         compileRegex(regex, 0); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * The constructor for a RegexFTPFileEntryParserImpl object. | ||||
|      * | ||||
|      * @param regex  The regular expression with which this object is | ||||
|      * initialized. | ||||
|      * @param flags the flags to apply, see {@link Pattern#compile(String, int)}. Use 0 for none. | ||||
|      * | ||||
|      * @throws 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. | ||||
|      * @since 3.4 | ||||
|      */ | ||||
|     public RegexFTPFileEntryParserImpl(String regex, final int flags) { | ||||
|         super(); | ||||
|         compileRegex(regex, flags); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 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() { | ||||
|         StringBuilder b = new StringBuilder(); | ||||
|         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  true | ||||
|      * @since 2.0 | ||||
|      * @throws IllegalArgumentException if the regex cannot be compiled | ||||
|      */ | ||||
|     public boolean setRegex(final String regex) { | ||||
|         compileRegex(regex, 0); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * Alter the current regular expression being utilised for entry parsing | ||||
|      * and create a new {@link Pattern} instance. | ||||
|      * @param regex The new regular expression | ||||
|      * @param flags the flags to apply, see {@link Pattern#compile(String, int)}. Use 0 for none. | ||||
|      * @return  true | ||||
|      * @since 3.4 | ||||
|      * @throws IllegalArgumentException if the regex cannot be compiled | ||||
|      */ | ||||
|     public boolean setRegex(final String regex, final int flags) { | ||||
|         compileRegex(regex, flags); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Compile the regex and store the {@link Pattern}. | ||||
|      * | ||||
|      * This is an internal method to do the work so the constructor does not | ||||
|      * have to call an overrideable method. | ||||
|      * | ||||
|      * @param regex the expression to compile | ||||
|      * @param flags the flags to apply, see {@link Pattern#compile(String, int)}. Use 0 for none. | ||||
|      * @throws IllegalArgumentException if the regex cannot be compiled | ||||
|      */ | ||||
|     private void compileRegex(final String regex, final int flags) { | ||||
|         try { | ||||
|             pattern = Pattern.compile(regex, flags); | ||||
|         } catch (PatternSyntaxException pse) { | ||||
|             throw new IllegalArgumentException("Unparseable regex supplied: " + regex); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,379 @@ | ||||
| /* | ||||
|  * 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 java.util.ListIterator; | ||||
| 
 | ||||
| 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 1781925 2017-02-06 16:43:40Z 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
 | ||||
| 
 | ||||
|     // Suffixes used in Japanese listings after the numeric values
 | ||||
|     private static final String JA_MONTH = "\u6708"; | ||||
|     private static final String JA_DAY   = "\u65e5"; | ||||
|     private static final String JA_YEAR  = "\u5e74"; | ||||
| 
 | ||||
|     private static final String DEFAULT_DATE_FORMAT_JA | ||||
|         = "M'" + JA_MONTH + "' d'" + JA_DAY + "' yyyy'" + JA_YEAR + "'"; //6月 3日 2003年
 | ||||
| 
 | ||||
|     private static final String DEFAULT_RECENT_DATE_FORMAT_JA | ||||
|         = "M'" + JA_MONTH + "' d'" + JA_DAY + "' HH:mm"; //8月 17日 20:10
 | ||||
| 
 | ||||
|     /** | ||||
|      * 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); | ||||
| 
 | ||||
|     /** | ||||
|      * 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 | ||||
|      *    Final letter may be appended: | ||||
|      *    +   file has extended security attributes (e.g. ACL) | ||||
|      *    Note: local listings on MacOSX also use '@'; | ||||
|      *    this is not allowed for here as does not appear to be shown by FTP servers | ||||
|      *    {@code @}   file has extended attributes | ||||
|      */ | ||||
|     private static final String REGEX = | ||||
|         "([bcdelfmpSs-])" // file type
 | ||||
|         +"(((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-])))\\+?" // permissions
 | ||||
| 
 | ||||
|         + "\\s*"                                        // separator TODO why allow it to be omitted??
 | ||||
| 
 | ||||
|         + "(\\d+)"                                      // link count
 | ||||
| 
 | ||||
|         + "\\s+" // separator
 | ||||
| 
 | ||||
|         + "(?:(\\S+(?:\\s\\S+)*?)\\s+)?"                // owner name (optional spaces)
 | ||||
|         + "(?:(\\S+(?:\\s\\S+)*)\\s+)?"                 // group name (optional spaces)
 | ||||
|         + "(\\d+(?:,\\s*\\d+)?)"                        // size or n,m
 | ||||
| 
 | ||||
|         + "\\s+" // separator
 | ||||
| 
 | ||||
|         /* | ||||
|          * numeric or standard format date: | ||||
|          *   yyyy-mm-dd (expecting hh:mm to follow) | ||||
|          *   MMM [d]d | ||||
|          *   [d]d MMM | ||||
|          *   N.B. use non-space for MMM to allow for languages such as German which use | ||||
|          *   diacritics (e.g. umlaut) in some abbreviations. | ||||
|          *   Japanese uses numeric day and month with suffixes to distinguish them | ||||
|          *   [d]dXX [d]dZZ | ||||
|         */ | ||||
|         + "("+ | ||||
|             "(?:\\d+[-/]\\d+[-/]\\d+)" + // yyyy-mm-dd
 | ||||
|             "|(?:\\S{3}\\s+\\d{1,2})" +  // MMM [d]d
 | ||||
|             "|(?:\\d{1,2}\\s+\\S{3})" + // [d]d MMM
 | ||||
|             "|(?:\\d{1,2}" + JA_MONTH + "\\s+\\d{1,2}" + JA_DAY + ")"+ | ||||
|            ")" | ||||
| 
 | ||||
|         + "\\s+" // separator
 | ||||
| 
 | ||||
|         /* | ||||
|            year (for non-recent standard format) - yyyy | ||||
|            or time (for numeric or recent standard format) [h]h:mm | ||||
|            or Japanese year - yyyyXX | ||||
|         */ | ||||
|         + "((?:\\d+(?::\\d+)?)|(?:\\d{4}" + JA_YEAR + "))" // (20)
 | ||||
| 
 | ||||
|         + "\\s" // separator
 | ||||
| 
 | ||||
|         + "(.*)"; // the rest (21)
 | ||||
| 
 | ||||
| 
 | ||||
|     // if true, leading spaces are trimmed from file names
 | ||||
|     // this was the case for the original implementation
 | ||||
|     final boolean trimLeadingSpaces; // package protected for access from test code
 | ||||
| 
 | ||||
|     /** | ||||
|      * The default constructor for a UnixFTPEntryParser object. | ||||
|      * | ||||
|      * @throws 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. | ||||
|      * @throws 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) | ||||
|     { | ||||
|         this(config, false); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 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. | ||||
|      * @param trimLeadingSpaces if {@code true}, trim leading spaces from file names | ||||
|      * @throws 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 3.4 | ||||
|      */ | ||||
|     public UnixFTPEntryParser(FTPClientConfig config, boolean trimLeadingSpaces) | ||||
|     { | ||||
|         super(REGEX); | ||||
|         configure(config); | ||||
|         this.trimLeadingSpaces = trimLeadingSpaces; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Preparse the list to discard "total nnn" lines | ||||
|      */ | ||||
|     @Override | ||||
|     public List<String> preParse(List<String> original) { | ||||
|         ListIterator<String> iter = original.listIterator(); | ||||
|         while (iter.hasNext()) { | ||||
|             String entry = iter.next(); | ||||
|             if (entry.matches("^total \\d+$")) { // NET-389
 | ||||
|                 iter.remove(); | ||||
|             } | ||||
|         } | ||||
|         return original; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 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. | ||||
|      * | ||||
|      * @param entry A line of text from the file listing | ||||
|      * @return An FTPFile instance corresponding to the supplied entry | ||||
|      */ | ||||
|     @Override | ||||
|     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); | ||||
|             if (trimLeadingSpaces) { | ||||
|                 name = name.replaceFirst("^\\s+", ""); | ||||
|             } | ||||
| 
 | ||||
|             try | ||||
|             { | ||||
|                 if (group(19).contains(JA_MONTH)) { // special processing for Japanese format
 | ||||
|                     FTPTimestampParserImpl jaParser = new FTPTimestampParserImpl(); | ||||
|                     jaParser.configure(new FTPClientConfig( | ||||
|                             FTPClientConfig.SYST_UNIX, DEFAULT_DATE_FORMAT_JA, DEFAULT_RECENT_DATE_FORMAT_JA)); | ||||
|                     file.setTimestamp(jaParser.parseTimestamp(datestr)); | ||||
|                 } else { | ||||
|                     file.setTimestamp(super.parseTimestamp(datestr)); | ||||
|                 } | ||||
|             } | ||||
|             catch (ParseException e) | ||||
|             { | ||||
|                  // intentionally do nothing
 | ||||
|             } | ||||
| 
 | ||||
|             // A 'whiteout' file is an ARTIFICIAL entry in any of several types of
 | ||||
|             // 'translucent' filesystems, of which a 'union' filesystem is one.
 | ||||
| 
 | ||||
|             // bcdelfmpSs-
 | ||||
|             switch (typeStr.charAt(0)) | ||||
|             { | ||||
|             case 'd': | ||||
|                 type = FTPFile.DIRECTORY_TYPE; | ||||
|                 break; | ||||
|             case 'e': // NET-39 => z/OS external link
 | ||||
|                 type = FTPFile.SYMBOLIC_LINK_TYPE; | ||||
|                 break; | ||||
|             case 'l': | ||||
|                 type = FTPFile.SYMBOLIC_LINK_TYPE; | ||||
|                 break; | ||||
|             case 'b': | ||||
|             case 'c': | ||||
|                 isDevice = true; | ||||
|                 type = FTPFile.FILE_TYPE; // TODO change this if DEVICE_TYPE implemented
 | ||||
|                 break; | ||||
|             case 'f': | ||||
|             case '-': | ||||
|                 type = FTPFile.FILE_TYPE; | ||||
|                 break; | ||||
|             default: // e.g. ? and w = whiteout
 | ||||
|                 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
 | ||||
|             } | ||||
| 
 | ||||
|             // oddball cases like symbolic links, file names
 | ||||
|             // with spaces in them.
 | ||||
|             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); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -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.ftp.parser; | ||||
| import java.io.BufferedReader; | ||||
| import java.io.IOException; | ||||
| import java.text.ParseException; | ||||
| import java.util.StringTokenizer; | ||||
| 
 | ||||
| import org.apache.commons.net.ftp.FTPClientConfig; | ||||
| import org.apache.commons.net.ftp.FTPFile; | ||||
| 
 | ||||
| /** | ||||
|  * 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> | ||||
|  * | ||||
|  * @version $Id: VMSFTPEntryParser.java 1752660 2016-07-14 13:25:39Z 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. | ||||
|      * | ||||
|      * @throws 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. | ||||
|      * @throws 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 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. | ||||
|      * | ||||
|      * @param entry A line of text from the file listing | ||||
|      * @return An FTPFile instance corresponding to the supplied entry | ||||
|      */ | ||||
|     @Override | ||||
|     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. | ||||
|      * @throws IOException thrown on any IO Error reading from the reader. | ||||
|      */ | ||||
|     @Override | ||||
|     public String readNextEntry(BufferedReader reader) throws IOException | ||||
|     { | ||||
|         String line = reader.readLine(); | ||||
|         StringBuilder entry = new StringBuilder(); | ||||
|         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); | ||||
|     } | ||||
| 
 | ||||
|     // DEPRECATED METHODS - for API compatibility only - DO NOT USE
 | ||||
| 
 | ||||
|     /** | ||||
|      * DO NOT USE | ||||
|      * @param listStream the stream | ||||
|      * @return the array of files | ||||
|      * @throws IOException on error | ||||
|      * @deprecated (2.2) No other FTPFileEntryParser implementations have this method. | ||||
|      */ | ||||
|     @Deprecated | ||||
|     public FTPFile[] parseFileList(java.io.InputStream listStream) throws IOException { | ||||
|         org.apache.commons.net.ftp.FTPListParseEngine engine = new org.apache.commons.net.ftp.FTPListParseEngine(this); | ||||
|         engine.readServerList(listStream, null); | ||||
|         return engine.getFiles(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| /* Emacs configuration | ||||
|  * Local variables:        ** | ||||
|  * mode:             java  ** | ||||
|  * c-basic-offset:   4     ** | ||||
|  * indent-tabs-mode: nil   ** | ||||
|  * End:                    ** | ||||
|  */ | ||||
| @ -0,0 +1,167 @@ | ||||
| /* | ||||
|  * 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> | ||||
|  * | ||||
|  * @version $Id: VMSVersioningFTPEntryParser.java 1747119 2016-06-07 02:22:24Z ggregory $ | ||||
|  * | ||||
|  * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions) | ||||
|  */ | ||||
| public class VMSVersioningFTPEntryParser extends VMSFTPEntryParser | ||||
| { | ||||
| 
 | ||||
|     private final Pattern _preparse_pattern_; | ||||
|     private static final String PRE_PARSE_REGEX = | ||||
|         "(.*?);([0-9]+)\\s*.*"; | ||||
| 
 | ||||
|     /** | ||||
|      * Constructor for a VMSFTPEntryParser object. | ||||
|      * | ||||
|      * @throws 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. | ||||
|      * @throws 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); | ||||
|         } | ||||
| 
 | ||||
|    } | ||||
| 
 | ||||
|     /** | ||||
|      * 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) { | ||||
|         HashMap<String, Integer> existingEntries = new HashMap<String, Integer>(); | ||||
|         ListIterator<String> iter = original.listIterator(); | ||||
|         while (iter.hasNext()) { | ||||
|             String entry = iter.next().trim(); | ||||
|             MatchResult result = null; | ||||
|             Matcher _preparse_matcher_ = _preparse_pattern_.matcher(entry); | ||||
|             if (_preparse_matcher_.matches()) { | ||||
|                 result = _preparse_matcher_.toMatchResult(); | ||||
|                 String name = result.group(1); | ||||
|                 String version = result.group(2); | ||||
|                 Integer nv = Integer.valueOf(version); | ||||
|                 Integer existing = existingEntries.get(name); | ||||
|                 if (null != existing) { | ||||
|                     if (nv.intValue() < existing.intValue()) { | ||||
|                         iter.remove();  // removes older version 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; | ||||
|             Matcher _preparse_matcher_ = _preparse_pattern_.matcher(entry); | ||||
|             if (_preparse_matcher_.matches()) { | ||||
|                 result = _preparse_matcher_.toMatchResult(); | ||||
|                 String name = result.group(1); | ||||
|                 String version = result.group(2); | ||||
|                 Integer nv = Integer.valueOf(version); | ||||
|                 Integer existing = existingEntries.get(name); | ||||
|                 if (null != existing) { | ||||
|                     if (nv.intValue() < existing.intValue()) { | ||||
|                         iter.remove(); // removes older version 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:                    ** | ||||
|  */ | ||||
| @ -0,0 +1,21 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  */ | ||||
| 
 | ||||
| /** | ||||
|  * FTP file listing parser classes | ||||
|  */ | ||||
| package org.apache.commons.net.ftp.parser; | ||||
| @ -0,0 +1,77 @@ | ||||
| /* | ||||
|  * 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.BufferedReader; | ||||
| import java.io.IOException; | ||||
| import java.io.Reader; | ||||
| 
 | ||||
| /** | ||||
|  * CRLFLineReader implements a readLine() method that requires | ||||
|  * exactly CRLF to terminate an input line. | ||||
|  * This is required for IMAP, which allows bare CR and LF. | ||||
|  * | ||||
|  * @since 3.0 | ||||
|  */ | ||||
| public final class CRLFLineReader extends BufferedReader | ||||
| { | ||||
|     private static final char LF = '\n'; | ||||
|     private static final char CR = '\r'; | ||||
| 
 | ||||
|     /** | ||||
|      * Creates a CRLFLineReader that wraps an existing Reader | ||||
|      * input source. | ||||
|      * @param reader  The Reader input source. | ||||
|      */ | ||||
|     public CRLFLineReader(Reader reader) | ||||
|     { | ||||
|         super(reader); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Read a line of text. | ||||
|      * A line is considered to be terminated by carriage return followed immediately by a linefeed. | ||||
|      * This contrasts with BufferedReader which also allows other combinations. | ||||
|      * @since 3.0 | ||||
|      */ | ||||
|     @Override | ||||
|     public String readLine() throws IOException { | ||||
|         StringBuilder sb = new StringBuilder(); | ||||
|         int intch; | ||||
|         boolean prevWasCR = false; | ||||
|         synchronized(lock) { // make thread-safe (hopefully!)
 | ||||
|             while((intch = read()) != -1) | ||||
|             { | ||||
|                 if (prevWasCR && intch == LF) { | ||||
|                     return sb.substring(0, sb.length()-1); | ||||
|                 } | ||||
|                 if (intch == CR) { | ||||
|                     prevWasCR = true; | ||||
|                 } else { | ||||
|                     prevWasCR = false; | ||||
|                 } | ||||
|                 sb.append((char) intch); | ||||
|             } | ||||
|         } | ||||
|         String string = sb.toString(); | ||||
|         if (string.length() == 0) { // immediate EOF
 | ||||
|             return null; | ||||
|         } | ||||
|         return string; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,118 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  * | ||||
|  * | ||||
|  * @see CopyStreamEvent | ||||
|  * @see CopyStreamListener | ||||
|  * @see Util | ||||
|  * @version $Id: CopyStreamAdapter.java 1741829 2016-05-01 00:24:44Z sebb $ | ||||
|  */ | ||||
| public class CopyStreamAdapter implements CopyStreamListener | ||||
| { | ||||
|     private final 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. | ||||
|      */ | ||||
|     @Override | ||||
|     public void bytesTransferred(CopyStreamEvent event) | ||||
|     { | ||||
|         for (EventListener listener : internalListeners) | ||||
|         { | ||||
|             ((CopyStreamListener) (listener)).bytesTransferred(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.  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. | ||||
|      */ | ||||
|     @Override | ||||
|     public void bytesTransferred(long totalBytesTransferred, | ||||
|                                  int bytesTransferred, long streamSize) | ||||
|     { | ||||
|         for (EventListener listener : internalListeners) | ||||
|         { | ||||
|             ((CopyStreamListener) (listener)).bytesTransferred( | ||||
|                     totalBytesTransferred, bytesTransferred, streamSize); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 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); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,111 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  * | ||||
|  * | ||||
|  * @see CopyStreamListener | ||||
|  * @see CopyStreamAdapter | ||||
|  * @see Util | ||||
|  * @version $Id: CopyStreamEvent.java 1652801 2015-01-18 17:10:05Z sebb $ | ||||
|  */ | ||||
| public class CopyStreamEvent extends EventObject | ||||
| { | ||||
|     private static final long serialVersionUID = -964927635655051867L; | ||||
| 
 | ||||
|     /** | ||||
|      * Constant used to indicate the stream size is unknown. | ||||
|      */ | ||||
|     public static final long UNKNOWN_STREAM_SIZE = -1; | ||||
| 
 | ||||
|     private final int bytesTransferred; | ||||
|     private final long totalBytesTransferred; | ||||
|     private final 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; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|       * @since 3.0 | ||||
|      */ | ||||
|     @Override | ||||
|     public String toString(){ | ||||
|         return getClass().getName() + "[source=" + source | ||||
|         + ", total=" + totalBytesTransferred | ||||
|         + ", bytes=" + bytesTransferred | ||||
|         + ", size=" + streamSize | ||||
|         + "]"; | ||||
|     } | ||||
| } | ||||
| @ -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 | ||||
|  * @version $Id: CopyStreamException.java 1299238 2012-03-10 17:12:28Z sebb $ | ||||
|  */ | ||||
| public class CopyStreamException extends IOException | ||||
| { | ||||
|     private static final long serialVersionUID = -2602899129433221532L; | ||||
| 
 | ||||
|     private final long totalBytesTransferred; | ||||
| 
 | ||||
|     /** | ||||
|      * 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); | ||||
|         initCause(exception); // merge this into super() call once we need 1.6+
 | ||||
|         totalBytesTransferred = bytesTransferred; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 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) getCause(); // cast is OK because it was initialised with an IOException
 | ||||
|     } | ||||
| } | ||||
| @ -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.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. | ||||
|  * | ||||
|  * | ||||
|  * @see CopyStreamEvent | ||||
|  * @see CopyStreamAdapter | ||||
|  * @see Util | ||||
|  * @version $Id: CopyStreamListener.java 1652801 2015-01-18 17:10:05Z sebb $ | ||||
|  */ | ||||
| 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); | ||||
| } | ||||
| @ -0,0 +1,251 @@ | ||||
| /* | ||||
|  * 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.BufferedReader; | ||||
| import java.io.IOException; | ||||
| 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, and ensures you cannot read past the end of the message. | ||||
|  * <p> | ||||
|  * Note: versions since 3.0 extend BufferedReader rather than Reader, | ||||
|  * and no longer change the CRLF into the local EOL. Also only DOT CR LF | ||||
|  * acts as EOF. | ||||
|  * @version $Id: DotTerminatedMessageReader.java 1747119 2016-06-07 02:22:24Z ggregory $ | ||||
|  */ | ||||
| public final class DotTerminatedMessageReader extends BufferedReader | ||||
| { | ||||
|     private static final char LF = '\n'; | ||||
|     private static final char CR = '\r'; | ||||
|     private static final int DOT = '.'; | ||||
| 
 | ||||
|     private boolean atBeginning; | ||||
|     private boolean eof; | ||||
|     private boolean seenCR; // was last character CR?
 | ||||
| 
 | ||||
|     /** | ||||
|      * 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); | ||||
|         // Assumes input is at start of message
 | ||||
|         atBeginning = true; | ||||
|         eof = false; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 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. | ||||
|      * @throws IOException If an error occurs while reading the underlying | ||||
|      *            stream. | ||||
|      */ | ||||
|     @Override | ||||
|     public int read() throws IOException { | ||||
|         synchronized (lock) { | ||||
|             if (eof) { | ||||
|                 return -1; // Don't allow read past EOF
 | ||||
|             } | ||||
|             int chint = super.read(); | ||||
|             if (chint == -1) { // True EOF
 | ||||
|                 eof = true; | ||||
|                 return -1; | ||||
|             } | ||||
|             if (atBeginning) { | ||||
|                 atBeginning = false; | ||||
|                 if (chint == DOT) { // Have DOT
 | ||||
|                     mark(2); // need to check for CR LF or DOT
 | ||||
|                     chint = super.read(); | ||||
|                     if (chint == -1) { // Should not happen
 | ||||
|                         // new Throwable("Trailing DOT").printStackTrace();
 | ||||
|                         eof = true; | ||||
|                         return DOT; // return the trailing DOT
 | ||||
|                     } | ||||
|                     if (chint == DOT) { // Have DOT DOT
 | ||||
|                         // no need to reset as we want to lose the first DOT
 | ||||
|                         return chint; // i.e. DOT
 | ||||
|                     } | ||||
|                     if (chint == CR) { // Have DOT CR
 | ||||
|                         chint = super.read(); | ||||
|                         if (chint == -1) { // Still only DOT CR - should not happen
 | ||||
|                             //new Throwable("Trailing DOT CR").printStackTrace();
 | ||||
|                             reset(); // So CR is picked up next time
 | ||||
|                             return DOT; // return the trailing DOT
 | ||||
|                         } | ||||
|                         if (chint == LF) { // DOT CR LF
 | ||||
|                             atBeginning = true; | ||||
|                             eof = true; | ||||
|                             // Do we need to clear the mark somehow?
 | ||||
|                             return -1; | ||||
|                         } | ||||
|                     } | ||||
|                     // Should not happen - lone DOT at beginning
 | ||||
|                     //new Throwable("Lone DOT followed by "+(char)chint).printStackTrace();
 | ||||
|                     reset(); | ||||
|                     return DOT; | ||||
|                 } // have DOT
 | ||||
|             } // atBeginning
 | ||||
| 
 | ||||
|             // Handle CRLF in normal flow
 | ||||
|             if (seenCR) { | ||||
|                 seenCR = false; | ||||
|                 if (chint == LF) { | ||||
|                     atBeginning = true; | ||||
|                 } | ||||
|             } | ||||
|             if (chint == CR) { | ||||
|                 seenCR = true; | ||||
|             } | ||||
|             return chint; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * 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. | ||||
|      * @throws 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. | ||||
|      * @throws IOException If an error occurs in reading the underlying | ||||
|      *            stream. | ||||
|      */ | ||||
|     @Override | ||||
|     public int read(char[] buffer, int offset, int length) throws IOException | ||||
|     { | ||||
|         if (length < 1) | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|         int ch; | ||||
|         synchronized (lock) | ||||
|         { | ||||
|             if ((ch = read()) == -1) | ||||
|             { | ||||
|                 return -1; | ||||
|             } | ||||
| 
 | ||||
|             int off = offset; | ||||
| 
 | ||||
|             do | ||||
|             { | ||||
|                 buffer[offset++] = (char) ch; | ||||
|             } | ||||
|             while (--length > 0 && (ch = read()) != -1); | ||||
| 
 | ||||
|             return (offset - off); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 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. | ||||
|      * @throws IOException  If an error occurs while reading the | ||||
|      *            underlying stream. | ||||
|      */ | ||||
|     @Override | ||||
|     public void close() throws IOException | ||||
|     { | ||||
|         synchronized (lock) | ||||
|         { | ||||
|             if (!eof) | ||||
|             { | ||||
|                 while (read() != -1) | ||||
|                 { | ||||
|                     // read to EOF
 | ||||
|                 } | ||||
|             } | ||||
|             eof = true; | ||||
|             atBeginning = false; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Read a line of text. | ||||
|      * A line is considered to be terminated by carriage return followed immediately by a linefeed. | ||||
|      * This contrasts with BufferedReader which also allows other combinations. | ||||
|      * @since 3.0 | ||||
|      */ | ||||
|     @Override | ||||
|     public String readLine() throws IOException { | ||||
|         StringBuilder sb = new StringBuilder(); | ||||
|         int intch; | ||||
|         synchronized(lock) { // make thread-safe (hopefully!)
 | ||||
|             while((intch = read()) != -1) | ||||
|             { | ||||
|                 if (intch == LF && atBeginning) { | ||||
|                     return sb.substring(0, sb.length()-1); | ||||
|                 } | ||||
|                 sb.append((char) intch); | ||||
|             } | ||||
|         } | ||||
|         String string = sb.toString(); | ||||
|         if (string.length() == 0) { // immediate EOF
 | ||||
|             return null; | ||||
|         } | ||||
|         // Should not happen - EOF without CRLF
 | ||||
|         //new Throwable(string).printStackTrace();
 | ||||
|         return string; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,219 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  * | ||||
|  * | ||||
|  ***/ | ||||
| 
 | ||||
| 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. | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @param ch  The character to write. | ||||
|      * @throws 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. | ||||
|      * | ||||
|      * @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. | ||||
|      * @throws 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. | ||||
|      * | ||||
|      * @param buffer  The character array to write. | ||||
|      * @throws 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. | ||||
|      * | ||||
|      * @param string  The String to write. | ||||
|      * @throws 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. | ||||
|      * | ||||
|      * @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. | ||||
|      * @throws 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. | ||||
|      * | ||||
|      * @throws 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. | ||||
|      * | ||||
|      * @throws 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; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,221 @@ | ||||
| /* | ||||
|  * 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; | ||||
| import java.io.UnsupportedEncodingException; | ||||
| 
 | ||||
| /*** | ||||
|  * 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. | ||||
|  * | ||||
|  * | ||||
|  ***/ | ||||
| 
 | ||||
| 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"); | ||||
|         try { | ||||
|             _lineSeparatorBytes = _lineSeparator.getBytes("US-ASCII"); | ||||
|         } catch (UnsupportedEncodingException e) { | ||||
|            throw new RuntimeException("Broken JVM - cannot find US-ASCII charset!",e); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     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. | ||||
|      * | ||||
|      * @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. | ||||
|      * @param input the stream to wrap | ||||
|      ***/ | ||||
|     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. | ||||
|      * | ||||
|      * @return The next character in the stream. Returns -1 if the end of the | ||||
|      *          stream has been reached. | ||||
|      * @throws 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. | ||||
|      * | ||||
|      * @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. | ||||
|      * @throws 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. | ||||
|      * | ||||
|      * @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. | ||||
|      * @throws IOException If an error occurs while reading the underlying | ||||
|      *            stream. | ||||
|      ***/ | ||||
|     @Override | ||||
|     public int read(byte buffer[], int offset, int length) throws IOException | ||||
|     { | ||||
|         if (_noConversionRequired) { | ||||
|             return super.read(buffer, offset, length); | ||||
|         } | ||||
| 
 | ||||
|         if (length < 1) { | ||||
|             return 0; | ||||
|         } | ||||
| 
 | ||||
|         int ch, off; | ||||
| 
 | ||||
|         ch = available(); | ||||
| 
 | ||||
|         __length = (length > ch ? ch : length); | ||||
| 
 | ||||
|         // 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); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     // PushbackInputStream in JDK 1.1.3 returns the wrong thing
 | ||||
|     // TODO - can we delete this override now?
 | ||||
|     /*** | ||||
|      * 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 | ||||
|     { | ||||
|         if (in == null) { | ||||
|             throw new IOException("Stream closed"); | ||||
|         } | ||||
|         return (buf.length - pos) + in.available(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,175 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  * | ||||
|  * | ||||
|  ***/ | ||||
| 
 | ||||
| public final class FromNetASCIIOutputStream extends FilterOutputStream | ||||
| { | ||||
|     private boolean __lastWasCR; | ||||
| 
 | ||||
|     /*** | ||||
|      * Creates a FromNetASCIIOutputStream instance that wraps an existing | ||||
|      * OutputStream. | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @param ch The byte to write. | ||||
|      * @throws 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. | ||||
|      * | ||||
|      * @param buffer  The byte array to write. | ||||
|      * @throws 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. | ||||
|      * | ||||
|      * @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. | ||||
|      * @throws 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. | ||||
|      * | ||||
|      * @throws 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(); | ||||
|     } | ||||
| } | ||||
| @ -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.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. | ||||
|  * | ||||
|  * | ||||
|  * @see SocketOutputStream | ||||
|  ***/ | ||||
| 
 | ||||
| public class SocketInputStream extends FilterInputStream | ||||
| { | ||||
|     private final 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. | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @throws IOException  If there is an error in closing the stream | ||||
|      *                         or socket. | ||||
|      ***/ | ||||
|     @Override | ||||
|     public void close() throws IOException | ||||
|     { | ||||
|         super.close(); | ||||
|         __socket.close(); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,88 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  * | ||||
|  * | ||||
|  * @see SocketInputStream | ||||
|  ***/ | ||||
| 
 | ||||
| public class SocketOutputStream extends FilterOutputStream | ||||
| { | ||||
|     private final 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. | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @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. | ||||
|      * @throws 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. | ||||
|      * | ||||
|      * @throws IOException  If there is an error in closing the stream | ||||
|      *                         or socket. | ||||
|      ***/ | ||||
|     @Override | ||||
|     public void close() throws IOException | ||||
|     { | ||||
|         super.close(); | ||||
|         __socket.close(); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,185 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  * | ||||
|  * | ||||
|  ***/ | ||||
| 
 | ||||
| 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. | ||||
|      * | ||||
|      * @param input  The InputStream to wrap. | ||||
|      ***/ | ||||
|     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. | ||||
|      * | ||||
|      * @return The next character in the stream. Returns -1 if the end of the | ||||
|      *          stream has been reached. | ||||
|      * @throws 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'; | ||||
|             } | ||||
|             //$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. | ||||
|      * | ||||
|      * @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. | ||||
|      * @throws 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. | ||||
|      * | ||||
|      * @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. | ||||
|      * @throws 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; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,120 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  * | ||||
|  * | ||||
|  ***/ | ||||
| 
 | ||||
| public final class ToNetASCIIOutputStream extends FilterOutputStream | ||||
| { | ||||
|     private boolean __lastWasCR; | ||||
| 
 | ||||
|     /*** | ||||
|      * Creates a ToNetASCIIOutputStream instance that wraps an existing | ||||
|      * OutputStream. | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @param ch The byte to write. | ||||
|      * @throws 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. | ||||
|      * | ||||
|      * @param buffer  The byte array to write. | ||||
|      * @throws 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. | ||||
|      * | ||||
|      * @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. | ||||
|      * @throws 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++]); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,390 @@ | ||||
| /* | ||||
|  * 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.Closeable; | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
| import java.io.OutputStream; | ||||
| import java.io.Reader; | ||||
| import java.io.Writer; | ||||
| import java.net.Socket; | ||||
| 
 | ||||
| /*** | ||||
|  * The Util class cannot be instantiated and stores short static convenience | ||||
|  * methods that are often quite useful. | ||||
|  * | ||||
|  * | ||||
|  * @see CopyStreamException | ||||
|  * @see CopyStreamListener | ||||
|  * @see CopyStreamAdapter | ||||
|  ***/ | ||||
| 
 | ||||
| public final class Util | ||||
| { | ||||
|     /** | ||||
|      * The default buffer size ({@value}) used by | ||||
|      * {@link #copyStream  copyStream } and {@link #copyReader  copyReader} | ||||
|      * and by the copyReader/copyStream methods if a zero or negative buffer size is supplied. | ||||
|      */ | ||||
|     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. | ||||
|      * | ||||
|      * @param source  The source InputStream. | ||||
|      * @param dest    The destination OutputStream. | ||||
|      * @param bufferSize  The number of bytes to buffer during the copy. | ||||
|      *            A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. | ||||
|      * @param streamSize  The number of bytes in the stream being copied. | ||||
|      *          Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown. | ||||
|      * Not currently used (though it is passed to {@link CopyStreamListener#bytesTransferred(long, int, long)} | ||||
|      * @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. | ||||
|      * @return number of bytes read/written | ||||
|      * @throws 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 numBytes; | ||||
|         long total = 0; | ||||
|         byte[] buffer = new byte[bufferSize > 0 ? bufferSize : DEFAULT_COPY_BUFFER_SIZE]; | ||||
| 
 | ||||
|         try | ||||
|         { | ||||
|             while ((numBytes = source.read(buffer)) != -1) | ||||
|             { | ||||
|                 // Technically, some read(byte[]) methods may return 0 and we cannot
 | ||||
|                 // accept that as an indication of EOF.
 | ||||
| 
 | ||||
|                 if (numBytes == 0) | ||||
|                 { | ||||
|                     int singleByte = source.read(); | ||||
|                     if (singleByte < 0) { | ||||
|                         break; | ||||
|                     } | ||||
|                     dest.write(singleByte); | ||||
|                     if(flush) { | ||||
|                         dest.flush(); | ||||
|                     } | ||||
|                     ++total; | ||||
|                     if (listener != null) { | ||||
|                         listener.bytesTransferred(total, 1, streamSize); | ||||
|                     } | ||||
|                     continue; | ||||
|                 } | ||||
| 
 | ||||
|                 dest.write(buffer, 0, numBytes); | ||||
|                 if(flush) { | ||||
|                     dest.flush(); | ||||
|                 } | ||||
|                 total += numBytes; | ||||
|                 if (listener != null) { | ||||
|                     listener.bytesTransferred(total, numBytes, 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. | ||||
|      * | ||||
|      * @param source  The source InputStream. | ||||
|      * @param dest    The destination OutputStream. | ||||
|      * @param bufferSize  The number of bytes to buffer during the copy. | ||||
|      *            A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. | ||||
|      * @param streamSize  The number of bytes in the stream being copied. | ||||
|      *          Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown. | ||||
|      * Not currently used (though it is passed to {@link CopyStreamListener#bytesTransferred(long, int, long)} | ||||
|      * @param listener  The CopyStreamListener to notify of progress.  If | ||||
|      *      this parameter is null, notification is not attempted. | ||||
|      * @return number of bytes read/written | ||||
|      * @throws 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. | ||||
|      * | ||||
|      * @param source  The source InputStream. | ||||
|      * @param dest    The destination OutputStream. | ||||
|      * @param bufferSize  The number of bytes to buffer during the copy. | ||||
|      *            A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. | ||||
|      * @return  The number of bytes read/written in the copy operation. | ||||
|      * @throws 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> | ||||
|      * @param source where to copy from | ||||
|      * @param dest  where to copy to | ||||
|      * @return number of bytes copied | ||||
|      * @throws CopyStreamException on error | ||||
|      ***/ | ||||
|     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. | ||||
|      * | ||||
|      * @param source  The source Reader. | ||||
|      * @param dest    The destination writer. | ||||
|      * @param bufferSize  The number of characters to buffer during the copy. | ||||
|      *            A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. | ||||
|      * @param streamSize  The number of characters in the stream being copied. | ||||
|      *          Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown. | ||||
|      * Not currently used (though it is passed to {@link CopyStreamListener#bytesTransferred(long, int, long)} | ||||
|      * @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. | ||||
|      * @throws 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 numChars; | ||||
|         long total = 0; | ||||
|         char[] buffer = new char[bufferSize > 0 ? bufferSize : DEFAULT_COPY_BUFFER_SIZE]; | ||||
| 
 | ||||
|         try | ||||
|         { | ||||
|             while ((numChars = source.read(buffer)) != -1) | ||||
|             { | ||||
|                 // Technically, some read(char[]) methods may return 0 and we cannot
 | ||||
|                 // accept that as an indication of EOF.
 | ||||
|                 if (numChars == 0) | ||||
|                 { | ||||
|                     int singleChar = source.read(); | ||||
|                     if (singleChar < 0) { | ||||
|                         break; | ||||
|                     } | ||||
|                     dest.write(singleChar); | ||||
|                     dest.flush(); | ||||
|                     ++total; | ||||
|                     if (listener != null) { | ||||
|                         listener.bytesTransferred(total, 1, streamSize); | ||||
|                     } | ||||
|                     continue; | ||||
|                 } | ||||
| 
 | ||||
|                 dest.write(buffer, 0, numChars); | ||||
|                 dest.flush(); | ||||
|                 total += numChars; | ||||
|                 if (listener != null) { | ||||
|                     listener.bytesTransferred(total, numChars, 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. | ||||
|      * | ||||
|      * @param source  The source Reader. | ||||
|      * @param dest    The destination writer. | ||||
|      * @param bufferSize  The number of characters to buffer during the copy. | ||||
|      *            A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. | ||||
|      * @return  The number of characters read/written in the copy operation. | ||||
|      * @throws 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> | ||||
|      * @param source where to copy from | ||||
|      * @param dest  where to copy to | ||||
|      * @return number of bytes copied | ||||
|      * @throws CopyStreamException on error | ||||
|      ***/ | ||||
|     public static final long copyReader(Reader source, Writer dest) | ||||
|     throws CopyStreamException | ||||
|     { | ||||
|         return copyReader(source, dest, DEFAULT_COPY_BUFFER_SIZE); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Closes the object quietly, catching rather than throwing IOException. | ||||
|      * Intended for use from finally blocks. | ||||
|      * | ||||
|      * @param closeable the object to close, may be {@code null} | ||||
|      * @since 3.0 | ||||
|      */ | ||||
|     public static void closeQuietly(Closeable closeable) { | ||||
|         if (closeable != null) { | ||||
|             try { | ||||
|                 closeable.close(); | ||||
|             } catch (IOException e) { | ||||
|                 // Ignored
 | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Closes the socket quietly, catching rather than throwing IOException. | ||||
|      * Intended for use from finally blocks. | ||||
|      * | ||||
|      * @param socket the socket to close, may be {@code null} | ||||
|      * @since 3.0 | ||||
|      */ | ||||
|     public static void closeQuietly(Socket socket) { | ||||
|         if (socket != null) { | ||||
|             try { | ||||
|                 socket.close(); | ||||
|             } catch (IOException e) { | ||||
|                 // Ignored
 | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,21 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  */ | ||||
| 
 | ||||
| /** | ||||
|  * Utility classes for IO support. | ||||
|  */ | ||||
| package org.apache.commons.net.io; | ||||
									
										
											File diff suppressed because it is too large
											Load Diff
										
									
								
							
						| @ -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.util; | ||||
| 
 | ||||
| import java.nio.charset.Charset; | ||||
| 
 | ||||
| /** | ||||
|  * Helps dealing with Charsets. | ||||
|  * | ||||
|  * @since 3.3 | ||||
|  */ | ||||
| public class Charsets { | ||||
| 
 | ||||
|     /** | ||||
|      * Returns a charset object for the given charset name. | ||||
|      * | ||||
|      * @param charsetName | ||||
|      *            The name of the requested charset; may be a canonical name, an alias, or null. If null, return the | ||||
|      *            default charset. | ||||
|      * @return A charset object for the named charset | ||||
|      */ | ||||
|     public static Charset toCharset(String charsetName) { | ||||
|         return charsetName == null ? Charset.defaultCharset() : Charset.forName(charsetName); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns a charset object for the given charset name. | ||||
|      * | ||||
|      * @param charsetName | ||||
|      *            The name of the requested charset; may be a canonical name, an alias, or null. | ||||
|      *            If null, return the default charset. | ||||
|      * @param defaultCharsetName the charset name to use if the requested charset is null | ||||
|      * | ||||
|      * @return A charset object for the named charset | ||||
|      */ | ||||
|     public static Charset toCharset(String charsetName, String defaultCharsetName) { | ||||
|         return charsetName == null ? Charset.forName(defaultCharsetName) : Charset.forName(charsetName); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,246 @@ | ||||
| /* | ||||
|  * 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.File; | ||||
| import java.io.FileInputStream; | ||||
| import java.io.IOException; | ||||
| import java.net.Socket; | ||||
| import java.security.GeneralSecurityException; | ||||
| import java.security.KeyStore; | ||||
| import java.security.KeyStoreException; | ||||
| import java.security.Principal; | ||||
| import java.security.PrivateKey; | ||||
| import java.security.cert.Certificate; | ||||
| import java.security.cert.X509Certificate; | ||||
| import java.util.Enumeration; | ||||
| 
 | ||||
| import javax.net.ssl.KeyManager; | ||||
| import javax.net.ssl.X509ExtendedKeyManager; | ||||
| 
 | ||||
| import org.apache.commons.net.io.Util; | ||||
| 
 | ||||
| /** | ||||
|  * General KeyManager utilities | ||||
|  * <p> | ||||
|  * How to use with a client certificate: | ||||
|  * <pre> | ||||
|  * KeyManager km = KeyManagerUtils.createClientKeyManager("JKS", | ||||
|  *     "/path/to/privatekeystore.jks","storepassword", | ||||
|  *     "privatekeyalias", "keypassword"); | ||||
|  * FTPSClient cl = new FTPSClient(); | ||||
|  * cl.setKeyManager(km); | ||||
|  * cl.connect(...); | ||||
|  * </pre> | ||||
|  * If using the default store type and the key password is the same as the | ||||
|  * store password, these parameters can be omitted. <br> | ||||
|  * If the desired key is the first or only key in the keystore, the keyAlias parameter | ||||
|  * can be omitted, in which case the code becomes: | ||||
|  * <pre> | ||||
|  * KeyManager km = KeyManagerUtils.createClientKeyManager( | ||||
|  *     "/path/to/privatekeystore.jks","storepassword"); | ||||
|  * FTPSClient cl = new FTPSClient(); | ||||
|  * cl.setKeyManager(km); | ||||
|  * cl.connect(...); | ||||
|  * </pre> | ||||
|  * | ||||
|  * @since 3.0 | ||||
|  */ | ||||
| public final class KeyManagerUtils { | ||||
| 
 | ||||
|     private static final String DEFAULT_STORE_TYPE = KeyStore.getDefaultType(); | ||||
| 
 | ||||
|     private KeyManagerUtils(){ | ||||
|         // Not instantiable
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Create a client key manager which returns a particular key. | ||||
|      * Does not handle server keys. | ||||
|      * | ||||
|      * @param ks the keystore to use | ||||
|      * @param keyAlias the alias of the key to use, may be {@code null} in which case the first key entry alias is used | ||||
|      * @param keyPass the password of the key to use | ||||
|      * @return the customised KeyManager | ||||
|      * @throws GeneralSecurityException if there is a problem creating the keystore | ||||
|      */ | ||||
|     public static KeyManager createClientKeyManager(KeyStore ks, String keyAlias, String keyPass) | ||||
|         throws GeneralSecurityException | ||||
|     { | ||||
|         ClientKeyStore cks = new ClientKeyStore(ks, keyAlias != null ? keyAlias : findAlias(ks), keyPass); | ||||
|         return new X509KeyManager(cks); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Create a client key manager which returns a particular key. | ||||
|      * Does not handle server keys. | ||||
|      * | ||||
|      * @param storeType the type of the keyStore, e.g. "JKS" | ||||
|      * @param storePath the path to the keyStore | ||||
|      * @param storePass the keyStore password | ||||
|      * @param keyAlias the alias of the key to use, may be {@code null} in which case the first key entry alias is used | ||||
|      * @param keyPass the password of the key to use | ||||
|      * @return the customised KeyManager | ||||
|      * @throws GeneralSecurityException if there is a problem creating the keystore | ||||
|      * @throws IOException if there is a problem creating the keystore | ||||
|      */ | ||||
|     public static KeyManager createClientKeyManager( | ||||
|             String storeType, File storePath, String storePass, String keyAlias, String keyPass) | ||||
|         throws IOException, GeneralSecurityException | ||||
|     { | ||||
|         KeyStore ks = loadStore(storeType, storePath, storePass); | ||||
|         return createClientKeyManager(ks, keyAlias, keyPass); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Create a client key manager which returns a particular key. | ||||
|      * Does not handle server keys. | ||||
|      * Uses the default store type and assumes the key password is the same as the store password | ||||
|      * | ||||
|      * @param storePath the path to the keyStore | ||||
|      * @param storePass the keyStore password | ||||
|      * @param keyAlias the alias of the key to use, may be {@code null} in which case the first key entry alias is used | ||||
|      * @return the customised KeyManager | ||||
|      * @throws IOException if there is a problem creating the keystore | ||||
|      * @throws GeneralSecurityException if there is a problem creating the keystore | ||||
|      */ | ||||
|     public static KeyManager createClientKeyManager(File storePath, String storePass, String keyAlias) | ||||
|         throws IOException, GeneralSecurityException | ||||
|     { | ||||
|         return createClientKeyManager(DEFAULT_STORE_TYPE, storePath, storePass, keyAlias, storePass); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Create a client key manager which returns a particular key. | ||||
|      * Does not handle server keys. | ||||
|      * Uses the default store type and assumes the key password is the same as the store password. | ||||
|      * The key alias is found by searching the keystore for the first private key entry | ||||
|      * | ||||
|      * @param storePath the path to the keyStore | ||||
|      * @param storePass the keyStore password | ||||
|      * @return the customised KeyManager | ||||
|      * @throws IOException if there is a problem creating the keystore | ||||
|      * @throws GeneralSecurityException if there is a problem creating the keystore | ||||
|      */ | ||||
|     public static KeyManager createClientKeyManager(File storePath, String storePass) | ||||
|         throws IOException, GeneralSecurityException | ||||
|     { | ||||
|         return createClientKeyManager(DEFAULT_STORE_TYPE, storePath, storePass, null, storePass); | ||||
|     } | ||||
| 
 | ||||
|     private static KeyStore loadStore(String storeType, File storePath, String storePass) | ||||
|         throws KeyStoreException,  IOException, GeneralSecurityException { | ||||
|         KeyStore ks = KeyStore.getInstance(storeType); | ||||
|         FileInputStream stream = null; | ||||
|         try { | ||||
|             stream = new FileInputStream(storePath); | ||||
|             ks.load(stream, storePass.toCharArray()); | ||||
|         } finally { | ||||
|             Util.closeQuietly(stream); | ||||
|         } | ||||
|         return ks; | ||||
|     } | ||||
| 
 | ||||
|     private static String findAlias(KeyStore ks) throws KeyStoreException { | ||||
|         Enumeration<String> e = ks.aliases(); | ||||
|         while(e.hasMoreElements()) { | ||||
|             String entry = e.nextElement(); | ||||
|             if (ks.isKeyEntry(entry)) { | ||||
|                 return entry; | ||||
|             } | ||||
|         } | ||||
|         throw new KeyStoreException("Cannot find a private key entry"); | ||||
|     } | ||||
| 
 | ||||
|     private static class ClientKeyStore { | ||||
| 
 | ||||
|         private final X509Certificate[] certChain; | ||||
|         private final PrivateKey key; | ||||
|         private final String keyAlias; | ||||
| 
 | ||||
|         ClientKeyStore(KeyStore ks, String keyAlias, String keyPass) throws GeneralSecurityException | ||||
|         { | ||||
|             this.keyAlias = keyAlias; | ||||
|             this.key = (PrivateKey) ks.getKey(this.keyAlias, keyPass.toCharArray()); | ||||
|             Certificate[] certs = ks.getCertificateChain(this.keyAlias); | ||||
|             X509Certificate[] X509certs = new X509Certificate[certs.length]; | ||||
|             for (int i=0; i < certs.length; i++) { | ||||
|                 X509certs[i] = (X509Certificate) certs[i]; | ||||
|             } | ||||
|             this.certChain = X509certs; | ||||
|         } | ||||
| 
 | ||||
|         final X509Certificate[] getCertificateChain() { | ||||
|             return this.certChain; | ||||
|         } | ||||
| 
 | ||||
|         final PrivateKey getPrivateKey() { | ||||
|             return this.key; | ||||
|         } | ||||
| 
 | ||||
|         final String getAlias() { | ||||
|             return this.keyAlias; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private static class X509KeyManager extends X509ExtendedKeyManager  { | ||||
| 
 | ||||
|         private final ClientKeyStore keyStore; | ||||
| 
 | ||||
|         X509KeyManager(final ClientKeyStore keyStore) { | ||||
|             this.keyStore = keyStore; | ||||
|         } | ||||
| 
 | ||||
|         // Call sequence: 1
 | ||||
|         @Override | ||||
|         public String chooseClientAlias(String[] keyType, Principal[] issuers, | ||||
|                 Socket socket) { | ||||
|             return keyStore.getAlias(); | ||||
|         } | ||||
| 
 | ||||
|         // Call sequence: 2
 | ||||
|         @Override | ||||
|         public X509Certificate[] getCertificateChain(String alias) { | ||||
|             return keyStore.getCertificateChain(); | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public String[] getClientAliases(String keyType, Principal[] issuers) { | ||||
|             return new String[]{ keyStore.getAlias()}; | ||||
|         } | ||||
| 
 | ||||
|         // Call sequence: 3
 | ||||
|         @Override | ||||
|         public PrivateKey getPrivateKey(String alias) { | ||||
|             return keyStore.getPrivateKey(); | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public String[] getServerAliases(String keyType, Principal[] issuers) { | ||||
|             return null; | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { | ||||
|             return null; | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,66 @@ | ||||
| /* | ||||
|  * 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; | ||||
| 
 | ||||
| /** | ||||
|  */ | ||||
| 
 | ||||
| public class ListenerList implements Serializable, Iterable<EventListener> | ||||
| { | ||||
|     private static final long serialVersionUID = -1934227607974228213L; | ||||
| 
 | ||||
|     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. | ||||
|      * | ||||
|      * @return an {@link Iterator} for the {@link EventListener} instances | ||||
|      * @since 2.0 | ||||
|      * TODO Check that this is a good defensive strategy | ||||
|      */ | ||||
|     @Override | ||||
|     public Iterator<EventListener> iterator() { | ||||
|             return __listeners.iterator(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,73 @@ | ||||
| /* | ||||
|  * 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.IOException; | ||||
| import java.security.GeneralSecurityException; | ||||
| import javax.net.ssl.KeyManager; | ||||
| import javax.net.ssl.SSLContext; | ||||
| import javax.net.ssl.TrustManager; | ||||
| 
 | ||||
| /** | ||||
|  * General utilities for SSLContext. | ||||
|  * @since 3.0 | ||||
|  */ | ||||
| public class SSLContextUtils { | ||||
| 
 | ||||
|     private SSLContextUtils() { | ||||
|         // Not instantiable
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Create and initialise an SSLContext. | ||||
|      * @param protocol the protocol used to instatiate the context | ||||
|      * @param keyManager the key manager, may be {@code null} | ||||
|      * @param trustManager the trust manager, may be {@code null} | ||||
|      * @return the initialised context. | ||||
|      * @throws IOException this is used to wrap any {@link GeneralSecurityException} that occurs | ||||
|      */ | ||||
|     public static SSLContext createSSLContext(String protocol, KeyManager keyManager, TrustManager trustManager) | ||||
|             throws IOException { | ||||
|         return createSSLContext(protocol, | ||||
|                 keyManager == null ? null : new KeyManager[] { keyManager }, | ||||
|                 trustManager == null ? null : new TrustManager[] { trustManager }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Create and initialise an SSLContext. | ||||
|      * @param protocol the protocol used to instatiate the context | ||||
|      * @param keyManagers the array of key managers, may be {@code null} but array entries must not be {@code null} | ||||
|      * @param trustManagers the array of trust managers, may be {@code null} but array entries must not be {@code null} | ||||
|      * @return the initialised context. | ||||
|      * @throws IOException this is used to wrap any {@link GeneralSecurityException} that occurs | ||||
|      */ | ||||
|     public static SSLContext createSSLContext(String protocol, KeyManager[] keyManagers, TrustManager[] trustManagers) | ||||
|         throws IOException { | ||||
|         SSLContext ctx; | ||||
|         try { | ||||
|             ctx = SSLContext.getInstance(protocol); | ||||
|             ctx.init(keyManagers, trustManagers, /*SecureRandom*/ null); | ||||
|         } catch (GeneralSecurityException e) { | ||||
|             IOException ioe = new IOException("Could not initialize SSL context"); | ||||
|             ioe.initCause(e); | ||||
|             throw ioe; | ||||
|         } | ||||
|         return ctx; | ||||
|     } | ||||
| } | ||||
| @ -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.lang.reflect.InvocationTargetException; | ||||
| import java.lang.reflect.Method; | ||||
| 
 | ||||
| import javax.net.ssl.SSLSocket; | ||||
| 
 | ||||
| /** | ||||
|  * General utilities for SSLSocket. | ||||
|  * @since 3.4 | ||||
|  */ | ||||
| public class SSLSocketUtils { | ||||
|     private SSLSocketUtils() { | ||||
|         // Not instantiable
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Enable the HTTPS endpoint identification algorithm on an SSLSocket. | ||||
|      * @param socket the SSL socket | ||||
|      * @return {@code true} on success (this is only supported on Java 1.7+) | ||||
|      */ | ||||
|     public static boolean enableEndpointNameVerification(SSLSocket socket) { | ||||
|         try { | ||||
|             Class<?> cls = Class.forName("javax.net.ssl.SSLParameters"); | ||||
|             Method setEndpointIdentificationAlgorithm = cls.getDeclaredMethod("setEndpointIdentificationAlgorithm", String.class); | ||||
|             Method getSSLParameters = SSLSocket.class.getDeclaredMethod("getSSLParameters"); | ||||
|             Method setSSLParameters = SSLSocket.class.getDeclaredMethod("setSSLParameters", cls); | ||||
|             if (setEndpointIdentificationAlgorithm != null && getSSLParameters != null && setSSLParameters != null) { | ||||
|                 Object sslParams = getSSLParameters.invoke(socket); | ||||
|                 if (sslParams != null) { | ||||
|                     setEndpointIdentificationAlgorithm.invoke(sslParams, "HTTPS"); | ||||
|                     setSSLParameters.invoke(socket, sslParams); | ||||
|                     return true; | ||||
|                 } | ||||
|             } | ||||
|         } catch (SecurityException e) { // Ignored
 | ||||
|         } catch (ClassNotFoundException e) { // Ignored
 | ||||
|         } catch (NoSuchMethodException e) { // Ignored
 | ||||
|         } catch (IllegalArgumentException e) { // Ignored
 | ||||
|         } catch (IllegalAccessException e) { // Ignored
 | ||||
|         } catch (InvocationTargetException e) { // Ignored
 | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,362 @@ | ||||
| /* | ||||
|  * 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" | ||||
|  * @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; | ||||
| 
 | ||||
|     /** Whether the broadcast/network address are included in host count */ | ||||
|     private boolean inclusiveHostCount = false; | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * 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" | ||||
|      * @throws IllegalArgumentException if the parameter is invalid, | ||||
|      * i.e. does not match n.n.n.n/m where n=1-3 decimal digits, m = 1-3 decimal digits in range 1-32 | ||||
|      */ | ||||
|     public SubnetUtils(String cidrNotation) { | ||||
|         calculate(cidrNotation); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Constructor that takes a dotted decimal address and a dotted decimal mask. | ||||
|      * @param address An IP address, e.g. "192.168.0.1" | ||||
|      * @param mask A dotted decimal netmask e.g. "255.255.0.0" | ||||
|      * @throws IllegalArgumentException if the address or mask is invalid, | ||||
|      * i.e. does not match n.n.n.n where n=1-3 decimal digits and the mask is not all zeros | ||||
|      */ | ||||
|     public SubnetUtils(String address, String mask) { | ||||
|         calculate(toCidrNotation(address, mask)); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * Returns <code>true</code> if the return value of {@link SubnetInfo#getAddressCount()} | ||||
|      * includes the network and broadcast addresses. | ||||
|      * @since 2.2 | ||||
|      * @return true if the hostcount includes the network and broadcast addresses | ||||
|      */ | ||||
|     public boolean isInclusiveHostCount() { | ||||
|         return inclusiveHostCount; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Set to <code>true</code> if you want the return value of {@link SubnetInfo#getAddressCount()} | ||||
|      * to include the network and broadcast addresses. | ||||
|      * @param inclusiveHostCount true if network and broadcast addresses are to be included | ||||
|      * @since 2.2 | ||||
|      */ | ||||
|     public void setInclusiveHostCount(boolean inclusiveHostCount) { | ||||
|         this.inclusiveHostCount = inclusiveHostCount; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * Convenience container for subnet summary information. | ||||
|      * | ||||
|      */ | ||||
|     public final class SubnetInfo { | ||||
|         /* Mask to convert unsigned int to a long (i.e. keep 32 bits) */ | ||||
|         private static final long UNSIGNED_INT_MASK = 0x0FFFFFFFFL; | ||||
| 
 | ||||
|         private SubnetInfo() {} | ||||
| 
 | ||||
|         private int netmask()       { return netmask; } | ||||
|         private int network()       { return network; } | ||||
|         private int address()       { return address; } | ||||
|         private int broadcast()     { return broadcast; } | ||||
| 
 | ||||
|         // long versions of the values (as unsigned int) which are more suitable for range checking
 | ||||
|         private long networkLong()  { return network &  UNSIGNED_INT_MASK; } | ||||
|         private long broadcastLong(){ return broadcast &  UNSIGNED_INT_MASK; } | ||||
| 
 | ||||
|         private int low() { | ||||
|             return (isInclusiveHostCount() ? network() : | ||||
|                 broadcastLong() - networkLong() > 1 ? network() + 1 : 0); | ||||
|         } | ||||
| 
 | ||||
|         private int high() { | ||||
|             return (isInclusiveHostCount() ? broadcast() : | ||||
|                 broadcastLong() - networkLong() > 1 ? broadcast() -1  : 0); | ||||
|         } | ||||
| 
 | ||||
|         /** | ||||
|          * Returns true if the parameter <code>address</code> is in the | ||||
|          * range of usable endpoint addresses for this subnet. This excludes the | ||||
|          * network and broadcast adresses. | ||||
|          * @param address A dot-delimited IPv4 address, e.g. "192.168.0.1" | ||||
|          * @return True if in range, false otherwise | ||||
|          */ | ||||
|         public boolean isInRange(String address) { | ||||
|             return isInRange(toInteger(address)); | ||||
|         } | ||||
| 
 | ||||
|         /** | ||||
|          * | ||||
|          * @param address the address to check | ||||
|          * @return true if it is in range | ||||
|          * @since 3.4 (made public) | ||||
|          */ | ||||
|         public boolean isInRange(int address) { | ||||
|             long addLong = address & UNSIGNED_INT_MASK; | ||||
|             long lowLong = low() & UNSIGNED_INT_MASK; | ||||
|             long highLong = high() & UNSIGNED_INT_MASK; | ||||
|             return addLong >= lowLong && addLong <= highLong; | ||||
|         } | ||||
| 
 | ||||
|         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())); | ||||
|         } | ||||
| 
 | ||||
|         /** | ||||
|          * Return the low address as a dotted IP address. | ||||
|          * Will be zero for CIDR/31 and CIDR/32 if the inclusive flag is false. | ||||
|          * | ||||
|          * @return the IP address in dotted format, may be "0.0.0.0" if there is no valid address | ||||
|          */ | ||||
|         public String getLowAddress() { | ||||
|             return format(toArray(low())); | ||||
|         } | ||||
| 
 | ||||
|         /** | ||||
|          * Return the high address as a dotted IP address. | ||||
|          * Will be zero for CIDR/31 and CIDR/32 if the inclusive flag is false. | ||||
|          * | ||||
|          * @return the IP address in dotted format, may be "0.0.0.0" if there is no valid address | ||||
|          */ | ||||
|         public String getHighAddress() { | ||||
|             return format(toArray(high())); | ||||
|         } | ||||
| 
 | ||||
|         /** | ||||
|          * Get the count of available addresses. | ||||
|          * Will be zero for CIDR/31 and CIDR/32 if the inclusive flag is false. | ||||
|          * @return the count of addresses, may be zero. | ||||
|          * @throws RuntimeException if the correct count is greater than {@code Integer.MAX_VALUE} | ||||
|          * @deprecated (3.4) use {@link #getAddressCountLong()} instead | ||||
|          */ | ||||
|         @Deprecated | ||||
|         public int getAddressCount() { | ||||
|             long countLong = getAddressCountLong(); | ||||
|             if (countLong > Integer.MAX_VALUE) { | ||||
|                 throw new RuntimeException("Count is larger than an integer: " + countLong); | ||||
|             } | ||||
|             // N.B. cannot be negative
 | ||||
|             return (int)countLong; | ||||
|         } | ||||
| 
 | ||||
|         /** | ||||
|          * Get the count of available addresses. | ||||
|          * Will be zero for CIDR/31 and CIDR/32 if the inclusive flag is false. | ||||
|          * @return the count of addresses, may be zero. | ||||
|          * @since 3.4 | ||||
|          */ | ||||
|         public long getAddressCountLong() { | ||||
|             long b = broadcastLong(); | ||||
|             long n = networkLong(); | ||||
|             long count = b - n + (isInclusiveHostCount() ? 1 : -1); | ||||
|             return count < 0 ? 0 : count; | ||||
|         } | ||||
| 
 | ||||
|         public int asInteger(String address) { | ||||
|             return toInteger(address); | ||||
|         } | ||||
| 
 | ||||
|         public String getCidrSignature() { | ||||
|             return toCidrNotation( | ||||
|                     format(toArray(address())), | ||||
|                     format(toArray(netmask())) | ||||
|             ); | ||||
|         } | ||||
| 
 | ||||
|         public String[] getAllAddresses() { | ||||
|             int ct = getAddressCount(); | ||||
|             String[] addresses = new String[ct]; | ||||
|             if (ct == 0) { | ||||
|                 return addresses; | ||||
|             } | ||||
|             for (int add = low(), j=0; add <= high(); ++add, ++j) { | ||||
|                 addresses[j] = format(toArray(add)); | ||||
|             } | ||||
|             return addresses; | ||||
|         } | ||||
| 
 | ||||
|         /** | ||||
|          * {@inheritDoc} | ||||
|          * @since 2.2 | ||||
|          */ | ||||
|         @Override | ||||
|         public String toString() { | ||||
|             final StringBuilder buf = new StringBuilder(); | ||||
|             buf.append("CIDR Signature:\t[").append(getCidrSignature()).append("]") | ||||
|                 .append(" Netmask: [").append(getNetmask()).append("]\n") | ||||
|                 .append("Network:\t[").append(getNetworkAddress()).append("]\n") | ||||
|                 .append("Broadcast:\t[").append(getBroadcastAddress()).append("]\n") | ||||
|                  .append("First Address:\t[").append(getLowAddress()).append("]\n") | ||||
|                  .append("Last Address:\t[").append(getHighAddress()).append("]\n") | ||||
|                  .append("# Addresses:\t[").append(getAddressCount()).append("]\n"); | ||||
|             return buf.toString(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Return a {@link SubnetInfo} instance that contains subnet-specific statistics | ||||
|      * @return new instance | ||||
|      */ | ||||
|     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); | ||||
|             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. | ||||
|      * Checks if a value x is in the range [begin,end]. | ||||
|      * Returns x if it is in range, throws an exception otherwise. | ||||
|      */ | ||||
|     private int rangeCheck(int value, int begin, int end) { | ||||
|         if (value >= begin && value <= end) { // (begin,end]
 | ||||
|             return value; | ||||
|         } | ||||
| 
 | ||||
|         throw new IllegalArgumentException("Value [" + value + "] not in range ["+begin+","+end+"]"); | ||||
|     } | ||||
| 
 | ||||
|     /* | ||||
|      * 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)); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,117 @@ | ||||
| /* | ||||
|  * 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.security.GeneralSecurityException; | ||||
| import java.security.KeyStore; | ||||
| import java.security.cert.CertificateException; | ||||
| import java.security.cert.X509Certificate; | ||||
| 
 | ||||
| import javax.net.ssl.TrustManagerFactory; | ||||
| import javax.net.ssl.X509TrustManager; | ||||
| 
 | ||||
| /** | ||||
|  * TrustManager utilities for generating TrustManagers. | ||||
|  * | ||||
|  * @since 3.0 | ||||
|  */ | ||||
| public final class TrustManagerUtils | ||||
| { | ||||
|     private static final X509Certificate[] EMPTY_X509CERTIFICATE_ARRAY = new X509Certificate[]{}; | ||||
| 
 | ||||
|     private static class TrustManager implements X509TrustManager { | ||||
| 
 | ||||
|         private final boolean checkServerValidity; | ||||
| 
 | ||||
|         TrustManager(boolean checkServerValidity) { | ||||
|             this.checkServerValidity = checkServerValidity; | ||||
|         } | ||||
| 
 | ||||
|         /** | ||||
|          * Never generates a CertificateException. | ||||
|          */ | ||||
|         @Override | ||||
|         public void checkClientTrusted(X509Certificate[] certificates, String authType) | ||||
|         { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public void checkServerTrusted(X509Certificate[] certificates, String authType) | ||||
|             throws CertificateException | ||||
|         { | ||||
|             if (checkServerValidity) { | ||||
|                 for (X509Certificate certificate : certificates) | ||||
|                 { | ||||
|                     certificate.checkValidity(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         /** | ||||
|          * @return an empty array of certificates | ||||
|          */ | ||||
|         @Override | ||||
|         public X509Certificate[] getAcceptedIssuers() | ||||
|         { | ||||
|             return EMPTY_X509CERTIFICATE_ARRAY; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private static final X509TrustManager ACCEPT_ALL=new TrustManager(false); | ||||
| 
 | ||||
|     private static final X509TrustManager CHECK_SERVER_VALIDITY=new TrustManager(true); | ||||
| 
 | ||||
|     /** | ||||
|      * Generate a TrustManager that performs no checks. | ||||
|      * | ||||
|      * @return the TrustManager | ||||
|      */ | ||||
|     public static X509TrustManager getAcceptAllTrustManager(){ | ||||
|         return ACCEPT_ALL; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Generate a TrustManager that checks server certificates for validity, | ||||
|      * but otherwise performs no checks. | ||||
|      * | ||||
|      * @return the validating TrustManager | ||||
|      */ | ||||
|     public static X509TrustManager getValidateServerCertificateTrustManager(){ | ||||
|         return CHECK_SERVER_VALIDITY; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Return the default TrustManager provided by the JVM. | ||||
|      * <p> | ||||
|      * This should be the same as the default used by | ||||
|      *  {@link javax.net.ssl.SSLContext#init(javax.net.ssl.KeyManager[], javax.net.ssl.TrustManager[], java.security.SecureRandom) | ||||
|      * SSLContext#init(KeyManager[], TrustManager[], SecureRandom)} | ||||
|      * when the TrustManager parameter is set to {@code null} | ||||
|      * @param keyStore the KeyStore to use, may be {@code null} | ||||
|      * @return the default TrustManager | ||||
|      * @throws GeneralSecurityException if an error occurs | ||||
|      */ | ||||
|     public static X509TrustManager getDefaultTrustManager(KeyStore keyStore) throws GeneralSecurityException { | ||||
|         String defaultAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); | ||||
|         TrustManagerFactory instance = TrustManagerFactory.getInstance(defaultAlgorithm); | ||||
|         instance.init(keyStore); | ||||
|         return (X509TrustManager) instance.getTrustManagers()[0]; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,21 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  */ | ||||
| 
 | ||||
| /** | ||||
|  * Utility classes | ||||
|  */ | ||||
| package org.apache.commons.net.util; | ||||
| @ -0,0 +1,16 @@ | ||||
| package com.arialyy.ftpplug; | ||||
| 
 | ||||
| import org.junit.Test; | ||||
| 
 | ||||
| import static org.junit.Assert.*; | ||||
| 
 | ||||
| /** | ||||
|  * Example local unit test, which will execute on the development machine (host). | ||||
|  * | ||||
|  * @see <a href="http://d.android.com/tools/testing">Testing documentation</a> | ||||
|  */ | ||||
| public class ExampleUnitTest { | ||||
|   @Test public void addition_isCorrect() throws Exception { | ||||
|     assertEquals(4, 2 + 2); | ||||
|   } | ||||
| } | ||||
| @ -1 +1 @@ | ||||
| include ':app', ':Aria', ':AriaAnnotations', ':AriaCompiler' | ||||
| include ':app', ':Aria', ':AriaAnnotations', ':AriaCompiler', ':AriaFtpPlug' | ||||
|  | ||||
					Loading…
					
					
				
		Reference in new issue