ftp
This commit is contained in:
@ -23,5 +23,6 @@ dependencies {
|
|||||||
testCompile 'junit:junit:4.12'
|
testCompile 'junit:junit:4.12'
|
||||||
compile 'com.android.support:appcompat-v7:23.1.1'
|
compile 'com.android.support:appcompat-v7:23.1.1'
|
||||||
compile project(':AriaAnnotations')
|
compile project(':AriaAnnotations')
|
||||||
|
compile project(':AriaFtpPlug')
|
||||||
}
|
}
|
||||||
apply from: 'bintray-release.gradle'
|
apply from: 'bintray-release.gradle'
|
||||||
|
@ -58,13 +58,24 @@ public class DownloadReceiver extends AbsReceiver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加载下载地址
|
* 加载Http下载功能
|
||||||
*/
|
*/
|
||||||
public DownloadTarget load(@NonNull String downloadUrl) {
|
public DownloadTarget load(@NonNull String downloadUrl) {
|
||||||
CheckUtil.checkDownloadUrl(downloadUrl);
|
CheckUtil.checkDownloadUrl(downloadUrl);
|
||||||
return new DownloadTarget(downloadUrl, targetName);
|
return new DownloadTarget(downloadUrl, targetName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载Ftp下载功能
|
||||||
|
*
|
||||||
|
* @param serverIp ftp服务器地址
|
||||||
|
* @param port ftp端口
|
||||||
|
* @param filePath 需要从ftp服务器上下载的文件的路径
|
||||||
|
*/
|
||||||
|
public FtpDownloadTarget load(String serverIp, String port, String filePath) {
|
||||||
|
return new FtpDownloadTarget(serverIp, port, filePath, targetName);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加载下载地址,如果任务组的中的下载地址改变了,则任务从新的一个任务组
|
* 加载下载地址,如果任务组的中的下载地址改变了,则任务从新的一个任务组
|
||||||
*/
|
*/
|
||||||
|
@ -19,8 +19,11 @@ package com.arialyy.aria.core.download;
|
|||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import com.arialyy.aria.core.AriaManager;
|
import com.arialyy.aria.core.AriaManager;
|
||||||
|
import com.arialyy.aria.core.download.downloader.FtpDownloadUtil;
|
||||||
|
import com.arialyy.aria.core.download.downloader.IDownloadUtil;
|
||||||
import com.arialyy.aria.core.download.downloader.SimpleDownloadUtil;
|
import com.arialyy.aria.core.download.downloader.SimpleDownloadUtil;
|
||||||
import com.arialyy.aria.core.inf.AbsNormalTask;
|
import com.arialyy.aria.core.inf.AbsNormalTask;
|
||||||
|
import com.arialyy.aria.core.inf.AbsTaskEntity;
|
||||||
import com.arialyy.aria.core.inf.IEntity;
|
import com.arialyy.aria.core.inf.IEntity;
|
||||||
import com.arialyy.aria.core.scheduler.ISchedulers;
|
import com.arialyy.aria.core.scheduler.ISchedulers;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@ -33,14 +36,18 @@ public class DownloadTask extends AbsNormalTask<DownloadEntity> {
|
|||||||
public static final String TAG = "DownloadTask";
|
public static final String TAG = "DownloadTask";
|
||||||
|
|
||||||
private DownloadListener mListener;
|
private DownloadListener mListener;
|
||||||
private SimpleDownloadUtil mUtil;
|
private IDownloadUtil mUtil;
|
||||||
|
|
||||||
private DownloadTask(DownloadTaskEntity taskEntity, Handler outHandler) {
|
private DownloadTask(DownloadTaskEntity taskEntity, Handler outHandler) {
|
||||||
mEntity = taskEntity.getEntity();
|
mEntity = taskEntity.getEntity();
|
||||||
mOutHandler = outHandler;
|
mOutHandler = outHandler;
|
||||||
mContext = AriaManager.APP;
|
mContext = AriaManager.APP;
|
||||||
mListener = new DownloadListener(this, mOutHandler);
|
mListener = new DownloadListener(this, mOutHandler);
|
||||||
mUtil = new SimpleDownloadUtil(taskEntity, mListener);
|
if (taskEntity.downloadType == AbsTaskEntity.HTTP) {
|
||||||
|
mUtil = new SimpleDownloadUtil(taskEntity, mListener);
|
||||||
|
}else if (taskEntity.downloadType == AbsTaskEntity.FTP){
|
||||||
|
mUtil = new FtpDownloadUtil(taskEntity, mListener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
package com.arialyy.aria.core.download;
|
package com.arialyy.aria.core.download;
|
||||||
|
|
||||||
import com.arialyy.aria.core.inf.AbsTaskEntity;
|
import com.arialyy.aria.core.inf.AbsTaskEntity;
|
||||||
|
import com.arialyy.aria.orm.Ignore;
|
||||||
import com.arialyy.aria.orm.OneToOne;
|
import com.arialyy.aria.orm.OneToOne;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -24,6 +25,17 @@ import com.arialyy.aria.orm.OneToOne;
|
|||||||
*/
|
*/
|
||||||
public class DownloadTaskEntity extends AbsTaskEntity<DownloadEntity> {
|
public class DownloadTaskEntity extends AbsTaskEntity<DownloadEntity> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 账号和密码
|
||||||
|
*/
|
||||||
|
@Ignore public String userName, userPw;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载类型
|
||||||
|
* {@link AbsTaskEntity#HTTP}、{@link AbsTaskEntity#FTP}
|
||||||
|
*/
|
||||||
|
public int downloadType = HTTP;
|
||||||
|
|
||||||
@OneToOne(table = DownloadEntity.class, key = "downloadPath") public DownloadEntity entity;
|
@OneToOne(table = DownloadEntity.class, key = "downloadPath") public DownloadEntity entity;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -204,6 +204,10 @@ public class DownloadGroupUtil implements IDownloadUtil {
|
|||||||
mListener.onResume(mCurrentLocation);
|
mListener.onResume(mCurrentLocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override public void setMaxSpeed(double maxSpeed) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建文件信息获取线程
|
* 创建文件信息获取线程
|
||||||
*/
|
*/
|
||||||
|
@ -1,10 +1,25 @@
|
|||||||
package com.arialyy.aria.core.download.downloader;
|
package com.arialyy.aria.core.download.downloader;
|
||||||
|
|
||||||
|
import com.arialyy.aria.core.download.DownloadEntity;
|
||||||
|
import com.arialyy.aria.core.download.DownloadTaskEntity;
|
||||||
|
import java.io.IOException;
|
||||||
|
import org.apache.commons.net.ftp.FTPClient;
|
||||||
|
import org.apache.commons.net.ftp.FTPReply;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Aria.Lao on 2017/7/21.
|
* Created by Aria.Lao on 2017/7/21.
|
||||||
*/
|
*/
|
||||||
public class FtpDownloadUtil implements IDownloadUtil, Runnable{
|
public class FtpDownloadUtil implements IDownloadUtil, Runnable {
|
||||||
|
|
||||||
|
private IDownloadListener mListener;
|
||||||
|
private DownloadTaskEntity mTaskEntity;
|
||||||
|
private DownloadEntity mEntity;
|
||||||
|
|
||||||
|
public FtpDownloadUtil(DownloadTaskEntity entity, IDownloadListener downloadListener) {
|
||||||
|
mTaskEntity = entity;
|
||||||
|
mListener = downloadListener;
|
||||||
|
mEntity = mTaskEntity.getEntity();
|
||||||
|
}
|
||||||
|
|
||||||
@Override public long getFileSize() {
|
@Override public long getFileSize() {
|
||||||
return 0;
|
return 0;
|
||||||
@ -27,14 +42,36 @@ public class FtpDownloadUtil implements IDownloadUtil, Runnable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override public void startDownload() {
|
@Override public void startDownload() {
|
||||||
|
mListener.onPre();
|
||||||
|
new Thread(this).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void resumeDownload() {
|
@Override public void resumeDownload() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override public void setMaxSpeed(double maxSpeed) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override public void run() {
|
@Override public void run() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void failDownload(String msg) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void test() throws IOException {
|
||||||
|
FTPClient client = new FTPClient();
|
||||||
|
client.connect(mEntity.getDownloadUrl());
|
||||||
|
client.login(mTaskEntity.userName, mTaskEntity.userPw);
|
||||||
|
int reply = client.getReplyCode();
|
||||||
|
if (!FTPReply.isPositiveCompletion(reply)) {
|
||||||
|
client.disconnect();
|
||||||
|
failDownload("无法连接到ftp服务器,错误码为:" + reply);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,4 +58,9 @@ public interface IDownloadUtil {
|
|||||||
* 从上次断点恢复下载
|
* 从上次断点恢复下载
|
||||||
*/
|
*/
|
||||||
void resumeDownload();
|
void resumeDownload();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置最大下载速度
|
||||||
|
*/
|
||||||
|
void setMaxSpeed(double maxSpeed);
|
||||||
}
|
}
|
@ -26,6 +26,8 @@ import java.util.Map;
|
|||||||
* Created by lyy on 2017/2/23.
|
* Created by lyy on 2017/2/23.
|
||||||
*/
|
*/
|
||||||
public abstract class AbsTaskEntity<ENTITY extends AbsEntity> extends DbEntity {
|
public abstract class AbsTaskEntity<ENTITY extends AbsEntity> extends DbEntity {
|
||||||
|
public static final int HTTP = 0x11;
|
||||||
|
public static final int FTP = 0x12;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Task实体对应的key
|
* Task实体对应的key
|
||||||
|
1
AriaFtpPlug/.gitignore
vendored
Normal file
1
AriaFtpPlug/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/build
|
25
AriaFtpPlug/build.gradle
Normal file
25
AriaFtpPlug/build.gradle
Normal file
@ -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'
|
||||||
|
}
|
25
AriaFtpPlug/proguard-rules.pro
vendored
Normal file
25
AriaFtpPlug/proguard-rules.pro
vendored
Normal file
@ -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());
|
||||||
|
}
|
||||||
|
}
|
12
AriaFtpPlug/src/main/AndroidManifest.xml
Normal file
12
AriaFtpPlug/src/main/AndroidManifest.xml
Normal file
@ -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>
|
8
AriaFtpPlug/src/main/java/com/arialyy/ftpplug/Test.java
Normal file
8
AriaFtpPlug/src/main/java/com/arialyy/ftpplug/Test.java
Normal file
@ -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);
|
||||||
|
}
|
1870
AriaFtpPlug/src/main/java/org/apache/commons/net/ftp/FTP.java
Normal file
1870
AriaFtpPlug/src/main/java/org/apache/commons/net/ftp/FTP.java
Normal file
File diff suppressed because it is too large
Load Diff
3980
AriaFtpPlug/src/main/java/org/apache/commons/net/ftp/FTPClient.java
Normal file
3980
AriaFtpPlug/src/main/java/org/apache/commons/net/ftp/FTPClient.java
Normal file
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
116
AriaFtpPlug/src/main/java/org/apache/commons/net/ftp/FTPCmd.java
Normal file
116
AriaFtpPlug/src/main/java/org/apache/commons/net/ftp/FTPCmd.java
Normal file
@ -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++]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
390
AriaFtpPlug/src/main/java/org/apache/commons/net/io/Util.java
Normal file
390
AriaFtpPlug/src/main/java/org/apache/commons/net/io/Util.java
Normal file
@ -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;
|
1046
AriaFtpPlug/src/main/java/org/apache/commons/net/util/Base64.java
Normal file
1046
AriaFtpPlug/src/main/java/org/apache/commons/net/util/Base64.java
Normal file
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'
|
||||||
|
Reference in New Issue
Block a user