commit
0d4f29d921
@ -0,0 +1,398 @@ |
|||||||
|
/* |
||||||
|
* 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.common; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.util.Log; |
||||||
|
import android.util.SparseArray; |
||||||
|
import com.arialyy.aria.core.AriaManager; |
||||||
|
import com.arialyy.aria.core.download.DownloadTaskEntity; |
||||||
|
import com.arialyy.aria.core.inf.AbsNormalEntity; |
||||||
|
import com.arialyy.aria.core.inf.AbsTaskEntity; |
||||||
|
import com.arialyy.aria.core.inf.IDownloadListener; |
||||||
|
import com.arialyy.aria.core.inf.IEventListener; |
||||||
|
import com.arialyy.aria.core.upload.UploadTaskEntity; |
||||||
|
import com.arialyy.aria.util.CommonUtil; |
||||||
|
import java.io.File; |
||||||
|
import java.util.Properties; |
||||||
|
import java.util.Set; |
||||||
|
import java.util.Timer; |
||||||
|
import java.util.TimerTask; |
||||||
|
import java.util.concurrent.ExecutorService; |
||||||
|
import java.util.concurrent.Executors; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by AriaL on 2017/7/1. |
||||||
|
* 文件下载器 |
||||||
|
*/ |
||||||
|
public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY extends AbsTaskEntity<ENTITY>> |
||||||
|
implements Runnable, IUtil { |
||||||
|
private final String TAG = "Downloader"; |
||||||
|
protected IEventListener mListener; |
||||||
|
protected TASK_ENTITY mTaskEntity; |
||||||
|
protected ENTITY mEntity; |
||||||
|
protected File mConfigFile;//信息配置文件
|
||||||
|
protected Context mContext; |
||||||
|
protected File mTempFile; //下载的文件
|
||||||
|
protected boolean isNewTask = true; |
||||||
|
protected StateConstance mConstance; |
||||||
|
private ExecutorService mFixedThreadPool; |
||||||
|
private int mThreadNum, mRealThreadNum; |
||||||
|
private SparseArray<AbsThreadTask> mTask = new SparseArray<>(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 小于1m的文件不启用多线程 |
||||||
|
*/ |
||||||
|
private static final long SUB_LEN = 1024 * 1024; |
||||||
|
private Timer mTimer; |
||||||
|
|
||||||
|
protected AbsFileer(IEventListener listener, TASK_ENTITY taskEntity) { |
||||||
|
mListener = listener; |
||||||
|
mTaskEntity = taskEntity; |
||||||
|
mEntity = mTaskEntity.getEntity(); |
||||||
|
mContext = AriaManager.APP; |
||||||
|
mConstance = new StateConstance(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void setMaxSpeed(double maxSpeed) { |
||||||
|
for (int i = 0; i < mThreadNum; i++) { |
||||||
|
AbsThreadTask task = mTask.get(i); |
||||||
|
if (task != null) { |
||||||
|
task.setMaxSpeed(maxSpeed); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public StateConstance getConstance() { |
||||||
|
return mConstance; |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void run() { |
||||||
|
startFlow(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 开始下载流程 |
||||||
|
*/ |
||||||
|
private void startFlow() { |
||||||
|
checkTask(); |
||||||
|
if (mListener instanceof IDownloadListener) { |
||||||
|
((IDownloadListener) mListener).onPostPre(mEntity.getFileSize()); |
||||||
|
} |
||||||
|
mConstance.cleanState(); |
||||||
|
mConstance.isRunning = true; |
||||||
|
if (!mTaskEntity.isSupportBP) { |
||||||
|
mThreadNum = 1; |
||||||
|
mConstance.THREAD_NUM = mThreadNum; |
||||||
|
handleNoSupportBP(); |
||||||
|
} else { |
||||||
|
mThreadNum = isNewTask ? (getNewTaskThreadNum()) : mRealThreadNum; |
||||||
|
mConstance.THREAD_NUM = mThreadNum; |
||||||
|
handleBreakpoint(); |
||||||
|
} |
||||||
|
startTimer(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 设置新任务的最大线程数 |
||||||
|
*/ |
||||||
|
protected int getNewTaskThreadNum() { |
||||||
|
return mEntity.getFileSize() <= SUB_LEN || mTaskEntity.requestType == AbsTaskEntity.FTP_DIR ? 1 |
||||||
|
: AriaManager.getInstance(mContext).getDownloadConfig().getThreadNum(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 启动进度获取定时器 |
||||||
|
*/ |
||||||
|
private void startTimer() { |
||||||
|
mTimer = new Timer(true); |
||||||
|
mTimer.schedule(new TimerTask() { |
||||||
|
@Override public void run() { |
||||||
|
if (mConstance.isComplete() || !mConstance.isRunning) { |
||||||
|
closeTimer(); |
||||||
|
} else if (mConstance.CURRENT_LOCATION >= 0) { |
||||||
|
mListener.onProgress(mConstance.CURRENT_LOCATION); |
||||||
|
} |
||||||
|
} |
||||||
|
}, 0, 1000); |
||||||
|
} |
||||||
|
|
||||||
|
private void closeTimer() { |
||||||
|
if (mTimer != null) { |
||||||
|
mTimer.purge(); |
||||||
|
mTimer.cancel(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override public long getFileSize() { |
||||||
|
return mEntity.getFileSize(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取当前下载位置 |
||||||
|
*/ |
||||||
|
@Override public long getCurrentLocation() { |
||||||
|
return mConstance.CURRENT_LOCATION; |
||||||
|
} |
||||||
|
|
||||||
|
@Override public boolean isRunning() { |
||||||
|
return mConstance.isRunning; |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void cancel() { |
||||||
|
closeTimer(); |
||||||
|
mConstance.isCancel = true; |
||||||
|
mConstance.isRunning = false; |
||||||
|
if (mFixedThreadPool != null) { |
||||||
|
mFixedThreadPool.shutdown(); |
||||||
|
} |
||||||
|
for (int i = 0; i < mThreadNum; i++) { |
||||||
|
AbsThreadTask task = mTask.get(i); |
||||||
|
if (task != null) { |
||||||
|
task.cancel(); |
||||||
|
} |
||||||
|
} |
||||||
|
if (mTaskEntity instanceof DownloadTaskEntity) { |
||||||
|
CommonUtil.delDownloadTaskConfig(mTaskEntity.removeFile, (DownloadTaskEntity) mTaskEntity); |
||||||
|
} else if (mTaskEntity instanceof UploadTaskEntity) { |
||||||
|
CommonUtil.delUploadTaskConfig(mTaskEntity.removeFile, (UploadTaskEntity) mTaskEntity); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void stop() { |
||||||
|
closeTimer(); |
||||||
|
if (mConstance.isComplete()) return; |
||||||
|
mConstance.isStop = true; |
||||||
|
mConstance.isRunning = false; |
||||||
|
if (mFixedThreadPool != null) { |
||||||
|
mFixedThreadPool.shutdown(); |
||||||
|
} |
||||||
|
for (int i = 0; i < mThreadNum; i++) { |
||||||
|
AbsThreadTask task = mTask.get(i); |
||||||
|
if (task != null) { |
||||||
|
task.stop(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 直接调用的时候会自动启动线程执行 |
||||||
|
*/ |
||||||
|
@Override public void start() { |
||||||
|
new Thread(this).start(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void resume() { |
||||||
|
start(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 返回该下载器的 |
||||||
|
*/ |
||||||
|
public IEventListener getListener() { |
||||||
|
return mListener; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 检查任务是否是新任务,新任务条件: |
||||||
|
* 1、文件不存在 |
||||||
|
* 2、下载记录文件不存在 |
||||||
|
* 3、下载记录文件缺失或不匹配 |
||||||
|
* 4、数据库记录不存在 |
||||||
|
* 5、不支持断点,则是新任务 |
||||||
|
*/ |
||||||
|
protected abstract void checkTask(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 检查记录文件,如果是新任务返回{@code true},否则返回{@code false} |
||||||
|
*/ |
||||||
|
protected boolean checkConfigFile() { |
||||||
|
Properties pro = CommonUtil.loadConfig(mConfigFile); |
||||||
|
if (pro.isEmpty()) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
Set<Object> keys = pro.keySet(); |
||||||
|
int num = 0; |
||||||
|
for (Object key : keys) { |
||||||
|
if (String.valueOf(key).contains("_record_")) { |
||||||
|
num++; |
||||||
|
} |
||||||
|
} |
||||||
|
if (num == 0) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
mRealThreadNum = num; |
||||||
|
for (int i = 0; i < mRealThreadNum; i++) { |
||||||
|
if (pro.getProperty(mTempFile.getName() + "_record_" + i) == null) { |
||||||
|
Object state = pro.getProperty(mTempFile.getName() + "_state_" + i); |
||||||
|
if (state != null && Integer.parseInt(state + "") == 1) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 恢复记录地址 |
||||||
|
* |
||||||
|
* @return true 表示下载完成 |
||||||
|
*/ |
||||||
|
private boolean resumeRecordLocation(int i, long startL, long endL) { |
||||||
|
mConstance.CURRENT_LOCATION += endL - startL; |
||||||
|
Log.d(TAG, "++++++++++ 线程_" + i + "_已经下载完成 ++++++++++"); |
||||||
|
mConstance.COMPLETE_THREAD_NUM++; |
||||||
|
mConstance.STOP_NUM++; |
||||||
|
mConstance.CANCEL_NUM++; |
||||||
|
if (mConstance.isComplete()) { |
||||||
|
if (mConfigFile.exists()) { |
||||||
|
mConfigFile.delete(); |
||||||
|
} |
||||||
|
mListener.onComplete(); |
||||||
|
mConstance.isRunning = false; |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 启动断点任务时,创建单线程任务 |
||||||
|
* |
||||||
|
* @param i 线程id |
||||||
|
* @param startL 该任务起始位置 |
||||||
|
* @param endL 该任务结束位置 |
||||||
|
* @param fileLength 该任务需要处理的文件长度 |
||||||
|
*/ |
||||||
|
private AbsThreadTask createSingThreadTask(int i, long startL, long endL, long fileLength) { |
||||||
|
SubThreadConfig<TASK_ENTITY> config = new SubThreadConfig<>(); |
||||||
|
config.FILE_SIZE = fileLength; |
||||||
|
config.URL = mEntity.isRedirect() ? mEntity.getRedirectUrl() : mEntity.getUrl(); |
||||||
|
config.TEMP_FILE = mTempFile; |
||||||
|
config.THREAD_ID = i; |
||||||
|
config.START_LOCATION = startL; |
||||||
|
config.END_LOCATION = endL; |
||||||
|
config.CONFIG_FILE_PATH = mConfigFile.getPath(); |
||||||
|
config.SUPPORT_BP = mTaskEntity.isSupportBP; |
||||||
|
config.TASK_ENTITY = mTaskEntity; |
||||||
|
return selectThreadTask(config); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 处理断点 |
||||||
|
*/ |
||||||
|
private void handleBreakpoint() { |
||||||
|
long fileLength = mEntity.getFileSize(); |
||||||
|
Properties pro = CommonUtil.loadConfig(mConfigFile); |
||||||
|
int blockSize = (int) (fileLength / mThreadNum); |
||||||
|
int[] recordL = new int[mThreadNum]; |
||||||
|
for (int i = 0; i < mThreadNum; i++) { |
||||||
|
recordL[i] = -1; |
||||||
|
} |
||||||
|
int rl = 0; |
||||||
|
if (isNewTask) { |
||||||
|
handleNewTask(); |
||||||
|
} |
||||||
|
for (int i = 0; i < mThreadNum; i++) { |
||||||
|
long startL = i * blockSize, endL = (i + 1) * blockSize; |
||||||
|
Object state = pro.getProperty(mTempFile.getName() + "_state_" + i); |
||||||
|
if (state != null && Integer.parseInt(state + "") == 1) { //该线程已经完成
|
||||||
|
if (resumeRecordLocation(i, startL, endL)) return; |
||||||
|
continue; |
||||||
|
} |
||||||
|
//分配下载位置
|
||||||
|
Object record = pro.getProperty(mTempFile.getName() + "_record_" + i); |
||||||
|
//如果有记录,则恢复下载
|
||||||
|
if (!isNewTask && record != null && Long.parseLong(record + "") >= 0) { |
||||||
|
Long r = Long.parseLong(record + ""); |
||||||
|
mConstance.CURRENT_LOCATION += r - startL; |
||||||
|
Log.d(TAG, "任务【" + mEntity.getFileName() + "】线程__" + i + "__恢复下载"); |
||||||
|
startL = r; |
||||||
|
recordL[rl] = i; |
||||||
|
rl++; |
||||||
|
} else { |
||||||
|
recordL[rl] = i; |
||||||
|
rl++; |
||||||
|
} |
||||||
|
if (i == (mThreadNum - 1)) { |
||||||
|
//最后一个线程的结束位置即为文件的总长度
|
||||||
|
endL = fileLength; |
||||||
|
} |
||||||
|
AbsThreadTask task = createSingThreadTask(i, startL, endL, fileLength); |
||||||
|
if (task == null) return; |
||||||
|
mTask.put(i, task); |
||||||
|
} |
||||||
|
startSingleTask(recordL); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 启动单线程下载任务 |
||||||
|
*/ |
||||||
|
private void startSingleTask(int[] recordL) { |
||||||
|
if (mConstance.CURRENT_LOCATION > 0) { |
||||||
|
mListener.onResume(mConstance.CURRENT_LOCATION); |
||||||
|
} else { |
||||||
|
mListener.onStart(mConstance.CURRENT_LOCATION); |
||||||
|
} |
||||||
|
mFixedThreadPool = Executors.newFixedThreadPool(recordL.length); |
||||||
|
for (int l : recordL) { |
||||||
|
if (l == -1) continue; |
||||||
|
Runnable task = mTask.get(l); |
||||||
|
if (task != null) { |
||||||
|
mFixedThreadPool.execute(task); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 处理新任务 |
||||||
|
*/ |
||||||
|
protected abstract void handleNewTask(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 处理不支持断点的下载 |
||||||
|
*/ |
||||||
|
private void handleNoSupportBP() { |
||||||
|
SubThreadConfig<TASK_ENTITY> config = new SubThreadConfig<>(); |
||||||
|
config.FILE_SIZE = mEntity.getFileSize(); |
||||||
|
config.URL = mEntity.isRedirect() ? mEntity.getRedirectUrl() : mEntity.getUrl(); |
||||||
|
config.TEMP_FILE = mTempFile; |
||||||
|
config.THREAD_ID = 0; |
||||||
|
config.START_LOCATION = 0; |
||||||
|
config.END_LOCATION = config.FILE_SIZE; |
||||||
|
config.CONFIG_FILE_PATH = mConfigFile.getPath(); |
||||||
|
config.SUPPORT_BP = mTaskEntity.isSupportBP; |
||||||
|
config.TASK_ENTITY = mTaskEntity; |
||||||
|
AbsThreadTask task = selectThreadTask(config); |
||||||
|
if (task == null) return; |
||||||
|
mTask.put(0, task); |
||||||
|
mFixedThreadPool = Executors.newFixedThreadPool(1); |
||||||
|
mFixedThreadPool.execute(task); |
||||||
|
mListener.onStart(0); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 选择单任务线程的类型 |
||||||
|
*/ |
||||||
|
protected abstract AbsThreadTask selectThreadTask(SubThreadConfig<TASK_ENTITY> config); |
||||||
|
|
||||||
|
protected void failDownload(String errorMsg) { |
||||||
|
closeTimer(); |
||||||
|
Log.e(TAG, errorMsg); |
||||||
|
mConstance.isRunning = false; |
||||||
|
mListener.onFail(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,204 @@ |
|||||||
|
/* |
||||||
|
* 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.common; |
||||||
|
|
||||||
|
import android.os.Build; |
||||||
|
import android.text.TextUtils; |
||||||
|
import android.util.Log; |
||||||
|
import com.arialyy.aria.core.AriaManager; |
||||||
|
import com.arialyy.aria.core.inf.AbsEntity; |
||||||
|
import com.arialyy.aria.core.inf.AbsTaskEntity; |
||||||
|
import com.arialyy.aria.core.inf.IEventListener; |
||||||
|
import com.arialyy.aria.core.upload.UploadEntity; |
||||||
|
import com.arialyy.aria.util.CommonUtil; |
||||||
|
import java.io.File; |
||||||
|
import java.io.IOException; |
||||||
|
import java.math.BigDecimal; |
||||||
|
import java.util.Properties; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by lyy on 2017/1/18. |
||||||
|
* 下载线程 |
||||||
|
*/ |
||||||
|
public abstract class AbsThreadTask<ENTITY extends AbsEntity, TASK_ENTITY extends AbsTaskEntity<ENTITY>> |
||||||
|
implements Runnable { |
||||||
|
private final String TAG = "AbsThreadTask"; |
||||||
|
protected long mChildCurrentLocation = 0, mSleepTime = 0; |
||||||
|
protected int mBufSize; |
||||||
|
protected String mConfigFPath; |
||||||
|
protected IEventListener mListener; |
||||||
|
protected StateConstance STATE; |
||||||
|
protected SubThreadConfig<TASK_ENTITY> mConfig; |
||||||
|
protected ENTITY mEntity; |
||||||
|
protected TASK_ENTITY mTaskEntity; |
||||||
|
/** |
||||||
|
* FTP 服务器编码 |
||||||
|
*/ |
||||||
|
public static String SERVER_CHARSET = "ISO-8859-1"; |
||||||
|
|
||||||
|
protected AbsThreadTask(StateConstance constance, IEventListener listener, |
||||||
|
SubThreadConfig<TASK_ENTITY> info) { |
||||||
|
AriaManager manager = AriaManager.getInstance(AriaManager.APP); |
||||||
|
STATE = constance; |
||||||
|
STATE.CONNECT_TIME_OUT = manager.getDownloadConfig().getConnectTimeOut(); |
||||||
|
STATE.READ_TIME_OUT = manager.getDownloadConfig().getIOTimeOut(); |
||||||
|
mListener = listener; |
||||||
|
mConfig = info; |
||||||
|
mTaskEntity = mConfig.TASK_ENTITY; |
||||||
|
mEntity = mTaskEntity.getEntity(); |
||||||
|
if (mConfig.SUPPORT_BP) { |
||||||
|
mConfigFPath = info.CONFIG_FILE_PATH; |
||||||
|
} |
||||||
|
mBufSize = manager.getDownloadConfig().getBuffSize(); |
||||||
|
setMaxSpeed(AriaManager.getInstance(AriaManager.APP).getDownloadConfig().getMsxSpeed()); |
||||||
|
} |
||||||
|
|
||||||
|
public void setMaxSpeed(double maxSpeed) { |
||||||
|
if (-0.9999 < maxSpeed && maxSpeed < 0.00001) { |
||||||
|
mSleepTime = 0; |
||||||
|
} else { |
||||||
|
BigDecimal db = new BigDecimal( |
||||||
|
((mBufSize / 1024) * (filterVersion() ? 1 : STATE.THREAD_NUM) / maxSpeed) * 1000); |
||||||
|
mSleepTime = db.setScale(0, BigDecimal.ROUND_HALF_UP).longValue(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private boolean filterVersion() { |
||||||
|
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 停止下载 |
||||||
|
*/ |
||||||
|
public void stop() { |
||||||
|
synchronized (AriaManager.LOCK) { |
||||||
|
try { |
||||||
|
if (mConfig.SUPPORT_BP) { |
||||||
|
STATE.STOP_NUM++; |
||||||
|
Log.d(TAG, "任务【" |
||||||
|
+ mConfig.TEMP_FILE.getName() |
||||||
|
+ "】thread__" |
||||||
|
+ mConfig.THREAD_ID |
||||||
|
+ "__停止, stop location ==> " |
||||||
|
+ mChildCurrentLocation); |
||||||
|
writeConfig(false, mChildCurrentLocation); |
||||||
|
if (STATE.isStop()) { |
||||||
|
Log.d(TAG, "任务【" + mConfig.TEMP_FILE.getName() + "】已停止"); |
||||||
|
STATE.isRunning = false; |
||||||
|
mListener.onStop(STATE.CURRENT_LOCATION); |
||||||
|
} |
||||||
|
} else { |
||||||
|
Log.d(TAG, "任务【" + mConfig.TEMP_FILE.getName() + "】已停止"); |
||||||
|
STATE.isRunning = false; |
||||||
|
mListener.onStop(STATE.CURRENT_LOCATION); |
||||||
|
} |
||||||
|
} catch (IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 下载中 |
||||||
|
*/ |
||||||
|
protected void progress(long len) { |
||||||
|
synchronized (AriaManager.LOCK) { |
||||||
|
mChildCurrentLocation += len; |
||||||
|
STATE.CURRENT_LOCATION += len; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 取消下载 |
||||||
|
*/ |
||||||
|
public void cancel() { |
||||||
|
synchronized (AriaManager.LOCK) { |
||||||
|
if (mConfig.SUPPORT_BP) { |
||||||
|
STATE.CANCEL_NUM++; |
||||||
|
Log.d(TAG, |
||||||
|
"任务【" + mConfig.TEMP_FILE.getName() + "】thread__" + mConfig.THREAD_ID + "__取消下载"); |
||||||
|
if (STATE.isCancel()) { |
||||||
|
File configFile = new File(mConfigFPath); |
||||||
|
if (configFile.exists()) { |
||||||
|
configFile.delete(); |
||||||
|
} |
||||||
|
if (mConfig.TEMP_FILE.exists() && !(mEntity instanceof UploadEntity)) { |
||||||
|
mConfig.TEMP_FILE.delete(); |
||||||
|
} |
||||||
|
Log.d(TAG, "任务【" + mConfig.TEMP_FILE.getName() + "】已取消"); |
||||||
|
STATE.isRunning = false; |
||||||
|
mListener.onCancel(); |
||||||
|
} |
||||||
|
} else { |
||||||
|
Log.d(TAG, "任务【" + mConfig.TEMP_FILE.getName() + "】已取消"); |
||||||
|
STATE.isRunning = false; |
||||||
|
mListener.onCancel(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 任务失败 |
||||||
|
*/ |
||||||
|
protected void fail(long currentLocation, String msg, Exception ex) { |
||||||
|
synchronized (AriaManager.LOCK) { |
||||||
|
try { |
||||||
|
STATE.FAIL_NUM++; |
||||||
|
STATE.isRunning = false; |
||||||
|
STATE.isStop = true; |
||||||
|
if (ex != null) { |
||||||
|
Log.e(TAG, msg + "\n" + CommonUtil.getPrintException(ex)); |
||||||
|
} else { |
||||||
|
Log.e(TAG, msg); |
||||||
|
} |
||||||
|
if (mConfig.SUPPORT_BP) { |
||||||
|
writeConfig(false, currentLocation); |
||||||
|
if (STATE.isFail()) { |
||||||
|
Log.e(TAG, "任务【" + mConfig.TEMP_FILE.getName() + "】执行失败"); |
||||||
|
mListener.onFail(); |
||||||
|
} |
||||||
|
} else { |
||||||
|
Log.e(TAG, "任务【" + mConfig.TEMP_FILE.getName() + "】执行失败"); |
||||||
|
mListener.onFail(); |
||||||
|
} |
||||||
|
} catch (IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 将记录写入到配置文件 |
||||||
|
*/ |
||||||
|
protected void writeConfig(boolean isComplete, long record) throws IOException { |
||||||
|
synchronized (AriaManager.LOCK) { |
||||||
|
String key = null, value = null; |
||||||
|
if (0 < record && record < mConfig.END_LOCATION) { |
||||||
|
key = mConfig.TEMP_FILE.getName() + "_record_" + mConfig.THREAD_ID; |
||||||
|
value = String.valueOf(record); |
||||||
|
} else if (record >= mConfig.END_LOCATION || isComplete) { |
||||||
|
key = mConfig.TEMP_FILE.getName() + "_state_" + mConfig.THREAD_ID; |
||||||
|
value = "1"; |
||||||
|
} |
||||||
|
if (!TextUtils.isEmpty(key) && !TextUtils.isEmpty(value)) { |
||||||
|
File configFile = new File(mConfigFPath); |
||||||
|
Properties pro = CommonUtil.loadConfig(configFile); |
||||||
|
pro.setProperty(key, value); |
||||||
|
CommonUtil.saveConfig(configFile, pro); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,25 @@ |
|||||||
|
package com.arialyy.aria.core.common; |
||||||
|
|
||||||
|
import com.arialyy.aria.core.inf.AbsTaskEntity; |
||||||
|
import java.io.File; |
||||||
|
|
||||||
|
/** |
||||||
|
* 子线程下载信息类 |
||||||
|
*/ |
||||||
|
public class SubThreadConfig<TASK_ENTITY extends AbsTaskEntity> { |
||||||
|
//线程Id
|
||||||
|
public int THREAD_ID; |
||||||
|
//下载文件大小
|
||||||
|
public long FILE_SIZE; |
||||||
|
//子线程启动下载位置
|
||||||
|
public long START_LOCATION; |
||||||
|
//子线程结束下载位置
|
||||||
|
public long END_LOCATION; |
||||||
|
//下载文件或上传的文件路径
|
||||||
|
public File TEMP_FILE; |
||||||
|
//服务器地址
|
||||||
|
public String URL; |
||||||
|
public String CONFIG_FILE_PATH; |
||||||
|
public TASK_ENTITY TASK_ENTITY; |
||||||
|
public boolean SUPPORT_BP = true; |
||||||
|
} |
@ -0,0 +1,202 @@ |
|||||||
|
/* |
||||||
|
* 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 com.arialyy.aria.core.inf.AbsDownloadTarget; |
||||||
|
import com.arialyy.aria.core.inf.AbsTarget; |
||||||
|
import com.arialyy.aria.core.inf.AbsTaskEntity; |
||||||
|
import com.arialyy.aria.orm.DbEntity; |
||||||
|
import com.arialyy.aria.util.CommonUtil; |
||||||
|
import java.io.File; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by Aria.Lao on 2017/7/26. |
||||||
|
*/ |
||||||
|
abstract class BaseGroupTarget<TARGET extends AbsTarget, TASK_ENTITY extends AbsTaskEntity> |
||||||
|
extends AbsDownloadTarget<TARGET, DownloadGroupEntity, TASK_ENTITY> { |
||||||
|
|
||||||
|
List<String> mUrls = new ArrayList<>(); |
||||||
|
String mGroupName; |
||||||
|
/** |
||||||
|
* 子任务文件名 |
||||||
|
*/ |
||||||
|
private List<String> mSubTaskFileName = new ArrayList<>(); |
||||||
|
/** |
||||||
|
* 是否已经设置了文件路径 |
||||||
|
*/ |
||||||
|
private boolean isSetDirPathed = false; |
||||||
|
|
||||||
|
/** |
||||||
|
* 查询任务组实体,如果数据库不存在该实体,则新创建一个新的任务组实体 |
||||||
|
*/ |
||||||
|
DownloadGroupEntity getDownloadGroupEntity() { |
||||||
|
DownloadGroupEntity entity = |
||||||
|
DbEntity.findFirst(DownloadGroupEntity.class, "groupName=?", mGroupName); |
||||||
|
if (entity == null) { |
||||||
|
entity = new DownloadGroupEntity(); |
||||||
|
entity.setGroupName(mGroupName); |
||||||
|
entity.setUrls(mUrls); |
||||||
|
entity.insert(); |
||||||
|
} |
||||||
|
return entity; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 设置任务组别名 |
||||||
|
*/ |
||||||
|
public TARGET setGroupAlias(String alias) { |
||||||
|
if (TextUtils.isEmpty(alias)) return (TARGET) this; |
||||||
|
mEntity.setAlias(alias); |
||||||
|
mEntity.update(); |
||||||
|
return (TARGET) this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 设置任务组的文件夹路径,在Aria中,任务组的所有子任务都会下载到以任务组组名的文件夹中。 |
||||||
|
* 如:groupDirPath = "/mnt/sdcard/download/group_test" |
||||||
|
* <pre> |
||||||
|
* {@code |
||||||
|
* + mnt |
||||||
|
* + sdcard |
||||||
|
* + download |
||||||
|
* + group_test |
||||||
|
* - task1.apk |
||||||
|
* - task2.apk |
||||||
|
* - task3.apk |
||||||
|
* .... |
||||||
|
* |
||||||
|
* } |
||||||
|
* </pre> |
||||||
|
* |
||||||
|
* @param groupDirPath 任务组保存文件夹路径 |
||||||
|
*/ |
||||||
|
public TARGET setDownloadDirPath(String groupDirPath) { |
||||||
|
if (TextUtils.isEmpty(groupDirPath)) { |
||||||
|
throw new NullPointerException("任务组文件夹保存路径不能为null"); |
||||||
|
} |
||||||
|
|
||||||
|
isSetDirPathed = true; |
||||||
|
if (mEntity.getDirPath().equals(groupDirPath)) return (TARGET) this; |
||||||
|
|
||||||
|
File file = new File(groupDirPath); |
||||||
|
if (file.exists() && file.isFile()) { |
||||||
|
throw new IllegalArgumentException("路径不能为文件"); |
||||||
|
} |
||||||
|
if (!file.exists()) { |
||||||
|
file.mkdirs(); |
||||||
|
} |
||||||
|
|
||||||
|
mEntity.setDirPath(groupDirPath); |
||||||
|
if (!TextUtils.isEmpty(mEntity.getDirPath())) { |
||||||
|
reChangeDirPath(groupDirPath); |
||||||
|
} else { |
||||||
|
mEntity.setSubTasks(createSubTask()); |
||||||
|
} |
||||||
|
mEntity.update(); |
||||||
|
return (TARGET) this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 改变任务组文件夹路径,修改文件夹路径会将子任务所有路径更换 |
||||||
|
* |
||||||
|
* @param newDirPath 新的文件夹路径 |
||||||
|
*/ |
||||||
|
private void reChangeDirPath(String newDirPath) { |
||||||
|
List<DownloadEntity> subTask = mEntity.getSubTask(); |
||||||
|
if (subTask != null && !subTask.isEmpty()) { |
||||||
|
for (DownloadEntity entity : subTask) { |
||||||
|
String oldPath = entity.getDownloadPath(); |
||||||
|
String newPath = newDirPath + "/" + entity.getFileName(); |
||||||
|
File file = new File(oldPath); |
||||||
|
file.renameTo(new File(newPath)); |
||||||
|
DbEntity.exeSql("UPDATE DownloadEntity SET downloadPath='" |
||||||
|
+ newPath |
||||||
|
+ "' WHERE downloadPath='" |
||||||
|
+ oldPath |
||||||
|
+ "'"); |
||||||
|
DbEntity.exeSql( |
||||||
|
"UPDATE DownloadTaskEntity SET key='" + newPath + "' WHERE key='" + oldPath + "'"); |
||||||
|
} |
||||||
|
} else { |
||||||
|
mEntity.setSubTasks(createSubTask()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 设置子任务文件名,该方法必须在{@link #setDownloadDirPath(String)}之后调用,否则不生效 |
||||||
|
*/ |
||||||
|
public TARGET setSubTaskFileName(List<String> subTaskFileName) { |
||||||
|
if (subTaskFileName == null || subTaskFileName.isEmpty()) return (TARGET) this; |
||||||
|
mSubTaskFileName.addAll(subTaskFileName); |
||||||
|
if (mUrls.size() != subTaskFileName.size()) { |
||||||
|
throw new IllegalArgumentException("下载链接数必须要和保存路径的数量一致"); |
||||||
|
} |
||||||
|
if (isSetDirPathed) { |
||||||
|
List<DownloadEntity> entities = mEntity.getSubTask(); |
||||||
|
int i = 0; |
||||||
|
for (DownloadEntity entity : entities) { |
||||||
|
String newName = mSubTaskFileName.get(i); |
||||||
|
updateSubFileName(entity, newName); |
||||||
|
i++; |
||||||
|
} |
||||||
|
} |
||||||
|
return (TARGET) this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 更新子任务文件名 |
||||||
|
*/ |
||||||
|
private void updateSubFileName(DownloadEntity entity, String newName) { |
||||||
|
if (!newName.equals(entity.getFileName())) { |
||||||
|
String oldPath = mEntity.getDirPath() + "/" + entity.getFileName(); |
||||||
|
String newPath = mEntity.getDirPath() + "/" + newName; |
||||||
|
File oldFile = new File(oldPath); |
||||||
|
if (oldFile.exists()) { |
||||||
|
oldFile.renameTo(new File(newPath)); |
||||||
|
} |
||||||
|
CommonUtil.renameDownloadConfig(oldFile.getName(), newName); |
||||||
|
DbEntity.exeSql( |
||||||
|
"UPDATE DownloadTaskEntity SET key='" + newPath + "' WHERE key='" + oldPath + "'"); |
||||||
|
entity.setDownloadPath(newPath); |
||||||
|
entity.setFileName(newName); |
||||||
|
entity.update(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 创建子任务 |
||||||
|
*/ |
||||||
|
private List<DownloadEntity> createSubTask() { |
||||||
|
List<DownloadEntity> list = new ArrayList<>(); |
||||||
|
for (int i = 0, len = mUrls.size(); i < len; i++) { |
||||||
|
DownloadEntity entity = new DownloadEntity(); |
||||||
|
entity.setUrl(mUrls.get(i)); |
||||||
|
String fileName = mSubTaskFileName.isEmpty() ? createFileName(entity.getUrl()) |
||||||
|
: mSubTaskFileName.get(i); |
||||||
|
entity.setDownloadPath(mEntity.getDirPath() + "/" + fileName); |
||||||
|
entity.setGroupName(mGroupName); |
||||||
|
entity.setGroupChild(true); |
||||||
|
entity.setFileName(fileName); |
||||||
|
entity.insert(); |
||||||
|
list.add(entity); |
||||||
|
} |
||||||
|
return list; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,102 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
import com.arialyy.aria.orm.DbEntity; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by Aria.Lao on 2017/7/26. |
||||||
|
* ftp文件夹下载 |
||||||
|
*/ |
||||||
|
public class FtpDirDownloadTarget |
||||||
|
extends BaseGroupTarget<FtpDirDownloadTarget, DownloadGroupTaskEntity> { |
||||||
|
private final String TAG = "FtpDirDownloadTarget"; |
||||||
|
private String serverIp, remotePath; |
||||||
|
private int port; |
||||||
|
|
||||||
|
FtpDirDownloadTarget(String url, String targetName) { |
||||||
|
init(url); |
||||||
|
String[] pp = url.split("/")[2].split(":"); |
||||||
|
mTargetName = targetName; |
||||||
|
serverIp = pp[0]; |
||||||
|
port = Integer.parseInt(pp[1]); |
||||||
|
mTaskEntity.requestType = AbsTaskEntity.FTP_DIR; |
||||||
|
mTaskEntity.serverIp = serverIp; |
||||||
|
mTaskEntity.port = port; |
||||||
|
remotePath = url.substring(url.indexOf(pp[1]) + pp[1].length(), url.length()); |
||||||
|
if (TextUtils.isEmpty(remotePath)) { |
||||||
|
throw new NullPointerException("ftp服务器地址不能为null"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void init(String key) { |
||||||
|
mGroupName = key; |
||||||
|
mTaskEntity = DbEntity.findFirst(DownloadGroupTaskEntity.class, "key=?", key); |
||||||
|
if (mTaskEntity == null) { |
||||||
|
mTaskEntity = new DownloadGroupTaskEntity(); |
||||||
|
mTaskEntity.key = key; |
||||||
|
mTaskEntity.entity = getDownloadGroupEntity(); |
||||||
|
mTaskEntity.insert(); |
||||||
|
} |
||||||
|
if (mTaskEntity.entity == null) { |
||||||
|
mTaskEntity.entity = getDownloadGroupEntity(); |
||||||
|
} |
||||||
|
mEntity = mTaskEntity.entity; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 设置字符编码 |
||||||
|
*/ |
||||||
|
public FtpDirDownloadTarget charSet(String charSet) { |
||||||
|
if (TextUtils.isEmpty(charSet)) return this; |
||||||
|
mTaskEntity.charSet = charSet; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* ftp 用户登录信息 |
||||||
|
* |
||||||
|
* @param userName ftp用户名 |
||||||
|
* @param password ftp用户密码 |
||||||
|
*/ |
||||||
|
public FtpDirDownloadTarget login(String userName, String password) { |
||||||
|
return login(userName, password, null); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* ftp 用户登录信息 |
||||||
|
* |
||||||
|
* @param userName ftp用户名 |
||||||
|
* @param password ftp用户密码 |
||||||
|
* @param account ftp账号 |
||||||
|
*/ |
||||||
|
public FtpDirDownloadTarget login(String userName, String password, String account) { |
||||||
|
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; |
||||||
|
mTaskEntity.account = account; |
||||||
|
return this; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,120 @@ |
|||||||
|
/* |
||||||
|
* 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.support.annotation.NonNull; |
||||||
|
import android.text.TextUtils; |
||||||
|
import android.util.Log; |
||||||
|
import com.arialyy.aria.core.inf.AbsTaskEntity; |
||||||
|
import com.arialyy.aria.util.CommonUtil; |
||||||
|
import java.io.File; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by lyy on 2016/12/5. |
||||||
|
* https://github.com/AriaLyy/Aria
|
||||||
|
*/ |
||||||
|
public class FtpDownloadTarget extends DownloadTarget { |
||||||
|
private final String TAG = "FtpDownloadTarget"; |
||||||
|
private String serverIp, remotePath; |
||||||
|
private int port; |
||||||
|
|
||||||
|
FtpDownloadTarget(String url, String targetName) { |
||||||
|
super(url, targetName); |
||||||
|
String[] pp = url.split("/")[2].split(":"); |
||||||
|
this.serverIp = pp[0]; |
||||||
|
this.port = Integer.parseInt(pp[1]); |
||||||
|
mTaskEntity.requestType = AbsTaskEntity.FTP; |
||||||
|
remotePath = url.substring(url.indexOf(pp[1]) + pp[1].length(), url.length()); |
||||||
|
if (TextUtils.isEmpty(remotePath)) { |
||||||
|
throw new NullPointerException("ftp服务器地址不能为null"); |
||||||
|
} |
||||||
|
int lastIndex = url.lastIndexOf("/"); |
||||||
|
mTaskEntity.serverIp = serverIp; |
||||||
|
mTaskEntity.port = port; |
||||||
|
mEntity.setFileName(url.substring(lastIndex + 1, url.length())); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 设置文件保存文件夹路径 |
||||||
|
* 关于文件名: |
||||||
|
* 1、如果保存路径是该文件的保存路径,如:/mnt/sdcard/file.zip,则使用路径中的文件名file.zip |
||||||
|
* 2、如果保存路径是文件夹路径,如:/mnt/sdcard/,则使用FTP服务器该文件的文件名 |
||||||
|
* |
||||||
|
* @param downloadPath 路径必须为文件路径,不能为文件夹路径 |
||||||
|
*/ |
||||||
|
@Override public FtpDownloadTarget setDownloadPath(@NonNull String downloadPath) { |
||||||
|
if (TextUtils.isEmpty(downloadPath)) { |
||||||
|
throw new IllegalArgumentException("文件保持路径不能为null"); |
||||||
|
} |
||||||
|
File file = new File(downloadPath); |
||||||
|
if (file.isDirectory()) { |
||||||
|
downloadPath += mEntity.getFileName(); |
||||||
|
} |
||||||
|
if (!downloadPath.equals(mEntity.getDownloadPath())) { |
||||||
|
File oldFile = new File(mEntity.getDownloadPath()); |
||||||
|
File newFile = new File(downloadPath); |
||||||
|
if (TextUtils.isEmpty(mEntity.getDownloadPath()) || oldFile.renameTo(newFile)) { |
||||||
|
mEntity.setDownloadPath(downloadPath); |
||||||
|
mEntity.setFileName(newFile.getName()); |
||||||
|
mTaskEntity.key = downloadPath; |
||||||
|
mEntity.update(); |
||||||
|
mTaskEntity.update(); |
||||||
|
CommonUtil.renameDownloadConfig(oldFile.getName(), newFile.getName()); |
||||||
|
} |
||||||
|
} |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 设置字符编码 |
||||||
|
*/ |
||||||
|
public FtpDownloadTarget charSet(String charSet) { |
||||||
|
if (TextUtils.isEmpty(charSet)) return this; |
||||||
|
mTaskEntity.charSet = charSet; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* ftp 用户登录信息 |
||||||
|
* |
||||||
|
* @param userName ftp用户名 |
||||||
|
* @param password ftp用户密码 |
||||||
|
*/ |
||||||
|
public FtpDownloadTarget login(String userName, String password) { |
||||||
|
return login(userName, password, null); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* ftp 用户登录信息 |
||||||
|
* |
||||||
|
* @param userName ftp用户名 |
||||||
|
* @param password ftp用户密码 |
||||||
|
* @param account ftp账号 |
||||||
|
*/ |
||||||
|
public FtpDownloadTarget login(String userName, String password, String account) { |
||||||
|
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; |
||||||
|
mTaskEntity.account = account; |
||||||
|
return this; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,155 @@ |
|||||||
|
/* |
||||||
|
* 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.downloader; |
||||||
|
|
||||||
|
import android.text.TextUtils; |
||||||
|
import android.util.Log; |
||||||
|
import com.arialyy.aria.core.AriaManager; |
||||||
|
import com.arialyy.aria.core.common.AbsThreadTask; |
||||||
|
import com.arialyy.aria.core.inf.AbsEntity; |
||||||
|
import com.arialyy.aria.core.inf.AbsTaskEntity; |
||||||
|
import com.arialyy.aria.util.CommonUtil; |
||||||
|
import java.io.IOException; |
||||||
|
import org.apache.commons.net.ftp.FTP; |
||||||
|
import org.apache.commons.net.ftp.FTPClient; |
||||||
|
import org.apache.commons.net.ftp.FTPFile; |
||||||
|
import org.apache.commons.net.ftp.FTPReply; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by Aria.Lao on 2017/7/25. |
||||||
|
* 获取ftp文件夹信息 |
||||||
|
*/ |
||||||
|
abstract class AbsFtpInfoThread<ENTITY extends AbsEntity, TASK_ENTITY extends AbsTaskEntity<ENTITY>> |
||||||
|
implements Runnable { |
||||||
|
|
||||||
|
private final String TAG = "HttpFileInfoThread"; |
||||||
|
protected ENTITY mEntity; |
||||||
|
protected TASK_ENTITY mTaskEntity; |
||||||
|
private int mConnectTimeOut; |
||||||
|
private OnFileInfoCallback mCallback; |
||||||
|
|
||||||
|
AbsFtpInfoThread(TASK_ENTITY taskEntity, OnFileInfoCallback callback) { |
||||||
|
mTaskEntity = taskEntity; |
||||||
|
mEntity = taskEntity.getEntity(); |
||||||
|
mConnectTimeOut = |
||||||
|
AriaManager.getInstance(AriaManager.APP).getDownloadConfig().getConnectTimeOut(); |
||||||
|
mCallback = callback; |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void run() { |
||||||
|
FTPClient client = null; |
||||||
|
try { |
||||||
|
String url = mTaskEntity.getEntity().getKey(); |
||||||
|
String[] pp = url.split("/")[2].split(":"); |
||||||
|
String serverIp = pp[0]; |
||||||
|
int port = Integer.parseInt(pp[1]); |
||||||
|
String remotePath = url.substring(url.indexOf(pp[1]) + pp[1].length(), url.length()); |
||||||
|
client = new FTPClient(); |
||||||
|
client.connect(serverIp, port); |
||||||
|
if (!TextUtils.isEmpty(mTaskEntity.account)) { |
||||||
|
client.login(mTaskEntity.userName, mTaskEntity.userPw); |
||||||
|
} else { |
||||||
|
client.login(mTaskEntity.userName, mTaskEntity.userPw, mTaskEntity.account); |
||||||
|
} |
||||||
|
int reply = client.getReplyCode(); |
||||||
|
if (!FTPReply.isPositiveCompletion(reply)) { |
||||||
|
client.disconnect(); |
||||||
|
failDownload("无法连接到ftp服务器,错误码为:" + reply); |
||||||
|
return; |
||||||
|
} |
||||||
|
client.setDataTimeout(mConnectTimeOut); |
||||||
|
String charSet = "UTF-8"; |
||||||
|
// 开启服务器对UTF-8的支持,如果服务器支持就用UTF-8编码
|
||||||
|
if (!TextUtils.isEmpty(mTaskEntity.charSet) || !FTPReply.isPositiveCompletion( |
||||||
|
client.sendCommand("OPTS UTF8", "ON"))) { |
||||||
|
charSet = mTaskEntity.charSet; |
||||||
|
} |
||||||
|
client.setControlEncoding(charSet); |
||||||
|
client.enterLocalPassiveMode(); |
||||||
|
client.setFileType(FTP.BINARY_FILE_TYPE); |
||||||
|
FTPFile[] files = |
||||||
|
client.listFiles(new String(remotePath.getBytes(charSet), AbsThreadTask.SERVER_CHARSET)); |
||||||
|
long size = getFileSize(files, client, remotePath); |
||||||
|
mEntity.setFileSize(size); |
||||||
|
reply = client.getReplyCode(); |
||||||
|
if (!FTPReply.isPositiveCompletion(reply)) { |
||||||
|
client.disconnect(); |
||||||
|
failDownload("获取文件信息错误,错误码为:" + reply); |
||||||
|
return; |
||||||
|
} |
||||||
|
mTaskEntity.code = reply; |
||||||
|
onPreComplete(); |
||||||
|
mEntity.update(); |
||||||
|
mTaskEntity.update(); |
||||||
|
mCallback.onComplete(mEntity.getKey(), reply); |
||||||
|
} catch (IOException e) { |
||||||
|
failDownload(e.getMessage()); |
||||||
|
} finally { |
||||||
|
if (client != null) { |
||||||
|
try { |
||||||
|
client.disconnect(); |
||||||
|
} catch (IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void start() { |
||||||
|
new Thread(this).start(); |
||||||
|
} |
||||||
|
|
||||||
|
protected void onPreComplete() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 遍历FTP服务器上对应文件或文件夹大小 |
||||||
|
* |
||||||
|
* @throws IOException |
||||||
|
*/ |
||||||
|
private long getFileSize(FTPFile[] files, FTPClient client, String dirName) throws IOException { |
||||||
|
long size = 0; |
||||||
|
String path = dirName + "/"; |
||||||
|
for (FTPFile file : files) { |
||||||
|
if (file.isFile()) { |
||||||
|
size += file.getSize(); |
||||||
|
handleFile(path + file.getName(), file); |
||||||
|
} else { |
||||||
|
size += getFileSize(client.listFiles( |
||||||
|
CommonUtil.strCharSetConvert(path + file.getName(), mTaskEntity.charSet)), client, |
||||||
|
path + file.getName()); |
||||||
|
} |
||||||
|
} |
||||||
|
return size; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 处理FTP文件信息 |
||||||
|
* |
||||||
|
* @param remotePath ftp服务器文件夹路径 |
||||||
|
* @param ftpFile ftp服务器上对应的文件 |
||||||
|
*/ |
||||||
|
void handleFile(String remotePath, FTPFile ftpFile) { |
||||||
|
} |
||||||
|
|
||||||
|
private void failDownload(String errorMsg) { |
||||||
|
Log.e(TAG, errorMsg); |
||||||
|
if (mCallback != null) { |
||||||
|
mCallback.onFail(mEntity.getKey(), errorMsg); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,399 @@ |
|||||||
|
/* |
||||||
|
* 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.downloader; |
||||||
|
|
||||||
|
import com.arialyy.aria.core.AriaManager; |
||||||
|
import com.arialyy.aria.core.common.IUtil; |
||||||
|
import com.arialyy.aria.core.download.DownloadEntity; |
||||||
|
import com.arialyy.aria.core.download.DownloadGroupTaskEntity; |
||||||
|
import com.arialyy.aria.core.download.DownloadTaskEntity; |
||||||
|
import com.arialyy.aria.core.inf.IDownloadListener; |
||||||
|
import com.arialyy.aria.core.inf.IEntity; |
||||||
|
import com.arialyy.aria.orm.DbEntity; |
||||||
|
import com.arialyy.aria.util.CommonUtil; |
||||||
|
import java.io.File; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Set; |
||||||
|
import java.util.Timer; |
||||||
|
import java.util.TimerTask; |
||||||
|
import java.util.concurrent.ExecutorService; |
||||||
|
import java.util.concurrent.Executors; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by AriaL on 2017/6/30. |
||||||
|
* 任务组核心逻辑 |
||||||
|
*/ |
||||||
|
abstract class AbsGroupUtil implements IUtil { |
||||||
|
private final String TAG = "DownloadGroupUtil"; |
||||||
|
/** |
||||||
|
* 任务组所有任务总大小 |
||||||
|
*/ |
||||||
|
long mTotalSize = 0; |
||||||
|
private long mCurrentLocation = 0; |
||||||
|
private ExecutorService mExePool; |
||||||
|
protected IDownloadGroupListener mListener; |
||||||
|
DownloadGroupTaskEntity mTaskEntity; |
||||||
|
private boolean isRunning = true; |
||||||
|
private Timer mTimer; |
||||||
|
/** |
||||||
|
* 初始化完成的任务书数 |
||||||
|
*/ |
||||||
|
int mInitNum = 0; |
||||||
|
/** |
||||||
|
* 初始化失败的任务数 |
||||||
|
*/ |
||||||
|
int mInitFailNum = 0; |
||||||
|
/** |
||||||
|
* 保存所有没有下载完成的任务,key为下载地址 |
||||||
|
*/ |
||||||
|
Map<String, DownloadTaskEntity> mExeMap = new HashMap<>(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 下载失败的映射表,key为下载地址 |
||||||
|
*/ |
||||||
|
Map<String, DownloadTaskEntity> mFailMap = new HashMap<>(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 下载器映射表,key为下载地址 |
||||||
|
*/ |
||||||
|
private Map<String, Downloader> mDownloaderMap = new HashMap<>(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 该任务组对应的所有任务 |
||||||
|
*/ |
||||||
|
private Map<String, DownloadTaskEntity> mTasksMap = new HashMap<>(); |
||||||
|
//已经完成的任务数
|
||||||
|
private int mCompleteNum = 0; |
||||||
|
//失败的任务数
|
||||||
|
private int mFailNum = 0; |
||||||
|
//实际的下载任务数
|
||||||
|
int mActualTaskNum = 0; |
||||||
|
|
||||||
|
AbsGroupUtil(IDownloadGroupListener listener, DownloadGroupTaskEntity taskEntity) { |
||||||
|
mListener = listener; |
||||||
|
mTaskEntity = taskEntity; |
||||||
|
mExePool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); |
||||||
|
List<DownloadTaskEntity> tasks = |
||||||
|
DbEntity.findDatas(DownloadTaskEntity.class, "groupName=?", mTaskEntity.key); |
||||||
|
if (tasks != null && !tasks.isEmpty()) { |
||||||
|
for (DownloadTaskEntity te : tasks) { |
||||||
|
mTasksMap.put(te.getEntity().getUrl(), te); |
||||||
|
} |
||||||
|
} |
||||||
|
for (DownloadEntity entity : mTaskEntity.entity.getSubTask()) { |
||||||
|
File file = new File(entity.getDownloadPath()); |
||||||
|
if (entity.getState() == IEntity.STATE_COMPLETE && file.exists()) { |
||||||
|
mCompleteNum++; |
||||||
|
mInitNum++; |
||||||
|
mCurrentLocation += entity.getFileSize(); |
||||||
|
} else { |
||||||
|
mExeMap.put(entity.getUrl(), createChildDownloadTask(entity)); |
||||||
|
mCurrentLocation += entity.getCurrentProgress(); |
||||||
|
mActualTaskNum++; |
||||||
|
} |
||||||
|
mTotalSize += entity.getFileSize(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override public long getFileSize() { |
||||||
|
return mTotalSize; |
||||||
|
} |
||||||
|
|
||||||
|
@Override public long getCurrentLocation() { |
||||||
|
return mCurrentLocation; |
||||||
|
} |
||||||
|
|
||||||
|
@Override public boolean isRunning() { |
||||||
|
return isRunning; |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void cancel() { |
||||||
|
closeTimer(false); |
||||||
|
mListener.onCancel(); |
||||||
|
onCancel(); |
||||||
|
if (!mExePool.isShutdown()) { |
||||||
|
mExePool.shutdown(); |
||||||
|
} |
||||||
|
|
||||||
|
Set<String> keys = mDownloaderMap.keySet(); |
||||||
|
for (String key : keys) { |
||||||
|
Downloader dt = mDownloaderMap.get(key); |
||||||
|
if (dt != null) { |
||||||
|
dt.cancel(); |
||||||
|
} |
||||||
|
} |
||||||
|
delDownloadInfo(); |
||||||
|
mTaskEntity.deleteData(); |
||||||
|
} |
||||||
|
|
||||||
|
public void onCancel() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 删除所有子任务的下载信息 |
||||||
|
*/ |
||||||
|
private void delDownloadInfo() { |
||||||
|
List<DownloadTaskEntity> tasks = |
||||||
|
DbEntity.findDatas(DownloadTaskEntity.class, "groupName=?", mTaskEntity.key); |
||||||
|
if (tasks == null || tasks.isEmpty()) return; |
||||||
|
for (DownloadTaskEntity taskEntity : tasks) { |
||||||
|
CommonUtil.delDownloadTaskConfig(taskEntity.removeFile, taskEntity); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void stop() { |
||||||
|
closeTimer(false); |
||||||
|
mListener.onStop(mCurrentLocation); |
||||||
|
onStop(); |
||||||
|
if (!mExePool.isShutdown()) { |
||||||
|
mExePool.shutdown(); |
||||||
|
} |
||||||
|
|
||||||
|
Set<String> keys = mDownloaderMap.keySet(); |
||||||
|
for (String key : keys) { |
||||||
|
Downloader dt = mDownloaderMap.get(key); |
||||||
|
if (dt != null) { |
||||||
|
dt.stop(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected void onStop() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override public void start() { |
||||||
|
isRunning = true; |
||||||
|
mFailNum = 0; |
||||||
|
mListener.onPre(); |
||||||
|
onStart(); |
||||||
|
} |
||||||
|
|
||||||
|
protected void onStart() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override public void resume() { |
||||||
|
start(); |
||||||
|
mListener.onResume(mCurrentLocation); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void setMaxSpeed(double maxSpeed) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
private void closeTimer(boolean isRunning) { |
||||||
|
this.isRunning = isRunning; |
||||||
|
if (mTimer != null) { |
||||||
|
mTimer.purge(); |
||||||
|
mTimer.cancel(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 开始进度流程 |
||||||
|
*/ |
||||||
|
void startRunningFlow() { |
||||||
|
closeTimer(true); |
||||||
|
mListener.onPostPre(mTotalSize); |
||||||
|
mListener.onStart(mCurrentLocation); |
||||||
|
mTimer = new Timer(true); |
||||||
|
mTimer.schedule(new TimerTask() { |
||||||
|
@Override public void run() { |
||||||
|
if (!isRunning) { |
||||||
|
closeTimer(false); |
||||||
|
} else if (mCurrentLocation >= 0) { |
||||||
|
mListener.onProgress(mCurrentLocation); |
||||||
|
} |
||||||
|
} |
||||||
|
}, 0, 1000); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 启动子任务下载器 |
||||||
|
*/ |
||||||
|
void startChildDownload(DownloadTaskEntity taskEntity) { |
||||||
|
ChildDownloadListener listener = new ChildDownloadListener(taskEntity); |
||||||
|
Downloader dt = new Downloader(listener, taskEntity); |
||||||
|
mDownloaderMap.put(taskEntity.getEntity().getUrl(), dt); |
||||||
|
if (mExePool.isShutdown()) return; |
||||||
|
mExePool.execute(dt); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 创建子任务下载信息 |
||||||
|
*/ |
||||||
|
DownloadTaskEntity createChildDownloadTask(DownloadEntity entity) { |
||||||
|
DownloadTaskEntity taskEntity = mTasksMap.get(entity.getUrl()); |
||||||
|
if (taskEntity != null) { |
||||||
|
taskEntity.entity = entity; |
||||||
|
//ftp登录的
|
||||||
|
taskEntity.userName = mTaskEntity.userName; |
||||||
|
taskEntity.userPw = mTaskEntity.userPw; |
||||||
|
taskEntity.account = mTaskEntity.account; |
||||||
|
return taskEntity; |
||||||
|
} |
||||||
|
taskEntity = new DownloadTaskEntity(); |
||||||
|
taskEntity.entity = entity; |
||||||
|
taskEntity.headers = mTaskEntity.headers; |
||||||
|
taskEntity.requestEnum = mTaskEntity.requestEnum; |
||||||
|
taskEntity.redirectUrlKey = mTaskEntity.redirectUrlKey; |
||||||
|
taskEntity.removeFile = mTaskEntity.removeFile; |
||||||
|
taskEntity.groupName = mTaskEntity.key; |
||||||
|
taskEntity.isGroupTask = true; |
||||||
|
taskEntity.requestType = mTaskEntity.requestType; |
||||||
|
//ftp登录的
|
||||||
|
taskEntity.userName = mTaskEntity.userName; |
||||||
|
taskEntity.userPw = mTaskEntity.userPw; |
||||||
|
taskEntity.account = mTaskEntity.account; |
||||||
|
taskEntity.key = entity.getDownloadPath(); |
||||||
|
taskEntity.save(); |
||||||
|
return taskEntity; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 子任务事件监听 |
||||||
|
*/ |
||||||
|
private class ChildDownloadListener implements IDownloadListener { |
||||||
|
|
||||||
|
DownloadTaskEntity taskEntity; |
||||||
|
DownloadEntity entity; |
||||||
|
|
||||||
|
long lastLen = 0; |
||||||
|
|
||||||
|
ChildDownloadListener(DownloadTaskEntity entity) { |
||||||
|
this.taskEntity = entity; |
||||||
|
this.entity = taskEntity.getEntity(); |
||||||
|
lastLen = this.entity.getCurrentProgress(); |
||||||
|
this.entity.setFailNum(0); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void onPre() { |
||||||
|
saveData(IEntity.STATE_PRE, -1); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void onPostPre(long fileSize) { |
||||||
|
entity.setFileSize(fileSize); |
||||||
|
entity.setConvertFileSize(CommonUtil.formatFileSize(fileSize)); |
||||||
|
saveData(IEntity.STATE_POST_PRE, -1); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void onResume(long resumeLocation) { |
||||||
|
saveData(IEntity.STATE_POST_PRE, IEntity.STATE_RUNNING); |
||||||
|
lastLen = resumeLocation; |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void onStart(long startLocation) { |
||||||
|
saveData(IEntity.STATE_POST_PRE, IEntity.STATE_RUNNING); |
||||||
|
lastLen = startLocation; |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void onProgress(long currentLocation) { |
||||||
|
long speed = currentLocation - lastLen; |
||||||
|
mCurrentLocation += speed; |
||||||
|
lastLen = currentLocation; |
||||||
|
entity.setCurrentProgress(currentLocation); |
||||||
|
handleSpeed(speed); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void onStop(long stopLocation) { |
||||||
|
saveData(IEntity.STATE_STOP, stopLocation); |
||||||
|
handleSpeed(0); |
||||||
|
mListener.onSubStop(entity); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void onCancel() { |
||||||
|
saveData(IEntity.STATE_CANCEL, -1); |
||||||
|
handleSpeed(0); |
||||||
|
mListener.onSubCancel(entity); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void onComplete() { |
||||||
|
saveData(IEntity.STATE_COMPLETE, entity.getFileSize()); |
||||||
|
mCompleteNum++; |
||||||
|
handleSpeed(0); |
||||||
|
mListener.onSubComplete(entity); |
||||||
|
//如果子任务完成的数量和总任务数一致,表示任务组任务已经完成
|
||||||
|
if (mCompleteNum >= mTaskEntity.getEntity().getSubTask().size()) { |
||||||
|
closeTimer(false); |
||||||
|
mListener.onComplete(); |
||||||
|
} else if (mCompleteNum + mFailNum >= mActualTaskNum) { |
||||||
|
//如果子任务完成数量加上失败的数量和总任务数一致,则任务组停止下载
|
||||||
|
closeTimer(false); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void onFail() { |
||||||
|
entity.setFailNum(entity.getFailNum() + 1); |
||||||
|
saveData(IEntity.STATE_FAIL, lastLen); |
||||||
|
handleSpeed(0); |
||||||
|
reTry(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 失败后重试下载,如果失败次数超过5次,不再重试 |
||||||
|
*/ |
||||||
|
private void reTry() { |
||||||
|
synchronized (AriaManager.LOCK) { |
||||||
|
if (entity.getFailNum() < 5 && isRunning) { |
||||||
|
reStartTask(); |
||||||
|
} else { |
||||||
|
mFailNum++; |
||||||
|
mListener.onSubFail(entity); |
||||||
|
//如果失败的任务数大于实际的下载任务数,任务组停止下载
|
||||||
|
if (mFailNum >= mActualTaskNum) { |
||||||
|
closeTimer(false); |
||||||
|
mListener.onStop(mCurrentLocation); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void reStartTask() { |
||||||
|
Timer timer = new Timer(); |
||||||
|
timer.schedule(new TimerTask() { |
||||||
|
@Override public void run() { |
||||||
|
Downloader dt = mDownloaderMap.get(entity.getUrl()); |
||||||
|
dt.start(); |
||||||
|
} |
||||||
|
}, 3000); |
||||||
|
} |
||||||
|
|
||||||
|
private void handleSpeed(long speed) { |
||||||
|
entity.setSpeed(speed); |
||||||
|
entity.setConvertSpeed(speed <= 0 ? "" : CommonUtil.formatFileSize(speed) + "/s"); |
||||||
|
} |
||||||
|
|
||||||
|
private void saveData(int state, long location) { |
||||||
|
entity.setState(state); |
||||||
|
entity.setComplete(state == IEntity.STATE_COMPLETE); |
||||||
|
if (entity.isComplete()) { |
||||||
|
entity.setCompleteTime(System.currentTimeMillis()); |
||||||
|
entity.setCurrentProgress(entity.getFileSize()); |
||||||
|
} else if (location > 0) { |
||||||
|
entity.setCurrentProgress(location); |
||||||
|
} |
||||||
|
entity.update(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void supportBreakpoint(boolean support) { |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -1,24 +0,0 @@ |
|||||||
package com.arialyy.aria.core.download.downloader; |
|
||||||
|
|
||||||
import com.arialyy.aria.core.download.DownloadTaskEntity; |
|
||||||
import java.io.File; |
|
||||||
|
|
||||||
/** |
|
||||||
* 子线程下载信息类 |
|
||||||
*/ |
|
||||||
final class ChildThreadConfigEntity { |
|
||||||
//线程Id
|
|
||||||
int THREAD_ID; |
|
||||||
//下载文件大小
|
|
||||||
long FILE_SIZE; |
|
||||||
//子线程启动下载位置
|
|
||||||
long START_LOCATION; |
|
||||||
//子线程结束下载位置
|
|
||||||
long END_LOCATION; |
|
||||||
//下载路径
|
|
||||||
File TEMP_FILE; |
|
||||||
String DOWNLOAD_URL; |
|
||||||
String CONFIG_FILE_PATH; |
|
||||||
DownloadTaskEntity DOWNLOAD_TASK_ENTITY; |
|
||||||
boolean IS_SUPPORT_BREAK_POINT = true; |
|
||||||
} |
|
@ -0,0 +1,118 @@ |
|||||||
|
/* |
||||||
|
* 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.downloader; |
||||||
|
|
||||||
|
import android.text.TextUtils; |
||||||
|
import android.util.Log; |
||||||
|
import com.arialyy.aria.core.AriaManager; |
||||||
|
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/26. |
||||||
|
*/ |
||||||
|
public class FtpClientHelp { |
||||||
|
private final String TAG = "FtpClientHelp"; |
||||||
|
private static volatile FtpClientHelp INSTANCE = null; |
||||||
|
|
||||||
|
private FTPClient client; |
||||||
|
private String serverIp, user, pw, account; |
||||||
|
private int port; |
||||||
|
|
||||||
|
private FtpClientHelp() { |
||||||
|
} |
||||||
|
|
||||||
|
public static FtpClientHelp getInstnce() { |
||||||
|
if (INSTANCE == null) { |
||||||
|
synchronized (AriaManager.LOCK) { |
||||||
|
INSTANCE = new FtpClientHelp(); |
||||||
|
} |
||||||
|
} |
||||||
|
return INSTANCE; |
||||||
|
} |
||||||
|
|
||||||
|
public FTPClient getClient() { |
||||||
|
if (client == null || !client.isConnected()) { |
||||||
|
createClient(); |
||||||
|
} |
||||||
|
return client; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 登录到FTP服务器,当客户端为null或客户端没有连接到FTP服务器时才会执行登录操作 |
||||||
|
*/ |
||||||
|
public FTPClient login(String serverIp, int port, String user, String pw, String account) { |
||||||
|
this.serverIp = serverIp; |
||||||
|
this.port = port; |
||||||
|
this.user = user; |
||||||
|
this.pw = pw; |
||||||
|
this.account = account; |
||||||
|
if (client == null || !client.isConnected()) { |
||||||
|
createClient(); |
||||||
|
} |
||||||
|
return client; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 登出 |
||||||
|
*/ |
||||||
|
public void logout() { |
||||||
|
try { |
||||||
|
if (client != null && client.isConnected()) { |
||||||
|
client.logout(); |
||||||
|
} |
||||||
|
} catch (IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
FTPClient createClient() { |
||||||
|
new Thread(new Runnable() { |
||||||
|
@Override public void run() { |
||||||
|
client = new FTPClient(); |
||||||
|
try { |
||||||
|
client.connect(serverIp, port); |
||||||
|
if (!TextUtils.isEmpty(account)) { |
||||||
|
client.login(user, pw); |
||||||
|
} else { |
||||||
|
client.login(user, pw, account); |
||||||
|
} |
||||||
|
int reply = client.getReplyCode(); |
||||||
|
if (!FTPReply.isPositiveCompletion(reply)) { |
||||||
|
client.disconnect(); |
||||||
|
Log.e(TAG, "无法连接到ftp服务器,错误码为:" + reply); |
||||||
|
} |
||||||
|
} catch (IOException e) { |
||||||
|
Log.d(TAG, e.getMessage()); |
||||||
|
} finally { |
||||||
|
synchronized (FtpClientHelp.this) { |
||||||
|
FtpClientHelp.this.notify(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}).start(); |
||||||
|
synchronized (FtpClientHelp.this) { |
||||||
|
try { |
||||||
|
wait(); |
||||||
|
} catch (InterruptedException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return client; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,68 @@ |
|||||||
|
/* |
||||||
|
* 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.downloader; |
||||||
|
|
||||||
|
import com.arialyy.aria.core.download.DownloadEntity; |
||||||
|
import com.arialyy.aria.core.download.DownloadGroupTaskEntity; |
||||||
|
import com.arialyy.aria.core.download.DownloadTaskEntity; |
||||||
|
import java.util.Set; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by Aria.Lao on 2017/7/27. |
||||||
|
* ftp文件夹下载工具 |
||||||
|
*/ |
||||||
|
public class FtpDirDownloadUtil extends AbsGroupUtil { |
||||||
|
public FtpDirDownloadUtil(IDownloadGroupListener listener, DownloadGroupTaskEntity taskEntity) { |
||||||
|
super(listener, taskEntity); |
||||||
|
} |
||||||
|
|
||||||
|
@Override protected void onStart() { |
||||||
|
super.onStart(); |
||||||
|
if (mTaskEntity.getEntity().getFileSize() > 1) { |
||||||
|
startDownload(); |
||||||
|
} else { |
||||||
|
new FtpDirInfoThread(mTaskEntity, new OnFileInfoCallback() { |
||||||
|
@Override public void onComplete(String url, int code) { |
||||||
|
if (code >= 200 && code < 300) { |
||||||
|
mTotalSize = mTaskEntity.getEntity().getFileSize(); |
||||||
|
for (DownloadEntity entity : mTaskEntity.entity.getSubTask()) { |
||||||
|
mExeMap.put(entity.getUrl(), createChildDownloadTask(entity)); |
||||||
|
} |
||||||
|
mActualTaskNum = mTaskEntity.entity.getSubTask().size(); |
||||||
|
startDownload(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void onFail(String url, String errorMsg) { |
||||||
|
mListener.onFail(); |
||||||
|
} |
||||||
|
}).start(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void startDownload() { |
||||||
|
int i = 0; |
||||||
|
Set<String> keys = mExeMap.keySet(); |
||||||
|
for (String key : keys) { |
||||||
|
DownloadTaskEntity taskEntity = mExeMap.get(key); |
||||||
|
if (taskEntity != null) { |
||||||
|
startChildDownload(taskEntity); |
||||||
|
i++; |
||||||
|
} |
||||||
|
} |
||||||
|
if (i == mExeMap.size()) startRunningFlow(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,68 @@ |
|||||||
|
/* |
||||||
|
* 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.downloader; |
||||||
|
|
||||||
|
import com.arialyy.aria.core.download.DownloadEntity; |
||||||
|
import com.arialyy.aria.core.download.DownloadGroupEntity; |
||||||
|
import com.arialyy.aria.core.download.DownloadGroupTaskEntity; |
||||||
|
import com.arialyy.aria.util.CommonUtil; |
||||||
|
import java.nio.charset.Charset; |
||||||
|
import java.util.ArrayList; |
||||||
|
import org.apache.commons.net.ftp.FTPFile; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by Aria.Lao on 2017/7/25. |
||||||
|
* 获取ftp文件夹信息 |
||||||
|
*/ |
||||||
|
class FtpDirInfoThread extends AbsFtpInfoThread<DownloadGroupEntity, DownloadGroupTaskEntity> { |
||||||
|
private long mSize = 0; |
||||||
|
|
||||||
|
FtpDirInfoThread(DownloadGroupTaskEntity taskEntity, OnFileInfoCallback callback) { |
||||||
|
super(taskEntity, callback); |
||||||
|
} |
||||||
|
|
||||||
|
@Override void handleFile(String remotePath, FTPFile ftpFile) { |
||||||
|
super.handleFile(remotePath, ftpFile); |
||||||
|
mSize += ftpFile.getSize(); |
||||||
|
addEntity(remotePath, ftpFile); |
||||||
|
} |
||||||
|
|
||||||
|
@Override protected void onPreComplete() { |
||||||
|
super.onPreComplete(); |
||||||
|
mEntity.setFileSize(mSize); |
||||||
|
} |
||||||
|
|
||||||
|
private void addEntity(String remotePath, FTPFile ftpFile) { |
||||||
|
DownloadEntity entity = new DownloadEntity(); |
||||||
|
entity.setUrl("ftp://" + mTaskEntity.serverIp + ":" + mTaskEntity.port + remotePath); |
||||||
|
entity.setDownloadPath(mEntity.getDirPath() + "/" + remotePath); |
||||||
|
int lastIndex = remotePath.lastIndexOf("/"); |
||||||
|
String fileName = lastIndex < 0 ? CommonUtil.keyToHashKey(remotePath) |
||||||
|
: remotePath.substring(lastIndex + 1, remotePath.length()); |
||||||
|
entity.setFileName(new String(fileName.getBytes(), Charset.forName(mTaskEntity.charSet))); |
||||||
|
entity.setGroupName(mEntity.getGroupName()); |
||||||
|
entity.setGroupChild(true); |
||||||
|
entity.setFileSize(ftpFile.getSize()); |
||||||
|
entity.insert(); |
||||||
|
if (mEntity.getUrls() == null) { |
||||||
|
mEntity.setUrls(new ArrayList<String>()); |
||||||
|
} |
||||||
|
if (mEntity.getSubTask() == null) { |
||||||
|
mEntity.setSubTasks(new ArrayList<DownloadEntity>()); |
||||||
|
} |
||||||
|
mEntity.getSubTask().add(entity); |
||||||
|
} |
||||||
|
} |
@ -1,40 +0,0 @@ |
|||||||
package com.arialyy.aria.core.download.downloader; |
|
||||||
|
|
||||||
/** |
|
||||||
* Created by Aria.Lao on 2017/7/21. |
|
||||||
*/ |
|
||||||
public class FtpDownloadUtil implements IDownloadUtil, Runnable{ |
|
||||||
|
|
||||||
|
|
||||||
@Override public long getFileSize() { |
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public long getCurrentLocation() { |
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public boolean isDownloading() { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void cancelDownload() { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
@Override public void stopDownload() { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
@Override public void startDownload() { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
@Override public void resumeDownload() { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
@Override public void run() { |
|
||||||
|
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,30 @@ |
|||||||
|
/* |
||||||
|
* 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.downloader; |
||||||
|
|
||||||
|
import com.arialyy.aria.core.download.DownloadEntity; |
||||||
|
import com.arialyy.aria.core.download.DownloadTaskEntity; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by Aria.Lao on 2017/7/25. |
||||||
|
* 获取ftp文件信息 |
||||||
|
*/ |
||||||
|
class FtpFileInfoThread extends AbsFtpInfoThread<DownloadEntity, DownloadTaskEntity> { |
||||||
|
|
||||||
|
FtpFileInfoThread(DownloadTaskEntity taskEntity, OnFileInfoCallback callback) { |
||||||
|
super(taskEntity, callback); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,151 @@ |
|||||||
|
/* |
||||||
|
* 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.downloader; |
||||||
|
|
||||||
|
import android.text.TextUtils; |
||||||
|
import android.util.Log; |
||||||
|
import com.arialyy.aria.core.common.AbsThreadTask; |
||||||
|
import com.arialyy.aria.core.common.StateConstance; |
||||||
|
import com.arialyy.aria.core.common.SubThreadConfig; |
||||||
|
import com.arialyy.aria.core.download.DownloadEntity; |
||||||
|
import com.arialyy.aria.core.download.DownloadTaskEntity; |
||||||
|
import com.arialyy.aria.core.inf.IDownloadListener; |
||||||
|
import com.arialyy.aria.util.BufferedRandomAccessFile; |
||||||
|
import java.io.File; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.InputStream; |
||||||
|
import org.apache.commons.net.ftp.FTP; |
||||||
|
import org.apache.commons.net.ftp.FTPClient; |
||||||
|
import org.apache.commons.net.ftp.FTPReply; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by Aria.Lao on 2017/7/24. |
||||||
|
* Ftp下载任务 |
||||||
|
*/ |
||||||
|
class FtpThreadTask extends AbsThreadTask<DownloadEntity, DownloadTaskEntity> { |
||||||
|
private final String TAG = "FtpThreadTask"; |
||||||
|
|
||||||
|
FtpThreadTask(StateConstance constance, IDownloadListener listener, |
||||||
|
SubThreadConfig<DownloadTaskEntity> downloadInfo) { |
||||||
|
super(constance, listener, downloadInfo); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void run() { |
||||||
|
FTPClient client = null; |
||||||
|
InputStream is = null; |
||||||
|
BufferedRandomAccessFile file = null; |
||||||
|
try { |
||||||
|
Log.d(TAG, "任务【" |
||||||
|
+ mConfig.TEMP_FILE.getName() |
||||||
|
+ "】线程__" |
||||||
|
+ mConfig.THREAD_ID |
||||||
|
+ "__开始下载【开始位置 : " |
||||||
|
+ mConfig.START_LOCATION |
||||||
|
+ ",结束位置:" |
||||||
|
+ mConfig.END_LOCATION |
||||||
|
+ "】"); |
||||||
|
String url = mEntity.getUrl(); |
||||||
|
String[] pp = url.split("/")[2].split(":"); |
||||||
|
String serverIp = pp[0]; |
||||||
|
int port = Integer.parseInt(pp[1]); |
||||||
|
String remotePath = url.substring(url.indexOf(pp[1]) + pp[1].length(), url.length()); |
||||||
|
client = new FTPClient(); |
||||||
|
client.connect(serverIp, port); |
||||||
|
if (!TextUtils.isEmpty(mTaskEntity.account)) { |
||||||
|
client.login(mTaskEntity.userName, mTaskEntity.userPw); |
||||||
|
} else { |
||||||
|
client.login(mTaskEntity.userName, mTaskEntity.userPw, mTaskEntity.account); |
||||||
|
} |
||||||
|
int reply = client.getReplyCode(); |
||||||
|
if (!FTPReply.isPositiveCompletion(reply)) { |
||||||
|
client.disconnect(); |
||||||
|
fail(STATE.CURRENT_LOCATION, "无法连接到ftp服务器,错误码为:" + reply, null); |
||||||
|
return; |
||||||
|
} |
||||||
|
String charSet = "UTF-8"; |
||||||
|
// 开启服务器对UTF-8的支持,如果服务器支持就用UTF-8编码
|
||||||
|
if (!TextUtils.isEmpty(mTaskEntity.charSet) || !FTPReply.isPositiveCompletion( |
||||||
|
client.sendCommand("OPTS UTF8", "ON"))) { |
||||||
|
charSet = mTaskEntity.charSet; |
||||||
|
} |
||||||
|
client.setControlEncoding(charSet); |
||||||
|
client.setDataTimeout(STATE.READ_TIME_OUT); |
||||||
|
client.enterLocalPassiveMode(); |
||||||
|
client.setFileType(FTP.BINARY_FILE_TYPE); |
||||||
|
client.setRestartOffset(mConfig.START_LOCATION); |
||||||
|
client.allocate(mBufSize); |
||||||
|
is = client.retrieveFileStream( |
||||||
|
new String(remotePath.getBytes(charSet), SERVER_CHARSET)); |
||||||
|
//发送第二次指令时,还需要再做一次判断
|
||||||
|
reply = client.getReplyCode(); |
||||||
|
if (!FTPReply.isPositivePreliminary(reply)) { |
||||||
|
client.disconnect(); |
||||||
|
fail(mChildCurrentLocation, "获取文件信息错误,错误码为:" + reply, null); |
||||||
|
return; |
||||||
|
} |
||||||
|
file = new BufferedRandomAccessFile(mConfig.TEMP_FILE, "rwd", mBufSize); |
||||||
|
file.seek(mConfig.START_LOCATION); |
||||||
|
byte[] buffer = new byte[mBufSize]; |
||||||
|
int len; |
||||||
|
//当前子线程的下载位置
|
||||||
|
mChildCurrentLocation = mConfig.START_LOCATION; |
||||||
|
while ((len = is.read(buffer)) != -1) { |
||||||
|
if (STATE.isCancel) break; |
||||||
|
if (STATE.isStop) break; |
||||||
|
if (mSleepTime > 0) Thread.sleep(mSleepTime); |
||||||
|
if (mChildCurrentLocation + len >= mConfig.END_LOCATION) { |
||||||
|
len = (int) (mConfig.END_LOCATION - mChildCurrentLocation); |
||||||
|
file.write(buffer, 0, len); |
||||||
|
progress(len); |
||||||
|
break; |
||||||
|
} else { |
||||||
|
file.write(buffer, 0, len); |
||||||
|
progress(len); |
||||||
|
} |
||||||
|
} |
||||||
|
if (STATE.isCancel || STATE.isStop) return; |
||||||
|
Log.i(TAG, "任务【" + mConfig.TEMP_FILE.getName() + "】线程__" + mConfig.THREAD_ID + "__下载完毕"); |
||||||
|
writeConfig(true, 1); |
||||||
|
STATE.COMPLETE_THREAD_NUM++; |
||||||
|
if (STATE.isComplete()) { |
||||||
|
File configFile = new File(mConfigFPath); |
||||||
|
if (configFile.exists()) { |
||||||
|
configFile.delete(); |
||||||
|
} |
||||||
|
STATE.isRunning = false; |
||||||
|
mListener.onComplete(); |
||||||
|
} |
||||||
|
} catch (IOException e) { |
||||||
|
fail(mChildCurrentLocation, "下载失败【" + mConfig.URL + "】", e); |
||||||
|
} catch (Exception e) { |
||||||
|
fail(mChildCurrentLocation, "获取流失败", e); |
||||||
|
} finally { |
||||||
|
try { |
||||||
|
if (file != null) { |
||||||
|
file.close(); |
||||||
|
} |
||||||
|
if (is != null) { |
||||||
|
is.close(); |
||||||
|
} |
||||||
|
if (client != null && client.isConnected()) { |
||||||
|
client.disconnect(); |
||||||
|
} |
||||||
|
} catch (IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,128 @@ |
|||||||
|
/* |
||||||
|
* 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.downloader; |
||||||
|
|
||||||
|
import android.util.Log; |
||||||
|
import com.arialyy.aria.core.common.AbsThreadTask; |
||||||
|
import com.arialyy.aria.core.common.StateConstance; |
||||||
|
import com.arialyy.aria.core.common.SubThreadConfig; |
||||||
|
import com.arialyy.aria.core.download.DownloadEntity; |
||||||
|
import com.arialyy.aria.core.download.DownloadTaskEntity; |
||||||
|
import com.arialyy.aria.core.inf.IDownloadListener; |
||||||
|
import com.arialyy.aria.util.BufferedRandomAccessFile; |
||||||
|
import java.io.File; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.InputStream; |
||||||
|
import java.net.HttpURLConnection; |
||||||
|
import java.net.MalformedURLException; |
||||||
|
import java.net.URL; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by lyy on 2017/1/18. |
||||||
|
* 下载线程 |
||||||
|
*/ |
||||||
|
final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DownloadTaskEntity> { |
||||||
|
private final String TAG = "HttpThreadTask"; |
||||||
|
|
||||||
|
HttpThreadTask(StateConstance constance, IDownloadListener listener, |
||||||
|
SubThreadConfig<DownloadTaskEntity> downloadInfo) { |
||||||
|
super(constance, listener, downloadInfo); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void run() { |
||||||
|
HttpURLConnection conn = null; |
||||||
|
InputStream is = null; |
||||||
|
BufferedRandomAccessFile file = null; |
||||||
|
try { |
||||||
|
URL url = new URL(mConfig.URL); |
||||||
|
conn = ConnectionHelp.handleConnection(url); |
||||||
|
if (mConfig.SUPPORT_BP) { |
||||||
|
Log.d(TAG, "任务【" |
||||||
|
+ mConfig.TEMP_FILE.getName() |
||||||
|
+ "】线程__" |
||||||
|
+ mConfig.THREAD_ID |
||||||
|
+ "__开始下载【开始位置 : " |
||||||
|
+ mConfig.START_LOCATION |
||||||
|
+ ",结束位置:" |
||||||
|
+ mConfig.END_LOCATION |
||||||
|
+ "】"); |
||||||
|
//在头里面请求下载开始位置和结束位置
|
||||||
|
conn.setRequestProperty("Range", |
||||||
|
"bytes=" + mConfig.START_LOCATION + "-" + (mConfig.END_LOCATION - 1)); |
||||||
|
} else { |
||||||
|
Log.w(TAG, "该下载不支持断点"); |
||||||
|
} |
||||||
|
conn = ConnectionHelp.setConnectParam(mConfig.TASK_ENTITY, conn); |
||||||
|
conn.setConnectTimeout(STATE.CONNECT_TIME_OUT); |
||||||
|
conn.setReadTimeout(STATE.READ_TIME_OUT); //设置读取流的等待时间,必须设置该参数
|
||||||
|
is = conn.getInputStream(); |
||||||
|
//创建可设置位置的文件
|
||||||
|
file = new BufferedRandomAccessFile(mConfig.TEMP_FILE, "rwd", mBufSize); |
||||||
|
//设置每条线程写入文件的位置
|
||||||
|
file.seek(mConfig.START_LOCATION); |
||||||
|
byte[] buffer = new byte[mBufSize]; |
||||||
|
int len; |
||||||
|
//当前子线程的下载位置
|
||||||
|
mChildCurrentLocation = mConfig.START_LOCATION; |
||||||
|
while ((len = is.read(buffer)) != -1) { |
||||||
|
if (STATE.isCancel) break; |
||||||
|
if (STATE.isStop) break; |
||||||
|
if (mSleepTime > 0) Thread.sleep(mSleepTime); |
||||||
|
file.write(buffer, 0, len); |
||||||
|
progress(len); |
||||||
|
} |
||||||
|
if (STATE.isCancel || STATE.isStop) return; |
||||||
|
//支持断点的处理
|
||||||
|
if (mConfig.SUPPORT_BP) { |
||||||
|
Log.i(TAG, "任务【" + mConfig.TEMP_FILE.getName() + "】线程__" + mConfig.THREAD_ID + "__下载完毕"); |
||||||
|
writeConfig(true, 1); |
||||||
|
STATE.COMPLETE_THREAD_NUM++; |
||||||
|
if (STATE.isComplete()) { |
||||||
|
File configFile = new File(mConfigFPath); |
||||||
|
if (configFile.exists()) { |
||||||
|
configFile.delete(); |
||||||
|
} |
||||||
|
STATE.isRunning = false; |
||||||
|
mListener.onComplete(); |
||||||
|
} |
||||||
|
} else { |
||||||
|
Log.i(TAG, "下载任务完成"); |
||||||
|
STATE.isRunning = false; |
||||||
|
mListener.onComplete(); |
||||||
|
} |
||||||
|
} catch (MalformedURLException e) { |
||||||
|
fail(mChildCurrentLocation, "下载链接异常", e); |
||||||
|
} catch (IOException e) { |
||||||
|
fail(mChildCurrentLocation, "下载失败【" + mConfig.URL + "】", e); |
||||||
|
} catch (Exception e) { |
||||||
|
fail(mChildCurrentLocation, "获取流失败", e); |
||||||
|
} finally { |
||||||
|
try { |
||||||
|
if (file != null) { |
||||||
|
file.close(); |
||||||
|
} |
||||||
|
if (is != null) { |
||||||
|
is.close(); |
||||||
|
} |
||||||
|
if (conn != null) { |
||||||
|
conn.disconnect(); |
||||||
|
} |
||||||
|
} catch (IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,17 @@ |
|||||||
|
package com.arialyy.aria.core.download.downloader; |
||||||
|
|
||||||
|
interface OnFileInfoCallback { |
||||||
|
/** |
||||||
|
* 处理完成 |
||||||
|
* |
||||||
|
* @param code 状态码 |
||||||
|
*/ |
||||||
|
void onComplete(String url, int code); |
||||||
|
|
||||||
|
/** |
||||||
|
* 请求失败 |
||||||
|
* |
||||||
|
* @param errorMsg 错误信息 |
||||||
|
*/ |
||||||
|
void onFail(String url, String errorMsg); |
||||||
|
} |
@ -1,288 +0,0 @@ |
|||||||
/* |
|
||||||
* 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.downloader; |
|
||||||
|
|
||||||
import android.os.Build; |
|
||||||
import android.text.TextUtils; |
|
||||||
import android.util.Log; |
|
||||||
import com.arialyy.aria.core.AriaManager; |
|
||||||
import com.arialyy.aria.util.BufferedRandomAccessFile; |
|
||||||
import com.arialyy.aria.util.CommonUtil; |
|
||||||
import java.io.File; |
|
||||||
import java.io.IOException; |
|
||||||
import java.io.InputStream; |
|
||||||
import java.math.BigDecimal; |
|
||||||
import java.net.HttpURLConnection; |
|
||||||
import java.net.MalformedURLException; |
|
||||||
import java.net.URL; |
|
||||||
import java.util.Properties; |
|
||||||
|
|
||||||
/** |
|
||||||
* Created by lyy on 2017/1/18. |
|
||||||
* 下载线程 |
|
||||||
*/ |
|
||||||
final class SingleThreadTask implements Runnable { |
|
||||||
private static final String TAG = "SingleThreadTask"; |
|
||||||
private ChildThreadConfigEntity mConfigEntity; |
|
||||||
private String mConfigFPath; |
|
||||||
private long mChildCurrentLocation = 0; |
|
||||||
private int mBufSize; |
|
||||||
private IDownloadListener mListener; |
|
||||||
private StateConstance CONSTANCE; |
|
||||||
private long mSleepTime = 0; |
|
||||||
|
|
||||||
SingleThreadTask(StateConstance constance, IDownloadListener listener, |
|
||||||
ChildThreadConfigEntity downloadInfo) { |
|
||||||
AriaManager manager = AriaManager.getInstance(AriaManager.APP); |
|
||||||
CONSTANCE = constance; |
|
||||||
CONSTANCE.CONNECT_TIME_OUT = manager.getDownloadConfig().getConnectTimeOut(); |
|
||||||
CONSTANCE.READ_TIME_OUT = manager.getDownloadConfig().getIOTimeOut(); |
|
||||||
mListener = listener; |
|
||||||
this.mConfigEntity = downloadInfo; |
|
||||||
if (mConfigEntity.IS_SUPPORT_BREAK_POINT) { |
|
||||||
mConfigFPath = downloadInfo.CONFIG_FILE_PATH; |
|
||||||
} |
|
||||||
mBufSize = manager.getDownloadConfig().getBuffSize(); |
|
||||||
setMaxSpeed(AriaManager.getInstance(AriaManager.APP).getDownloadConfig().getMsxSpeed()); |
|
||||||
} |
|
||||||
|
|
||||||
void setMaxSpeed(double maxSpeed) { |
|
||||||
if (-0.9999 < maxSpeed && maxSpeed < 0.00001) { |
|
||||||
mSleepTime = 0; |
|
||||||
} else { |
|
||||||
BigDecimal db = new BigDecimal( |
|
||||||
((mBufSize / 1024) * (filterVersion() ? 1 : CONSTANCE.THREAD_NUM) / maxSpeed) * 1000); |
|
||||||
mSleepTime = db.setScale(0, BigDecimal.ROUND_HALF_UP).longValue(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private boolean filterVersion() { |
|
||||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void run() { |
|
||||||
HttpURLConnection conn = null; |
|
||||||
InputStream is = null; |
|
||||||
BufferedRandomAccessFile file = null; |
|
||||||
try { |
|
||||||
URL url = new URL(mConfigEntity.DOWNLOAD_URL); |
|
||||||
conn = ConnectionHelp.handleConnection(url); |
|
||||||
if (mConfigEntity.IS_SUPPORT_BREAK_POINT) { |
|
||||||
Log.d(TAG, "任务【" |
|
||||||
+ mConfigEntity.TEMP_FILE.getName() |
|
||||||
+ "】线程__" |
|
||||||
+ mConfigEntity.THREAD_ID |
|
||||||
+ "__开始下载【开始位置 : " |
|
||||||
+ mConfigEntity.START_LOCATION |
|
||||||
+ ",结束位置:" |
|
||||||
+ mConfigEntity.END_LOCATION |
|
||||||
+ "】"); |
|
||||||
//在头里面请求下载开始位置和结束位置
|
|
||||||
conn.setRequestProperty("Range", |
|
||||||
"bytes=" + mConfigEntity.START_LOCATION + "-" + (mConfigEntity.END_LOCATION - 1)); |
|
||||||
} else { |
|
||||||
Log.w(TAG, "该下载不支持断点"); |
|
||||||
} |
|
||||||
conn = ConnectionHelp.setConnectParam(mConfigEntity.DOWNLOAD_TASK_ENTITY, conn); |
|
||||||
conn.setConnectTimeout(CONSTANCE.CONNECT_TIME_OUT); |
|
||||||
conn.setReadTimeout(CONSTANCE.READ_TIME_OUT); //设置读取流的等待时间,必须设置该参数
|
|
||||||
is = conn.getInputStream(); |
|
||||||
//创建可设置位置的文件
|
|
||||||
file = new BufferedRandomAccessFile(mConfigEntity.TEMP_FILE, "rwd", mBufSize); |
|
||||||
//设置每条线程写入文件的位置
|
|
||||||
file.seek(mConfigEntity.START_LOCATION); |
|
||||||
byte[] buffer = new byte[mBufSize]; |
|
||||||
int len; |
|
||||||
//当前子线程的下载位置
|
|
||||||
mChildCurrentLocation = mConfigEntity.START_LOCATION; |
|
||||||
while ((len = is.read(buffer)) != -1) { |
|
||||||
if (CONSTANCE.isCancel) break; |
|
||||||
if (CONSTANCE.isStop) break; |
|
||||||
if (mSleepTime > 0) Thread.sleep(mSleepTime); |
|
||||||
file.write(buffer, 0, len); |
|
||||||
progress(len); |
|
||||||
} |
|
||||||
if (CONSTANCE.isCancel) return; |
|
||||||
//停止状态不需要删除记录文件
|
|
||||||
if (CONSTANCE.isStop) return; |
|
||||||
//支持断点的处理
|
|
||||||
if (mConfigEntity.IS_SUPPORT_BREAK_POINT) { |
|
||||||
Log.i(TAG, "任务【" |
|
||||||
+ mConfigEntity.TEMP_FILE.getName() |
|
||||||
+ "】线程__" |
|
||||||
+ mConfigEntity.THREAD_ID |
|
||||||
+ "__下载完毕"); |
|
||||||
writeConfig(true, 1); |
|
||||||
CONSTANCE.COMPLETE_THREAD_NUM++; |
|
||||||
if (CONSTANCE.isComplete()) { |
|
||||||
File configFile = new File(mConfigFPath); |
|
||||||
if (configFile.exists()) { |
|
||||||
configFile.delete(); |
|
||||||
} |
|
||||||
CONSTANCE.isDownloading = false; |
|
||||||
mListener.onComplete(); |
|
||||||
} |
|
||||||
} else { |
|
||||||
Log.i(TAG, "下载任务完成"); |
|
||||||
CONSTANCE.isDownloading = false; |
|
||||||
mListener.onComplete(); |
|
||||||
} |
|
||||||
} catch (MalformedURLException e) { |
|
||||||
failDownload(mChildCurrentLocation, "下载链接异常", e); |
|
||||||
} catch (IOException e) { |
|
||||||
failDownload(mChildCurrentLocation, "下载失败【" + mConfigEntity.DOWNLOAD_URL + "】", e); |
|
||||||
} catch (Exception e) { |
|
||||||
failDownload(mChildCurrentLocation, "获取流失败", e); |
|
||||||
} finally { |
|
||||||
try { |
|
||||||
if (file != null) { |
|
||||||
file.close(); |
|
||||||
} |
|
||||||
if (is != null) { |
|
||||||
is.close(); |
|
||||||
} |
|
||||||
if (conn != null) { |
|
||||||
conn.disconnect(); |
|
||||||
} |
|
||||||
} catch (IOException e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 停止下载 |
|
||||||
*/ |
|
||||||
protected void stop() { |
|
||||||
synchronized (AriaManager.LOCK) { |
|
||||||
try { |
|
||||||
if (mConfigEntity.IS_SUPPORT_BREAK_POINT) { |
|
||||||
CONSTANCE.STOP_NUM++; |
|
||||||
Log.d(TAG, "任务【" |
|
||||||
+ mConfigEntity.TEMP_FILE.getName() |
|
||||||
+ "】thread__" |
|
||||||
+ mConfigEntity.THREAD_ID |
|
||||||
+ "__停止, stop location ==> " |
|
||||||
+ mChildCurrentLocation); |
|
||||||
writeConfig(false, mChildCurrentLocation); |
|
||||||
if (CONSTANCE.isStop()) { |
|
||||||
Log.d(TAG, "任务【" + mConfigEntity.TEMP_FILE.getName() + "】已停止"); |
|
||||||
CONSTANCE.isDownloading = false; |
|
||||||
mListener.onStop(CONSTANCE.CURRENT_LOCATION); |
|
||||||
} |
|
||||||
} else { |
|
||||||
Log.d(TAG, "任务【" + mConfigEntity.TEMP_FILE.getName() + "】已停止"); |
|
||||||
CONSTANCE.isDownloading = false; |
|
||||||
mListener.onStop(CONSTANCE.CURRENT_LOCATION); |
|
||||||
} |
|
||||||
} catch (IOException e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 下载中 |
|
||||||
*/ |
|
||||||
private void progress(long len) { |
|
||||||
synchronized (AriaManager.LOCK) { |
|
||||||
mChildCurrentLocation += len; |
|
||||||
CONSTANCE.CURRENT_LOCATION += len; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 取消下载 |
|
||||||
*/ |
|
||||||
protected void cancel() { |
|
||||||
synchronized (AriaManager.LOCK) { |
|
||||||
if (mConfigEntity.IS_SUPPORT_BREAK_POINT) { |
|
||||||
CONSTANCE.CANCEL_NUM++; |
|
||||||
Log.d(TAG, "任务【" |
|
||||||
+ mConfigEntity.TEMP_FILE.getName() |
|
||||||
+ "】thread__" |
|
||||||
+ mConfigEntity.THREAD_ID |
|
||||||
+ "__取消下载"); |
|
||||||
if (CONSTANCE.isCancel()) { |
|
||||||
File configFile = new File(mConfigFPath); |
|
||||||
if (configFile.exists()) { |
|
||||||
configFile.delete(); |
|
||||||
} |
|
||||||
if (mConfigEntity.TEMP_FILE.exists()) { |
|
||||||
mConfigEntity.TEMP_FILE.delete(); |
|
||||||
} |
|
||||||
Log.d(TAG, "任务【" + mConfigEntity.TEMP_FILE.getName() + "】已取消"); |
|
||||||
CONSTANCE.isDownloading = false; |
|
||||||
mListener.onCancel(); |
|
||||||
} |
|
||||||
} else { |
|
||||||
Log.d(TAG, "任务【" + mConfigEntity.TEMP_FILE.getName() + "】已取消"); |
|
||||||
CONSTANCE.isDownloading = false; |
|
||||||
mListener.onCancel(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 下载失败 |
|
||||||
*/ |
|
||||||
private void failDownload(long currentLocation, String msg, Exception ex) { |
|
||||||
synchronized (AriaManager.LOCK) { |
|
||||||
try { |
|
||||||
CONSTANCE.FAIL_NUM++; |
|
||||||
CONSTANCE.isDownloading = false; |
|
||||||
CONSTANCE.isStop = true; |
|
||||||
if (ex != null) { |
|
||||||
Log.e(TAG, msg + "\n" + CommonUtil.getPrintException(ex)); |
|
||||||
} |
|
||||||
if (mConfigEntity.IS_SUPPORT_BREAK_POINT) { |
|
||||||
writeConfig(false, currentLocation); |
|
||||||
if (CONSTANCE.isFail()) { |
|
||||||
Log.e(TAG, "任务【" + mConfigEntity.TEMP_FILE.getName() + "】下载失败"); |
|
||||||
mListener.onFail(); |
|
||||||
} |
|
||||||
} else { |
|
||||||
Log.e(TAG, "任务【" + mConfigEntity.TEMP_FILE.getName() + "】下载失败"); |
|
||||||
mListener.onFail(); |
|
||||||
} |
|
||||||
} catch (IOException e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 将记录写入到配置文件 |
|
||||||
*/ |
|
||||||
private void writeConfig(boolean isComplete, long record) throws IOException { |
|
||||||
synchronized (AriaManager.LOCK) { |
|
||||||
String key = null, value = null; |
|
||||||
if (0 < record && record < mConfigEntity.END_LOCATION) { |
|
||||||
key = mConfigEntity.TEMP_FILE.getName() + "_record_" + mConfigEntity.THREAD_ID; |
|
||||||
value = String.valueOf(record); |
|
||||||
} else if (record >= mConfigEntity.END_LOCATION || isComplete) { |
|
||||||
key = mConfigEntity.TEMP_FILE.getName() + "_state_" + mConfigEntity.THREAD_ID; |
|
||||||
value = "1"; |
|
||||||
} |
|
||||||
if (!TextUtils.isEmpty(key) && !TextUtils.isEmpty(value)) { |
|
||||||
File configFile = new File(mConfigFPath); |
|
||||||
Properties pro = CommonUtil.loadConfig(configFile); |
|
||||||
pro.setProperty(key, value); |
|
||||||
CommonUtil.saveConfig(configFile, pro); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,138 @@ |
|||||||
|
/* |
||||||
|
* 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.upload; |
||||||
|
|
||||||
|
import android.os.Handler; |
||||||
|
import com.arialyy.aria.core.AriaManager; |
||||||
|
import com.arialyy.aria.core.inf.AbsEntity; |
||||||
|
import com.arialyy.aria.core.inf.AbsTask; |
||||||
|
import com.arialyy.aria.core.inf.IEntity; |
||||||
|
import com.arialyy.aria.core.inf.IUploadListener; |
||||||
|
import com.arialyy.aria.core.scheduler.ISchedulers; |
||||||
|
import com.arialyy.aria.util.CommonUtil; |
||||||
|
import java.lang.ref.WeakReference; |
||||||
|
|
||||||
|
/** |
||||||
|
* 下载监听类 |
||||||
|
*/ |
||||||
|
class BaseUListener<ENTITY extends AbsEntity, TASK extends AbsTask<ENTITY>> |
||||||
|
implements IUploadListener { |
||||||
|
private WeakReference<Handler> outHandler; |
||||||
|
private long mLastLen = 0; //上一次发送长度
|
||||||
|
private boolean isFirst = true; |
||||||
|
protected ENTITY mEntity; |
||||||
|
protected TASK mTask; |
||||||
|
private boolean isConvertSpeed = false; |
||||||
|
boolean isWait = false; |
||||||
|
|
||||||
|
BaseUListener(TASK task, Handler outHandler) { |
||||||
|
this.outHandler = new WeakReference<>(outHandler); |
||||||
|
this.mTask = new WeakReference<>(task).get(); |
||||||
|
this.mEntity = this.mTask.getEntity(); |
||||||
|
final AriaManager manager = AriaManager.getInstance(AriaManager.APP); |
||||||
|
isConvertSpeed = manager.getDownloadConfig().isConvertSpeed(); |
||||||
|
mLastLen = mEntity.getCurrentProgress(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void onPre() { |
||||||
|
saveData(IEntity.STATE_PRE, -1); |
||||||
|
sendInState2Target(ISchedulers.PRE); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void onStart(long startLocation) { |
||||||
|
saveData(IEntity.STATE_RUNNING, startLocation); |
||||||
|
sendInState2Target(ISchedulers.START); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void onResume(long resumeLocation) { |
||||||
|
saveData(IEntity.STATE_RUNNING, resumeLocation); |
||||||
|
sendInState2Target(ISchedulers.RESUME); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void onProgress(long currentLocation) { |
||||||
|
mEntity.setCurrentProgress(currentLocation); |
||||||
|
long speed = currentLocation - mLastLen; |
||||||
|
if (isFirst) { |
||||||
|
speed = 0; |
||||||
|
isFirst = false; |
||||||
|
} |
||||||
|
handleSpeed(speed); |
||||||
|
sendInState2Target(ISchedulers.RUNNING); |
||||||
|
mLastLen = currentLocation; |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void onStop(long stopLocation) { |
||||||
|
saveData(isWait ? IEntity.STATE_WAIT : IEntity.STATE_STOP, stopLocation); |
||||||
|
handleSpeed(0); |
||||||
|
sendInState2Target(ISchedulers.STOP); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void onCancel() { |
||||||
|
saveData(IEntity.STATE_CANCEL, -1); |
||||||
|
handleSpeed(0); |
||||||
|
sendInState2Target(ISchedulers.CANCEL); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void onComplete() { |
||||||
|
saveData(IEntity.STATE_COMPLETE, mEntity.getFileSize()); |
||||||
|
handleSpeed(0); |
||||||
|
sendInState2Target(ISchedulers.COMPLETE); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void onFail() { |
||||||
|
mEntity.setFailNum(mEntity.getFailNum() + 1); |
||||||
|
saveData(IEntity.STATE_FAIL, mEntity.getCurrentProgress()); |
||||||
|
handleSpeed(0); |
||||||
|
sendInState2Target(ISchedulers.FAIL); |
||||||
|
} |
||||||
|
|
||||||
|
private void handleSpeed(long speed) { |
||||||
|
if (isConvertSpeed) { |
||||||
|
mEntity.setConvertSpeed(CommonUtil.formatFileSize(speed < 0 ? 0 : speed) + "/s"); |
||||||
|
} else { |
||||||
|
mEntity.setSpeed(speed < 0 ? 0 : speed); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 将任务状态发送给下载器 |
||||||
|
* |
||||||
|
* @param state {@link ISchedulers#START} |
||||||
|
*/ |
||||||
|
private void sendInState2Target(int state) { |
||||||
|
if (outHandler.get() != null) { |
||||||
|
outHandler.get().obtainMessage(state, mTask).sendToTarget(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void saveData(int state, long location) { |
||||||
|
mEntity.setComplete(state == IEntity.STATE_COMPLETE); |
||||||
|
if (state == IEntity.STATE_CANCEL) { |
||||||
|
mEntity.deleteData(); |
||||||
|
} else if (state == IEntity.STATE_COMPLETE) { |
||||||
|
mEntity.setState(state); |
||||||
|
mEntity.setCompleteTime(System.currentTimeMillis()); |
||||||
|
mEntity.setCurrentProgress(mEntity.getFileSize()); |
||||||
|
mEntity.update(); |
||||||
|
} else { |
||||||
|
mEntity.setState(state); |
||||||
|
if (location != -1) { |
||||||
|
mEntity.setCurrentProgress(location); |
||||||
|
} |
||||||
|
mEntity.update(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,80 @@ |
|||||||
|
/* |
||||||
|
* 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.upload; |
||||||
|
|
||||||
|
import android.text.TextUtils; |
||||||
|
import android.util.Log; |
||||||
|
import com.arialyy.aria.core.inf.AbsTaskEntity; |
||||||
|
import com.arialyy.aria.core.inf.AbsUploadTarget; |
||||||
|
import com.arialyy.aria.orm.DbEntity; |
||||||
|
import java.io.File; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by Aria.Lao on 2017/7/27. |
||||||
|
* ftp单任务上传 |
||||||
|
*/ |
||||||
|
public class FtpUploadTarget |
||||||
|
extends AbsUploadTarget<FtpUploadTarget, UploadEntity, UploadTaskEntity> { |
||||||
|
private final String TAG = "FtpUploadTarget"; |
||||||
|
|
||||||
|
FtpUploadTarget(String filePath, String targetName) { |
||||||
|
this.mTargetName = targetName; |
||||||
|
mTaskEntity = DbEntity.findFirst(UploadTaskEntity.class, "key=?", filePath); |
||||||
|
if (mTaskEntity == null) { |
||||||
|
mTaskEntity = new UploadTaskEntity(); |
||||||
|
mTaskEntity.entity = getUploadEntity(filePath); |
||||||
|
} |
||||||
|
if (mTaskEntity.entity == null) { |
||||||
|
mTaskEntity.entity = getUploadEntity(filePath); |
||||||
|
} |
||||||
|
mTaskEntity.requestType = AbsTaskEntity.FTP; |
||||||
|
mEntity = mTaskEntity.entity; |
||||||
|
File file = new File(filePath); |
||||||
|
mEntity.setFileName(file.getName()); |
||||||
|
mEntity.setFileSize(file.length()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* ftp 用户登录信息 |
||||||
|
* |
||||||
|
* @param userName ftp用户名 |
||||||
|
* @param password ftp用户密码 |
||||||
|
*/ |
||||||
|
public FtpUploadTarget login(String userName, String password) { |
||||||
|
return login(userName, password, null); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* ftp 用户登录信息 |
||||||
|
* |
||||||
|
* @param userName ftp用户名 |
||||||
|
* @param password ftp用户密码 |
||||||
|
* @param account ftp账号 |
||||||
|
*/ |
||||||
|
public FtpUploadTarget login(String userName, String password, String account) { |
||||||
|
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; |
||||||
|
mTaskEntity.account = account; |
||||||
|
return this; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,185 @@ |
|||||||
|
/* |
||||||
|
* 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.upload.uploader; |
||||||
|
|
||||||
|
import android.text.TextUtils; |
||||||
|
import android.util.Log; |
||||||
|
import com.arialyy.aria.core.common.AbsThreadTask; |
||||||
|
import com.arialyy.aria.core.common.StateConstance; |
||||||
|
import com.arialyy.aria.core.common.SubThreadConfig; |
||||||
|
import com.arialyy.aria.core.inf.IEventListener; |
||||||
|
import com.arialyy.aria.core.upload.UploadEntity; |
||||||
|
import com.arialyy.aria.core.upload.UploadTaskEntity; |
||||||
|
import com.arialyy.aria.util.BufferedRandomAccessFile; |
||||||
|
import java.io.File; |
||||||
|
import java.io.FileInputStream; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.OutputStream; |
||||||
|
import org.apache.commons.net.ftp.FTP; |
||||||
|
import org.apache.commons.net.ftp.FTPClient; |
||||||
|
import org.apache.commons.net.ftp.FTPFile; |
||||||
|
import org.apache.commons.net.ftp.FTPReply; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by Aria.Lao on 2017/7/28. |
||||||
|
* FTP 单线程上传任务,需要FTP 服务器给用户打开删除和读入IO的权限 |
||||||
|
*/ |
||||||
|
class FtpThreadTask extends AbsThreadTask<UploadEntity, UploadTaskEntity> { |
||||||
|
private final String TAG = "FtpThreadTask"; |
||||||
|
private String dir, remotePath, charSet; |
||||||
|
|
||||||
|
FtpThreadTask(StateConstance constance, IEventListener listener, |
||||||
|
SubThreadConfig<UploadTaskEntity> info) { |
||||||
|
super(constance, listener, info); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void run() { |
||||||
|
FTPClient client = null; |
||||||
|
OutputStream os = null; |
||||||
|
BufferedRandomAccessFile file = null; |
||||||
|
try { |
||||||
|
Log.d(TAG, "任务【" |
||||||
|
+ mConfig.TEMP_FILE.getName() |
||||||
|
+ "】线程__" |
||||||
|
+ mConfig.THREAD_ID |
||||||
|
+ "__开始上传【开始位置 : " |
||||||
|
+ mConfig.START_LOCATION |
||||||
|
+ ",结束位置:" |
||||||
|
+ mConfig.END_LOCATION |
||||||
|
+ "】"); |
||||||
|
//当前子线程的下载位置
|
||||||
|
mChildCurrentLocation = mConfig.START_LOCATION; |
||||||
|
client = createClient(); |
||||||
|
if (client == null) return; |
||||||
|
client.makeDirectory(dir); |
||||||
|
client.changeWorkingDirectory(dir); |
||||||
|
client.setRestartOffset(mConfig.START_LOCATION); |
||||||
|
file = new BufferedRandomAccessFile(mConfig.TEMP_FILE, "rwd", mBufSize); |
||||||
|
file.seek(mConfig.START_LOCATION); |
||||||
|
if (!isRemoteComplete(client)) { |
||||||
|
os = client.storeFileStream(new String(remotePath.getBytes(charSet), SERVER_CHARSET)); |
||||||
|
//发送第二次指令时,还需要再做一次判断
|
||||||
|
int reply = client.getReplyCode(); |
||||||
|
if (!FTPReply.isPositivePreliminary(reply)) { |
||||||
|
client.disconnect(); |
||||||
|
fail(mChildCurrentLocation, "上传文件错误,错误码为:" + reply, null); |
||||||
|
return; |
||||||
|
} |
||||||
|
upload(file, os); |
||||||
|
} |
||||||
|
if (STATE.isCancel || STATE.isStop) return; |
||||||
|
Log.i(TAG, "任务【" + mConfig.TEMP_FILE.getName() + "】线程__" + mConfig.THREAD_ID + "__上传完毕"); |
||||||
|
writeConfig(true, 1); |
||||||
|
STATE.COMPLETE_THREAD_NUM++; |
||||||
|
if (STATE.isComplete()) { |
||||||
|
File configFile = new File(mConfigFPath); |
||||||
|
if (configFile.exists()) { |
||||||
|
configFile.delete(); |
||||||
|
} |
||||||
|
STATE.isRunning = false; |
||||||
|
mListener.onComplete(); |
||||||
|
} |
||||||
|
} catch (IOException e) { |
||||||
|
fail(mChildCurrentLocation, "上传失败【" + mConfig.URL + "】", e); |
||||||
|
} catch (Exception e) { |
||||||
|
fail(mChildCurrentLocation, "获取流失败", e); |
||||||
|
} finally { |
||||||
|
try { |
||||||
|
if (file != null) { |
||||||
|
file.close(); |
||||||
|
} |
||||||
|
if (os != null) { |
||||||
|
os.close(); |
||||||
|
} |
||||||
|
if (client != null && client.isConnected()) { |
||||||
|
client.disconnect(); |
||||||
|
} |
||||||
|
} catch (IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 远程文件是否已经玩飞车 |
||||||
|
* |
||||||
|
* @return true 任务已经完成 |
||||||
|
*/ |
||||||
|
private boolean isRemoteComplete(FTPClient client) throws IOException { |
||||||
|
FTPFile[] files = client.listFiles(new String(remotePath.getBytes(charSet), SERVER_CHARSET)); |
||||||
|
return files.length != 0 && files[0].getSize() == mEntity.getFileSize(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 执行上传操作 |
||||||
|
*/ |
||||||
|
private void upload(BufferedRandomAccessFile file, OutputStream os) |
||||||
|
throws IOException, InterruptedException { |
||||||
|
int len; |
||||||
|
byte[] buffer = new byte[mBufSize]; |
||||||
|
while ((len = file.read(buffer)) != -1) { |
||||||
|
if (STATE.isCancel) break; |
||||||
|
if (STATE.isStop) break; |
||||||
|
if (mSleepTime > 0) Thread.sleep(mSleepTime); |
||||||
|
if (mChildCurrentLocation + len >= mConfig.END_LOCATION) { |
||||||
|
len = (int) (mConfig.END_LOCATION - mChildCurrentLocation); |
||||||
|
os.write(buffer, 0, len); |
||||||
|
progress(len); |
||||||
|
break; |
||||||
|
} else { |
||||||
|
os.write(buffer, 0, len); |
||||||
|
progress(len); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 构建FTP客户端 |
||||||
|
*/ |
||||||
|
private FTPClient createClient() throws IOException { |
||||||
|
String url = mEntity.getUrl(); |
||||||
|
String[] pp = url.split("/")[2].split(":"); |
||||||
|
String serverIp = pp[0]; |
||||||
|
int port = Integer.parseInt(pp[1]); |
||||||
|
dir = url.substring(url.indexOf(pp[1]) + pp[1].length(), url.length()); |
||||||
|
remotePath = dir + "/" + mEntity.getFileName(); |
||||||
|
FTPClient client = new FTPClient(); |
||||||
|
client.connect(serverIp, port); |
||||||
|
if (!TextUtils.isEmpty(mTaskEntity.account)) { |
||||||
|
client.login(mTaskEntity.userName, mTaskEntity.userPw); |
||||||
|
} else { |
||||||
|
client.login(mTaskEntity.userName, mTaskEntity.userPw, mTaskEntity.account); |
||||||
|
} |
||||||
|
int reply = client.getReplyCode(); |
||||||
|
if (!FTPReply.isPositiveCompletion(reply)) { |
||||||
|
client.disconnect(); |
||||||
|
fail(STATE.CURRENT_LOCATION, "无法连接到ftp服务器,错误码为:" + reply, null); |
||||||
|
return null; |
||||||
|
} |
||||||
|
charSet = "UTF-8"; |
||||||
|
// 开启服务器对UTF-8的支持,如果服务器支持就用UTF-8编码
|
||||||
|
if (!TextUtils.isEmpty(mTaskEntity.charSet) || !FTPReply.isPositiveCompletion( |
||||||
|
client.sendCommand("OPTS UTF8", "ON"))) { |
||||||
|
charSet = mTaskEntity.charSet; |
||||||
|
} |
||||||
|
client.setControlEncoding(charSet); |
||||||
|
client.setDataTimeout(STATE.READ_TIME_OUT); |
||||||
|
client.enterLocalPassiveMode(); |
||||||
|
client.setFileType(FTP.BINARY_FILE_TYPE); |
||||||
|
client.allocate(mBufSize); |
||||||
|
return client; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,83 @@ |
|||||||
|
/* |
||||||
|
* 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.upload.uploader; |
||||||
|
|
||||||
|
import com.arialyy.aria.core.common.IUtil; |
||||||
|
import com.arialyy.aria.core.inf.IUploadListener; |
||||||
|
import com.arialyy.aria.core.upload.UploadEntity; |
||||||
|
import com.arialyy.aria.core.upload.UploadTaskEntity; |
||||||
|
import com.arialyy.aria.util.CheckUtil; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by lyy on 2017/2/9. |
||||||
|
* 简单的文件上传工具 |
||||||
|
*/ |
||||||
|
public class SimpleUploadUtil implements IUtil, Runnable { |
||||||
|
private static final String TAG = "SimpleUploadUtil"; |
||||||
|
|
||||||
|
private UploadEntity mUploadEntity; |
||||||
|
private UploadTaskEntity mTaskEntity; |
||||||
|
private IUploadListener mListener; |
||||||
|
private Uploader mUploader; |
||||||
|
|
||||||
|
public SimpleUploadUtil(UploadTaskEntity taskEntity, IUploadListener listener) { |
||||||
|
mTaskEntity = taskEntity; |
||||||
|
CheckUtil.checkTaskEntity(taskEntity); |
||||||
|
mUploadEntity = taskEntity.getEntity(); |
||||||
|
if (listener == null) { |
||||||
|
throw new IllegalArgumentException("上传监听不能为空"); |
||||||
|
} |
||||||
|
mListener = listener; |
||||||
|
mUploader = new Uploader(mListener, taskEntity); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void run() { |
||||||
|
mListener.onPre(); |
||||||
|
mUploader.start(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public long getFileSize() { |
||||||
|
return mUploader.getFileSize(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public long getCurrentLocation() { |
||||||
|
return mUploader.getCurrentLocation(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public boolean isRunning() { |
||||||
|
return mUploader.isRunning(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void cancel() { |
||||||
|
mUploader.cancel(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void stop() { |
||||||
|
mUploader.stop(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void start() { |
||||||
|
new Thread(this).start(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void resume() { |
||||||
|
mUploader.cancel(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void setMaxSpeed(double maxSpeed) { |
||||||
|
mUploader.setMaxSpeed(maxSpeed); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,87 @@ |
|||||||
|
/* |
||||||
|
* 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.upload.uploader; |
||||||
|
|
||||||
|
import com.arialyy.aria.core.AriaManager; |
||||||
|
import com.arialyy.aria.core.common.AbsFileer; |
||||||
|
import com.arialyy.aria.core.common.AbsThreadTask; |
||||||
|
import com.arialyy.aria.core.common.SubThreadConfig; |
||||||
|
import com.arialyy.aria.core.inf.AbsTaskEntity; |
||||||
|
import com.arialyy.aria.core.inf.IUploadListener; |
||||||
|
import com.arialyy.aria.core.upload.UploadEntity; |
||||||
|
import com.arialyy.aria.core.upload.UploadTaskEntity; |
||||||
|
import com.arialyy.aria.orm.DbEntity; |
||||||
|
import com.arialyy.aria.util.CommonUtil; |
||||||
|
import java.io.File; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by Aria.Lao on 2017/7/27. |
||||||
|
* 文件上传器 |
||||||
|
*/ |
||||||
|
class Uploader extends AbsFileer<UploadEntity, UploadTaskEntity> { |
||||||
|
|
||||||
|
Uploader(IUploadListener listener, UploadTaskEntity taskEntity) { |
||||||
|
super(listener, taskEntity); |
||||||
|
mTempFile = new File(mEntity.getFilePath()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 检查任务是否是新任务,新任务条件: |
||||||
|
* 1、文件不存在 |
||||||
|
* 2、记录文件不存在 |
||||||
|
* 3、记录文件缺失或不匹配 |
||||||
|
* 4、数据库记录不存在 |
||||||
|
* 5、不支持断点,则是新任务 |
||||||
|
*/ |
||||||
|
protected void checkTask() { |
||||||
|
mConfigFile = new File(mContext.getFilesDir().getPath() |
||||||
|
+ AriaManager.UPLOAD_TEMP_DIR |
||||||
|
+ mEntity.getFileName() |
||||||
|
+ ".properties"); |
||||||
|
if (!mTaskEntity.isSupportBP) { |
||||||
|
isNewTask = true; |
||||||
|
return; |
||||||
|
} |
||||||
|
if (!mConfigFile.exists()) { //记录文件被删除,则重新下载
|
||||||
|
isNewTask = true; |
||||||
|
CommonUtil.createFile(mConfigFile.getPath()); |
||||||
|
} else if (DbEntity.findFirst(UploadEntity.class, "filePath=?", mEntity.getFilePath()) |
||||||
|
== null) { |
||||||
|
isNewTask = true; |
||||||
|
} else { |
||||||
|
isNewTask = checkConfigFile(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override protected void handleNewTask() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override protected int getNewTaskThreadNum() { |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
@Override protected AbsThreadTask selectThreadTask(SubThreadConfig<UploadTaskEntity> config) { |
||||||
|
switch (mTaskEntity.requestType) { |
||||||
|
case AbsTaskEntity.FTP: |
||||||
|
case AbsTaskEntity.FTP_DIR: |
||||||
|
return new FtpThreadTask(mConstance, mListener, config); |
||||||
|
case AbsTaskEntity.HTTP: |
||||||
|
return new HttpThreadTask(mConstance, (IUploadListener) mListener, config); |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,314 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.apache.commons.net; |
||||||
|
|
||||||
|
import java.net.DatagramSocket; |
||||||
|
import java.net.InetAddress; |
||||||
|
import java.net.SocketException; |
||||||
|
import java.nio.charset.Charset; |
||||||
|
|
||||||
|
/*** |
||||||
|
* The DatagramSocketClient provides the basic operations that are required |
||||||
|
* of client objects accessing datagram sockets. It is meant to be |
||||||
|
* subclassed to avoid having to rewrite the same code over and over again |
||||||
|
* to open a socket, close a socket, set timeouts, etc. Of special note |
||||||
|
* is the {@link #setDatagramSocketFactory setDatagramSocketFactory } |
||||||
|
* method, which allows you to control the type of DatagramSocket the |
||||||
|
* DatagramSocketClient creates for network communications. This is |
||||||
|
* especially useful for adding things like proxy support as well as better |
||||||
|
* support for applets. For |
||||||
|
* example, you could create a |
||||||
|
* {@link org.apache.commons.net.DatagramSocketFactory} |
||||||
|
* that |
||||||
|
* requests browser security capabilities before creating a socket. |
||||||
|
* All classes derived from DatagramSocketClient should use the |
||||||
|
* {@link #_socketFactory_ _socketFactory_ } member variable to |
||||||
|
* create DatagramSocket instances rather than instantiating |
||||||
|
* them by directly invoking a constructor. By honoring this contract |
||||||
|
* you guarantee that a user will always be able to provide his own |
||||||
|
* Socket implementations by substituting his own SocketFactory. |
||||||
|
* |
||||||
|
* |
||||||
|
* @see DatagramSocketFactory |
||||||
|
***/ |
||||||
|
|
||||||
|
public abstract class DatagramSocketClient |
||||||
|
{ |
||||||
|
/*** |
||||||
|
* The default DatagramSocketFactory shared by all DatagramSocketClient |
||||||
|
* instances. |
||||||
|
***/ |
||||||
|
private static final DatagramSocketFactory __DEFAULT_SOCKET_FACTORY = |
||||||
|
new DefaultDatagramSocketFactory(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Charset to use for byte IO. |
||||||
|
*/ |
||||||
|
private Charset charset = Charset.defaultCharset(); |
||||||
|
|
||||||
|
/*** The timeout to use after opening a socket. ***/ |
||||||
|
protected int _timeout_; |
||||||
|
|
||||||
|
/*** The datagram socket used for the connection. ***/ |
||||||
|
protected DatagramSocket _socket_; |
||||||
|
|
||||||
|
/*** |
||||||
|
* A status variable indicating if the client's socket is currently open. |
||||||
|
***/ |
||||||
|
protected boolean _isOpen_; |
||||||
|
|
||||||
|
/*** The datagram socket's DatagramSocketFactory. ***/ |
||||||
|
protected DatagramSocketFactory _socketFactory_; |
||||||
|
|
||||||
|
/*** |
||||||
|
* Default constructor for DatagramSocketClient. Initializes |
||||||
|
* _socket_ to null, _timeout_ to 0, and _isOpen_ to false. |
||||||
|
***/ |
||||||
|
public DatagramSocketClient() |
||||||
|
{ |
||||||
|
_socket_ = null; |
||||||
|
_timeout_ = 0; |
||||||
|
_isOpen_ = false; |
||||||
|
_socketFactory_ = __DEFAULT_SOCKET_FACTORY; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*** |
||||||
|
* Opens a DatagramSocket on the local host at the first available port. |
||||||
|
* Also sets the timeout on the socket to the default timeout set |
||||||
|
* by {@link #setDefaultTimeout setDefaultTimeout() }. |
||||||
|
* <p> |
||||||
|
* _isOpen_ is set to true after calling this method and _socket_ |
||||||
|
* is set to the newly opened socket. |
||||||
|
* |
||||||
|
* @throws SocketException If the socket could not be opened or the |
||||||
|
* timeout could not be set. |
||||||
|
***/ |
||||||
|
public void open() throws SocketException |
||||||
|
{ |
||||||
|
_socket_ = _socketFactory_.createDatagramSocket(); |
||||||
|
_socket_.setSoTimeout(_timeout_); |
||||||
|
_isOpen_ = true; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*** |
||||||
|
* Opens a DatagramSocket on the local host at a specified port. |
||||||
|
* Also sets the timeout on the socket to the default timeout set |
||||||
|
* by {@link #setDefaultTimeout setDefaultTimeout() }. |
||||||
|
* <p> |
||||||
|
* _isOpen_ is set to true after calling this method and _socket_ |
||||||
|
* is set to the newly opened socket. |
||||||
|
* |
||||||
|
* @param port The port to use for the socket. |
||||||
|
* @throws SocketException If the socket could not be opened or the |
||||||
|
* timeout could not be set. |
||||||
|
***/ |
||||||
|
public void open(int port) throws SocketException |
||||||
|
{ |
||||||
|
_socket_ = _socketFactory_.createDatagramSocket(port); |
||||||
|
_socket_.setSoTimeout(_timeout_); |
||||||
|
_isOpen_ = true; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*** |
||||||
|
* Opens a DatagramSocket at the specified address on the local host |
||||||
|
* at a specified port. |
||||||
|
* Also sets the timeout on the socket to the default timeout set |
||||||
|
* by {@link #setDefaultTimeout setDefaultTimeout() }. |
||||||
|
* <p> |
||||||
|
* _isOpen_ is set to true after calling this method and _socket_ |
||||||
|
* is set to the newly opened socket. |
||||||
|
* |
||||||
|
* @param port The port to use for the socket. |
||||||
|
* @param laddr The local address to use. |
||||||
|
* @throws SocketException If the socket could not be opened or the |
||||||
|
* timeout could not be set. |
||||||
|
***/ |
||||||
|
public void open(int port, InetAddress laddr) throws SocketException |
||||||
|
{ |
||||||
|
_socket_ = _socketFactory_.createDatagramSocket(port, laddr); |
||||||
|
_socket_.setSoTimeout(_timeout_); |
||||||
|
_isOpen_ = true; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*** |
||||||
|
* Closes the DatagramSocket used for the connection. |
||||||
|
* You should call this method after you've finished using the class
|
||||||
|
* instance and also before you call {@link #open open() } |
||||||
|
* again. _isOpen_ is set to false and _socket_ is set to null. |
||||||
|
***/ |
||||||
|
public void close() |
||||||
|
{ |
||||||
|
if (_socket_ != null) { |
||||||
|
_socket_.close(); |
||||||
|
} |
||||||
|
_socket_ = null; |
||||||
|
_isOpen_ = false; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*** |
||||||
|
* Returns true if the client has a currently open socket. |
||||||
|
* |
||||||
|
* @return True if the client has a currently open socket, false otherwise. |
||||||
|
***/ |
||||||
|
public boolean isOpen() |
||||||
|
{ |
||||||
|
return _isOpen_; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*** |
||||||
|
* Set the default timeout in milliseconds to use when opening a socket. |
||||||
|
* After a call to open, the timeout for the socket is set using this value. |
||||||
|
* This method should be used prior to a call to {@link #open open()} |
||||||
|
* and should not be confused with {@link #setSoTimeout setSoTimeout()} |
||||||
|
* which operates on the currently open socket. _timeout_ contains |
||||||
|
* the new timeout value. |
||||||
|
* |
||||||
|
* @param timeout The timeout in milliseconds to use for the datagram socket |
||||||
|
* connection. |
||||||
|
***/ |
||||||
|
public void setDefaultTimeout(int timeout) |
||||||
|
{ |
||||||
|
_timeout_ = timeout; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*** |
||||||
|
* Returns the default timeout in milliseconds that is used when |
||||||
|
* opening a socket. |
||||||
|
* |
||||||
|
* @return The default timeout in milliseconds that is used when |
||||||
|
* opening a socket. |
||||||
|
***/ |
||||||
|
public int getDefaultTimeout() |
||||||
|
{ |
||||||
|
return _timeout_; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*** |
||||||
|
* Set the timeout in milliseconds of a currently open connection. |
||||||
|
* Only call this method after a connection has been opened |
||||||
|
* by {@link #open open()}. |
||||||
|
* |
||||||
|
* @param timeout The timeout in milliseconds to use for the currently |
||||||
|
* open datagram socket connection. |
||||||
|
* @throws SocketException if an error setting the timeout |
||||||
|
***/ |
||||||
|
public void setSoTimeout(int timeout) throws SocketException |
||||||
|
{ |
||||||
|
_socket_.setSoTimeout(timeout); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*** |
||||||
|
* Returns the timeout in milliseconds of the currently opened socket. |
||||||
|
* If you call this method when the client socket is not open, |
||||||
|
* a NullPointerException is thrown. |
||||||
|
* |
||||||
|
* @return The timeout in milliseconds of the currently opened socket. |
||||||
|
* @throws SocketException if an error getting the timeout |
||||||
|
***/ |
||||||
|
public int getSoTimeout() throws SocketException |
||||||
|
{ |
||||||
|
return _socket_.getSoTimeout(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*** |
||||||
|
* Returns the port number of the open socket on the local host used |
||||||
|
* for the connection. If you call this method when the client socket |
||||||
|
* is not open, a NullPointerException is thrown. |
||||||
|
* |
||||||
|
* @return The port number of the open socket on the local host used |
||||||
|
* for the connection. |
||||||
|
***/ |
||||||
|
public int getLocalPort() |
||||||
|
{ |
||||||
|
return _socket_.getLocalPort(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*** |
||||||
|
* Returns the local address to which the client's socket is bound. |
||||||
|
* If you call this method when the client socket is not open, a |
||||||
|
* NullPointerException is thrown. |
||||||
|
* |
||||||
|
* @return The local address to which the client's socket is bound. |
||||||
|
***/ |
||||||
|
public InetAddress getLocalAddress() |
||||||
|
{ |
||||||
|
return _socket_.getLocalAddress(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*** |
||||||
|
* Sets the DatagramSocketFactory used by the DatagramSocketClient |
||||||
|
* to open DatagramSockets. If the factory value is null, then a default |
||||||
|
* factory is used (only do this to reset the factory after having |
||||||
|
* previously altered it). |
||||||
|
* |
||||||
|
* @param factory The new DatagramSocketFactory the DatagramSocketClient |
||||||
|
* should use. |
||||||
|
***/ |
||||||
|
public void setDatagramSocketFactory(DatagramSocketFactory factory) |
||||||
|
{ |
||||||
|
if (factory == null) { |
||||||
|
_socketFactory_ = __DEFAULT_SOCKET_FACTORY; |
||||||
|
} else { |
||||||
|
_socketFactory_ = factory; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the charset name. |
||||||
|
* |
||||||
|
* @return the charset name. |
||||||
|
* @since 3.3 |
||||||
|
* TODO Will be deprecated once the code requires Java 1.6 as a mininmum |
||||||
|
*/ |
||||||
|
public String getCharsetName() { |
||||||
|
return charset.name(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the charset. |
||||||
|
* |
||||||
|
* @return the charset. |
||||||
|
* @since 3.3 |
||||||
|
*/ |
||||||
|
public Charset getCharset() { |
||||||
|
return charset; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the charset. |
||||||
|
* |
||||||
|
* @param charset the charset. |
||||||
|
* @since 3.3 |
||||||
|
*/ |
||||||
|
public void setCharset(Charset charset) { |
||||||
|
this.charset = charset; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,69 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.apache.commons.net; |
||||||
|
|
||||||
|
import java.net.DatagramSocket; |
||||||
|
import java.net.InetAddress; |
||||||
|
import java.net.SocketException; |
||||||
|
|
||||||
|
/*** |
||||||
|
* The DatagramSocketFactory interface provides a means for the |
||||||
|
* programmer to control the creation of datagram sockets and |
||||||
|
* provide his own DatagramSocket implementations for use by all |
||||||
|
* classes derived from |
||||||
|
* {@link DatagramSocketClient} |
||||||
|
* . |
||||||
|
* This allows you to provide your own DatagramSocket implementations and |
||||||
|
* to perform security checks or browser capability requests before |
||||||
|
* creating a DatagramSocket. |
||||||
|
* |
||||||
|
* |
||||||
|
***/ |
||||||
|
|
||||||
|
public interface DatagramSocketFactory |
||||||
|
{ |
||||||
|
|
||||||
|
/*** |
||||||
|
* Creates a DatagramSocket on the local host at the first available port. |
||||||
|
* @return the socket |
||||||
|
* |
||||||
|
* @throws SocketException If the socket could not be created. |
||||||
|
***/ |
||||||
|
public DatagramSocket createDatagramSocket() throws SocketException; |
||||||
|
|
||||||
|
/*** |
||||||
|
* Creates a DatagramSocket on the local host at a specified port. |
||||||
|
* |
||||||
|
* @param port The port to use for the socket. |
||||||
|
* @return the socket |
||||||
|
* @throws SocketException If the socket could not be created. |
||||||
|
***/ |
||||||
|
public DatagramSocket createDatagramSocket(int port) throws SocketException; |
||||||
|
|
||||||
|
/*** |
||||||
|
* Creates a DatagramSocket at the specified address on the local host |
||||||
|
* at a specified port. |
||||||
|
* |
||||||
|
* @param port The port to use for the socket. |
||||||
|
* @param laddr The local address to use. |
||||||
|
* @return the socket |
||||||
|
* @throws SocketException If the socket could not be created. |
||||||
|
***/ |
||||||
|
public DatagramSocket createDatagramSocket(int port, InetAddress laddr) |
||||||
|
throws SocketException; |
||||||
|
} |
@ -0,0 +1,79 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.apache.commons.net; |
||||||
|
|
||||||
|
import java.net.DatagramSocket; |
||||||
|
import java.net.InetAddress; |
||||||
|
import java.net.SocketException; |
||||||
|
|
||||||
|
/*** |
||||||
|
* DefaultDatagramSocketFactory implements the DatagramSocketFactory |
||||||
|
* interface by simply wrapping the java.net.DatagramSocket |
||||||
|
* constructors. It is the default DatagramSocketFactory used by |
||||||
|
* {@link DatagramSocketClient} |
||||||
|
* implementations. |
||||||
|
* |
||||||
|
* |
||||||
|
* @see DatagramSocketFactory |
||||||
|
* @see DatagramSocketClient |
||||||
|
* @see DatagramSocketClient#setDatagramSocketFactory |
||||||
|
***/ |
||||||
|
|
||||||
|
public class DefaultDatagramSocketFactory implements DatagramSocketFactory |
||||||
|
{ |
||||||
|
|
||||||
|
/*** |
||||||
|
* Creates a DatagramSocket on the local host at the first available port. |
||||||
|
* @return a new DatagramSocket |
||||||
|
* @throws SocketException If the socket could not be created. |
||||||
|
***/ |
||||||
|
@Override |
||||||
|
public DatagramSocket createDatagramSocket() throws SocketException |
||||||
|
{ |
||||||
|
return new DatagramSocket(); |
||||||
|
} |
||||||
|
|
||||||
|
/*** |
||||||
|
* Creates a DatagramSocket on the local host at a specified port. |
||||||
|
* |
||||||
|
* @param port The port to use for the socket. |
||||||
|
* @return a new DatagramSocket |
||||||
|
* @throws SocketException If the socket could not be created. |
||||||
|
***/ |
||||||
|
@Override |
||||||
|
public DatagramSocket createDatagramSocket(int port) throws SocketException |
||||||
|
{ |
||||||
|
return new DatagramSocket(port); |
||||||
|
} |
||||||
|
|
||||||
|
/*** |
||||||
|
* Creates a DatagramSocket at the specified address on the local host |
||||||
|
* at a specified port. |
||||||
|
* |
||||||
|
* @param port The port to use for the socket. |
||||||
|
* @param laddr The local address to use. |
||||||
|
* @return a new DatagramSocket |
||||||
|
* @throws SocketException If the socket could not be created. |
||||||
|
***/ |
||||||
|
@Override |
||||||
|
public DatagramSocket createDatagramSocket(int port, InetAddress laddr) |
||||||
|
throws SocketException |
||||||
|
{ |
||||||
|
return new DatagramSocket(port, laddr); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,230 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.apache.commons.net; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.net.InetAddress; |
||||||
|
import java.net.InetSocketAddress; |
||||||
|
import java.net.Proxy; |
||||||
|
import java.net.ServerSocket; |
||||||
|
import java.net.Socket; |
||||||
|
import java.net.UnknownHostException; |
||||||
|
|
||||||
|
import javax.net.SocketFactory; |
||||||
|
|
||||||
|
/*** |
||||||
|
* DefaultSocketFactory implements the SocketFactory interface by |
||||||
|
* simply wrapping the java.net.Socket and java.net.ServerSocket |
||||||
|
* constructors. It is the default SocketFactory used by |
||||||
|
* {@link SocketClient} |
||||||
|
* implementations. |
||||||
|
* |
||||||
|
* |
||||||
|
* @see SocketFactory |
||||||
|
* @see SocketClient |
||||||
|
* @see SocketClient#setSocketFactory |
||||||
|
***/ |
||||||
|
|
||||||
|
public class DefaultSocketFactory extends SocketFactory |
||||||
|
{ |
||||||
|
/** The proxy to use when creating new sockets. */ |
||||||
|
private final Proxy connProxy; |
||||||
|
|
||||||
|
/** |
||||||
|
* The default constructor. |
||||||
|
*/ |
||||||
|
public DefaultSocketFactory() |
||||||
|
{ |
||||||
|
this(null); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* A constructor for sockets with proxy support. |
||||||
|
* |
||||||
|
* @param proxy The Proxy to use when creating new Sockets. |
||||||
|
* @since 3.2 |
||||||
|
*/ |
||||||
|
public DefaultSocketFactory(Proxy proxy) |
||||||
|
{ |
||||||
|
connProxy = proxy; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates an unconnected Socket. |
||||||
|
* |
||||||
|
* @return A new unconnected Socket. |
||||||
|
* @throws IOException If an I/O error occurs while creating the Socket. |
||||||
|
* @since 3.2 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public Socket createSocket() throws IOException |
||||||
|
{ |
||||||
|
if (connProxy != null) |
||||||
|
{ |
||||||
|
return new Socket(connProxy); |
||||||
|
} |
||||||
|
return new Socket(); |
||||||
|
} |
||||||
|
|
||||||
|
/*** |
||||||
|
* Creates a Socket connected to the given host and port. |
||||||
|
* |
||||||
|
* @param host The hostname to connect to. |
||||||
|
* @param port The port to connect to. |
||||||
|
* @return A Socket connected to the given host and port. |
||||||
|
* @throws UnknownHostException If the hostname cannot be resolved. |
||||||
|
* @throws IOException If an I/O error occurs while creating the Socket. |
||||||
|
***/ |
||||||
|
@Override |
||||||
|
public Socket createSocket(String host, int port) |
||||||
|
throws UnknownHostException, IOException |
||||||
|
{ |
||||||
|
if (connProxy != null) |
||||||
|
{ |
||||||
|
Socket s = new Socket(connProxy); |
||||||
|
s.connect(new InetSocketAddress(host, port)); |
||||||
|
return s; |
||||||
|
} |
||||||
|
return new Socket(host, port); |
||||||
|
} |
||||||
|
|
||||||
|
/*** |
||||||
|
* Creates a Socket connected to the given host and port. |
||||||
|
* |
||||||
|
* @param address The address of the host to connect to. |
||||||
|
* @param port The port to connect to. |
||||||
|
* @return A Socket connected to the given host and port. |
||||||
|
* @throws IOException If an I/O error occurs while creating the Socket. |
||||||
|
***/ |
||||||
|
@Override |
||||||
|
public Socket createSocket(InetAddress address, int port) |
||||||
|
throws IOException |
||||||
|
{ |
||||||
|
if (connProxy != null) |
||||||
|
{ |
||||||
|
Socket s = new Socket(connProxy); |
||||||
|
s.connect(new InetSocketAddress(address, port)); |
||||||
|
return s; |
||||||
|
} |
||||||
|
return new Socket(address, port); |
||||||
|
} |
||||||
|
|
||||||
|
/*** |
||||||
|
* Creates a Socket connected to the given host and port and |
||||||
|
* originating from the specified local address and port. |
||||||
|
* |
||||||
|
* @param host The hostname to connect to. |
||||||
|
* @param port The port to connect to. |
||||||
|
* @param localAddr The local address to use. |
||||||
|
* @param localPort The local port to use. |
||||||
|
* @return A Socket connected to the given host and port. |
||||||
|
* @throws UnknownHostException If the hostname cannot be resolved. |
||||||
|
* @throws IOException If an I/O error occurs while creating the Socket. |
||||||
|
***/ |
||||||
|
@Override |
||||||
|
public Socket createSocket(String host, int port, |
||||||
|
InetAddress localAddr, int localPort) |
||||||
|
throws UnknownHostException, IOException |
||||||
|
{ |
||||||
|
if (connProxy != null) |
||||||
|
{ |
||||||
|
Socket s = new Socket(connProxy); |
||||||
|
s.bind(new InetSocketAddress(localAddr, localPort)); |
||||||
|
s.connect(new InetSocketAddress(host, port)); |
||||||
|
return s; |
||||||
|
} |
||||||
|
return new Socket(host, port, localAddr, localPort); |
||||||
|
} |
||||||
|
|
||||||
|
/*** |
||||||
|
* Creates a Socket connected to the given host and port and |
||||||
|
* originating from the specified local address and port. |
||||||
|
* |
||||||
|
* @param address The address of the host to connect to. |
||||||
|
* @param port The port to connect to. |
||||||
|
* @param localAddr The local address to use. |
||||||
|
* @param localPort The local port to use. |
||||||
|
* @return A Socket connected to the given host and port. |
||||||
|
* @throws IOException If an I/O error occurs while creating the Socket. |
||||||
|
***/ |
||||||
|
@Override |
||||||
|
public Socket createSocket(InetAddress address, int port, |
||||||
|
InetAddress localAddr, int localPort) |
||||||
|
throws IOException |
||||||
|
{ |
||||||
|
if (connProxy != null) |
||||||
|
{ |
||||||
|
Socket s = new Socket(connProxy); |
||||||
|
s.bind(new InetSocketAddress(localAddr, localPort)); |
||||||
|
s.connect(new InetSocketAddress(address, port)); |
||||||
|
return s; |
||||||
|
} |
||||||
|
return new Socket(address, port, localAddr, localPort); |
||||||
|
} |
||||||
|
|
||||||
|
/*** |
||||||
|
* Creates a ServerSocket bound to a specified port. A port |
||||||
|
* of 0 will create the ServerSocket on a system-determined free port. |
||||||
|
* |
||||||
|
* @param port The port on which to listen, or 0 to use any free port. |
||||||
|
* @return A ServerSocket that will listen on a specified port. |
||||||
|
* @throws IOException If an I/O error occurs while creating |
||||||
|
* the ServerSocket. |
||||||
|
***/ |
||||||
|
public ServerSocket createServerSocket(int port) throws IOException |
||||||
|
{ |
||||||
|
return new ServerSocket(port); |
||||||
|
} |
||||||
|
|
||||||
|
/*** |
||||||
|
* Creates a ServerSocket bound to a specified port with a given |
||||||
|
* maximum queue length for incoming connections. A port of 0 will |
||||||
|
* create the ServerSocket on a system-determined free port. |
||||||
|
* |
||||||
|
* @param port The port on which to listen, or 0 to use any free port. |
||||||
|
* @param backlog The maximum length of the queue for incoming connections. |
||||||
|
* @return A ServerSocket that will listen on a specified port. |
||||||
|
* @throws IOException If an I/O error occurs while creating |
||||||
|
* the ServerSocket. |
||||||
|
***/ |
||||||
|
public ServerSocket createServerSocket(int port, int backlog) |
||||||
|
throws IOException |
||||||
|
{ |
||||||
|
return new ServerSocket(port, backlog); |
||||||
|
} |
||||||
|
|
||||||
|
/*** |
||||||
|
* Creates a ServerSocket bound to a specified port on a given local |
||||||
|
* address with a given maximum queue length for incoming connections. |
||||||
|
* A port of 0 will |
||||||
|
* create the ServerSocket on a system-determined free port. |
||||||
|
* |
||||||
|
* @param port The port on which to listen, or 0 to use any free port. |
||||||
|
* @param backlog The maximum length of the queue for incoming connections. |
||||||
|
* @param bindAddr The local address to which the ServerSocket should bind. |
||||||
|
* @return A ServerSocket that will listen on a specified port. |
||||||
|
* @throws IOException If an I/O error occurs while creating |
||||||
|
* the ServerSocket. |
||||||
|
***/ |
||||||
|
public ServerSocket createServerSocket(int port, int backlog, |
||||||
|
InetAddress bindAddr) |
||||||
|
throws IOException |
||||||
|
{ |
||||||
|
return new ServerSocket(port, backlog, bindAddr); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,56 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.apache.commons.net; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
/*** |
||||||
|
* This exception is used to indicate that the reply from a server |
||||||
|
* could not be interpreted. Most of the NetComponents classes attempt |
||||||
|
* to be as lenient as possible when receiving server replies. Many |
||||||
|
* server implementations deviate from IETF protocol specifications, making |
||||||
|
* it necessary to be as flexible as possible. However, there will be |
||||||
|
* certain situations where it is not possible to continue an operation |
||||||
|
* because the server reply could not be interpreted in a meaningful manner. |
||||||
|
* In these cases, a MalformedServerReplyException should be thrown. |
||||||
|
* |
||||||
|
* |
||||||
|
***/ |
||||||
|
|
||||||
|
public class MalformedServerReplyException extends IOException |
||||||
|
{ |
||||||
|
|
||||||
|
private static final long serialVersionUID = 6006765264250543945L; |
||||||
|
|
||||||
|
/*** Constructs a MalformedServerReplyException with no message ***/ |
||||||
|
public MalformedServerReplyException() |
||||||
|
{ |
||||||
|
super(); |
||||||
|
} |
||||||
|
|
||||||
|
/*** |
||||||
|
* Constructs a MalformedServerReplyException with a specified message. |
||||||
|
* |
||||||
|
* @param message The message explaining the reason for the exception. |
||||||
|
***/ |
||||||
|
public MalformedServerReplyException(String message) |
||||||
|
{ |
||||||
|
super(message); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,202 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.apache.commons.net; |
||||||
|
|
||||||
|
import java.io.PrintStream; |
||||||
|
import java.io.PrintWriter; |
||||||
|
|
||||||
|
/*** |
||||||
|
* This is a support class for some of the example programs. It is |
||||||
|
* a sample implementation of the ProtocolCommandListener interface
|
||||||
|
* which just prints out to a specified stream all command/reply traffic. |
||||||
|
* |
||||||
|
* @since 2.0 |
||||||
|
***/ |
||||||
|
|
||||||
|
public class PrintCommandListener implements ProtocolCommandListener |
||||||
|
{ |
||||||
|
private final PrintWriter __writer; |
||||||
|
private final boolean __nologin; |
||||||
|
private final char __eolMarker; |
||||||
|
private final boolean __directionMarker; |
||||||
|
|
||||||
|
/** |
||||||
|
* Create the default instance which prints everything. |
||||||
|
* |
||||||
|
* @param stream where to write the commands and responses |
||||||
|
* e.g. System.out |
||||||
|
* @since 3.0 |
||||||
|
*/ |
||||||
|
public PrintCommandListener(PrintStream stream) |
||||||
|
{ |
||||||
|
this(new PrintWriter(stream)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create an instance which optionally suppresses login command text |
||||||
|
* and indicates where the EOL starts with the specified character. |
||||||
|
* |
||||||
|
* @param stream where to write the commands and responses |
||||||
|
* @param suppressLogin if {@code true}, only print command name for login |
||||||
|
* |
||||||
|
* @since 3.0 |
||||||
|
*/ |
||||||
|
public PrintCommandListener(PrintStream stream, boolean suppressLogin) { |
||||||
|
this(new PrintWriter(stream), suppressLogin); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create an instance which optionally suppresses login command text |
||||||
|
* and indicates where the EOL starts with the specified character. |
||||||
|
* |
||||||
|
* @param stream where to write the commands and responses |
||||||
|
* @param suppressLogin if {@code true}, only print command name for login |
||||||
|
* @param eolMarker if non-zero, add a marker just before the EOL. |
||||||
|
* |
||||||
|
* @since 3.0 |
||||||
|
*/ |
||||||
|
public PrintCommandListener(PrintStream stream, boolean suppressLogin, char eolMarker) { |
||||||
|
this(new PrintWriter(stream), suppressLogin, eolMarker); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create an instance which optionally suppresses login command text |
||||||
|
* and indicates where the EOL starts with the specified character. |
||||||
|
* |
||||||
|
* @param stream where to write the commands and responses |
||||||
|
* @param suppressLogin if {@code true}, only print command name for login |
||||||
|
* @param eolMarker if non-zero, add a marker just before the EOL. |
||||||
|
* @param showDirection if {@code true}, add {@code "> "} or {@code "< "} as appropriate to the output |
||||||
|
* |
||||||
|
* @since 3.0 |
||||||
|
*/ |
||||||
|
public PrintCommandListener(PrintStream stream, boolean suppressLogin, char eolMarker, boolean showDirection) { |
||||||
|
this(new PrintWriter(stream), suppressLogin, eolMarker, showDirection); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create the default instance which prints everything. |
||||||
|
* |
||||||
|
* @param writer where to write the commands and responses |
||||||
|
*/ |
||||||
|
public PrintCommandListener(PrintWriter writer) |
||||||
|
{ |
||||||
|
this(writer, false); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create an instance which optionally suppresses login command text. |
||||||
|
* |
||||||
|
* @param writer where to write the commands and responses |
||||||
|
* @param suppressLogin if {@code true}, only print command name for login |
||||||
|
* |
||||||
|
* @since 3.0 |
||||||
|
*/ |
||||||
|
public PrintCommandListener(PrintWriter writer, boolean suppressLogin) |
||||||
|
{ |
||||||
|
this(writer, suppressLogin, (char) 0); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create an instance which optionally suppresses login command text |
||||||
|
* and indicates where the EOL starts with the specified character. |
||||||
|
* |
||||||
|
* @param writer where to write the commands and responses |
||||||
|
* @param suppressLogin if {@code true}, only print command name for login |
||||||
|
* @param eolMarker if non-zero, add a marker just before the EOL. |
||||||
|
* |
||||||
|
* @since 3.0 |
||||||
|
*/ |
||||||
|
public PrintCommandListener(PrintWriter writer, boolean suppressLogin, char eolMarker) |
||||||
|
{ |
||||||
|
this(writer, suppressLogin, eolMarker, false); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create an instance which optionally suppresses login command text |
||||||
|
* and indicates where the EOL starts with the specified character. |
||||||
|
* |
||||||
|
* @param writer where to write the commands and responses |
||||||
|
* @param suppressLogin if {@code true}, only print command name for login |
||||||
|
* @param eolMarker if non-zero, add a marker just before the EOL. |
||||||
|
* @param showDirection if {@code true}, add {@code ">} " or {@code "< "} as appropriate to the output |
||||||
|
* |
||||||
|
* @since 3.0 |
||||||
|
*/ |
||||||
|
public PrintCommandListener(PrintWriter writer, boolean suppressLogin, char eolMarker, boolean showDirection) |
||||||
|
{ |
||||||
|
__writer = writer; |
||||||
|
__nologin = suppressLogin; |
||||||
|
__eolMarker = eolMarker; |
||||||
|
__directionMarker = showDirection; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void protocolCommandSent(ProtocolCommandEvent event) |
||||||
|
{ |
||||||
|
if (__directionMarker) { |
||||||
|
__writer.print("> "); |
||||||
|
} |
||||||
|
if (__nologin) { |
||||||
|
String cmd = event.getCommand(); |
||||||
|
if ("PASS".equalsIgnoreCase(cmd) || "USER".equalsIgnoreCase(cmd)) { |
||||||
|
__writer.print(cmd); |
||||||
|
__writer.println(" *******"); // Don't bother with EOL marker for this!
|
||||||
|
} else { |
||||||
|
final String IMAP_LOGIN = "LOGIN"; |
||||||
|
if (IMAP_LOGIN.equalsIgnoreCase(cmd)) { // IMAP
|
||||||
|
String msg = event.getMessage(); |
||||||
|
msg=msg.substring(0, msg.indexOf(IMAP_LOGIN)+IMAP_LOGIN.length()); |
||||||
|
__writer.print(msg); |
||||||
|
__writer.println(" *******"); // Don't bother with EOL marker for this!
|
||||||
|
} else { |
||||||
|
__writer.print(getPrintableString(event.getMessage())); |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
__writer.print(getPrintableString(event.getMessage())); |
||||||
|
} |
||||||
|
__writer.flush(); |
||||||
|
} |
||||||
|
|
||||||
|
private String getPrintableString(String msg){ |
||||||
|
if (__eolMarker == 0) { |
||||||
|
return msg; |
||||||
|
} |
||||||
|
int pos = msg.indexOf(SocketClient.NETASCII_EOL); |
||||||
|
if (pos > 0) { |
||||||
|
StringBuilder sb = new StringBuilder(); |
||||||
|
sb.append(msg.substring(0,pos)); |
||||||
|
sb.append(__eolMarker); |
||||||
|
sb.append(msg.substring(pos)); |
||||||
|
return sb.toString(); |
||||||
|
} |
||||||
|
return msg; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void protocolReplyReceived(ProtocolCommandEvent event) |
||||||
|
{ |
||||||
|
if (__directionMarker) { |
||||||
|
__writer.print("< "); |
||||||
|
} |
||||||
|
__writer.print(event.getMessage()); |
||||||
|
__writer.flush(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,148 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.apache.commons.net; |
||||||
|
import java.util.EventObject; |
||||||
|
|
||||||
|
/*** |
||||||
|
* There exists a large class of IETF protocols that work by sending an |
||||||
|
* ASCII text command and arguments to a server, and then receiving an |
||||||
|
* ASCII text reply. For debugging and other purposes, it is extremely |
||||||
|
* useful to log or keep track of the contents of the protocol messages. |
||||||
|
* The ProtocolCommandEvent class coupled with the |
||||||
|
* {@link org.apache.commons.net.ProtocolCommandListener} |
||||||
|
* interface facilitate this process. |
||||||
|
* |
||||||
|
* |
||||||
|
* @see ProtocolCommandListener |
||||||
|
* @see ProtocolCommandSupport |
||||||
|
***/ |
||||||
|
|
||||||
|
public class ProtocolCommandEvent extends EventObject |
||||||
|
{ |
||||||
|
private static final long serialVersionUID = 403743538418947240L; |
||||||
|
|
||||||
|
private final int __replyCode; |
||||||
|
private final boolean __isCommand; |
||||||
|
private final String __message, __command; |
||||||
|
|
||||||
|
/*** |
||||||
|
* Creates a ProtocolCommandEvent signalling a command was sent to |
||||||
|
* the server. ProtocolCommandEvents created with this constructor |
||||||
|
* should only be sent after a command has been sent, but before the |
||||||
|
* reply has been received. |
||||||
|
* |
||||||
|
* @param source The source of the event. |
||||||
|
* @param command The string representation of the command type sent, not |
||||||
|
* including the arguments (e.g., "STAT" or "GET"). |
||||||
|
* @param message The entire command string verbatim as sent to the server, |
||||||
|
* including all arguments. |
||||||
|
***/ |
||||||
|
public ProtocolCommandEvent(Object source, String command, String message) |
||||||
|
{ |
||||||
|
super(source); |
||||||
|
__replyCode = 0; |
||||||
|
__message = message; |
||||||
|
__isCommand = true; |
||||||
|
__command = command; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*** |
||||||
|
* Creates a ProtocolCommandEvent signalling a reply to a command was |
||||||
|
* received. ProtocolCommandEvents created with this constructor |
||||||
|
* should only be sent after a complete command reply has been received |
||||||
|
* fromt a server. |
||||||
|
* |
||||||
|
* @param source The source of the event. |
||||||
|
* @param replyCode The integer code indicating the natureof the reply. |
||||||
|
* This will be the protocol integer value for protocols |
||||||
|
* that use integer reply codes, or the reply class constant |
||||||
|
* corresponding to the reply for protocols like POP3 that use |
||||||
|
* strings like OK rather than integer codes (i.e., POP3Repy.OK). |
||||||
|
* @param message The entire reply as received from the server. |
||||||
|
***/ |
||||||
|
public ProtocolCommandEvent(Object source, int replyCode, String message) |
||||||
|
{ |
||||||
|
super(source); |
||||||
|
__replyCode = replyCode; |
||||||
|
__message = message; |
||||||
|
__isCommand = false; |
||||||
|
__command = null; |
||||||
|
} |
||||||
|
|
||||||
|
/*** |
||||||
|
* Returns the string representation of the command type sent (e.g., "STAT" |
||||||
|
* or "GET"). If the ProtocolCommandEvent is a reply event, then null |
||||||
|
* is returned. |
||||||
|
* |
||||||
|
* @return The string representation of the command type sent, or null |
||||||
|
* if this is a reply event. |
||||||
|
***/ |
||||||
|
public String getCommand() |
||||||
|
{ |
||||||
|
return __command; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*** |
||||||
|
* Returns the reply code of the received server reply. Undefined if |
||||||
|
* this is not a reply event. |
||||||
|
* |
||||||
|
* @return The reply code of the received server reply. Undefined if |
||||||
|
* not a reply event. |
||||||
|
***/ |
||||||
|
public int getReplyCode() |
||||||
|
{ |
||||||
|
return __replyCode; |
||||||
|
} |
||||||
|
|
||||||
|
/*** |
||||||
|
* Returns true if the ProtocolCommandEvent was generated as a result |
||||||
|
* of sending a command. |
||||||
|
* |
||||||
|
* @return true If the ProtocolCommandEvent was generated as a result |
||||||
|
* of sending a command. False otherwise. |
||||||
|
***/ |
||||||
|
public boolean isCommand() |
||||||
|
{ |
||||||
|
return __isCommand; |
||||||
|
} |
||||||
|
|
||||||
|
/*** |
||||||
|
* Returns true if the ProtocolCommandEvent was generated as a result |
||||||
|
* of receiving a reply. |
||||||
|
* |
||||||
|
* @return true If the ProtocolCommandEvent was generated as a result |
||||||
|
* of receiving a reply. False otherwise. |
||||||
|
***/ |
||||||
|
public boolean isReply() |
||||||
|
{ |
||||||
|
return !isCommand(); |
||||||
|
} |
||||||
|
|
||||||
|
/*** |
||||||
|
* Returns the entire message sent to or received from the server. |
||||||
|
* Includes the line terminator. |
||||||
|
* |
||||||
|
* @return The entire message sent to or received from the server. |
||||||
|
***/ |
||||||
|
public String getMessage() |
||||||
|
{ |
||||||
|
return __message; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,58 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.apache.commons.net; |
||||||
|
import java.util.EventListener; |
||||||
|
|
||||||
|
/*** |
||||||
|
* There exists a large class of IETF protocols that work by sending an |
||||||
|
* ASCII text command and arguments to a server, and then receiving an |
||||||
|
* ASCII text reply. For debugging and other purposes, it is extremely |
||||||
|
* useful to log or keep track of the contents of the protocol messages. |
||||||
|
* The ProtocolCommandListener interface coupled with the |
||||||
|
* {@link ProtocolCommandEvent} class facilitate this process. |
||||||
|
* <p> |
||||||
|
* To receive ProtocolCommandEvents, you merely implement the |
||||||
|
* ProtocolCommandListener interface and register the class as a listener |
||||||
|
* with a ProtocolCommandEvent source such as |
||||||
|
* {@link org.apache.commons.net.ftp.FTPClient}. |
||||||
|
* |
||||||
|
* |
||||||
|
* @see ProtocolCommandEvent |
||||||
|
* @see ProtocolCommandSupport |
||||||
|
***/ |
||||||
|
|
||||||
|
public interface ProtocolCommandListener extends EventListener |
||||||
|
{ |
||||||
|
|
||||||
|
/*** |
||||||
|
* This method is invoked by a ProtocolCommandEvent source after |
||||||
|
* sending a protocol command to a server. |
||||||
|
* |
||||||
|
* @param event The ProtocolCommandEvent fired. |
||||||
|
***/ |
||||||
|
public void protocolCommandSent(ProtocolCommandEvent event); |
||||||
|
|
||||||
|
/*** |
||||||
|
* This method is invoked by a ProtocolCommandEvent source after |
||||||
|
* receiving a reply from a server. |
||||||
|
* |
||||||
|
* @param event The ProtocolCommandEvent fired. |
||||||
|
***/ |
||||||
|
public void protocolReplyReceived(ProtocolCommandEvent event); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,135 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.apache.commons.net; |
||||||
|
|
||||||
|
import java.io.Serializable; |
||||||
|
import java.util.EventListener; |
||||||
|
|
||||||
|
import org.apache.commons.net.util.ListenerList; |
||||||
|
|
||||||
|
/*** |
||||||
|
* ProtocolCommandSupport is a convenience class for managing a list of |
||||||
|
* ProtocolCommandListeners and firing ProtocolCommandEvents. You can |
||||||
|
* simply delegate ProtocolCommandEvent firing and listener |
||||||
|
* registering/unregistering tasks to this class. |
||||||
|
* |
||||||
|
* |
||||||
|
* @see ProtocolCommandEvent |
||||||
|
* @see ProtocolCommandListener |
||||||
|
***/ |
||||||
|
|
||||||
|
public class ProtocolCommandSupport implements Serializable |
||||||
|
{ |
||||||
|
private static final long serialVersionUID = -8017692739988399978L; |
||||||
|
|
||||||
|
private final Object __source; |
||||||
|
private final ListenerList __listeners; |
||||||
|
|
||||||
|
/*** |
||||||
|
* Creates a ProtocolCommandSupport instance using the indicated source |
||||||
|
* as the source of ProtocolCommandEvents. |
||||||
|
* |
||||||
|
* @param source The source to use for all generated ProtocolCommandEvents. |
||||||
|
***/ |
||||||
|
public ProtocolCommandSupport(Object source) |
||||||
|
{ |
||||||
|
__listeners = new ListenerList(); |
||||||
|
__source = source; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*** |
||||||
|
* Fires a ProtocolCommandEvent signalling the sending of a command to all |
||||||
|
* registered listeners, invoking their |
||||||
|
* {@link org.apache.commons.net.ProtocolCommandListener#protocolCommandSent protocolCommandSent() } |
||||||
|
* methods. |
||||||
|
* |
||||||
|
* @param command The string representation of the command type sent, not |
||||||
|
* including the arguments (e.g., "STAT" or "GET"). |
||||||
|
* @param message The entire command string verbatim as sent to the server, |
||||||
|
* including all arguments. |
||||||
|
***/ |
||||||
|
public void fireCommandSent(String command, String message) |
||||||
|
{ |
||||||
|
ProtocolCommandEvent event; |
||||||
|
|
||||||
|
event = new ProtocolCommandEvent(__source, command, message); |
||||||
|
|
||||||
|
for (EventListener listener : __listeners) |
||||||
|
{ |
||||||
|
((ProtocolCommandListener)listener).protocolCommandSent(event); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/*** |
||||||
|
* Fires a ProtocolCommandEvent signalling the reception of a command reply |
||||||
|
* to all registered listeners, invoking their |
||||||
|
* {@link org.apache.commons.net.ProtocolCommandListener#protocolReplyReceived protocolReplyReceived() } |
||||||
|
* methods. |
||||||
|
* |
||||||
|
* @param replyCode The integer code indicating the natureof the reply. |
||||||
|
* This will be the protocol integer value for protocols |
||||||
|
* that use integer reply codes, or the reply class constant |
||||||
|
* corresponding to the reply for protocols like POP3 that use |
||||||
|
* strings like OK rather than integer codes (i.e., POP3Repy.OK). |
||||||
|
* @param message The entire reply as received from the server. |
||||||
|
***/ |
||||||
|
public void fireReplyReceived(int replyCode, String message) |
||||||
|
{ |
||||||
|
ProtocolCommandEvent event; |
||||||
|
event = new ProtocolCommandEvent(__source, replyCode, message); |
||||||
|
|
||||||
|
for (EventListener listener : __listeners) |
||||||
|
{ |
||||||
|
((ProtocolCommandListener)listener).protocolReplyReceived(event); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/*** |
||||||
|
* Adds a ProtocolCommandListener. |
||||||
|
* |
||||||
|
* @param listener The ProtocolCommandListener to add. |
||||||
|
***/ |
||||||
|
public void addProtocolCommandListener(ProtocolCommandListener listener) |
||||||
|
{ |
||||||
|
__listeners.addListener(listener); |
||||||
|
} |
||||||
|
|
||||||
|
/*** |
||||||
|
* Removes a ProtocolCommandListener. |
||||||
|
* |
||||||
|
* @param listener The ProtocolCommandListener to remove. |
||||||
|
***/ |
||||||
|
public void removeProtocolCommandListener(ProtocolCommandListener listener) |
||||||
|
{ |
||||||
|
__listeners.removeListener(listener); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*** |
||||||
|
* Returns the number of ProtocolCommandListeners currently registered. |
||||||
|
* |
||||||
|
* @return The number of ProtocolCommandListeners currently registered. |
||||||
|
***/ |
||||||
|
public int getListenerCount() |
||||||
|
{ |
||||||
|
return __listeners.getListenerCount(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
@ -0,0 +1,887 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.apache.commons.net; |
||||||
|
|
||||||
|
import java.io.Closeable; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.InputStream; |
||||||
|
import java.io.OutputStream; |
||||||
|
import java.net.InetAddress; |
||||||
|
import java.net.InetSocketAddress; |
||||||
|
import java.net.Proxy; |
||||||
|
import java.net.Socket; |
||||||
|
import java.net.SocketException; |
||||||
|
import java.nio.charset.Charset; |
||||||
|
|
||||||
|
import javax.net.ServerSocketFactory; |
||||||
|
import javax.net.SocketFactory; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* The SocketClient provides the basic operations that are required of |
||||||
|
* client objects accessing sockets. It is meant to be |
||||||
|
* subclassed to avoid having to rewrite the same code over and over again |
||||||
|
* to open a socket, close a socket, set timeouts, etc. Of special note |
||||||
|
* is the {@link #setSocketFactory setSocketFactory } |
||||||
|
* method, which allows you to control the type of Socket the SocketClient |
||||||
|
* creates for initiating network connections. This is especially useful |
||||||
|
* for adding SSL or proxy support as well as better support for applets. For |
||||||
|
* example, you could create a |
||||||
|
* {@link SocketFactory} that |
||||||
|
* requests browser security capabilities before creating a socket. |
||||||
|
* All classes derived from SocketClient should use the |
||||||
|
* {@link #_socketFactory_ _socketFactory_ } member variable to |
||||||
|
* create Socket and ServerSocket instances rather than instantiating |
||||||
|
* them by directly invoking a constructor. By honoring this contract |
||||||
|
* you guarantee that a user will always be able to provide his own |
||||||
|
* Socket implementations by substituting his own SocketFactory. |
||||||
|
* @see SocketFactory |
||||||
|
*/ |
||||||
|
public abstract class SocketClient |
||||||
|
{ |
||||||
|
/** |
||||||
|
* The end of line character sequence used by most IETF protocols. That |
||||||
|
* is a carriage return followed by a newline: "\r\n" |
||||||
|
*/ |
||||||
|
public static final String NETASCII_EOL = "\r\n"; |
||||||
|
|
||||||
|
/** The default SocketFactory shared by all SocketClient instances. */ |
||||||
|
private static final SocketFactory __DEFAULT_SOCKET_FACTORY = |
||||||
|
SocketFactory.getDefault(); |
||||||
|
|
||||||
|
/** The default {@link ServerSocketFactory} */ |
||||||
|
private static final ServerSocketFactory __DEFAULT_SERVER_SOCKET_FACTORY = |
||||||
|
ServerSocketFactory.getDefault(); |
||||||
|
|
||||||
|
/** |
||||||
|
* A ProtocolCommandSupport object used to manage the registering of |
||||||
|
* ProtocolCommandListeners and the firing of ProtocolCommandEvents. |
||||||
|
*/ |
||||||
|
private ProtocolCommandSupport __commandSupport; |
||||||
|
|
||||||
|
/** The timeout to use after opening a socket. */ |
||||||
|
protected int _timeout_; |
||||||
|
|
||||||
|
/** The socket used for the connection. */ |
||||||
|
protected Socket _socket_; |
||||||
|
|
||||||
|
/** The hostname used for the connection (null = no hostname supplied). */ |
||||||
|
protected String _hostname_; |
||||||
|
|
||||||
|
/** The default port the client should connect to. */ |
||||||
|
protected int _defaultPort_; |
||||||
|
|
||||||
|
/** The socket's InputStream. */ |
||||||
|
protected InputStream _input_; |
||||||
|
|
||||||
|
/** The socket's OutputStream. */ |
||||||
|
protected OutputStream _output_; |
||||||
|
|
||||||
|
/** The socket's SocketFactory. */ |
||||||
|
protected SocketFactory _socketFactory_; |
||||||
|
|
||||||
|
/** The socket's ServerSocket Factory. */ |
||||||
|
protected ServerSocketFactory _serverSocketFactory_; |
||||||
|
|
||||||
|
/** The socket's connect timeout (0 = infinite timeout) */ |
||||||
|
private static final int DEFAULT_CONNECT_TIMEOUT = 0; |
||||||
|
protected int connectTimeout = DEFAULT_CONNECT_TIMEOUT; |
||||||
|
|
||||||
|
/** Hint for SO_RCVBUF size */ |
||||||
|
private int receiveBufferSize = -1; |
||||||
|
|
||||||
|
/** Hint for SO_SNDBUF size */ |
||||||
|
private int sendBufferSize = -1; |
||||||
|
|
||||||
|
/** The proxy to use when connecting. */ |
||||||
|
private Proxy connProxy; |
||||||
|
|
||||||
|
/** |
||||||
|
* Charset to use for byte IO. |
||||||
|
*/ |
||||||
|
private Charset charset = Charset.defaultCharset(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Default constructor for SocketClient. Initializes |
||||||
|
* _socket_ to null, _timeout_ to 0, _defaultPort to 0, |
||||||
|
* _isConnected_ to false, charset to {@code Charset.defaultCharset()} |
||||||
|
* and _socketFactory_ to a shared instance of |
||||||
|
* {@link org.apache.commons.net.DefaultSocketFactory}. |
||||||
|
*/ |
||||||
|
public SocketClient() |
||||||
|
{ |
||||||
|
_socket_ = null; |
||||||
|
_hostname_ = null; |
||||||
|
_input_ = null; |
||||||
|
_output_ = null; |
||||||
|
_timeout_ = 0; |
||||||
|
_defaultPort_ = 0; |
||||||
|
_socketFactory_ = __DEFAULT_SOCKET_FACTORY; |
||||||
|
_serverSocketFactory_ = __DEFAULT_SERVER_SOCKET_FACTORY; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Because there are so many connect() methods, the _connectAction_() |
||||||
|
* method is provided as a means of performing some action immediately |
||||||
|
* after establishing a connection, rather than reimplementing all |
||||||
|
* of the connect() methods. The last action performed by every |
||||||
|
* connect() method after opening a socket is to call this method. |
||||||
|
* <p> |
||||||
|
* This method sets the timeout on the just opened socket to the default |
||||||
|
* timeout set by {@link #setDefaultTimeout setDefaultTimeout() }, |
||||||
|
* sets _input_ and _output_ to the socket's InputStream and OutputStream |
||||||
|
* respectively, and sets _isConnected_ to true. |
||||||
|
* <p> |
||||||
|
* Subclasses overriding this method should start by calling |
||||||
|
* <code> super._connectAction_() </code> first to ensure the |
||||||
|
* initialization of the aforementioned protected variables. |
||||||
|
* @throws IOException (SocketException) if a problem occurs with the socket |
||||||
|
*/ |
||||||
|
protected void _connectAction_() throws IOException |
||||||
|
{ |
||||||
|
_socket_.setSoTimeout(_timeout_); |
||||||
|
_input_ = _socket_.getInputStream(); |
||||||
|
_output_ = _socket_.getOutputStream(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Opens a Socket connected to a remote host at the specified port and |
||||||
|
* originating from the current host at a system assigned port. |
||||||
|
* Before returning, {@link #_connectAction_ _connectAction_() } |
||||||
|
* is called to perform connection initialization actions. |
||||||
|
* <p> |
||||||
|
* @param host The remote host. |
||||||
|
* @param port The port to connect to on the remote host. |
||||||
|
* @throws SocketException If the socket timeout could not be set. |
||||||
|
* @throws IOException If the socket could not be opened. In most |
||||||
|
* cases you will only want to catch IOException since SocketException is |
||||||
|
* derived from it. |
||||||
|
*/ |
||||||
|
public void connect(InetAddress host, int port) |
||||||
|
throws SocketException, IOException |
||||||
|
{ |
||||||
|
_hostname_ = null; |
||||||
|
_connect(host, port, null, -1); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Opens a Socket connected to a remote host at the specified port and |
||||||
|
* originating from the current host at a system assigned port. |
||||||
|
* Before returning, {@link #_connectAction_ _connectAction_() } |
||||||
|
* is called to perform connection initialization actions. |
||||||
|
* <p> |
||||||
|
* @param hostname The name of the remote host. |
||||||
|
* @param port The port to connect to on the remote host. |
||||||
|
* @throws SocketException If the socket timeout could not be set. |
||||||
|
* @throws IOException If the socket could not be opened. In most |
||||||
|
* cases you will only want to catch IOException since SocketException is |
||||||
|
* derived from it. |
||||||
|
* @throws java.net.UnknownHostException If the hostname cannot be resolved. |
||||||
|
*/ |
||||||
|
public void connect(String hostname, int port) |
||||||
|
throws SocketException, IOException |
||||||
|
{ |
||||||
|
_hostname_ = hostname; |
||||||
|
_connect(InetAddress.getByName(hostname), port, null, -1); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Opens a Socket connected to a remote host at the specified port and |
||||||
|
* originating from the specified local address and port. |
||||||
|
* Before returning, {@link #_connectAction_ _connectAction_() } |
||||||
|
* is called to perform connection initialization actions. |
||||||
|
* <p> |
||||||
|
* @param host The remote host. |
||||||
|
* @param port The port to connect to on the remote host. |
||||||
|
* @param localAddr The local address to use. |
||||||
|
* @param localPort The local port to use. |
||||||
|
* @throws SocketException If the socket timeout could not be set. |
||||||
|
* @throws IOException If the socket could not be opened. In most |
||||||
|
* cases you will only want to catch IOException since SocketException is |
||||||
|
* derived from it. |
||||||
|
*/ |
||||||
|
public void connect(InetAddress host, int port, |
||||||
|
InetAddress localAddr, int localPort) |
||||||
|
throws SocketException, IOException |
||||||
|
{ |
||||||
|
_hostname_ = null; |
||||||
|
_connect(host, port, localAddr, localPort); |
||||||
|
} |
||||||
|
|
||||||
|
// helper method to allow code to be shared with connect(String,...) methods
|
||||||
|
private void _connect(InetAddress host, int port, InetAddress localAddr, int localPort) |
||||||
|
throws SocketException, IOException |
||||||
|
{ |
||||||
|
_socket_ = _socketFactory_.createSocket(); |
||||||
|
if (receiveBufferSize != -1) { |
||||||
|
_socket_.setReceiveBufferSize(receiveBufferSize); |
||||||
|
} |
||||||
|
if (sendBufferSize != -1) { |
||||||
|
_socket_.setSendBufferSize(sendBufferSize); |
||||||
|
} |
||||||
|
if (localAddr != null) { |
||||||
|
_socket_.bind(new InetSocketAddress(localAddr, localPort)); |
||||||
|
} |
||||||
|
_socket_.connect(new InetSocketAddress(host, port), connectTimeout); |
||||||
|
_connectAction_(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Opens a Socket connected to a remote host at the specified port and |
||||||
|
* originating from the specified local address and port. |
||||||
|
* Before returning, {@link #_connectAction_ _connectAction_() } |
||||||
|
* is called to perform connection initialization actions. |
||||||
|
* <p> |
||||||
|
* @param hostname The name of the remote host. |
||||||
|
* @param port The port to connect to on the remote host. |
||||||
|
* @param localAddr The local address to use. |
||||||
|
* @param localPort The local port to use. |
||||||
|
* @throws SocketException If the socket timeout could not be set. |
||||||
|
* @throws IOException If the socket could not be opened. In most |
||||||
|
* cases you will only want to catch IOException since SocketException is |
||||||
|
* derived from it. |
||||||
|
* @throws java.net.UnknownHostException If the hostname cannot be resolved. |
||||||
|
*/ |
||||||
|
public void connect(String hostname, int port, |
||||||
|
InetAddress localAddr, int localPort) |
||||||
|
throws SocketException, IOException |
||||||
|
{ |
||||||
|
_hostname_ = hostname; |
||||||
|
_connect(InetAddress.getByName(hostname), port, localAddr, localPort); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Opens a Socket connected to a remote host at the current default port |
||||||
|
* and originating from the current host at a system assigned port. |
||||||
|
* Before returning, {@link #_connectAction_ _connectAction_() } |
||||||
|
* is called to perform connection initialization actions. |
||||||
|
* <p> |
||||||
|
* @param host The remote host. |
||||||
|
* @throws SocketException If the socket timeout could not be set. |
||||||
|
* @throws IOException If the socket could not be opened. In most |
||||||
|
* cases you will only want to catch IOException since SocketException is |
||||||
|
* derived from it. |
||||||
|
*/ |
||||||
|
public void connect(InetAddress host) throws SocketException, IOException |
||||||
|
{ |
||||||
|
_hostname_ = null; |
||||||
|
connect(host, _defaultPort_); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Opens a Socket connected to a remote host at the current default |
||||||
|
* port and originating from the current host at a system assigned port. |
||||||
|
* Before returning, {@link #_connectAction_ _connectAction_() } |
||||||
|
* is called to perform connection initialization actions. |
||||||
|
* <p> |
||||||
|
* @param hostname The name of the remote host. |
||||||
|
* @throws SocketException If the socket timeout could not be set. |
||||||
|
* @throws IOException If the socket could not be opened. In most |
||||||
|
* cases you will only want to catch IOException since SocketException is |
||||||
|
* derived from it. |
||||||
|
* @throws java.net.UnknownHostException If the hostname cannot be resolved. |
||||||
|
*/ |
||||||
|
public void connect(String hostname) throws SocketException, IOException |
||||||
|
{ |
||||||
|
connect(hostname, _defaultPort_); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Disconnects the socket connection. |
||||||
|
* You should call this method after you've finished using the class
|
||||||
|
* instance and also before you call |
||||||
|
* {@link #connect connect() } |
||||||
|
* again. _isConnected_ is set to false, _socket_ is set to null, |
||||||
|
* _input_ is set to null, and _output_ is set to null. |
||||||
|
* <p> |
||||||
|
* @throws IOException If there is an error closing the socket. |
||||||
|
*/ |
||||||
|
public void disconnect() throws IOException |
||||||
|
{ |
||||||
|
closeQuietly(_socket_); |
||||||
|
closeQuietly(_input_); |
||||||
|
closeQuietly(_output_); |
||||||
|
_socket_ = null; |
||||||
|
_hostname_ = null; |
||||||
|
_input_ = null; |
||||||
|
_output_ = null; |
||||||
|
} |
||||||
|
|
||||||
|
private void closeQuietly(Socket socket) { |
||||||
|
if (socket != null){ |
||||||
|
try { |
||||||
|
socket.close(); |
||||||
|
} catch (IOException e) { |
||||||
|
// Ignored
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void closeQuietly(Closeable close){ |
||||||
|
if (close != null){ |
||||||
|
try { |
||||||
|
close.close(); |
||||||
|
} catch (IOException e) { |
||||||
|
// Ignored
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
/** |
||||||
|
* Returns true if the client is currently connected to a server. |
||||||
|
* <p> |
||||||
|
* Delegates to {@link Socket#isConnected()} |
||||||
|
* @return True if the client is currently connected to a server, |
||||||
|
* false otherwise. |
||||||
|
*/ |
||||||
|
public boolean isConnected() |
||||||
|
{ |
||||||
|
if (_socket_ == null) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
return _socket_.isConnected(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Make various checks on the socket to test if it is available for use. |
||||||
|
* Note that the only sure test is to use it, but these checks may help |
||||||
|
* in some cases. |
||||||
|
* @see <a href="https://issues.apache.org/jira/browse/NET-350">NET-350</a> |
||||||
|
* @return {@code true} if the socket appears to be available for use |
||||||
|
* @since 3.0 |
||||||
|
*/ |
||||||
|
public boolean isAvailable(){ |
||||||
|
if (isConnected()) { |
||||||
|
try |
||||||
|
{ |
||||||
|
if (_socket_.getInetAddress() == null) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
if (_socket_.getPort() == 0) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
if (_socket_.getRemoteSocketAddress() == null) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
if (_socket_.isClosed()) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
/* these aren't exact checks (a Socket can be half-open), |
||||||
|
but since we usually require two-way data transfer, |
||||||
|
we check these here too: */ |
||||||
|
if (_socket_.isInputShutdown()) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
if (_socket_.isOutputShutdown()) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
/* ignore the result, catch exceptions: */ |
||||||
|
_socket_.getInputStream(); |
||||||
|
_socket_.getOutputStream(); |
||||||
|
} |
||||||
|
catch (IOException ioex) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} else { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the default port the SocketClient should connect to when a port |
||||||
|
* is not specified. The {@link #_defaultPort_ _defaultPort_ } |
||||||
|
* variable stores this value. If never set, the default port is equal |
||||||
|
* to zero. |
||||||
|
* <p> |
||||||
|
* @param port The default port to set. |
||||||
|
*/ |
||||||
|
public void setDefaultPort(int port) |
||||||
|
{ |
||||||
|
_defaultPort_ = port; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the current value of the default port (stored in |
||||||
|
* {@link #_defaultPort_ _defaultPort_ }). |
||||||
|
* <p> |
||||||
|
* @return The current value of the default port. |
||||||
|
*/ |
||||||
|
public int getDefaultPort() |
||||||
|
{ |
||||||
|
return _defaultPort_; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Set the default timeout in milliseconds to use when opening a socket. |
||||||
|
* This value is only used previous to a call to |
||||||
|
* {@link #connect connect()} |
||||||
|
* and should not be confused with {@link #setSoTimeout setSoTimeout()} |
||||||
|
* which operates on an the currently opened socket. _timeout_ contains |
||||||
|
* the new timeout value. |
||||||
|
* <p> |
||||||
|
* @param timeout The timeout in milliseconds to use for the socket |
||||||
|
* connection. |
||||||
|
*/ |
||||||
|
public void setDefaultTimeout(int timeout) |
||||||
|
{ |
||||||
|
_timeout_ = timeout; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the default timeout in milliseconds that is used when |
||||||
|
* opening a socket. |
||||||
|
* <p> |
||||||
|
* @return The default timeout in milliseconds that is used when |
||||||
|
* opening a socket. |
||||||
|
*/ |
||||||
|
public int getDefaultTimeout() |
||||||
|
{ |
||||||
|
return _timeout_; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Set the timeout in milliseconds of a currently open connection. |
||||||
|
* Only call this method after a connection has been opened |
||||||
|
* by {@link #connect connect()}. |
||||||
|
* <p> |
||||||
|
* To set the initial timeout, use {@link #setDefaultTimeout(int)} instead. |
||||||
|
* |
||||||
|
* @param timeout The timeout in milliseconds to use for the currently |
||||||
|
* open socket connection. |
||||||
|
* @throws SocketException If the operation fails. |
||||||
|
* @throws NullPointerException if the socket is not currently open |
||||||
|
*/ |
||||||
|
public void setSoTimeout(int timeout) throws SocketException |
||||||
|
{ |
||||||
|
_socket_.setSoTimeout(timeout); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Set the underlying socket send buffer size. |
||||||
|
* <p> |
||||||
|
* @param size The size of the buffer in bytes. |
||||||
|
* @throws SocketException never thrown, but subclasses might want to do so |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
public void setSendBufferSize(int size) throws SocketException { |
||||||
|
sendBufferSize = size; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the current sendBuffer size |
||||||
|
* @return the size, or -1 if not initialised |
||||||
|
* @since 3.0 |
||||||
|
*/ |
||||||
|
protected int getSendBufferSize(){ |
||||||
|
return sendBufferSize; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the underlying socket receive buffer size. |
||||||
|
* <p> |
||||||
|
* @param size The size of the buffer in bytes. |
||||||
|
* @throws SocketException never (but subclasses may wish to do so) |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
public void setReceiveBufferSize(int size) throws SocketException { |
||||||
|
receiveBufferSize = size; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the current receivedBuffer size |
||||||
|
* @return the size, or -1 if not initialised |
||||||
|
* @since 3.0 |
||||||
|
*/ |
||||||
|
protected int getReceiveBufferSize(){ |
||||||
|
return receiveBufferSize; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the timeout in milliseconds of the currently opened socket. |
||||||
|
* <p> |
||||||
|
* @return The timeout in milliseconds of the currently opened socket. |
||||||
|
* @throws SocketException If the operation fails. |
||||||
|
* @throws NullPointerException if the socket is not currently open |
||||||
|
*/ |
||||||
|
public int getSoTimeout() throws SocketException |
||||||
|
{ |
||||||
|
return _socket_.getSoTimeout(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Enables or disables the Nagle's algorithm (TCP_NODELAY) on the |
||||||
|
* currently opened socket. |
||||||
|
* <p> |
||||||
|
* @param on True if Nagle's algorithm is to be enabled, false if not. |
||||||
|
* @throws SocketException If the operation fails. |
||||||
|
* @throws NullPointerException if the socket is not currently open |
||||||
|
*/ |
||||||
|
public void setTcpNoDelay(boolean on) throws SocketException |
||||||
|
{ |
||||||
|
_socket_.setTcpNoDelay(on); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Returns true if Nagle's algorithm is enabled on the currently opened |
||||||
|
* socket. |
||||||
|
* <p> |
||||||
|
* @return True if Nagle's algorithm is enabled on the currently opened |
||||||
|
* socket, false otherwise. |
||||||
|
* @throws SocketException If the operation fails. |
||||||
|
* @throws NullPointerException if the socket is not currently open |
||||||
|
*/ |
||||||
|
public boolean getTcpNoDelay() throws SocketException |
||||||
|
{ |
||||||
|
return _socket_.getTcpNoDelay(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the SO_KEEPALIVE flag on the currently opened socket. |
||||||
|
* |
||||||
|
* From the Javadocs, the default keepalive time is 2 hours (although this is |
||||||
|
* implementation dependent). It looks as though the Windows WSA sockets implementation |
||||||
|
* allows a specific keepalive value to be set, although this seems not to be the case on |
||||||
|
* other systems. |
||||||
|
* @param keepAlive If true, keepAlive is turned on |
||||||
|
* @throws SocketException if there is a problem with the socket |
||||||
|
* @throws NullPointerException if the socket is not currently open |
||||||
|
* @since 2.2 |
||||||
|
*/ |
||||||
|
public void setKeepAlive(boolean keepAlive) throws SocketException { |
||||||
|
_socket_.setKeepAlive(keepAlive); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the current value of the SO_KEEPALIVE flag on the currently opened socket. |
||||||
|
* Delegates to {@link Socket#getKeepAlive()} |
||||||
|
* @return True if SO_KEEPALIVE is enabled. |
||||||
|
* @throws SocketException if there is a problem with the socket |
||||||
|
* @throws NullPointerException if the socket is not currently open |
||||||
|
* @since 2.2 |
||||||
|
*/ |
||||||
|
public boolean getKeepAlive() throws SocketException { |
||||||
|
return _socket_.getKeepAlive(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the SO_LINGER timeout on the currently opened socket. |
||||||
|
* <p> |
||||||
|
* @param on True if linger is to be enabled, false if not. |
||||||
|
* @param val The linger timeout (in hundredths of a second?) |
||||||
|
* @throws SocketException If the operation fails. |
||||||
|
* @throws NullPointerException if the socket is not currently open |
||||||
|
*/ |
||||||
|
public void setSoLinger(boolean on, int val) throws SocketException |
||||||
|
{ |
||||||
|
_socket_.setSoLinger(on, val); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the current SO_LINGER timeout of the currently opened socket. |
||||||
|
* <p> |
||||||
|
* @return The current SO_LINGER timeout. If SO_LINGER is disabled returns |
||||||
|
* -1. |
||||||
|
* @throws SocketException If the operation fails. |
||||||
|
* @throws NullPointerException if the socket is not currently open |
||||||
|
*/ |
||||||
|
public int getSoLinger() throws SocketException |
||||||
|
{ |
||||||
|
return _socket_.getSoLinger(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the port number of the open socket on the local host used |
||||||
|
* for the connection. |
||||||
|
* Delegates to {@link Socket#getLocalPort()} |
||||||
|
* <p> |
||||||
|
* @return The port number of the open socket on the local host used |
||||||
|
* for the connection. |
||||||
|
* @throws NullPointerException if the socket is not currently open |
||||||
|
*/ |
||||||
|
public int getLocalPort() |
||||||
|
{ |
||||||
|
return _socket_.getLocalPort(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the local address to which the client's socket is bound. |
||||||
|
* Delegates to {@link Socket#getLocalAddress()} |
||||||
|
* <p> |
||||||
|
* @return The local address to which the client's socket is bound. |
||||||
|
* @throws NullPointerException if the socket is not currently open |
||||||
|
*/ |
||||||
|
public InetAddress getLocalAddress() |
||||||
|
{ |
||||||
|
return _socket_.getLocalAddress(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the port number of the remote host to which the client is |
||||||
|
* connected. |
||||||
|
* Delegates to {@link Socket#getPort()} |
||||||
|
* <p> |
||||||
|
* @return The port number of the remote host to which the client is |
||||||
|
* connected. |
||||||
|
* @throws NullPointerException if the socket is not currently open |
||||||
|
*/ |
||||||
|
public int getRemotePort() |
||||||
|
{ |
||||||
|
return _socket_.getPort(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @return The remote address to which the client is connected. |
||||||
|
* Delegates to {@link Socket#getInetAddress()} |
||||||
|
* @throws NullPointerException if the socket is not currently open |
||||||
|
*/ |
||||||
|
public InetAddress getRemoteAddress() |
||||||
|
{ |
||||||
|
return _socket_.getInetAddress(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Verifies that the remote end of the given socket is connected to the |
||||||
|
* the same host that the SocketClient is currently connected to. This |
||||||
|
* is useful for doing a quick security check when a client needs to |
||||||
|
* accept a connection from a server, such as an FTP data connection or |
||||||
|
* a BSD R command standard error stream. |
||||||
|
* <p> |
||||||
|
* @param socket the item to check against |
||||||
|
* @return True if the remote hosts are the same, false if not. |
||||||
|
*/ |
||||||
|
public boolean verifyRemote(Socket socket) |
||||||
|
{ |
||||||
|
InetAddress host1, host2; |
||||||
|
|
||||||
|
host1 = socket.getInetAddress(); |
||||||
|
host2 = getRemoteAddress(); |
||||||
|
|
||||||
|
return host1.equals(host2); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the SocketFactory used by the SocketClient to open socket |
||||||
|
* connections. If the factory value is null, then a default |
||||||
|
* factory is used (only do this to reset the factory after having |
||||||
|
* previously altered it). |
||||||
|
* Any proxy setting is discarded. |
||||||
|
* <p> |
||||||
|
* @param factory The new SocketFactory the SocketClient should use. |
||||||
|
*/ |
||||||
|
public void setSocketFactory(SocketFactory factory) |
||||||
|
{ |
||||||
|
if (factory == null) { |
||||||
|
_socketFactory_ = __DEFAULT_SOCKET_FACTORY; |
||||||
|
} else { |
||||||
|
_socketFactory_ = factory; |
||||||
|
} |
||||||
|
// re-setting the socket factory makes the proxy setting useless,
|
||||||
|
// so set the field to null so that getProxy() doesn't return a
|
||||||
|
// Proxy that we're actually not using.
|
||||||
|
connProxy = null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the ServerSocketFactory used by the SocketClient to open ServerSocket |
||||||
|
* connections. If the factory value is null, then a default |
||||||
|
* factory is used (only do this to reset the factory after having |
||||||
|
* previously altered it). |
||||||
|
* <p> |
||||||
|
* @param factory The new ServerSocketFactory the SocketClient should use. |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
public void setServerSocketFactory(ServerSocketFactory factory) { |
||||||
|
if (factory == null) { |
||||||
|
_serverSocketFactory_ = __DEFAULT_SERVER_SOCKET_FACTORY; |
||||||
|
} else { |
||||||
|
_serverSocketFactory_ = factory; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the connection timeout in milliseconds, which will be passed to the {@link Socket} object's |
||||||
|
* connect() method. |
||||||
|
* @param connectTimeout The connection timeout to use (in ms) |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
public void setConnectTimeout(int connectTimeout) { |
||||||
|
this.connectTimeout = connectTimeout; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the underlying socket connection timeout. |
||||||
|
* @return timeout (in ms) |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
public int getConnectTimeout() { |
||||||
|
return connectTimeout; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the underlying {@link ServerSocketFactory} |
||||||
|
* @return The server socket factory |
||||||
|
* @since 2.2 |
||||||
|
*/ |
||||||
|
public ServerSocketFactory getServerSocketFactory() { |
||||||
|
return _serverSocketFactory_; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Adds a ProtocolCommandListener. |
||||||
|
* |
||||||
|
* @param listener The ProtocolCommandListener to add. |
||||||
|
* @since 3.0 |
||||||
|
*/ |
||||||
|
public void addProtocolCommandListener(ProtocolCommandListener listener) { |
||||||
|
getCommandSupport().addProtocolCommandListener(listener); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Removes a ProtocolCommandListener. |
||||||
|
* |
||||||
|
* @param listener The ProtocolCommandListener to remove. |
||||||
|
* @since 3.0 |
||||||
|
*/ |
||||||
|
public void removeProtocolCommandListener(ProtocolCommandListener listener) { |
||||||
|
getCommandSupport().removeProtocolCommandListener(listener); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* If there are any listeners, send them the reply details. |
||||||
|
* |
||||||
|
* @param replyCode the code extracted from the reply |
||||||
|
* @param reply the full reply text |
||||||
|
* @since 3.0 |
||||||
|
*/ |
||||||
|
protected void fireReplyReceived(int replyCode, String reply) { |
||||||
|
if (getCommandSupport().getListenerCount() > 0) { |
||||||
|
getCommandSupport().fireReplyReceived(replyCode, reply); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* If there are any listeners, send them the command details. |
||||||
|
* |
||||||
|
* @param command the command name |
||||||
|
* @param message the complete message, including command name |
||||||
|
* @since 3.0 |
||||||
|
*/ |
||||||
|
protected void fireCommandSent(String command, String message) { |
||||||
|
if (getCommandSupport().getListenerCount() > 0) { |
||||||
|
getCommandSupport().fireCommandSent(command, message); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create the CommandSupport instance if required |
||||||
|
*/ |
||||||
|
protected void createCommandSupport(){ |
||||||
|
__commandSupport = new ProtocolCommandSupport(this); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Subclasses can override this if they need to provide their own |
||||||
|
* instance field for backwards compatibilty. |
||||||
|
* |
||||||
|
* @return the CommandSupport instance, may be {@code null} |
||||||
|
* @since 3.0 |
||||||
|
*/ |
||||||
|
protected ProtocolCommandSupport getCommandSupport() { |
||||||
|
return __commandSupport; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the proxy for use with all the connections. |
||||||
|
* The proxy is used for connections established after the |
||||||
|
* call to this method. |
||||||
|
* |
||||||
|
* @param proxy the new proxy for connections. |
||||||
|
* @since 3.2 |
||||||
|
*/ |
||||||
|
public void setProxy(Proxy proxy) { |
||||||
|
setSocketFactory(new DefaultSocketFactory(proxy)); |
||||||
|
connProxy = proxy; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the proxy for use with all the connections. |
||||||
|
* @return the current proxy for connections. |
||||||
|
*/ |
||||||
|
public Proxy getProxy() { |
||||||
|
return connProxy; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the charset name. |
||||||
|
* |
||||||
|
* @return the charset. |
||||||
|
* @since 3.3 |
||||||
|
* @deprecated Since the code now requires Java 1.6 as a mininmum |
||||||
|
*/ |
||||||
|
@Deprecated |
||||||
|
public String getCharsetName() { |
||||||
|
return charset.name(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the charset. |
||||||
|
* |
||||||
|
* @return the charset. |
||||||
|
* @since 3.3 |
||||||
|
*/ |
||||||
|
public Charset getCharset() { |
||||||
|
return charset; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the charset. |
||||||
|
* |
||||||
|
* @param charset the charset. |
||||||
|
* @since 3.3 |
||||||
|
*/ |
||||||
|
public void setCharset(Charset charset) { |
||||||
|
this.charset = charset; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
* N.B. Fields cannot be pulled up into a super-class without breaking binary compatibility, |
||||||
|
* so the abstract method is needed to pass the instance to the methods which were moved here. |
||||||
|
*/ |
||||||
|
} |
||||||
|
|
||||||
|
|
@ -0,0 +1,35 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.apache.commons.net.ftp; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* This interface adds the aspect of configurability by means of |
||||||
|
* a supplied FTPClientConfig object to other classes in the |
||||||
|
* system, especially listing parsers. |
||||||
|
*/ |
||||||
|
public interface Configurable { |
||||||
|
|
||||||
|
/** |
||||||
|
* @param config the object containing the configuration data |
||||||
|
* @throws IllegalArgumentException if the elements of the |
||||||
|
* <code>config</code> are somehow inadequate to configure the |
||||||
|
* Configurable object. |
||||||
|
*/ |
||||||
|
public void configure(FTPClientConfig config); |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,709 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.apache.commons.net.ftp; |
||||||
|
|
||||||
|
import java.text.DateFormatSymbols; |
||||||
|
import java.util.Collection; |
||||||
|
import java.util.Locale; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.StringTokenizer; |
||||||
|
import java.util.TreeMap; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* This class implements an alternate means of configuring the |
||||||
|
* {@link FTPClient FTPClient} object and |
||||||
|
* also subordinate objects which it uses. Any class implementing the |
||||||
|
* {@link Configurable Configurable } |
||||||
|
* interface can be configured by this object. |
||||||
|
* </p><p> |
||||||
|
* In particular this class was designed primarily to support configuration |
||||||
|
* of FTP servers which express file timestamps in formats and languages |
||||||
|
* other than those for the US locale, which although it is the most common |
||||||
|
* is not universal. Unfortunately, nothing in the FTP spec allows this to |
||||||
|
* be determined in an automated way, so manual configuration such as this |
||||||
|
* is necessary. |
||||||
|
* </p><p> |
||||||
|
* This functionality was designed to allow existing clients to work exactly |
||||||
|
* as before without requiring use of this component. This component should |
||||||
|
* only need to be explicitly invoked by the user of this package for problem |
||||||
|
* cases that previous implementations could not solve. |
||||||
|
* </p> |
||||||
|
* <h3>Examples of use of FTPClientConfig</h3> |
||||||
|
* Use cases: |
||||||
|
* You are trying to access a server that |
||||||
|
* <ul> |
||||||
|
* <li>lists files with timestamps that use month names in languages other |
||||||
|
* than English</li> |
||||||
|
* <li>lists files with timestamps that use date formats other |
||||||
|
* than the American English "standard" <code>MM dd yyyy</code></li> |
||||||
|
* <li>is in different timezone and you need accurate timestamps for |
||||||
|
* dependency checking as in Ant</li> |
||||||
|
* </ul> |
||||||
|
* <p> |
||||||
|
* Unpaged (whole list) access on a UNIX server that uses French month names |
||||||
|
* but uses the "standard" <code>MMM d yyyy</code> date formatting |
||||||
|
* <pre> |
||||||
|
* FTPClient f=FTPClient(); |
||||||
|
* FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX); |
||||||
|
* conf.setServerLanguageCode("fr"); |
||||||
|
* f.configure(conf); |
||||||
|
* f.connect(server); |
||||||
|
* f.login(username, password); |
||||||
|
* FTPFile[] files = listFiles(directory); |
||||||
|
* </pre> |
||||||
|
* <p> |
||||||
|
* Paged access on a UNIX server that uses Danish month names |
||||||
|
* and "European" date formatting in Denmark's time zone, when you |
||||||
|
* are in some other time zone. |
||||||
|
* <pre> |
||||||
|
* FTPClient f=FTPClient(); |
||||||
|
* FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX); |
||||||
|
* conf.setServerLanguageCode("da"); |
||||||
|
* conf.setDefaultDateFormat("d MMM yyyy"); |
||||||
|
* conf.setRecentDateFormat("d MMM HH:mm"); |
||||||
|
* conf.setTimeZoneId("Europe/Copenhagen"); |
||||||
|
* f.configure(conf); |
||||||
|
* f.connect(server); |
||||||
|
* f.login(username, password); |
||||||
|
* FTPListParseEngine engine = |
||||||
|
* f.initiateListParsing("com.whatever.YourOwnParser", directory); |
||||||
|
* |
||||||
|
* while (engine.hasNext()) { |
||||||
|
* FTPFile[] files = engine.getNext(25); // "page size" you want
|
||||||
|
* //do whatever you want with these files, display them, etc.
|
||||||
|
* //expensive FTPFile objects not created until needed.
|
||||||
|
* } |
||||||
|
* </pre> |
||||||
|
* <p> |
||||||
|
* Unpaged (whole list) access on a VMS server that uses month names |
||||||
|
* in a language not {@link #getSupportedLanguageCodes() supported} by the system. |
||||||
|
* but uses the "standard" <code>MMM d yyyy</code> date formatting |
||||||
|
* <pre> |
||||||
|
* FTPClient f=FTPClient(); |
||||||
|
* FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_VMS); |
||||||
|
* conf.setShortMonthNames( |
||||||
|
* "jan|feb|mar|apr|ma\u00ED|j\u00FAn|j\u00FAl|\u00e1g\u00FA|sep|okt|n\u00F3v|des"); |
||||||
|
* f.configure(conf); |
||||||
|
* f.connect(server); |
||||||
|
* f.login(username, password); |
||||||
|
* FTPFile[] files = listFiles(directory); |
||||||
|
* </pre> |
||||||
|
* <p> |
||||||
|
* Unpaged (whole list) access on a Windows-NT server in a different time zone. |
||||||
|
* (Note, since the NT Format uses numeric date formatting, language issues |
||||||
|
* are irrelevant here). |
||||||
|
* <pre> |
||||||
|
* FTPClient f=FTPClient(); |
||||||
|
* FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_NT); |
||||||
|
* conf.setTimeZoneId("America/Denver"); |
||||||
|
* f.configure(conf); |
||||||
|
* f.connect(server); |
||||||
|
* f.login(username, password); |
||||||
|
* FTPFile[] files = listFiles(directory); |
||||||
|
* </pre> |
||||||
|
* Unpaged (whole list) access on a Windows-NT server in a different time zone |
||||||
|
* but which has been configured to use a unix-style listing format. |
||||||
|
* <pre> |
||||||
|
* FTPClient f=FTPClient(); |
||||||
|
* FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX); |
||||||
|
* conf.setTimeZoneId("America/Denver"); |
||||||
|
* f.configure(conf); |
||||||
|
* f.connect(server); |
||||||
|
* f.login(username, password); |
||||||
|
* FTPFile[] files = listFiles(directory); |
||||||
|
* </pre> |
||||||
|
* |
||||||
|
* @since 1.4 |
||||||
|
* @see Configurable |
||||||
|
* @see FTPClient |
||||||
|
* @see org.apache.commons.net.ftp.parser.FTPTimestampParserImpl#configure(FTPClientConfig) |
||||||
|
* @see org.apache.commons.net.ftp.parser.ConfigurableFTPFileEntryParserImpl |
||||||
|
*/ |
||||||
|
public class FTPClientConfig |
||||||
|
{ |
||||||
|
|
||||||
|
/** |
||||||
|
* Identifier by which a unix-based ftp server is known throughout |
||||||
|
* the commons-net ftp system. |
||||||
|
*/ |
||||||
|
public static final String SYST_UNIX = "UNIX"; |
||||||
|
|
||||||
|
/** |
||||||
|
* Identifier for alternate UNIX parser; same as {@link #SYST_UNIX} but leading spaces are |
||||||
|
* trimmed from file names. This is to maintain backwards compatibility with |
||||||
|
* the original behaviour of the parser which ignored multiple spaces between the date |
||||||
|
* and the start of the file name. |
||||||
|
* @since 3.4 |
||||||
|
*/ |
||||||
|
public static final String SYST_UNIX_TRIM_LEADING = "UNIX_LTRIM"; |
||||||
|
|
||||||
|
/** |
||||||
|
* Identifier by which a vms-based ftp server is known throughout |
||||||
|
* the commons-net ftp system. |
||||||
|
*/ |
||||||
|
public static final String SYST_VMS = "VMS"; |
||||||
|
|
||||||
|
/** |
||||||
|
* Identifier by which a WindowsNT-based ftp server is known throughout |
||||||
|
* the commons-net ftp system. |
||||||
|
*/ |
||||||
|
public static final String SYST_NT = "WINDOWS"; |
||||||
|
|
||||||
|
/** |
||||||
|
* Identifier by which an OS/2-based ftp server is known throughout |
||||||
|
* the commons-net ftp system. |
||||||
|
*/ |
||||||
|
public static final String SYST_OS2 = "OS/2"; |
||||||
|
|
||||||
|
/** |
||||||
|
* Identifier by which an OS/400-based ftp server is known throughout |
||||||
|
* the commons-net ftp system. |
||||||
|
*/ |
||||||
|
public static final String SYST_OS400 = "OS/400"; |
||||||
|
|
||||||
|
/** |
||||||
|
* Identifier by which an AS/400-based ftp server is known throughout |
||||||
|
* the commons-net ftp system. |
||||||
|
*/ |
||||||
|
public static final String SYST_AS400 = "AS/400"; |
||||||
|
|
||||||
|
/** |
||||||
|
* Identifier by which an MVS-based ftp server is known throughout |
||||||
|
* the commons-net ftp system. |
||||||
|
*/ |
||||||
|
public static final String SYST_MVS = "MVS"; |
||||||
|
|
||||||
|
/** |
||||||
|
* Some servers return an "UNKNOWN Type: L8" message |
||||||
|
* in response to the SYST command. We set these to be a Unix-type system. |
||||||
|
* This may happen if the ftpd in question was compiled without system |
||||||
|
* information. |
||||||
|
* |
||||||
|
* NET-230 - Updated to be UPPERCASE so that the check done in |
||||||
|
* createFileEntryParser will succeed. |
||||||
|
* |
||||||
|
* @since 1.5 |
||||||
|
*/ |
||||||
|
public static final String SYST_L8 = "TYPE: L8"; |
||||||
|
|
||||||
|
/** |
||||||
|
* Identifier by which an Netware-based ftp server is known throughout |
||||||
|
* the commons-net ftp system. |
||||||
|
* |
||||||
|
* @since 1.5 |
||||||
|
*/ |
||||||
|
public static final String SYST_NETWARE = "NETWARE"; |
||||||
|
|
||||||
|
/** |
||||||
|
* Identifier by which a Mac pre OS-X -based ftp server is known throughout |
||||||
|
* the commons-net ftp system. |
||||||
|
* |
||||||
|
* @since 3.1 |
||||||
|
*/ |
||||||
|
// Full string is "MACOS Peter's Server"; the substring below should be enough
|
||||||
|
public static final String SYST_MACOS_PETER = "MACOS PETER"; // NET-436
|
||||||
|
|
||||||
|
private final String serverSystemKey; |
||||||
|
private String defaultDateFormatStr = null; |
||||||
|
private String recentDateFormatStr = null; |
||||||
|
private boolean lenientFutureDates = true; // NET-407
|
||||||
|
private String serverLanguageCode = null; |
||||||
|
private String shortMonthNames = null; |
||||||
|
private String serverTimeZoneId = null; |
||||||
|
private boolean saveUnparseableEntries = false; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* The main constructor for an FTPClientConfig object |
||||||
|
* @param systemKey key representing system type of the server being |
||||||
|
* connected to. See {@link #getServerSystemKey() serverSystemKey} |
||||||
|
* If set to the empty string, then FTPClient uses the system type returned by the server. |
||||||
|
* However this is not recommended for general use; |
||||||
|
* the correct system type should be set if it is known. |
||||||
|
*/ |
||||||
|
public FTPClientConfig(String systemKey) { |
||||||
|
this.serverSystemKey = systemKey; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Convenience constructor mainly for use in testing. |
||||||
|
* Constructs a UNIX configuration. |
||||||
|
*/ |
||||||
|
public FTPClientConfig() { |
||||||
|
this(SYST_UNIX); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor which allows setting of the format string member fields |
||||||
|
* @param systemKey key representing system type of the server being |
||||||
|
* connected to. See |
||||||
|
* {@link #getServerSystemKey() serverSystemKey} |
||||||
|
* @param defaultDateFormatStr See |
||||||
|
* {@link #setDefaultDateFormatStr(String) defaultDateFormatStr} |
||||||
|
* @param recentDateFormatStr See |
||||||
|
* {@link #setRecentDateFormatStr(String) recentDateFormatStr} |
||||||
|
* @since 3.6 |
||||||
|
*/ |
||||||
|
public FTPClientConfig(String systemKey, |
||||||
|
String defaultDateFormatStr, |
||||||
|
String recentDateFormatStr) |
||||||
|
{ |
||||||
|
this(systemKey); |
||||||
|
this.defaultDateFormatStr = defaultDateFormatStr; |
||||||
|
this.recentDateFormatStr = recentDateFormatStr; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor which allows setting of most member fields |
||||||
|
* @param systemKey key representing system type of the server being |
||||||
|
* connected to. See |
||||||
|
* {@link #getServerSystemKey() serverSystemKey} |
||||||
|
* @param defaultDateFormatStr See |
||||||
|
* {@link #setDefaultDateFormatStr(String) defaultDateFormatStr} |
||||||
|
* @param recentDateFormatStr See |
||||||
|
* {@link #setRecentDateFormatStr(String) recentDateFormatStr} |
||||||
|
* @param serverLanguageCode See |
||||||
|
* {@link #setServerLanguageCode(String) serverLanguageCode} |
||||||
|
* @param shortMonthNames See |
||||||
|
* {@link #setShortMonthNames(String) shortMonthNames} |
||||||
|
* @param serverTimeZoneId See |
||||||
|
* {@link #setServerTimeZoneId(String) serverTimeZoneId} |
||||||
|
*/ |
||||||
|
public FTPClientConfig(String systemKey, |
||||||
|
String defaultDateFormatStr, |
||||||
|
String recentDateFormatStr, |
||||||
|
String serverLanguageCode, |
||||||
|
String shortMonthNames, |
||||||
|
String serverTimeZoneId) |
||||||
|
{ |
||||||
|
this(systemKey); |
||||||
|
this.defaultDateFormatStr = defaultDateFormatStr; |
||||||
|
this.recentDateFormatStr = recentDateFormatStr; |
||||||
|
this.serverLanguageCode = serverLanguageCode; |
||||||
|
this.shortMonthNames = shortMonthNames; |
||||||
|
this.serverTimeZoneId = serverTimeZoneId; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor which allows setting of all member fields |
||||||
|
* @param systemKey key representing system type of the server being |
||||||
|
* connected to. See |
||||||
|
* {@link #getServerSystemKey() serverSystemKey} |
||||||
|
* @param defaultDateFormatStr See |
||||||
|
* {@link #setDefaultDateFormatStr(String) defaultDateFormatStr} |
||||||
|
* @param recentDateFormatStr See |
||||||
|
* {@link #setRecentDateFormatStr(String) recentDateFormatStr} |
||||||
|
* @param serverLanguageCode See |
||||||
|
* {@link #setServerLanguageCode(String) serverLanguageCode} |
||||||
|
* @param shortMonthNames See |
||||||
|
* {@link #setShortMonthNames(String) shortMonthNames} |
||||||
|
* @param serverTimeZoneId See |
||||||
|
* {@link #setServerTimeZoneId(String) serverTimeZoneId} |
||||||
|
* @param lenientFutureDates See |
||||||
|
* {@link #setLenientFutureDates(boolean) lenientFutureDates} |
||||||
|
* @param saveUnparseableEntries See |
||||||
|
* {@link #setUnparseableEntries(boolean) saveUnparseableEntries} |
||||||
|
*/ |
||||||
|
public FTPClientConfig(String systemKey, |
||||||
|
String defaultDateFormatStr, |
||||||
|
String recentDateFormatStr, |
||||||
|
String serverLanguageCode, |
||||||
|
String shortMonthNames, |
||||||
|
String serverTimeZoneId, |
||||||
|
boolean lenientFutureDates, |
||||||
|
boolean saveUnparseableEntries) |
||||||
|
{ |
||||||
|
this(systemKey); |
||||||
|
this.defaultDateFormatStr = defaultDateFormatStr; |
||||||
|
this.lenientFutureDates = lenientFutureDates; |
||||||
|
this.recentDateFormatStr = recentDateFormatStr; |
||||||
|
this.saveUnparseableEntries = saveUnparseableEntries; |
||||||
|
this.serverLanguageCode = serverLanguageCode; |
||||||
|
this.shortMonthNames = shortMonthNames; |
||||||
|
this.serverTimeZoneId = serverTimeZoneId; |
||||||
|
} |
||||||
|
|
||||||
|
// Copy constructor, intended for use by FTPClient only
|
||||||
|
FTPClientConfig(String systemKey, FTPClientConfig config) { |
||||||
|
this.serverSystemKey = systemKey; |
||||||
|
this.defaultDateFormatStr = config.defaultDateFormatStr; |
||||||
|
this.lenientFutureDates = config.lenientFutureDates; |
||||||
|
this.recentDateFormatStr = config.recentDateFormatStr; |
||||||
|
this.saveUnparseableEntries = config.saveUnparseableEntries; |
||||||
|
this.serverLanguageCode = config.serverLanguageCode; |
||||||
|
this.serverTimeZoneId = config.serverTimeZoneId; |
||||||
|
this.shortMonthNames = config.shortMonthNames; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Copy constructor |
||||||
|
* @param config source |
||||||
|
* @since 3.6 |
||||||
|
*/ |
||||||
|
public FTPClientConfig(FTPClientConfig config) { |
||||||
|
this.serverSystemKey = config.serverSystemKey; |
||||||
|
this.defaultDateFormatStr = config.defaultDateFormatStr; |
||||||
|
this.lenientFutureDates = config.lenientFutureDates; |
||||||
|
this.recentDateFormatStr = config.recentDateFormatStr; |
||||||
|
this.saveUnparseableEntries = config.saveUnparseableEntries; |
||||||
|
this.serverLanguageCode = config.serverLanguageCode; |
||||||
|
this.serverTimeZoneId = config.serverTimeZoneId; |
||||||
|
this.shortMonthNames = config.shortMonthNames; |
||||||
|
} |
||||||
|
|
||||||
|
private static final Map<String, Object> LANGUAGE_CODE_MAP = new TreeMap<String, Object>(); |
||||||
|
static { |
||||||
|
|
||||||
|
// if there are other commonly used month name encodings which
|
||||||
|
// correspond to particular locales, please add them here.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// many locales code short names for months as all three letters
|
||||||
|
// these we handle simply.
|
||||||
|
LANGUAGE_CODE_MAP.put("en", Locale.ENGLISH); |
||||||
|
LANGUAGE_CODE_MAP.put("de",Locale.GERMAN); |
||||||
|
LANGUAGE_CODE_MAP.put("it",Locale.ITALIAN); |
||||||
|
LANGUAGE_CODE_MAP.put("es", new Locale("es", "", "")); // spanish
|
||||||
|
LANGUAGE_CODE_MAP.put("pt", new Locale("pt", "", "")); // portuguese
|
||||||
|
LANGUAGE_CODE_MAP.put("da", new Locale("da", "", "")); // danish
|
||||||
|
LANGUAGE_CODE_MAP.put("sv", new Locale("sv", "", "")); // swedish
|
||||||
|
LANGUAGE_CODE_MAP.put("no", new Locale("no", "", "")); // norwegian
|
||||||
|
LANGUAGE_CODE_MAP.put("nl", new Locale("nl", "", "")); // dutch
|
||||||
|
LANGUAGE_CODE_MAP.put("ro", new Locale("ro", "", "")); // romanian
|
||||||
|
LANGUAGE_CODE_MAP.put("sq", new Locale("sq", "", "")); // albanian
|
||||||
|
LANGUAGE_CODE_MAP.put("sh", new Locale("sh", "", "")); // serbo-croatian
|
||||||
|
LANGUAGE_CODE_MAP.put("sk", new Locale("sk", "", "")); // slovak
|
||||||
|
LANGUAGE_CODE_MAP.put("sl", new Locale("sl", "", "")); // slovenian
|
||||||
|
|
||||||
|
|
||||||
|
// some don't
|
||||||
|
LANGUAGE_CODE_MAP.put("fr", |
||||||
|
"jan|f\u00e9v|mar|avr|mai|jun|jui|ao\u00fb|sep|oct|nov|d\u00e9c"); //french
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Getter for the serverSystemKey property. This property |
||||||
|
* specifies the general type of server to which the client connects. |
||||||
|
* Should be either one of the <code>FTPClientConfig.SYST_*</code> codes |
||||||
|
* or else the fully qualified class name of a parser implementing both |
||||||
|
* the <code>FTPFileEntryParser</code> and <code>Configurable</code> |
||||||
|
* interfaces. |
||||||
|
* @return Returns the serverSystemKey property. |
||||||
|
*/ |
||||||
|
public String getServerSystemKey() { |
||||||
|
return serverSystemKey; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* getter for the {@link #setDefaultDateFormatStr(String) defaultDateFormatStr} |
||||||
|
* property. |
||||||
|
* @return Returns the defaultDateFormatStr property. |
||||||
|
*/ |
||||||
|
public String getDefaultDateFormatStr() { |
||||||
|
return defaultDateFormatStr; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* getter for the {@link #setRecentDateFormatStr(String) recentDateFormatStr} property. |
||||||
|
* @return Returns the recentDateFormatStr property. |
||||||
|
*/ |
||||||
|
|
||||||
|
public String getRecentDateFormatStr() { |
||||||
|
return recentDateFormatStr; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* getter for the {@link #setServerTimeZoneId(String) serverTimeZoneId} property. |
||||||
|
* @return Returns the serverTimeZoneId property. |
||||||
|
*/ |
||||||
|
public String getServerTimeZoneId() { |
||||||
|
return serverTimeZoneId; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* getter for the {@link #setShortMonthNames(String) shortMonthNames} |
||||||
|
* property. |
||||||
|
* </p> |
||||||
|
* @return Returns the shortMonthNames. |
||||||
|
*/ |
||||||
|
public String getShortMonthNames() { |
||||||
|
return shortMonthNames; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* getter for the {@link #setServerLanguageCode(String) serverLanguageCode} property. |
||||||
|
* </p> |
||||||
|
* @return Returns the serverLanguageCode property. |
||||||
|
*/ |
||||||
|
public String getServerLanguageCode() { |
||||||
|
return serverLanguageCode; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* getter for the {@link #setLenientFutureDates(boolean) lenientFutureDates} property. |
||||||
|
* </p> |
||||||
|
* @return Returns the lenientFutureDates. |
||||||
|
* @since 1.5 |
||||||
|
*/ |
||||||
|
public boolean isLenientFutureDates() { |
||||||
|
return lenientFutureDates; |
||||||
|
} |
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* setter for the defaultDateFormatStr property. This property |
||||||
|
* specifies the main date format that will be used by a parser configured |
||||||
|
* by this configuration to parse file timestamps. If this is not |
||||||
|
* specified, such a parser will use as a default value, the most commonly |
||||||
|
* used format which will be in as used in <code>en_US</code> locales. |
||||||
|
* </p><p> |
||||||
|
* This should be in the format described for |
||||||
|
* <code>java.text.SimpleDateFormat</code>. |
||||||
|
* property. |
||||||
|
* </p> |
||||||
|
* @param defaultDateFormatStr The defaultDateFormatStr to set. |
||||||
|
*/ |
||||||
|
public void setDefaultDateFormatStr(String defaultDateFormatStr) { |
||||||
|
this.defaultDateFormatStr = defaultDateFormatStr; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* setter for the recentDateFormatStr property. This property |
||||||
|
* specifies a secondary date format that will be used by a parser |
||||||
|
* configured by this configuration to parse file timestamps, typically |
||||||
|
* those less than a year old. If this is not specified, such a parser |
||||||
|
* will not attempt to parse using an alternate format. |
||||||
|
* </p> |
||||||
|
* <p> |
||||||
|
* This is used primarily in unix-based systems. |
||||||
|
* </p> |
||||||
|
* <p> |
||||||
|
* This should be in the format described for |
||||||
|
* <code>java.text.SimpleDateFormat</code>. |
||||||
|
* </p> |
||||||
|
* @param recentDateFormatStr The recentDateFormatStr to set. |
||||||
|
*/ |
||||||
|
public void setRecentDateFormatStr(String recentDateFormatStr) { |
||||||
|
this.recentDateFormatStr = recentDateFormatStr; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* setter for the lenientFutureDates property. This boolean property |
||||||
|
* (default: false) only has meaning when a |
||||||
|
* {@link #setRecentDateFormatStr(String) recentDateFormatStr} property |
||||||
|
* has been set. In that case, if this property is set true, then the |
||||||
|
* parser, when it encounters a listing parseable with the recent date |
||||||
|
* format, will only consider a date to belong to the previous year if |
||||||
|
* it is more than one day in the future. This will allow all |
||||||
|
* out-of-synch situations (whether based on "slop" - i.e. servers simply |
||||||
|
* out of synch with one another or because of time zone differences - |
||||||
|
* but in the latter case it is highly recommended to use the |
||||||
|
* {@link #setServerTimeZoneId(String) serverTimeZoneId} property |
||||||
|
* instead) to resolve correctly. |
||||||
|
* </p><p> |
||||||
|
* This is used primarily in unix-based systems. |
||||||
|
* </p> |
||||||
|
* @param lenientFutureDates set true to compensate for out-of-synch |
||||||
|
* conditions. |
||||||
|
*/ |
||||||
|
public void setLenientFutureDates(boolean lenientFutureDates) { |
||||||
|
this.lenientFutureDates = lenientFutureDates; |
||||||
|
} |
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* setter for the serverTimeZoneId property. This property |
||||||
|
* allows a time zone to be specified corresponding to that known to be |
||||||
|
* used by an FTP server in file listings. This might be particularly |
||||||
|
* useful to clients such as Ant that try to use these timestamps for |
||||||
|
* dependency checking. |
||||||
|
* </p><p> |
||||||
|
* This should be one of the identifiers used by |
||||||
|
* <code>java.util.TimeZone</code> to refer to time zones, for example, |
||||||
|
* <code>America/Chicago</code> or <code>Asia/Rangoon</code>. |
||||||
|
* </p> |
||||||
|
* @param serverTimeZoneId The serverTimeZoneId to set. |
||||||
|
*/ |
||||||
|
public void setServerTimeZoneId(String serverTimeZoneId) { |
||||||
|
this.serverTimeZoneId = serverTimeZoneId; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* setter for the shortMonthNames property. |
||||||
|
* This property allows the user to specify a set of month names |
||||||
|
* used by the server that is different from those that may be |
||||||
|
* specified using the {@link #setServerLanguageCode(String) serverLanguageCode} |
||||||
|
* property. |
||||||
|
* </p><p> |
||||||
|
* This should be a string containing twelve strings each composed of |
||||||
|
* three characters, delimited by pipe (|) characters. Currently, |
||||||
|
* only 8-bit ASCII characters are known to be supported. For example, |
||||||
|
* a set of month names used by a hypothetical Icelandic FTP server might |
||||||
|
* conceivably be specified as |
||||||
|
* <code>"jan|feb|mar|apr|maí|jún|júl|ágú|sep|okt|nóv|des"</code>. |
||||||
|
* </p> |
||||||
|
* @param shortMonthNames The value to set to the shortMonthNames property. |
||||||
|
*/ |
||||||
|
public void setShortMonthNames(String shortMonthNames) { |
||||||
|
this.shortMonthNames = shortMonthNames; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* setter for the serverLanguageCode property. This property allows |
||||||
|
* user to specify a |
||||||
|
* <a href="http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt"> |
||||||
|
* two-letter ISO-639 language code</a> that will be used to |
||||||
|
* configure the set of month names used by the file timestamp parser. |
||||||
|
* If neither this nor the {@link #setShortMonthNames(String) shortMonthNames} |
||||||
|
* is specified, parsing will assume English month names, which may or |
||||||
|
* may not be significant, depending on whether the date format(s) |
||||||
|
* specified via {@link #setDefaultDateFormatStr(String) defaultDateFormatStr} |
||||||
|
* and/or {@link #setRecentDateFormatStr(String) recentDateFormatStr} are using |
||||||
|
* numeric or alphabetic month names. |
||||||
|
* </p> |
||||||
|
* <p>If the code supplied is not supported here, <code>en_US</code> |
||||||
|
* month names will be used. We are supporting here those language |
||||||
|
* codes which, when a <code> java.util.Locale</code> is constucted |
||||||
|
* using it, and a <code>java.text.SimpleDateFormat</code> is |
||||||
|
* constructed using that Locale, the array returned by the |
||||||
|
* SimpleDateFormat's <code>getShortMonths()</code> method consists |
||||||
|
* solely of three 8-bit ASCII character strings. Additionally, |
||||||
|
* languages which do not meet this requirement are included if a |
||||||
|
* common alternative set of short month names is known to be used. |
||||||
|
* This means that users who can tell us of additional such encodings |
||||||
|
* may get them added to the list of supported languages by contacting |
||||||
|
* the Apache Commons Net team. |
||||||
|
* </p> |
||||||
|
* <p><strong> |
||||||
|
* Please note that this attribute will NOT be used to determine a |
||||||
|
* locale-based date format for the language. </strong> |
||||||
|
* Experience has shown that many if not most FTP servers outside the |
||||||
|
* United States employ the standard <code>en_US</code> date format |
||||||
|
* orderings of <code>MMM d yyyy</code> and <code>MMM d HH:mm</code> |
||||||
|
* and attempting to deduce this automatically here would cause more |
||||||
|
* problems than it would solve. The date format must be changed |
||||||
|
* via the {@link #setDefaultDateFormatStr(String) defaultDateFormatStr} and/or |
||||||
|
* {@link #setRecentDateFormatStr(String) recentDateFormatStr} parameters. |
||||||
|
* </p> |
||||||
|
* @param serverLanguageCode The value to set to the serverLanguageCode property. |
||||||
|
*/ |
||||||
|
public void setServerLanguageCode(String serverLanguageCode) { |
||||||
|
this.serverLanguageCode = serverLanguageCode; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Looks up the supplied language code in the internally maintained table of |
||||||
|
* language codes. Returns a DateFormatSymbols object configured with |
||||||
|
* short month names corresponding to the code. If there is no corresponding |
||||||
|
* entry in the table, the object returned will be that for |
||||||
|
* <code>Locale.US</code> |
||||||
|
* @param languageCode See {@link #setServerLanguageCode(String) serverLanguageCode} |
||||||
|
* @return a DateFormatSymbols object configured with short month names |
||||||
|
* corresponding to the supplied code, or with month names for |
||||||
|
* <code>Locale.US</code> if there is no corresponding entry in the internal |
||||||
|
* table. |
||||||
|
*/ |
||||||
|
public static DateFormatSymbols lookupDateFormatSymbols(String languageCode) |
||||||
|
{ |
||||||
|
Object lang = LANGUAGE_CODE_MAP.get(languageCode); |
||||||
|
if (lang != null) { |
||||||
|
if (lang instanceof Locale) { |
||||||
|
return new DateFormatSymbols((Locale) lang); |
||||||
|
} else if (lang instanceof String){ |
||||||
|
return getDateFormatSymbols((String) lang); |
||||||
|
} |
||||||
|
} |
||||||
|
return new DateFormatSymbols(Locale.US); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a DateFormatSymbols object configured with short month names |
||||||
|
* as in the supplied string |
||||||
|
* @param shortmonths This should be as described in |
||||||
|
* {@link #setShortMonthNames(String) shortMonthNames} |
||||||
|
* @return a DateFormatSymbols object configured with short month names |
||||||
|
* as in the supplied string |
||||||
|
*/ |
||||||
|
public static DateFormatSymbols getDateFormatSymbols(String shortmonths) |
||||||
|
{ |
||||||
|
String[] months = splitShortMonthString(shortmonths); |
||||||
|
DateFormatSymbols dfs = new DateFormatSymbols(Locale.US); |
||||||
|
dfs.setShortMonths(months); |
||||||
|
return dfs; |
||||||
|
} |
||||||
|
|
||||||
|
private static String[] splitShortMonthString(String shortmonths) { |
||||||
|
StringTokenizer st = new StringTokenizer(shortmonths, "|"); |
||||||
|
int monthcnt = st.countTokens(); |
||||||
|
if (12 != monthcnt) { |
||||||
|
throw new IllegalArgumentException( |
||||||
|
"expecting a pipe-delimited string containing 12 tokens"); |
||||||
|
} |
||||||
|
String[] months = new String[13]; |
||||||
|
int pos = 0; |
||||||
|
while(st.hasMoreTokens()) { |
||||||
|
months[pos++] = st.nextToken(); |
||||||
|
} |
||||||
|
months[pos]=""; |
||||||
|
return months; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a Collection of all the language codes currently supported |
||||||
|
* by this class. See {@link #setServerLanguageCode(String) serverLanguageCode} |
||||||
|
* for a functional descrption of language codes within this system. |
||||||
|
* |
||||||
|
* @return a Collection of all the language codes currently supported |
||||||
|
* by this class
|
||||||
|
*/ |
||||||
|
public static Collection<String> getSupportedLanguageCodes() { |
||||||
|
return LANGUAGE_CODE_MAP.keySet(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Allow list parsing methods to create basic FTPFile entries if parsing fails. |
||||||
|
* <p> |
||||||
|
* In this case, the FTPFile will contain only the unparsed entry {@link FTPFile#getRawListing()} |
||||||
|
* and {@link FTPFile#isValid()} will return {@code false} |
||||||
|
* @param saveUnparseable if true, then create FTPFile entries if parsing fails |
||||||
|
* @since 3.4 |
||||||
|
*/ |
||||||
|
public void setUnparseableEntries(boolean saveUnparseable) { |
||||||
|
this.saveUnparseableEntries = saveUnparseable; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return true if list parsing should return FTPFile entries even for unparseable response lines |
||||||
|
* <p> |
||||||
|
* If true, the FTPFile for any unparseable entries will contain only the unparsed entry |
||||||
|
* {@link FTPFile#getRawListing()} and {@link FTPFile#isValid()} will return {@code false} |
||||||
|
* @since 3.4 |
||||||
|
*/ |
||||||
|
public boolean getUnparseableEntries() { |
||||||
|
return this.saveUnparseableEntries; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,116 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.apache.commons.net.ftp; |
||||||
|
|
||||||
|
/** |
||||||
|
* @since 3.3 |
||||||
|
*/ |
||||||
|
public enum FTPCmd { |
||||||
|
ABOR, |
||||||
|
ACCT, |
||||||
|
ALLO, |
||||||
|
APPE, |
||||||
|
CDUP, |
||||||
|
CWD, |
||||||
|
DELE, |
||||||
|
EPRT, |
||||||
|
EPSV, |
||||||
|
FEAT, |
||||||
|
HELP, |
||||||
|
LIST, |
||||||
|
MDTM, |
||||||
|
MFMT, |
||||||
|
MKD, |
||||||
|
MLSD, |
||||||
|
MLST, |
||||||
|
MODE, |
||||||
|
NLST, |
||||||
|
NOOP, |
||||||
|
PASS, |
||||||
|
PASV, |
||||||
|
PORT, |
||||||
|
PWD, |
||||||
|
QUIT, |
||||||
|
REIN, |
||||||
|
REST, |
||||||
|
RETR, |
||||||
|
RMD, |
||||||
|
RNFR, |
||||||
|
RNTO, |
||||||
|
SITE, |
||||||
|
SMNT, |
||||||
|
STAT, |
||||||
|
STOR, |
||||||
|
STOU, |
||||||
|
STRU, |
||||||
|
SYST, |
||||||
|
TYPE, |
||||||
|
USER, |
||||||
|
; |
||||||
|
|
||||||
|
// Aliases
|
||||||
|
|
||||||
|
public static final FTPCmd ABORT = ABOR; |
||||||
|
public static final FTPCmd ACCOUNT = ACCT; |
||||||
|
public static final FTPCmd ALLOCATE = ALLO; |
||||||
|
public static final FTPCmd APPEND = APPE; |
||||||
|
public static final FTPCmd CHANGE_TO_PARENT_DIRECTORY = CDUP; |
||||||
|
public static final FTPCmd CHANGE_WORKING_DIRECTORY = CWD; |
||||||
|
public static final FTPCmd DATA_PORT = PORT; |
||||||
|
public static final FTPCmd DELETE = DELE; |
||||||
|
public static final FTPCmd FEATURES = FEAT; |
||||||
|
public static final FTPCmd FILE_STRUCTURE = STRU; |
||||||
|
public static final FTPCmd GET_MOD_TIME = MDTM; |
||||||
|
public static final FTPCmd LOGOUT = QUIT; |
||||||
|
public static final FTPCmd MAKE_DIRECTORY = MKD; |
||||||
|
public static final FTPCmd MOD_TIME = MDTM; |
||||||
|
public static final FTPCmd NAME_LIST = NLST; |
||||||
|
public static final FTPCmd PASSIVE = PASV; |
||||||
|
public static final FTPCmd PASSWORD = PASS; |
||||||
|
public static final FTPCmd PRINT_WORKING_DIRECTORY = PWD; |
||||||
|
public static final FTPCmd REINITIALIZE = REIN; |
||||||
|
public static final FTPCmd REMOVE_DIRECTORY = RMD; |
||||||
|
public static final FTPCmd RENAME_FROM = RNFR; |
||||||
|
public static final FTPCmd RENAME_TO = RNTO; |
||||||
|
public static final FTPCmd REPRESENTATION_TYPE = TYPE; |
||||||
|
public static final FTPCmd RESTART = REST; |
||||||
|
public static final FTPCmd RETRIEVE = RETR; |
||||||
|
public static final FTPCmd SET_MOD_TIME = MFMT; |
||||||
|
public static final FTPCmd SITE_PARAMETERS = SITE; |
||||||
|
public static final FTPCmd STATUS = STAT; |
||||||
|
public static final FTPCmd STORE = STOR; |
||||||
|
public static final FTPCmd STORE_UNIQUE = STOU; |
||||||
|
public static final FTPCmd STRUCTURE_MOUNT = SMNT; |
||||||
|
public static final FTPCmd SYSTEM = SYST; |
||||||
|
public static final FTPCmd TRANSFER_MODE = MODE; |
||||||
|
public static final FTPCmd USERNAME = USER; |
||||||
|
|
||||||
|
/** |
||||||
|
* Retrieve the FTP protocol command string corresponding to a specified |
||||||
|
* command code. |
||||||
|
* |
||||||
|
* @return The FTP protcol command string corresponding to a specified |
||||||
|
* command code. |
||||||
|
*/ |
||||||
|
public final String getCommand() |
||||||
|
{ |
||||||
|
return this.name(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,171 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.apache.commons.net.ftp; |
||||||
|
|
||||||
|
/** |
||||||
|
* FTPCommand stores a set of constants for FTP command codes. To interpret |
||||||
|
* the meaning of the codes, familiarity with RFC 959 is assumed. |
||||||
|
* The mnemonic constant names are transcriptions from the code descriptions |
||||||
|
* of RFC 959. For those who think in terms of the actual FTP commands, |
||||||
|
* a set of constants such as {@link #USER USER } are provided |
||||||
|
* where the constant name is the same as the FTP command. |
||||||
|
* |
||||||
|
* @deprecated use {@link FTPCmd} instead |
||||||
|
*/ |
||||||
|
@Deprecated |
||||||
|
public final class FTPCommand |
||||||
|
{ |
||||||
|
|
||||||
|
public static final int USER = 0; |
||||||
|
public static final int PASS = 1; |
||||||
|
public static final int ACCT = 2; |
||||||
|
public static final int CWD = 3; |
||||||
|
public static final int CDUP = 4; |
||||||
|
public static final int SMNT = 5; |
||||||
|
public static final int REIN = 6; |
||||||
|
public static final int QUIT = 7; |
||||||
|
public static final int PORT = 8; |
||||||
|
public static final int PASV = 9; |
||||||
|
public static final int TYPE = 10; |
||||||
|
public static final int STRU = 11; |
||||||
|
public static final int MODE = 12; |
||||||
|
public static final int RETR = 13; |
||||||
|
public static final int STOR = 14; |
||||||
|
public static final int STOU = 15; |
||||||
|
public static final int APPE = 16; |
||||||
|
public static final int ALLO = 17; |
||||||
|
public static final int REST = 18; |
||||||
|
public static final int RNFR = 19; |
||||||
|
public static final int RNTO = 20; |
||||||
|
public static final int ABOR = 21; |
||||||
|
public static final int DELE = 22; |
||||||
|
public static final int RMD = 23; |
||||||
|
public static final int MKD = 24; |
||||||
|
public static final int PWD = 25; |
||||||
|
public static final int LIST = 26; |
||||||
|
public static final int NLST = 27; |
||||||
|
public static final int SITE = 28; |
||||||
|
public static final int SYST = 29; |
||||||
|
public static final int STAT = 30; |
||||||
|
public static final int HELP = 31; |
||||||
|
public static final int NOOP = 32; |
||||||
|
/** @since 2.0 */ |
||||||
|
public static final int MDTM = 33; |
||||||
|
/** @since 2.2 */ |
||||||
|
public static final int FEAT = 34; |
||||||
|
/** @since 2.2 */ |
||||||
|
public static final int MFMT = 35; |
||||||
|
/** @since 2.2 */ |
||||||
|
public static final int EPSV = 36; |
||||||
|
/** @since 2.2 */ |
||||||
|
public static final int EPRT = 37; |
||||||
|
|
||||||
|
/** |
||||||
|
* Machine parseable list for a directory |
||||||
|
* @since 3.0 |
||||||
|
*/ |
||||||
|
public static final int MLSD = 38; |
||||||
|
|
||||||
|
/** |
||||||
|
* Machine parseable list for a single file |
||||||
|
* @since 3.0 |
||||||
|
*/ |
||||||
|
public static final int MLST = 39; |
||||||
|
|
||||||
|
// Must agree with final entry above; used to check array size
|
||||||
|
private static final int LAST = MLST; |
||||||
|
|
||||||
|
public static final int USERNAME = USER; |
||||||
|
public static final int PASSWORD = PASS; |
||||||
|
public static final int ACCOUNT = ACCT; |
||||||
|
public static final int CHANGE_WORKING_DIRECTORY = CWD; |
||||||
|
public static final int CHANGE_TO_PARENT_DIRECTORY = CDUP; |
||||||
|
public static final int STRUCTURE_MOUNT = SMNT; |
||||||
|
public static final int REINITIALIZE = REIN; |
||||||
|
public static final int LOGOUT = QUIT; |
||||||
|
public static final int DATA_PORT = PORT; |
||||||
|
public static final int PASSIVE = PASV; |
||||||
|
public static final int REPRESENTATION_TYPE = TYPE; |
||||||
|
public static final int FILE_STRUCTURE = STRU; |
||||||
|
public static final int TRANSFER_MODE = MODE; |
||||||
|
public static final int RETRIEVE = RETR; |
||||||
|
public static final int STORE = STOR; |
||||||
|
public static final int STORE_UNIQUE = STOU; |
||||||
|
public static final int APPEND = APPE; |
||||||
|
public static final int ALLOCATE = ALLO; |
||||||
|
public static final int RESTART = REST; |
||||||
|
public static final int RENAME_FROM = RNFR; |
||||||
|
public static final int RENAME_TO = RNTO; |
||||||
|
public static final int ABORT = ABOR; |
||||||
|
public static final int DELETE = DELE; |
||||||
|
public static final int REMOVE_DIRECTORY = RMD; |
||||||
|
public static final int MAKE_DIRECTORY = MKD; |
||||||
|
public static final int PRINT_WORKING_DIRECTORY = PWD; |
||||||
|
// public static final int LIST = LIST;
|
||||||
|
public static final int NAME_LIST = NLST; |
||||||
|
public static final int SITE_PARAMETERS = SITE; |
||||||
|
public static final int SYSTEM = SYST; |
||||||
|
public static final int STATUS = STAT; |
||||||
|
//public static final int HELP = HELP;
|
||||||
|
//public static final int NOOP = NOOP;
|
||||||
|
|
||||||
|
/** @since 2.0 */ |
||||||
|
public static final int MOD_TIME = MDTM; |
||||||
|
|
||||||
|
/** @since 2.2 */ |
||||||
|
public static final int FEATURES = FEAT; |
||||||
|
/** @since 2.2 */ |
||||||
|
public static final int GET_MOD_TIME = MDTM; |
||||||
|
/** @since 2.2 */ |
||||||
|
public static final int SET_MOD_TIME = MFMT; |
||||||
|
|
||||||
|
// Cannot be instantiated
|
||||||
|
private FTPCommand() |
||||||
|
{} |
||||||
|
|
||||||
|
private static final String[] _commands = { |
||||||
|
"USER", "PASS", "ACCT", "CWD", "CDUP", "SMNT", "REIN", "QUIT", "PORT", |
||||||
|
"PASV", "TYPE", "STRU", "MODE", "RETR", "STOR", "STOU", "APPE", "ALLO", |
||||||
|
"REST", "RNFR", "RNTO", "ABOR", "DELE", "RMD", "MKD", "PWD", "LIST", |
||||||
|
"NLST", "SITE", "SYST", "STAT", "HELP", "NOOP", "MDTM", "FEAT", "MFMT", |
||||||
|
"EPSV", "EPRT", "MLSD", "MLST" }; |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// default access needed for Unit test
|
||||||
|
static void checkArray(){ |
||||||
|
int expectedLength = LAST+1; |
||||||
|
if (_commands.length != expectedLength) { |
||||||
|
throw new RuntimeException("Incorrect _commands array. Should have length " |
||||||
|
+expectedLength+" found "+_commands.length); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Retrieve the FTP protocol command string corresponding to a specified |
||||||
|
* command code. |
||||||
|
* |
||||||
|
* @param command The command code. |
||||||
|
* @return The FTP protcol command string corresponding to a specified |
||||||
|
* command code. |
||||||
|
*/ |
||||||
|
public static final String getCommand(int command) |
||||||
|
{ |
||||||
|
return _commands[command]; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,55 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.apache.commons.net.ftp; |
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
/*** |
||||||
|
* FTPConnectionClosedException is used to indicate the premature or |
||||||
|
* unexpected closing of an FTP connection resulting from a |
||||||
|
* {@link FTPReply#SERVICE_NOT_AVAILABLE FTPReply.SERVICE_NOT_AVAILABLE } |
||||||
|
* response (FTP reply code 421) to a |
||||||
|
* failed FTP command. This exception is derived from IOException and |
||||||
|
* therefore may be caught either as an IOException or specifically as an |
||||||
|
* FTPConnectionClosedException. |
||||||
|
* |
||||||
|
* @see FTP |
||||||
|
* @see FTPClient |
||||||
|
***/ |
||||||
|
|
||||||
|
public class FTPConnectionClosedException extends IOException |
||||||
|
{ |
||||||
|
|
||||||
|
private static final long serialVersionUID = 3500547241659379952L; |
||||||
|
|
||||||
|
/*** Constructs a FTPConnectionClosedException with no message ***/ |
||||||
|
public FTPConnectionClosedException() |
||||||
|
{ |
||||||
|
super(); |
||||||
|
} |
||||||
|
|
||||||
|
/*** |
||||||
|
* Constructs a FTPConnectionClosedException with a specified message. |
||||||
|
* |
||||||
|
* @param message The message explaining the reason for the exception. |
||||||
|
***/ |
||||||
|
public FTPConnectionClosedException(String message) |
||||||
|
{ |
||||||
|
super(message); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,536 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.apache.commons.net.ftp; |
||||||
|
import java.io.Serializable; |
||||||
|
import java.util.Calendar; |
||||||
|
import java.util.Date; |
||||||
|
import java.util.Formatter; |
||||||
|
import java.util.TimeZone; |
||||||
|
|
||||||
|
/*** |
||||||
|
* The FTPFile class is used to represent information about files stored |
||||||
|
* on an FTP server. |
||||||
|
* |
||||||
|
* @see FTPFileEntryParser |
||||||
|
* @see FTPClient#listFiles |
||||||
|
***/ |
||||||
|
|
||||||
|
public class FTPFile implements Serializable |
||||||
|
{ |
||||||
|
private static final long serialVersionUID = 9010790363003271996L; |
||||||
|
|
||||||
|
/** A constant indicating an FTPFile is a file. ***/ |
||||||
|
public static final int FILE_TYPE = 0; |
||||||
|
/** A constant indicating an FTPFile is a directory. ***/ |
||||||
|
public static final int DIRECTORY_TYPE = 1; |
||||||
|
/** A constant indicating an FTPFile is a symbolic link. ***/ |
||||||
|
public static final int SYMBOLIC_LINK_TYPE = 2; |
||||||
|
/** A constant indicating an FTPFile is of unknown type. ***/ |
||||||
|
public static final int UNKNOWN_TYPE = 3; |
||||||
|
|
||||||
|
/** A constant indicating user access permissions. ***/ |
||||||
|
public static final int USER_ACCESS = 0; |
||||||
|
/** A constant indicating group access permissions. ***/ |
||||||
|
public static final int GROUP_ACCESS = 1; |
||||||
|
/** A constant indicating world access permissions. ***/ |
||||||
|
public static final int WORLD_ACCESS = 2; |
||||||
|
|
||||||
|
/** A constant indicating file/directory read permission. ***/ |
||||||
|
public static final int READ_PERMISSION = 0; |
||||||
|
/** A constant indicating file/directory write permission. ***/ |
||||||
|
public static final int WRITE_PERMISSION = 1; |
||||||
|
/** |
||||||
|
* A constant indicating file execute permission or directory listing |
||||||
|
* permission. |
||||||
|
***/ |
||||||
|
public static final int EXECUTE_PERMISSION = 2; |
||||||
|
|
||||||
|
private int _type, _hardLinkCount; |
||||||
|
private long _size; |
||||||
|
private String _rawListing, _user, _group, _name, _link; |
||||||
|
private Calendar _date; |
||||||
|
// If this is null, then list entry parsing failed
|
||||||
|
private final boolean[] _permissions[]; // e.g. _permissions[USER_ACCESS][READ_PERMISSION]
|
||||||
|
|
||||||
|
/*** Creates an empty FTPFile. ***/ |
||||||
|
public FTPFile() |
||||||
|
{ |
||||||
|
_permissions = new boolean[3][3]; |
||||||
|
_type = UNKNOWN_TYPE; |
||||||
|
// init these to values that do not occur in listings
|
||||||
|
// so can distinguish which fields are unset
|
||||||
|
_hardLinkCount = 0; // 0 is invalid as a link count
|
||||||
|
_size = -1; // 0 is valid, so use -1
|
||||||
|
_user = ""; |
||||||
|
_group = ""; |
||||||
|
_date = null; |
||||||
|
_name = null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor for use by {@link FTPListParseEngine} only. |
||||||
|
* Used to create FTPFile entries for failed parses |
||||||
|
* @param rawListing line that could not be parsed. |
||||||
|
* @since 3.4 |
||||||
|
*/ |
||||||
|
FTPFile(String rawListing) |
||||||
|
{ |
||||||
|
_permissions = null; // flag that entry is invalid
|
||||||
|
_rawListing = rawListing; |
||||||
|
_type = UNKNOWN_TYPE; |
||||||
|
// init these to values that do not occur in listings
|
||||||
|
// so can distinguish which fields are unset
|
||||||
|
_hardLinkCount = 0; // 0 is invalid as a link count
|
||||||
|
_size = -1; // 0 is valid, so use -1
|
||||||
|
_user = ""; |
||||||
|
_group = ""; |
||||||
|
_date = null; |
||||||
|
_name = null; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*** |
||||||
|
* Set the original FTP server raw listing from which the FTPFile was |
||||||
|
* created. |
||||||
|
* |
||||||
|
* @param rawListing The raw FTP server listing. |
||||||
|
***/ |
||||||
|
public void setRawListing(String rawListing) |
||||||
|
{ |
||||||
|
_rawListing = rawListing; |
||||||
|
} |
||||||
|
|
||||||
|
/*** |
||||||
|
* Get the original FTP server raw listing used to initialize the FTPFile. |
||||||
|
* |
||||||
|
* @return The original FTP server raw listing used to initialize the |
||||||
|
* FTPFile. |
||||||
|
***/ |
||||||
|
public String getRawListing() |
||||||
|
{ |
||||||
|
return _rawListing; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*** |
||||||
|
* Determine if the file is a directory. |
||||||
|
* |
||||||
|
* @return True if the file is of type <code>DIRECTORY_TYPE</code>, false if |
||||||
|
* not. |
||||||
|
***/ |
||||||
|
public boolean isDirectory() |
||||||
|
{ |
||||||
|
return (_type == DIRECTORY_TYPE); |
||||||
|
} |
||||||
|
|
||||||
|
/*** |
||||||
|
* Determine if the file is a regular file. |
||||||
|
* |
||||||
|
* @return True if the file is of type <code>FILE_TYPE</code>, false if |
||||||
|
* not. |
||||||
|
***/ |
||||||
|
public boolean isFile() |
||||||
|
{ |
||||||
|
return (_type == FILE_TYPE); |
||||||
|
} |
||||||
|
|
||||||
|
/*** |
||||||
|
* Determine if the file is a symbolic link. |
||||||
|
* |
||||||
|
* @return True if the file is of type <code>UNKNOWN_TYPE</code>, false if |
||||||
|
* not. |
||||||
|
***/ |
||||||
|
public boolean isSymbolicLink() |
||||||
|
{ |
||||||
|
return (_type == SYMBOLIC_LINK_TYPE); |
||||||
|
} |
||||||
|
|
||||||
|
/*** |
||||||
|
* Determine if the type of the file is unknown. |
||||||
|
* |
||||||
|
* @return True if the file is of type <code>UNKNOWN_TYPE</code>, false if |
||||||
|
* not. |
||||||
|
***/ |
||||||
|
public boolean isUnknown() |
||||||
|
{ |
||||||
|
return (_type == UNKNOWN_TYPE); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Used to indicate whether an entry is valid or not. |
||||||
|
* If the entry is invalid, only the {@link #getRawListing()} method will be useful. |
||||||
|
* Other methods may fail. |
||||||
|
* |
||||||
|
* Used in conjunction with list parsing that preseverves entries that failed to parse. |
||||||
|
* @see FTPClientConfig#setUnparseableEntries(boolean) |
||||||
|
* @return true if the entry is valid |
||||||
|
* @since 3.4 |
||||||
|
*/ |
||||||
|
public boolean isValid() { |
||||||
|
return (_permissions != null); |
||||||
|
} |
||||||
|
|
||||||
|
/*** |
||||||
|
* Set the type of the file (<code>DIRECTORY_TYPE</code>, |
||||||
|
* <code>FILE_TYPE</code>, etc.). |
||||||
|
* |
||||||
|
* @param type The integer code representing the type of the file. |
||||||
|
***/ |
||||||
|
public void setType(int type) |
||||||
|
{ |
||||||
|
_type = type; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*** |
||||||
|
* Return the type of the file (one of the <code>_TYPE</code> constants), |
||||||
|
* e.g., if it is a directory, a regular file, or a symbolic link. |
||||||
|
* |
||||||
|
* @return The type of the file. |
||||||
|
***/ |
||||||
|
public int getType() |
||||||
|
{ |
||||||
|
return _type; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*** |
||||||
|
* Set the name of the file. |
||||||
|
* |
||||||
|
* @param name The name of the file. |
||||||
|
***/ |
||||||
|
public void setName(String name) |
||||||
|
{ |
||||||
|
_name = name; |
||||||
|
} |
||||||
|
|
||||||
|
/*** |
||||||
|
* Return the name of the file. |
||||||
|
* |
||||||
|
* @return The name of the file. |
||||||
|
***/ |
||||||
|
public String getName() |
||||||
|
{ |
||||||
|
return _name; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Set the file size in bytes. |
||||||
|
* @param size The file size in bytes. |
||||||
|
*/ |
||||||
|
public void setSize(long size) |
||||||
|
{ |
||||||
|
_size = size; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*** |
||||||
|
* Return the file size in bytes. |
||||||
|
* |
||||||
|
* @return The file size in bytes. |
||||||
|
***/ |
||||||
|
public long getSize() |
||||||
|
{ |
||||||
|
return _size; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*** |
||||||
|
* Set the number of hard links to this file. This is not to be |
||||||
|
* confused with symbolic links. |
||||||
|
* |
||||||
|
* @param links The number of hard links to this file. |
||||||
|
***/ |
||||||
|
public void setHardLinkCount(int links) |
||||||
|
{ |
||||||
|
_hardLinkCount = links; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*** |
||||||
|
* Return the number of hard links to this file. This is not to be |
||||||
|
* confused with symbolic links. |
||||||
|
* |
||||||
|
* @return The number of hard links to this file. |
||||||
|
***/ |
||||||
|
public int getHardLinkCount() |
||||||
|
{ |
||||||
|
return _hardLinkCount; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*** |
||||||
|
* Set the name of the group owning the file. This may be |
||||||
|
* a string representation of the group number. |
||||||
|
* |
||||||
|
* @param group The name of the group owning the file. |
||||||
|
***/ |
||||||
|
public void setGroup(String group) |
||||||
|
{ |
||||||
|
_group = group; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*** |
||||||
|
* Returns the name of the group owning the file. Sometimes this will be |
||||||
|
* a string representation of the group number. |
||||||
|
* |
||||||
|
* @return The name of the group owning the file. |
||||||
|
***/ |
||||||
|
public String getGroup() |
||||||
|
{ |
||||||
|
return _group; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*** |
||||||
|
* Set the name of the user owning the file. This may be |
||||||
|
* a string representation of the user number; |
||||||
|
* |
||||||
|
* @param user The name of the user owning the file. |
||||||
|
***/ |
||||||
|
public void setUser(String user) |
||||||
|
{ |
||||||
|
_user = user; |
||||||
|
} |
||||||
|
|
||||||
|
/*** |
||||||
|
* Returns the name of the user owning the file. Sometimes this will be |
||||||
|
* a string representation of the user number. |
||||||
|
* |
||||||
|
* @return The name of the user owning the file. |
||||||
|
***/ |
||||||
|
public String getUser() |
||||||
|
{ |
||||||
|
return _user; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*** |
||||||
|
* If the FTPFile is a symbolic link, use this method to set the name of the |
||||||
|
* file being pointed to by the symbolic link. |
||||||
|
* |
||||||
|
* @param link The file pointed to by the symbolic link. |
||||||
|
***/ |
||||||
|
public void setLink(String link) |
||||||
|
{ |
||||||
|
_link = link; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*** |
||||||
|
* If the FTPFile is a symbolic link, this method returns the name of the |
||||||
|
* file being pointed to by the symbolic link. Otherwise it returns null. |
||||||
|
* |
||||||
|
* @return The file pointed to by the symbolic link (null if the FTPFile |
||||||
|
* is not a symbolic link). |
||||||
|
***/ |
||||||
|
public String getLink() |
||||||
|
{ |
||||||
|
return _link; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*** |
||||||
|
* Set the file timestamp. This usually the last modification time. |
||||||
|
* The parameter is not cloned, so do not alter its value after calling |
||||||
|
* this method. |
||||||
|
* |
||||||
|
* @param date A Calendar instance representing the file timestamp. |
||||||
|
***/ |
||||||
|
public void setTimestamp(Calendar date) |
||||||
|
{ |
||||||
|
_date = date; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*** |
||||||
|
* Returns the file timestamp. This usually the last modification time. |
||||||
|
* |
||||||
|
* @return A Calendar instance representing the file timestamp. |
||||||
|
***/ |
||||||
|
public Calendar getTimestamp() |
||||||
|
{ |
||||||
|
return _date; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*** |
||||||
|
* Set if the given access group (one of the <code> _ACCESS </code> |
||||||
|
* constants) has the given access permission (one of the |
||||||
|
* <code> _PERMISSION </code> constants) to the file. |
||||||
|
* |
||||||
|
* @param access The access group (one of the <code> _ACCESS </code> |
||||||
|
* constants) |
||||||
|
* @param permission The access permission (one of the |
||||||
|
* <code> _PERMISSION </code> constants) |
||||||
|
* @param value True if permission is allowed, false if not. |
||||||
|
* @throws ArrayIndexOutOfBoundsException if either of the parameters is out of range |
||||||
|
***/ |
||||||
|
public void setPermission(int access, int permission, boolean value) |
||||||
|
{ |
||||||
|
_permissions[access][permission] = value; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*** |
||||||
|
* Determines if the given access group (one of the <code> _ACCESS </code> |
||||||
|
* constants) has the given access permission (one of the |
||||||
|
* <code> _PERMISSION </code> constants) to the file. |
||||||
|
* |
||||||
|
* @param access The access group (one of the <code> _ACCESS </code> |
||||||
|
* constants) |
||||||
|
* @param permission The access permission (one of the |
||||||
|
* <code> _PERMISSION </code> constants) |
||||||
|
* @throws ArrayIndexOutOfBoundsException if either of the parameters is out of range |
||||||
|
* @return true if {@link #isValid()} is {@code true &&} the associated permission is set; |
||||||
|
* {@code false} otherwise. |
||||||
|
***/ |
||||||
|
public boolean hasPermission(int access, int permission) |
||||||
|
{ |
||||||
|
if (_permissions == null) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
return _permissions[access][permission]; |
||||||
|
} |
||||||
|
|
||||||
|
/*** |
||||||
|
* Returns a string representation of the FTPFile information. |
||||||
|
* |
||||||
|
* @return A string representation of the FTPFile information. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public String toString() |
||||||
|
{ |
||||||
|
return getRawListing(); |
||||||
|
} |
||||||
|
|
||||||
|
/*** |
||||||
|
* Returns a string representation of the FTPFile information. |
||||||
|
* This currently mimics the Unix listing format. |
||||||
|
* This method uses the timezone of the Calendar entry, which is |
||||||
|
* the server time zone (if one was provided) otherwise it is |
||||||
|
* the local time zone. |
||||||
|
* <p> |
||||||
|
* Note: if the instance is not valid {@link #isValid()}, no useful |
||||||
|
* information can be returned. In this case, use {@link #getRawListing()} |
||||||
|
* instead. |
||||||
|
* |
||||||
|
* @return A string representation of the FTPFile information. |
||||||
|
* @since 3.0 |
||||||
|
*/ |
||||||
|
public String toFormattedString() |
||||||
|
{ |
||||||
|
return toFormattedString(null); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a string representation of the FTPFile information. |
||||||
|
* This currently mimics the Unix listing format. |
||||||
|
* This method allows the Calendar time zone to be overridden. |
||||||
|
* <p> |
||||||
|
* Note: if the instance is not valid {@link #isValid()}, no useful |
||||||
|
* information can be returned. In this case, use {@link #getRawListing()} |
||||||
|
* instead. |
||||||
|
* @param timezone the timezone to use for displaying the time stamp |
||||||
|
* If {@code null}, then use the Calendar entry timezone |
||||||
|
* @return A string representation of the FTPFile information. |
||||||
|
* @since 3.4 |
||||||
|
*/ |
||||||
|
public String toFormattedString(final String timezone) |
||||||
|
{ |
||||||
|
|
||||||
|
if (!isValid()) { |
||||||
|
return "[Invalid: could not parse file entry]"; |
||||||
|
} |
||||||
|
StringBuilder sb = new StringBuilder(); |
||||||
|
Formatter fmt = new Formatter(sb); |
||||||
|
sb.append(formatType()); |
||||||
|
sb.append(permissionToString(USER_ACCESS)); |
||||||
|
sb.append(permissionToString(GROUP_ACCESS)); |
||||||
|
sb.append(permissionToString(WORLD_ACCESS)); |
||||||
|
fmt.format(" %4d", Integer.valueOf(getHardLinkCount())); |
||||||
|
fmt.format(" %-8s %-8s", getUser(), getGroup()); |
||||||
|
fmt.format(" %8d", Long.valueOf(getSize())); |
||||||
|
Calendar timestamp = getTimestamp(); |
||||||
|
if (timestamp != null) { |
||||||
|
if (timezone != null) { |
||||||
|
TimeZone newZone = TimeZone.getTimeZone(timezone); |
||||||
|
if (!newZone.equals(timestamp.getTimeZone())){ |
||||||
|
Date original = timestamp.getTime(); |
||||||
|
Calendar newStamp = Calendar.getInstance(newZone); |
||||||
|
newStamp.setTime(original); |
||||||
|
timestamp = newStamp; |
||||||
|
} |
||||||
|
} |
||||||
|
fmt.format(" %1$tY-%1$tm-%1$td", timestamp); |
||||||
|
// Only display time units if they are present
|
||||||
|
if (timestamp.isSet(Calendar.HOUR_OF_DAY)) { |
||||||
|
fmt.format(" %1$tH", timestamp); |
||||||
|
if (timestamp.isSet(Calendar.MINUTE)) { |
||||||
|
fmt.format(":%1$tM", timestamp); |
||||||
|
if (timestamp.isSet(Calendar.SECOND)) { |
||||||
|
fmt.format(":%1$tS", timestamp); |
||||||
|
if (timestamp.isSet(Calendar.MILLISECOND)) { |
||||||
|
fmt.format(".%1$tL", timestamp); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
fmt.format(" %1$tZ", timestamp); |
||||||
|
} |
||||||
|
} |
||||||
|
sb.append(' '); |
||||||
|
sb.append(getName()); |
||||||
|
fmt.close(); |
||||||
|
return sb.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
private char formatType(){ |
||||||
|
switch(_type) { |
||||||
|
case FILE_TYPE: |
||||||
|
return '-'; |
||||||
|
case DIRECTORY_TYPE: |
||||||
|
return 'd'; |
||||||
|
case SYMBOLIC_LINK_TYPE: |
||||||
|
return 'l'; |
||||||
|
default: |
||||||
|
return '?'; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private String permissionToString(int access ){ |
||||||
|
StringBuilder sb = new StringBuilder(); |
||||||
|
if (hasPermission(access, READ_PERMISSION)) { |
||||||
|
sb.append('r'); |
||||||
|
} else { |
||||||
|
sb.append('-'); |
||||||
|
} |
||||||
|
if (hasPermission(access, WRITE_PERMISSION)) { |
||||||
|
sb.append('w'); |
||||||
|
} else { |
||||||
|
sb.append('-'); |
||||||
|
} |
||||||
|
if (hasPermission(access, EXECUTE_PERMISSION)) { |
||||||
|
sb.append('x'); |
||||||
|
} else { |
||||||
|
sb.append('-'); |
||||||
|
} |
||||||
|
return sb.toString(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,134 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.apache.commons.net.ftp; |
||||||
|
import java.io.BufferedReader; |
||||||
|
import java.io.IOException; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* FTPFileEntryParser defines the interface for parsing a single FTP file |
||||||
|
* listing and converting that information into an |
||||||
|
* {@link FTPFile} instance. |
||||||
|
* Sometimes you will want to parse unusual listing formats, in which |
||||||
|
* case you would create your own implementation of FTPFileEntryParser and |
||||||
|
* if necessary, subclass FTPFile. |
||||||
|
* <p> |
||||||
|
* Here are some examples showing how to use one of the classes that |
||||||
|
* implement this interface. |
||||||
|
* <p> |
||||||
|
* |
||||||
|
* The first example uses the <code>FTPClient.listFiles()</code> |
||||||
|
* API to pull the whole list from the subfolder <code>subfolder</code> in |
||||||
|
* one call, attempting to automatically detect the parser type. This |
||||||
|
* method, without a parserKey parameter, indicates that autodection should |
||||||
|
* be used. |
||||||
|
* |
||||||
|
* <pre> |
||||||
|
* FTPClient f=FTPClient(); |
||||||
|
* f.connect(server); |
||||||
|
* f.login(username, password); |
||||||
|
* FTPFile[] files = f.listFiles("subfolder"); |
||||||
|
* </pre> |
||||||
|
* |
||||||
|
* The second example uses the <code>FTPClient.listFiles()</code> |
||||||
|
* API to pull the whole list from the current working directory in one call, |
||||||
|
* but specifying by classname the parser to be used. For this particular |
||||||
|
* parser class, this approach is necessary since there is no way to |
||||||
|
* autodetect this server type. |
||||||
|
* |
||||||
|
* <pre> |
||||||
|
* FTPClient f=FTPClient(); |
||||||
|
* f.connect(server); |
||||||
|
* f.login(username, password); |
||||||
|
* FTPFile[] files = f.listFiles( |
||||||
|
* "org.apache.commons.net.ftp.parser.EnterpriseUnixFTPFileEntryParser", |
||||||
|
* "."); |
||||||
|
* </pre> |
||||||
|
* |
||||||
|
* The third example uses the <code>FTPClient.listFiles()</code> |
||||||
|
* API to pull a single file listing in an arbitrary directory in one call, |
||||||
|
* specifying by KEY the parser to be used, in this case, VMS. |
||||||
|
* |
||||||
|
* <pre> |
||||||
|
* FTPClient f=FTPClient(); |
||||||
|
* f.connect(server); |
||||||
|
* f.login(username, password); |
||||||
|
* FTPFile[] files = f.listFiles("VMS", "subfolder/foo.java"); |
||||||
|
* </pre> |
||||||
|
* |
||||||
|
* For an alternative approach, see the {@link FTPListParseEngine} class
|
||||||
|
* which provides iterative access. |
||||||
|
* |
||||||
|
* @version $Id: FTPFileEntryParser.java 1747119 2016-06-07 02:22:24Z ggregory $ |
||||||
|
* @see FTPFile |
||||||
|
* @see FTPClient#listFiles() |
||||||
|
*/ |
||||||
|
public interface FTPFileEntryParser |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Parses a line of an FTP server file listing and converts it into a usable |
||||||
|
* format in the form of an <code> FTPFile </code> instance. If the |
||||||
|
* file listing line doesn't describe a file, <code> null </code> should be |
||||||
|
* returned, otherwise a <code> FTPFile </code> instance representing the |
||||||
|
* files in the directory is returned. |
||||||
|
* |
||||||
|
* @param listEntry A line of text from the file listing |
||||||
|
* @return An FTPFile instance corresponding to the supplied entry |
||||||
|
*/ |
||||||
|
FTPFile parseFTPEntry(String listEntry); |
||||||
|
|
||||||
|
/** |
||||||
|
* Reads the next entry using the supplied BufferedReader object up to |
||||||
|
* whatever delemits one entry from the next. Implementors must define |
||||||
|
* this for the particular ftp system being parsed. In many but not all |
||||||
|
* cases, this can be defined simply by calling BufferedReader.readLine(). |
||||||
|
* |
||||||
|
* @param reader The BufferedReader object from which entries are to be |
||||||
|
* read. |
||||||
|
* |
||||||
|
* @return A string representing the next ftp entry or null if none found. |
||||||
|
* @throws IOException thrown on any IO Error reading from the reader. |
||||||
|
*/ |
||||||
|
String readNextEntry(BufferedReader reader) throws IOException; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* This method is a hook for those implementors (such as |
||||||
|
* VMSVersioningFTPEntryParser, and possibly others) which need to |
||||||
|
* perform some action upon the FTPFileList after it has been created |
||||||
|
* from the server stream, but before any clients see the list. |
||||||
|
* |
||||||
|
* The default implementation can be a no-op. |
||||||
|
* |
||||||
|
* @param original Original list after it has been created from the server stream |
||||||
|
* |
||||||
|
* @return Original list as processed by this method. |
||||||
|
*/ |
||||||
|
List<String> preParse(List<String> original); |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/* Emacs configuration |
||||||
|
* Local variables: ** |
||||||
|
* mode: java ** |
||||||
|
* c-basic-offset: 4 ** |
||||||
|
* indent-tabs-mode: nil ** |
||||||
|
* End: ** |
||||||
|
*/ |
@ -0,0 +1,79 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.apache.commons.net.ftp; |
||||||
|
import java.io.BufferedReader; |
||||||
|
import java.io.IOException; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* This abstract class implements both the older FTPFileListParser and |
||||||
|
* newer FTPFileEntryParser interfaces with default functionality. |
||||||
|
* All the classes in the parser subpackage inherit from this. |
||||||
|
* |
||||||
|
*/ |
||||||
|
public abstract class FTPFileEntryParserImpl |
||||||
|
implements FTPFileEntryParser |
||||||
|
{ |
||||||
|
/** |
||||||
|
* The constructor for a FTPFileEntryParserImpl object. |
||||||
|
*/ |
||||||
|
public FTPFileEntryParserImpl() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Reads the next entry using the supplied BufferedReader object up to |
||||||
|
* whatever delimits one entry from the next. This default implementation |
||||||
|
* simply calls BufferedReader.readLine(). |
||||||
|
* |
||||||
|
* @param reader The BufferedReader object from which entries are to be |
||||||
|
* read. |
||||||
|
* |
||||||
|
* @return A string representing the next ftp entry or null if none found. |
||||||
|
* @throws IOException thrown on any IO Error reading from the reader. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public String readNextEntry(BufferedReader reader) throws IOException |
||||||
|
{ |
||||||
|
return reader.readLine(); |
||||||
|
} |
||||||
|
/** |
||||||
|
* This method is a hook for those implementors (such as |
||||||
|
* VMSVersioningFTPEntryParser, and possibly others) which need to |
||||||
|
* perform some action upon the FTPFileList after it has been created |
||||||
|
* from the server stream, but before any clients see the list. |
||||||
|
* |
||||||
|
* This default implementation does nothing. |
||||||
|
* |
||||||
|
* @param original Original list after it has been created from the server stream |
||||||
|
* |
||||||
|
* @return <code>original</code> unmodified. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public List<String> preParse(List<String> original) { |
||||||
|
return original; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* Emacs configuration |
||||||
|
* Local variables: ** |
||||||
|
* mode: java ** |
||||||
|
* c-basic-offset: 4 ** |
||||||
|
* indent-tabs-mode: nil ** |
||||||
|
* End: ** |
||||||
|
*/ |
@ -0,0 +1,33 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.apache.commons.net.ftp; |
||||||
|
|
||||||
|
/** |
||||||
|
* Perform filtering on FTPFile entries. |
||||||
|
* @since 2.2 |
||||||
|
*/ |
||||||
|
public interface FTPFileFilter { |
||||||
|
/** |
||||||
|
* Checks if an FTPFile entry should be included or not. |
||||||
|
* |
||||||
|
* @param file entry to be checked for inclusion. May be <code>null</code>. |
||||||
|
* @return <code>true</code> if the file is to be included, <code>false</code> otherwise |
||||||
|
*/ |
||||||
|
public boolean accept(FTPFile file); |
||||||
|
} |
@ -0,0 +1,57 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.apache.commons.net.ftp; |
||||||
|
|
||||||
|
/** |
||||||
|
* Implements some simple FTPFileFilter classes. |
||||||
|
* @since 2.2 |
||||||
|
*/ |
||||||
|
public class FTPFileFilters { |
||||||
|
|
||||||
|
/** |
||||||
|
* Accepts all FTPFile entries, including null. |
||||||
|
*/ |
||||||
|
public static final FTPFileFilter ALL = new FTPFileFilter() { |
||||||
|
@Override |
||||||
|
public boolean accept(FTPFile file) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
/** |
||||||
|
* Accepts all non-null FTPFile entries. |
||||||
|
*/ |
||||||
|
public static final FTPFileFilter NON_NULL = new FTPFileFilter() { |
||||||
|
@Override |
||||||
|
public boolean accept(FTPFile file) { |
||||||
|
return file != null; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
/** |
||||||
|
* Accepts all (non-null) FTPFile directory entries. |
||||||
|
*/ |
||||||
|
public static final FTPFileFilter DIRECTORIES = new FTPFileFilter() { |
||||||
|
@Override |
||||||
|
public boolean accept(FTPFile file) { |
||||||
|
return file != null && file.isDirectory(); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,202 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.apache.commons.net.ftp; |
||||||
|
|
||||||
|
import java.io.BufferedReader; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.InputStream; |
||||||
|
import java.io.InputStreamReader; |
||||||
|
import java.io.OutputStream; |
||||||
|
import java.io.Reader; |
||||||
|
import java.io.UnsupportedEncodingException; |
||||||
|
import java.net.Inet6Address; |
||||||
|
import java.net.Socket; |
||||||
|
import java.net.SocketException; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import org.apache.commons.net.util.Base64; |
||||||
|
|
||||||
|
/** |
||||||
|
* Experimental attempt at FTP client that tunnels over an HTTP proxy connection. |
||||||
|
* |
||||||
|
* @since 2.2 |
||||||
|
*/ |
||||||
|
public class FTPHTTPClient extends FTPClient { |
||||||
|
private final String proxyHost; |
||||||
|
private final int proxyPort; |
||||||
|
private final String proxyUsername; |
||||||
|
private final String proxyPassword; |
||||||
|
|
||||||
|
private static final byte[] CRLF={'\r', '\n'}; |
||||||
|
private final Base64 base64 = new Base64(); |
||||||
|
|
||||||
|
private String tunnelHost; // Save the host when setting up a tunnel (needed for EPSV)
|
||||||
|
|
||||||
|
public FTPHTTPClient(String proxyHost, int proxyPort, String proxyUser, String proxyPass) { |
||||||
|
this.proxyHost = proxyHost; |
||||||
|
this.proxyPort = proxyPort; |
||||||
|
this.proxyUsername = proxyUser; |
||||||
|
this.proxyPassword = proxyPass; |
||||||
|
this.tunnelHost = null; |
||||||
|
} |
||||||
|
|
||||||
|
public FTPHTTPClient(String proxyHost, int proxyPort) { |
||||||
|
this(proxyHost, proxyPort, null, null); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritDoc} |
||||||
|
* |
||||||
|
* @throws IllegalStateException if connection mode is not passive |
||||||
|
* @deprecated (3.3) Use {@link FTPClient#_openDataConnection_(FTPCmd, String)} instead |
||||||
|
*/ |
||||||
|
// Kept to maintain binary compatibility
|
||||||
|
// Not strictly necessary, but Clirr complains even though there is a super-impl
|
||||||
|
@Override |
||||||
|
@Deprecated |
||||||
|
protected Socket _openDataConnection_(int command, String arg) |
||||||
|
throws IOException { |
||||||
|
return super._openDataConnection_(command, arg); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritDoc} |
||||||
|
* |
||||||
|
* @throws IllegalStateException if connection mode is not passive |
||||||
|
* @since 3.1 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
protected Socket _openDataConnection_(String command, String arg) |
||||||
|
throws IOException { |
||||||
|
//Force local passive mode, active mode not supported by through proxy
|
||||||
|
if (getDataConnectionMode() != PASSIVE_LOCAL_DATA_CONNECTION_MODE) { |
||||||
|
throw new IllegalStateException("Only passive connection mode supported"); |
||||||
|
} |
||||||
|
|
||||||
|
final boolean isInet6Address = getRemoteAddress() instanceof Inet6Address; |
||||||
|
String passiveHost = null; |
||||||
|
|
||||||
|
boolean attemptEPSV = isUseEPSVwithIPv4() || isInet6Address; |
||||||
|
if (attemptEPSV && epsv() == FTPReply.ENTERING_EPSV_MODE) { |
||||||
|
_parseExtendedPassiveModeReply(_replyLines.get(0)); |
||||||
|
passiveHost = this.tunnelHost; |
||||||
|
} else { |
||||||
|
if (isInet6Address) { |
||||||
|
return null; // Must use EPSV for IPV6
|
||||||
|
} |
||||||
|
// If EPSV failed on IPV4, revert to PASV
|
||||||
|
if (pasv() != FTPReply.ENTERING_PASSIVE_MODE) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
_parsePassiveModeReply(_replyLines.get(0)); |
||||||
|
passiveHost = this.getPassiveHost(); |
||||||
|
} |
||||||
|
|
||||||
|
Socket socket = _socketFactory_.createSocket(proxyHost, proxyPort); |
||||||
|
InputStream is = socket.getInputStream(); |
||||||
|
OutputStream os = socket.getOutputStream(); |
||||||
|
tunnelHandshake(passiveHost, this.getPassivePort(), is, os); |
||||||
|
if ((getRestartOffset() > 0) && !restart(getRestartOffset())) { |
||||||
|
socket.close(); |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
if (!FTPReply.isPositivePreliminary(sendCommand(command, arg))) { |
||||||
|
socket.close(); |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
return socket; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void connect(String host, int port) throws SocketException, IOException { |
||||||
|
|
||||||
|
_socket_ = _socketFactory_.createSocket(proxyHost, proxyPort); |
||||||
|
_input_ = _socket_.getInputStream(); |
||||||
|
_output_ = _socket_.getOutputStream(); |
||||||
|
Reader socketIsReader; |
||||||
|
try { |
||||||
|
socketIsReader = tunnelHandshake(host, port, _input_, _output_); |
||||||
|
} |
||||||
|
catch (Exception e) { |
||||||
|
IOException ioe = new IOException("Could not connect to " + host+ " using port " + port); |
||||||
|
ioe.initCause(e); |
||||||
|
throw ioe; |
||||||
|
} |
||||||
|
super._connectAction_(socketIsReader); |
||||||
|
} |
||||||
|
|
||||||
|
private BufferedReader tunnelHandshake(String host, int port, InputStream input, OutputStream output) throws IOException, |
||||||
|
UnsupportedEncodingException { |
||||||
|
final String connectString = "CONNECT " + host + ":" + port + " HTTP/1.1"; |
||||||
|
final String hostString = "Host: " + host + ":" + port; |
||||||
|
|
||||||
|
this.tunnelHost = host; |
||||||
|
output.write(connectString.getBytes("UTF-8")); // TODO what is the correct encoding?
|
||||||
|
output.write(CRLF); |
||||||
|
output.write(hostString.getBytes("UTF-8")); |
||||||
|
output.write(CRLF); |
||||||
|
|
||||||
|
if (proxyUsername != null && proxyPassword != null) { |
||||||
|
final String auth = proxyUsername + ":" + proxyPassword; |
||||||
|
final String header = "Proxy-Authorization: Basic " |
||||||
|
+ base64.encodeToString(auth.getBytes("UTF-8")); |
||||||
|
output.write(header.getBytes("UTF-8")); |
||||||
|
} |
||||||
|
output.write(CRLF); |
||||||
|
|
||||||
|
List<String> response = new ArrayList<String>(); |
||||||
|
BufferedReader reader = new BufferedReader( |
||||||
|
new InputStreamReader(input, getCharset())); |
||||||
|
|
||||||
|
for (String line = reader.readLine(); line != null |
||||||
|
&& line.length() > 0; line = reader.readLine()) { |
||||||
|
response.add(line); |
||||||
|
} |
||||||
|
|
||||||
|
int size = response.size(); |
||||||
|
if (size == 0) { |
||||||
|
throw new IOException("No response from proxy"); |
||||||
|
} |
||||||
|
|
||||||
|
String code = null; |
||||||
|
String resp = response.get(0); |
||||||
|
if (resp.startsWith("HTTP/") && resp.length() >= 12) { |
||||||
|
code = resp.substring(9, 12); |
||||||
|
} else { |
||||||
|
throw new IOException("Invalid response from proxy: " + resp); |
||||||
|
} |
||||||
|
|
||||||
|
if (!"200".equals(code)) { |
||||||
|
StringBuilder msg = new StringBuilder(); |
||||||
|
msg.append("HTTPTunnelConnector: connection failed\r\n"); |
||||||
|
msg.append("Response received from the proxy:\r\n"); |
||||||
|
for (String line : response) { |
||||||
|
msg.append(line); |
||||||
|
msg.append("\r\n"); |
||||||
|
} |
||||||
|
throw new IOException(msg.toString()); |
||||||
|
} |
||||||
|
return reader; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
@ -0,0 +1,329 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.apache.commons.net.ftp; |
||||||
|
|
||||||
|
import java.io.BufferedReader; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.InputStream; |
||||||
|
import java.io.InputStreamReader; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Iterator; |
||||||
|
import java.util.LinkedList; |
||||||
|
import java.util.List; |
||||||
|
import java.util.ListIterator; |
||||||
|
|
||||||
|
import org.apache.commons.net.util.Charsets; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* This class handles the entire process of parsing a listing of |
||||||
|
* file entries from the server. |
||||||
|
* <p> |
||||||
|
* This object defines a two-part parsing mechanism. |
||||||
|
* <p> |
||||||
|
* The first part is comprised of reading the raw input into an internal |
||||||
|
* list of strings. Every item in this list corresponds to an actual |
||||||
|
* file. All extraneous matter emitted by the server will have been |
||||||
|
* removed by the end of this phase. This is accomplished in conjunction |
||||||
|
* with the FTPFileEntryParser associated with this engine, by calling |
||||||
|
* its methods <code>readNextEntry()</code> - which handles the issue of |
||||||
|
* what delimits one entry from another, usually but not always a line |
||||||
|
* feed and <code>preParse()</code> - which handles removal of |
||||||
|
* extraneous matter such as the preliminary lines of a listing, removal |
||||||
|
* of duplicates on versioning systems, etc. |
||||||
|
* <p> |
||||||
|
* The second part is composed of the actual parsing, again in conjunction |
||||||
|
* with the particular parser used by this engine. This is controlled |
||||||
|
* by an iterator over the internal list of strings. This may be done |
||||||
|
* either in block mode, by calling the <code>getNext()</code> and |
||||||
|
* <code>getPrevious()</code> methods to provide "paged" output of less |
||||||
|
* than the whole list at one time, or by calling the |
||||||
|
* <code>getFiles()</code> method to return the entire list. |
||||||
|
* <p> |
||||||
|
* Examples: |
||||||
|
* <p> |
||||||
|
* Paged access: |
||||||
|
* <pre> |
||||||
|
* FTPClient f=FTPClient(); |
||||||
|
* f.connect(server); |
||||||
|
* f.login(username, password); |
||||||
|
* FTPListParseEngine engine = f.initiateListParsing(directory); |
||||||
|
* |
||||||
|
* while (engine.hasNext()) { |
||||||
|
* FTPFile[] files = engine.getNext(25); // "page size" you want
|
||||||
|
* //do whatever you want with these files, display them, etc.
|
||||||
|
* //expensive FTPFile objects not created until needed.
|
||||||
|
* } |
||||||
|
* </pre> |
||||||
|
* <p> |
||||||
|
* For unpaged access, simply use FTPClient.listFiles(). That method |
||||||
|
* uses this class transparently. |
||||||
|
* @version $Id: FTPListParseEngine.java 1747119 2016-06-07 02:22:24Z ggregory $ |
||||||
|
*/ |
||||||
|
public class FTPListParseEngine { |
||||||
|
private List<String> entries = new LinkedList<String>(); |
||||||
|
private ListIterator<String> _internalIterator = entries.listIterator(); |
||||||
|
|
||||||
|
private final FTPFileEntryParser parser; |
||||||
|
// Should invalid files (parse failures) be allowed?
|
||||||
|
private final boolean saveUnparseableEntries; |
||||||
|
|
||||||
|
public FTPListParseEngine(FTPFileEntryParser parser) { |
||||||
|
this(parser, null); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Intended for use by FTPClient only |
||||||
|
* @since 3.4 |
||||||
|
*/ |
||||||
|
FTPListParseEngine(FTPFileEntryParser parser, FTPClientConfig configuration) { |
||||||
|
this.parser = parser; |
||||||
|
if (configuration != null) { |
||||||
|
this.saveUnparseableEntries = configuration.getUnparseableEntries(); |
||||||
|
} else { |
||||||
|
this.saveUnparseableEntries = false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* handle the initial reading and preparsing of the list returned by |
||||||
|
* the server. After this method has completed, this object will contain |
||||||
|
* a list of unparsed entries (Strings) each referring to a unique file |
||||||
|
* on the server. |
||||||
|
* |
||||||
|
* @param stream input stream provided by the server socket. |
||||||
|
* @param encoding the encoding to be used for reading the stream |
||||||
|
* |
||||||
|
* @throws IOException |
||||||
|
* thrown on any failure to read from the sever. |
||||||
|
*/ |
||||||
|
public void readServerList(InputStream stream, String encoding) |
||||||
|
throws IOException |
||||||
|
{ |
||||||
|
this.entries = new LinkedList<String>(); |
||||||
|
readStream(stream, encoding); |
||||||
|
this.parser.preParse(this.entries); |
||||||
|
resetIterator(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Internal method for reading the input into the <code>entries</code> list. |
||||||
|
* After this method has completed, <code>entries</code> will contain a |
||||||
|
* collection of entries (as defined by |
||||||
|
* <code>FTPFileEntryParser.readNextEntry()</code>), but this may contain |
||||||
|
* various non-entry preliminary lines from the server output, duplicates, |
||||||
|
* and other data that will not be part of the final listing. |
||||||
|
* |
||||||
|
* @param stream The socket stream on which the input will be read. |
||||||
|
* @param encoding The encoding to use. |
||||||
|
* |
||||||
|
* @throws IOException |
||||||
|
* thrown on any failure to read the stream |
||||||
|
*/ |
||||||
|
private void readStream(InputStream stream, String encoding) throws IOException |
||||||
|
{ |
||||||
|
BufferedReader reader = new BufferedReader( |
||||||
|
new InputStreamReader(stream, Charsets.toCharset(encoding))); |
||||||
|
|
||||||
|
String line = this.parser.readNextEntry(reader); |
||||||
|
|
||||||
|
while (line != null) |
||||||
|
{ |
||||||
|
this.entries.add(line); |
||||||
|
line = this.parser.readNextEntry(reader); |
||||||
|
} |
||||||
|
reader.close(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns an array of at most <code>quantityRequested</code> FTPFile |
||||||
|
* objects starting at this object's internal iterator's current position. |
||||||
|
* If fewer than <code>quantityRequested</code> such |
||||||
|
* elements are available, the returned array will have a length equal |
||||||
|
* to the number of entries at and after after the current position. |
||||||
|
* If no such entries are found, this array will have a length of 0. |
||||||
|
* |
||||||
|
* After this method is called this object's internal iterator is advanced |
||||||
|
* by a number of positions equal to the size of the array returned. |
||||||
|
* |
||||||
|
* @param quantityRequested |
||||||
|
* the maximum number of entries we want to get. |
||||||
|
* |
||||||
|
* @return an array of at most <code>quantityRequested</code> FTPFile |
||||||
|
* objects starting at the current position of this iterator within its |
||||||
|
* list and at least the number of elements which exist in the list at |
||||||
|
* and after its current position. |
||||||
|
* <p><b> |
||||||
|
* NOTE:</b> This array may contain null members if any of the |
||||||
|
* individual file listings failed to parse. The caller should |
||||||
|
* check each entry for null before referencing it. |
||||||
|
*/ |
||||||
|
public FTPFile[] getNext(int quantityRequested) { |
||||||
|
List<FTPFile> tmpResults = new LinkedList<FTPFile>(); |
||||||
|
int count = quantityRequested; |
||||||
|
while (count > 0 && this._internalIterator.hasNext()) { |
||||||
|
String entry = this._internalIterator.next(); |
||||||
|
FTPFile temp = this.parser.parseFTPEntry(entry); |
||||||
|
if (temp == null && saveUnparseableEntries) { |
||||||
|
temp = new FTPFile(entry); |
||||||
|
} |
||||||
|
tmpResults.add(temp); |
||||||
|
count--; |
||||||
|
} |
||||||
|
return tmpResults.toArray(new FTPFile[tmpResults.size()]); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns an array of at most <code>quantityRequested</code> FTPFile |
||||||
|
* objects starting at this object's internal iterator's current position, |
||||||
|
* and working back toward the beginning. |
||||||
|
* |
||||||
|
* If fewer than <code>quantityRequested</code> such |
||||||
|
* elements are available, the returned array will have a length equal |
||||||
|
* to the number of entries at and after after the current position. |
||||||
|
* If no such entries are found, this array will have a length of 0. |
||||||
|
* |
||||||
|
* After this method is called this object's internal iterator is moved |
||||||
|
* back by a number of positions equal to the size of the array returned. |
||||||
|
* |
||||||
|
* @param quantityRequested |
||||||
|
* the maximum number of entries we want to get. |
||||||
|
* |
||||||
|
* @return an array of at most <code>quantityRequested</code> FTPFile |
||||||
|
* objects starting at the current position of this iterator within its |
||||||
|
* list and at least the number of elements which exist in the list at |
||||||
|
* and after its current position. This array will be in the same order |
||||||
|
* as the underlying list (not reversed). |
||||||
|
* <p><b> |
||||||
|
* NOTE:</b> This array may contain null members if any of the |
||||||
|
* individual file listings failed to parse. The caller should |
||||||
|
* check each entry for null before referencing it. |
||||||
|
*/ |
||||||
|
public FTPFile[] getPrevious(int quantityRequested) { |
||||||
|
List<FTPFile> tmpResults = new LinkedList<FTPFile>(); |
||||||
|
int count = quantityRequested; |
||||||
|
while (count > 0 && this._internalIterator.hasPrevious()) { |
||||||
|
String entry = this._internalIterator.previous(); |
||||||
|
FTPFile temp = this.parser.parseFTPEntry(entry); |
||||||
|
if (temp == null && saveUnparseableEntries) { |
||||||
|
temp = new FTPFile(entry); |
||||||
|
} |
||||||
|
tmpResults.add(0,temp); |
||||||
|
count--; |
||||||
|
} |
||||||
|
return tmpResults.toArray(new FTPFile[tmpResults.size()]); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns an array of FTPFile objects containing the whole list of |
||||||
|
* files returned by the server as read by this object's parser. |
||||||
|
* |
||||||
|
* @return an array of FTPFile objects containing the whole list of |
||||||
|
* files returned by the server as read by this object's parser. |
||||||
|
* None of the entries will be null |
||||||
|
* @throws IOException - not ever thrown, may be removed in a later release |
||||||
|
*/ |
||||||
|
public FTPFile[] getFiles() |
||||||
|
throws IOException // TODO remove; not actually thrown
|
||||||
|
{ |
||||||
|
return getFiles(FTPFileFilters.NON_NULL); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns an array of FTPFile objects containing the whole list of |
||||||
|
* files returned by the server as read by this object's parser. |
||||||
|
* The files are filtered before being added to the array. |
||||||
|
* |
||||||
|
* @param filter FTPFileFilter, must not be <code>null</code>. |
||||||
|
* |
||||||
|
* @return an array of FTPFile objects containing the whole list of |
||||||
|
* files returned by the server as read by this object's parser. |
||||||
|
* <p><b> |
||||||
|
* NOTE:</b> This array may contain null members if any of the |
||||||
|
* individual file listings failed to parse. The caller should |
||||||
|
* check each entry for null before referencing it, or use the |
||||||
|
* a filter such as {@link FTPFileFilters#NON_NULL} which does not |
||||||
|
* allow null entries. |
||||||
|
* @since 2.2 |
||||||
|
* @throws IOException - not ever thrown, may be removed in a later release |
||||||
|
*/ |
||||||
|
public FTPFile[] getFiles(FTPFileFilter filter) |
||||||
|
throws IOException // TODO remove; not actually thrown
|
||||||
|
{ |
||||||
|
List<FTPFile> tmpResults = new ArrayList<FTPFile>(); |
||||||
|
Iterator<String> iter = this.entries.iterator(); |
||||||
|
while (iter.hasNext()) { |
||||||
|
String entry = iter.next(); |
||||||
|
FTPFile temp = this.parser.parseFTPEntry(entry); |
||||||
|
if (temp == null && saveUnparseableEntries) { |
||||||
|
temp = new FTPFile(entry); |
||||||
|
} |
||||||
|
if (filter.accept(temp)){ |
||||||
|
tmpResults.add(temp); |
||||||
|
} |
||||||
|
} |
||||||
|
return tmpResults.toArray(new FTPFile[tmpResults.size()]); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* convenience method to allow clients to know whether this object's |
||||||
|
* internal iterator's current position is at the end of the list. |
||||||
|
* |
||||||
|
* @return true if internal iterator is not at end of list, false |
||||||
|
* otherwise. |
||||||
|
*/ |
||||||
|
public boolean hasNext() { |
||||||
|
return _internalIterator.hasNext(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* convenience method to allow clients to know whether this object's |
||||||
|
* internal iterator's current position is at the beginning of the list. |
||||||
|
* |
||||||
|
* @return true if internal iterator is not at beginning of list, false |
||||||
|
* otherwise. |
||||||
|
*/ |
||||||
|
public boolean hasPrevious() { |
||||||
|
return _internalIterator.hasPrevious(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* resets this object's internal iterator to the beginning of the list. |
||||||
|
*/ |
||||||
|
public void resetIterator() { |
||||||
|
this._internalIterator = this.entries.listIterator(); |
||||||
|
} |
||||||
|
|
||||||
|
// DEPRECATED METHODS - for API compatibility only - DO NOT USE
|
||||||
|
|
||||||
|
/** |
||||||
|
* Do not use. |
||||||
|
* @param stream the stream from which to read |
||||||
|
* @throws IOException on error |
||||||
|
* @deprecated use {@link #readServerList(InputStream, String)} instead |
||||||
|
*/ |
||||||
|
@Deprecated |
||||||
|
public void readServerList(InputStream stream) |
||||||
|
throws IOException |
||||||
|
{ |
||||||
|
readServerList(stream, null); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,201 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.apache.commons.net.ftp; |
||||||
|
|
||||||
|
/*** |
||||||
|
* FTPReply stores a set of constants for FTP reply codes. To interpret |
||||||
|
* the meaning of the codes, familiarity with RFC 959 is assumed. |
||||||
|
* The mnemonic constant names are transcriptions from the code descriptions |
||||||
|
* of RFC 959. |
||||||
|
* <p> |
||||||
|
* TODO replace with an enum |
||||||
|
***/ |
||||||
|
|
||||||
|
public final class FTPReply |
||||||
|
{ |
||||||
|
|
||||||
|
public static final int RESTART_MARKER = 110; |
||||||
|
public static final int SERVICE_NOT_READY = 120; |
||||||
|
public static final int DATA_CONNECTION_ALREADY_OPEN = 125; |
||||||
|
public static final int FILE_STATUS_OK = 150; |
||||||
|
public static final int COMMAND_OK = 200; |
||||||
|
public static final int COMMAND_IS_SUPERFLUOUS = 202; |
||||||
|
public static final int SYSTEM_STATUS = 211; |
||||||
|
public static final int DIRECTORY_STATUS = 212; |
||||||
|
public static final int FILE_STATUS = 213; |
||||||
|
public static final int HELP_MESSAGE = 214; |
||||||
|
public static final int NAME_SYSTEM_TYPE = 215; |
||||||
|
public static final int SERVICE_READY = 220; |
||||||
|
public static final int SERVICE_CLOSING_CONTROL_CONNECTION = 221; |
||||||
|
public static final int DATA_CONNECTION_OPEN = 225; |
||||||
|
public static final int CLOSING_DATA_CONNECTION = 226; |
||||||
|
public static final int ENTERING_PASSIVE_MODE = 227; |
||||||
|
/** @since 2.2 */ |
||||||
|
public static final int ENTERING_EPSV_MODE = 229; |
||||||
|
public static final int USER_LOGGED_IN = 230; |
||||||
|
public static final int FILE_ACTION_OK = 250; |
||||||
|
public static final int PATHNAME_CREATED = 257; |
||||||
|
public static final int NEED_PASSWORD = 331; |
||||||
|
public static final int NEED_ACCOUNT = 332; |
||||||
|
public static final int FILE_ACTION_PENDING = 350; |
||||||
|
public static final int SERVICE_NOT_AVAILABLE = 421; |
||||||
|
public static final int CANNOT_OPEN_DATA_CONNECTION = 425; |
||||||
|
public static final int TRANSFER_ABORTED = 426; |
||||||
|
public static final int FILE_ACTION_NOT_TAKEN = 450; |
||||||
|
public static final int ACTION_ABORTED = 451; |
||||||
|
public static final int INSUFFICIENT_STORAGE = 452; |
||||||
|
public static final int UNRECOGNIZED_COMMAND = 500; |
||||||
|
public static final int SYNTAX_ERROR_IN_ARGUMENTS = 501; |
||||||
|
public static final int COMMAND_NOT_IMPLEMENTED = 502; |
||||||
|
public static final int BAD_COMMAND_SEQUENCE = 503; |
||||||
|
public static final int COMMAND_NOT_IMPLEMENTED_FOR_PARAMETER = 504; |
||||||
|
public static final int NOT_LOGGED_IN = 530; |
||||||
|
public static final int NEED_ACCOUNT_FOR_STORING_FILES = 532; |
||||||
|
public static final int FILE_UNAVAILABLE = 550; |
||||||
|
public static final int PAGE_TYPE_UNKNOWN = 551; |
||||||
|
public static final int STORAGE_ALLOCATION_EXCEEDED = 552; |
||||||
|
public static final int FILE_NAME_NOT_ALLOWED = 553; |
||||||
|
|
||||||
|
// FTPS Reply Codes
|
||||||
|
|
||||||
|
/** @since 2.0 */ |
||||||
|
public static final int SECURITY_DATA_EXCHANGE_COMPLETE = 234; |
||||||
|
/** @since 2.0 */ |
||||||
|
public static final int SECURITY_DATA_EXCHANGE_SUCCESSFULLY = 235; |
||||||
|
/** @since 2.0 */ |
||||||
|
public static final int SECURITY_MECHANISM_IS_OK = 334; |
||||||
|
/** @since 2.0 */ |
||||||
|
public static final int SECURITY_DATA_IS_ACCEPTABLE = 335; |
||||||
|
/** @since 2.0 */ |
||||||
|
public static final int UNAVAILABLE_RESOURCE = 431; |
||||||
|
/** @since 2.2 */ |
||||||
|
public static final int BAD_TLS_NEGOTIATION_OR_DATA_ENCRYPTION_REQUIRED = 522; |
||||||
|
/** @since 2.0 */ |
||||||
|
public static final int DENIED_FOR_POLICY_REASONS = 533; |
||||||
|
/** @since 2.0 */ |
||||||
|
public static final int REQUEST_DENIED = 534; |
||||||
|
/** @since 2.0 */ |
||||||
|
public static final int FAILED_SECURITY_CHECK = 535; |
||||||
|
/** @since 2.0 */ |
||||||
|
public static final int REQUESTED_PROT_LEVEL_NOT_SUPPORTED = 536; |
||||||
|
|
||||||
|
// IPv6 error codes
|
||||||
|
// Note this is also used as an FTPS error code reply
|
||||||
|
/** @since 2.2 */ |
||||||
|
public static final int EXTENDED_PORT_FAILURE = 522; |
||||||
|
|
||||||
|
// Cannot be instantiated
|
||||||
|
private FTPReply() |
||||||
|
{} |
||||||
|
|
||||||
|
/*** |
||||||
|
* Determine if a reply code is a positive preliminary response. All |
||||||
|
* codes beginning with a 1 are positive preliminary responses. |
||||||
|
* Postitive preliminary responses are used to indicate tentative success. |
||||||
|
* No further commands can be issued to the FTP server after a positive |
||||||
|
* preliminary response until a follow up response is received from the |
||||||
|
* server. |
||||||
|
* |
||||||
|
* @param reply The reply code to test. |
||||||
|
* @return True if a reply code is a postive preliminary response, false |
||||||
|
* if not. |
||||||
|
***/ |
||||||
|
public static boolean isPositivePreliminary(int reply) |
||||||
|
{ |
||||||
|
return (reply >= 100 && reply < 200); |
||||||
|
} |
||||||
|
|
||||||
|
/*** |
||||||
|
* Determine if a reply code is a positive completion response. All |
||||||
|
* codes beginning with a 2 are positive completion responses. |
||||||
|
* The FTP server will send a positive completion response on the final |
||||||
|
* successful completion of a command. |
||||||
|
* |
||||||
|
* @param reply The reply code to test. |
||||||
|
* @return True if a reply code is a postive completion response, false |
||||||
|
* if not. |
||||||
|
***/ |
||||||
|
public static boolean isPositiveCompletion(int reply) |
||||||
|
{ |
||||||
|
return (reply >= 200 && reply < 300); |
||||||
|
} |
||||||
|
|
||||||
|
/*** |
||||||
|
* Determine if a reply code is a positive intermediate response. All |
||||||
|
* codes beginning with a 3 are positive intermediate responses. |
||||||
|
* The FTP server will send a positive intermediate response on the |
||||||
|
* successful completion of one part of a multi-part sequence of |
||||||
|
* commands. For example, after a successful USER command, a positive |
||||||
|
* intermediate response will be sent to indicate that the server is |
||||||
|
* ready for the PASS command. |
||||||
|
* |
||||||
|
* @param reply The reply code to test. |
||||||
|
* @return True if a reply code is a postive intermediate response, false |
||||||
|
* if not. |
||||||
|
***/ |
||||||
|
public static boolean isPositiveIntermediate(int reply) |
||||||
|
{ |
||||||
|
return (reply >= 300 && reply < 400); |
||||||
|
} |
||||||
|
|
||||||
|
/*** |
||||||
|
* Determine if a reply code is a negative transient response. All |
||||||
|
* codes beginning with a 4 are negative transient responses. |
||||||
|
* The FTP server will send a negative transient response on the |
||||||
|
* failure of a command that can be reattempted with success. |
||||||
|
* |
||||||
|
* @param reply The reply code to test. |
||||||
|
* @return True if a reply code is a negative transient response, false |
||||||
|
* if not. |
||||||
|
***/ |
||||||
|
public static boolean isNegativeTransient(int reply) |
||||||
|
{ |
||||||
|
return (reply >= 400 && reply < 500); |
||||||
|
} |
||||||
|
|
||||||
|
/*** |
||||||
|
* Determine if a reply code is a negative permanent response. All |
||||||
|
* codes beginning with a 5 are negative permanent responses. |
||||||
|
* The FTP server will send a negative permanent response on the |
||||||
|
* failure of a command that cannot be reattempted with success. |
||||||
|
* |
||||||
|
* @param reply The reply code to test. |
||||||
|
* @return True if a reply code is a negative permanent response, false |
||||||
|
* if not. |
||||||
|
***/ |
||||||
|
public static boolean isNegativePermanent(int reply) |
||||||
|
{ |
||||||
|
return (reply >= 500 && reply < 600); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Determine if a reply code is a protected response. |
||||||
|
* @param reply The reply code to test. |
||||||
|
* @return True if a reply code is a protected response, false |
||||||
|
* if not. |
||||||
|
* @since 3.0 |
||||||
|
*/ |
||||||
|
public static boolean isProtectedReplyCode(int reply) |
||||||
|
{ |
||||||
|
// actually, only 3 protected reply codes are
|
||||||
|
// defined in RFC 2228: 631, 632 and 633.
|
||||||
|
return (reply >= 600 && reply < 700); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,925 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.apache.commons.net.ftp; |
||||||
|
|
||||||
|
import java.io.BufferedReader; |
||||||
|
import java.io.BufferedWriter; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.InputStreamReader; |
||||||
|
import java.io.OutputStreamWriter; |
||||||
|
import java.net.Socket; |
||||||
|
|
||||||
|
import javax.net.ssl.HostnameVerifier; |
||||||
|
import javax.net.ssl.KeyManager; |
||||||
|
import javax.net.ssl.SSLContext; |
||||||
|
import javax.net.ssl.SSLException; |
||||||
|
import javax.net.ssl.SSLHandshakeException; |
||||||
|
import javax.net.ssl.SSLSocket; |
||||||
|
import javax.net.ssl.SSLSocketFactory; |
||||||
|
import javax.net.ssl.TrustManager; |
||||||
|
|
||||||
|
import org.apache.commons.net.util.Base64; |
||||||
|
import org.apache.commons.net.util.SSLContextUtils; |
||||||
|
import org.apache.commons.net.util.SSLSocketUtils; |
||||||
|
import org.apache.commons.net.util.TrustManagerUtils; |
||||||
|
|
||||||
|
/** |
||||||
|
* FTP over SSL processing. If desired, the JVM property -Djavax.net.debug=all can be used to |
||||||
|
* see wire-level SSL details. |
||||||
|
* |
||||||
|
* Warning: the hostname is not verified against the certificate by default, use |
||||||
|
* {@link #setHostnameVerifier(HostnameVerifier)} or {@link #setEndpointCheckingEnabled(boolean)} |
||||||
|
* (on Java 1.7+) to enable verification. Verification is only performed on client mode connections. |
||||||
|
* @version $Id: FTPSClient.java 1747829 2016-06-11 00:57:57Z sebb $ |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
public class FTPSClient extends FTPClient { |
||||||
|
|
||||||
|
// From http://www.iana.org/assignments/port-numbers
|
||||||
|
|
||||||
|
// ftps-data 989/tcp ftp protocol, data, over TLS/SSL
|
||||||
|
// ftps-data 989/udp ftp protocol, data, over TLS/SSL
|
||||||
|
// ftps 990/tcp ftp protocol, control, over TLS/SSL
|
||||||
|
// ftps 990/udp ftp protocol, control, over TLS/SSL
|
||||||
|
|
||||||
|
public static final int DEFAULT_FTPS_DATA_PORT = 989; |
||||||
|
public static final int DEFAULT_FTPS_PORT = 990; |
||||||
|
|
||||||
|
/** The value that I can set in PROT command (C = Clear, P = Protected) */ |
||||||
|
private static final String[] PROT_COMMAND_VALUE = {"C","E","S","P"}; |
||||||
|
/** Default PROT Command */ |
||||||
|
private static final String DEFAULT_PROT = "C"; |
||||||
|
/** Default secure socket protocol name, i.e. TLS */ |
||||||
|
private static final String DEFAULT_PROTOCOL = "TLS"; |
||||||
|
|
||||||
|
/** The AUTH (Authentication/Security Mechanism) command. */ |
||||||
|
private static final String CMD_AUTH = "AUTH"; |
||||||
|
/** The ADAT (Authentication/Security Data) command. */ |
||||||
|
private static final String CMD_ADAT = "ADAT"; |
||||||
|
/** The PROT (Data Channel Protection Level) command. */ |
||||||
|
private static final String CMD_PROT = "PROT"; |
||||||
|
/** The PBSZ (Protection Buffer Size) command. */ |
||||||
|
private static final String CMD_PBSZ = "PBSZ"; |
||||||
|
/** The MIC (Integrity Protected Command) command. */ |
||||||
|
private static final String CMD_MIC = "MIC"; |
||||||
|
/** The CONF (Confidentiality Protected Command) command. */ |
||||||
|
private static final String CMD_CONF = "CONF"; |
||||||
|
/** The ENC (Privacy Protected Command) command. */ |
||||||
|
private static final String CMD_ENC = "ENC"; |
||||||
|
/** The CCC (Clear Command Channel) command. */ |
||||||
|
private static final String CMD_CCC = "CCC"; |
||||||
|
|
||||||
|
/** The security mode. (True - Implicit Mode / False - Explicit Mode) */ |
||||||
|
private final boolean isImplicit; |
||||||
|
/** The secure socket protocol to be used, e.g. SSL/TLS. */ |
||||||
|
private final String protocol; |
||||||
|
/** The AUTH Command value */ |
||||||
|
private String auth = DEFAULT_PROTOCOL; |
||||||
|
/** The context object. */ |
||||||
|
private SSLContext context; |
||||||
|
/** The socket object. */ |
||||||
|
private Socket plainSocket; |
||||||
|
/** Controls whether a new SSL session may be established by this socket. Default true. */ |
||||||
|
private boolean isCreation = true; |
||||||
|
/** The use client mode flag. */ |
||||||
|
private boolean isClientMode = true; |
||||||
|
/** The need client auth flag. */ |
||||||
|
private boolean isNeedClientAuth = false; |
||||||
|
/** The want client auth flag. */ |
||||||
|
private boolean isWantClientAuth = false; |
||||||
|
/** The cipher suites */ |
||||||
|
private String[] suites = null; |
||||||
|
/** The protocol versions */ |
||||||
|
private String[] protocols = null; |
||||||
|
|
||||||
|
/** The FTPS {@link TrustManager} implementation, default validate only |
||||||
|
* {@link TrustManagerUtils#getValidateServerCertificateTrustManager()}. |
||||||
|
*/ |
||||||
|
private TrustManager trustManager = TrustManagerUtils.getValidateServerCertificateTrustManager(); |
||||||
|
|
||||||
|
/** The {@link KeyManager}, default null (i.e. use system default). */ |
||||||
|
private KeyManager keyManager = null; |
||||||
|
|
||||||
|
/** The {@link HostnameVerifier} to use post-TLS, default null (i.e. no verification). */ |
||||||
|
private HostnameVerifier hostnameVerifier = null; |
||||||
|
|
||||||
|
/** Use Java 1.7+ HTTPS Endpoint Identification Algorithim. */ |
||||||
|
private boolean tlsEndpointChecking; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor for FTPSClient, calls {@link #FTPSClient(String, boolean)}. |
||||||
|
* |
||||||
|
* Sets protocol to {@link #DEFAULT_PROTOCOL} - i.e. TLS - and security mode to explicit (isImplicit = false) |
||||||
|
*/ |
||||||
|
public FTPSClient() { |
||||||
|
this(DEFAULT_PROTOCOL, false); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor for FTPSClient, using {@link #DEFAULT_PROTOCOL} - i.e. TLS |
||||||
|
* Calls {@link #FTPSClient(String, boolean)} |
||||||
|
* @param isImplicit The security mode (Implicit/Explicit). |
||||||
|
*/ |
||||||
|
public FTPSClient(boolean isImplicit) { |
||||||
|
this(DEFAULT_PROTOCOL, isImplicit); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor for FTPSClient, using explict mode, calls {@link #FTPSClient(String, boolean)}. |
||||||
|
* |
||||||
|
* @param protocol the protocol to use |
||||||
|
*/ |
||||||
|
public FTPSClient(String protocol) { |
||||||
|
this(protocol, false); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor for FTPSClient allowing specification of protocol |
||||||
|
* and security mode. If isImplicit is true, the port is set to |
||||||
|
* {@link #DEFAULT_FTPS_PORT} i.e. 990. |
||||||
|
* The default TrustManager is set from {@link TrustManagerUtils#getValidateServerCertificateTrustManager()} |
||||||
|
* @param protocol the protocol |
||||||
|
* @param isImplicit The security mode(Implicit/Explicit). |
||||||
|
*/ |
||||||
|
public FTPSClient(String protocol, boolean isImplicit) { |
||||||
|
super(); |
||||||
|
this.protocol = protocol; |
||||||
|
this.isImplicit = isImplicit; |
||||||
|
if (isImplicit) { |
||||||
|
setDefaultPort(DEFAULT_FTPS_PORT); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor for FTPSClient, using {@link #DEFAULT_PROTOCOL} - i.e. TLS |
||||||
|
* The default TrustManager is set from {@link TrustManagerUtils#getValidateServerCertificateTrustManager()} |
||||||
|
* @param isImplicit The security mode(Implicit/Explicit). |
||||||
|
* @param context A pre-configured SSL Context |
||||||
|
*/ |
||||||
|
public FTPSClient(boolean isImplicit, SSLContext context) { |
||||||
|
this(DEFAULT_PROTOCOL, isImplicit); |
||||||
|
this.context = context; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor for FTPSClient, using {@link #DEFAULT_PROTOCOL} - i.e. TLS |
||||||
|
* and isImplicit {@code false} |
||||||
|
* Calls {@link #FTPSClient(boolean, SSLContext)} |
||||||
|
* @param context A pre-configured SSL Context |
||||||
|
*/ |
||||||
|
public FTPSClient(SSLContext context) { |
||||||
|
this(false, context); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Set AUTH command use value. |
||||||
|
* This processing is done before connected processing. |
||||||
|
* @param auth AUTH command use value. |
||||||
|
*/ |
||||||
|
public void setAuthValue(String auth) { |
||||||
|
this.auth = auth; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Return AUTH command use value. |
||||||
|
* @return AUTH command use value. |
||||||
|
*/ |
||||||
|
public String getAuthValue() { |
||||||
|
return this.auth; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Because there are so many connect() methods, |
||||||
|
* the _connectAction_() method is provided as a means of performing |
||||||
|
* some action immediately after establishing a connection, |
||||||
|
* rather than reimplementing all of the connect() methods. |
||||||
|
* @throws IOException If it throw by _connectAction_. |
||||||
|
* @see org.apache.commons.net.SocketClient#_connectAction_() |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
protected void _connectAction_() throws IOException { |
||||||
|
// Implicit mode.
|
||||||
|
if (isImplicit) { |
||||||
|
sslNegotiation(); |
||||||
|
} |
||||||
|
super._connectAction_(); |
||||||
|
// Explicit mode.
|
||||||
|
if (!isImplicit) { |
||||||
|
execAUTH(); |
||||||
|
sslNegotiation(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* AUTH command. |
||||||
|
* @throws SSLException If it server reply code not equal "234" and "334". |
||||||
|
* @throws IOException If an I/O error occurs while either sending |
||||||
|
* the command. |
||||||
|
*/ |
||||||
|
protected void execAUTH() throws SSLException, IOException { |
||||||
|
int replyCode = sendCommand(CMD_AUTH, auth); |
||||||
|
if (FTPReply.SECURITY_MECHANISM_IS_OK == replyCode) { |
||||||
|
// replyCode = 334
|
||||||
|
// I carry out an ADAT command.
|
||||||
|
} else if (FTPReply.SECURITY_DATA_EXCHANGE_COMPLETE != replyCode) { |
||||||
|
throw new SSLException(getReplyString()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Performs a lazy init of the SSL context |
||||||
|
* @throws IOException |
||||||
|
*/ |
||||||
|
private void initSslContext() throws IOException { |
||||||
|
if (context == null) { |
||||||
|
context = SSLContextUtils.createSSLContext(protocol, getKeyManager(), getTrustManager()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* SSL/TLS negotiation. Acquires an SSL socket of a control |
||||||
|
* connection and carries out handshake processing. |
||||||
|
* @throws IOException If server negotiation fails |
||||||
|
*/ |
||||||
|
protected void sslNegotiation() throws IOException { |
||||||
|
plainSocket = _socket_; |
||||||
|
initSslContext(); |
||||||
|
|
||||||
|
SSLSocketFactory ssf = context.getSocketFactory(); |
||||||
|
String host = (_hostname_ != null) ? _hostname_ : getRemoteAddress().getHostAddress(); |
||||||
|
int port = _socket_.getPort(); |
||||||
|
SSLSocket socket = |
||||||
|
(SSLSocket) ssf.createSocket(_socket_, host, port, false); |
||||||
|
socket.setEnableSessionCreation(isCreation); |
||||||
|
socket.setUseClientMode(isClientMode); |
||||||
|
|
||||||
|
// client mode
|
||||||
|
if (isClientMode) { |
||||||
|
if (tlsEndpointChecking) { |
||||||
|
SSLSocketUtils.enableEndpointNameVerification(socket); |
||||||
|
} |
||||||
|
} else { // server mode
|
||||||
|
socket.setNeedClientAuth(isNeedClientAuth); |
||||||
|
socket.setWantClientAuth(isWantClientAuth); |
||||||
|
} |
||||||
|
|
||||||
|
if (protocols != null) { |
||||||
|
socket.setEnabledProtocols(protocols); |
||||||
|
} |
||||||
|
if (suites != null) { |
||||||
|
socket.setEnabledCipherSuites(suites); |
||||||
|
} |
||||||
|
socket.startHandshake(); |
||||||
|
|
||||||
|
// TODO the following setup appears to duplicate that in the super class methods
|
||||||
|
_socket_ = socket; |
||||||
|
_controlInput_ = new BufferedReader(new InputStreamReader( |
||||||
|
socket .getInputStream(), getControlEncoding())); |
||||||
|
_controlOutput_ = new BufferedWriter(new OutputStreamWriter( |
||||||
|
socket.getOutputStream(), getControlEncoding())); |
||||||
|
|
||||||
|
if (isClientMode) { |
||||||
|
if (hostnameVerifier != null && !hostnameVerifier.verify(host, socket.getSession())) { |
||||||
|
throw new SSLHandshakeException("Hostname doesn't match certificate"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the {@link KeyManager} instance. |
||||||
|
* @return The {@link KeyManager} instance |
||||||
|
*/ |
||||||
|
private KeyManager getKeyManager() { |
||||||
|
return keyManager; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set a {@link KeyManager} to use |
||||||
|
* |
||||||
|
* @param keyManager The KeyManager implementation to set. |
||||||
|
* @see org.apache.commons.net.util.KeyManagerUtils |
||||||
|
*/ |
||||||
|
public void setKeyManager(KeyManager keyManager) { |
||||||
|
this.keyManager = keyManager; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Controls whether a new SSL session may be established by this socket. |
||||||
|
* @param isCreation The established socket flag. |
||||||
|
*/ |
||||||
|
public void setEnabledSessionCreation(boolean isCreation) { |
||||||
|
this.isCreation = isCreation; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns true if new SSL sessions may be established by this socket. |
||||||
|
* When the underlying {@link Socket} instance is not SSL-enabled (i.e. an |
||||||
|
* instance of {@link SSLSocket} with {@link SSLSocket}{@link #getEnableSessionCreation()}) enabled, |
||||||
|
* this returns False. |
||||||
|
* @return true - Indicates that sessions may be created; |
||||||
|
* this is the default. |
||||||
|
* false - indicates that an existing session must be resumed. |
||||||
|
*/ |
||||||
|
public boolean getEnableSessionCreation() { |
||||||
|
if (_socket_ instanceof SSLSocket) { |
||||||
|
return ((SSLSocket)_socket_).getEnableSessionCreation(); |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Configures the socket to require client authentication. |
||||||
|
* @param isNeedClientAuth The need client auth flag. |
||||||
|
*/ |
||||||
|
public void setNeedClientAuth(boolean isNeedClientAuth) { |
||||||
|
this.isNeedClientAuth = isNeedClientAuth; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns true if the socket will require client authentication. |
||||||
|
* When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns false. |
||||||
|
* @return true - If the server mode socket should request |
||||||
|
* that the client authenticate itself. |
||||||
|
*/ |
||||||
|
public boolean getNeedClientAuth() { |
||||||
|
if (_socket_ instanceof SSLSocket) { |
||||||
|
return ((SSLSocket)_socket_).getNeedClientAuth(); |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Configures the socket to request client authentication, |
||||||
|
* but only if such a request is appropriate to the cipher |
||||||
|
* suite negotiated. |
||||||
|
* @param isWantClientAuth The want client auth flag. |
||||||
|
*/ |
||||||
|
public void setWantClientAuth(boolean isWantClientAuth) { |
||||||
|
this.isWantClientAuth = isWantClientAuth; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns true if the socket will request client authentication. |
||||||
|
* When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns false. |
||||||
|
* @return true - If the server mode socket should request |
||||||
|
* that the client authenticate itself. |
||||||
|
*/ |
||||||
|
public boolean getWantClientAuth() { |
||||||
|
if (_socket_ instanceof SSLSocket) { |
||||||
|
return ((SSLSocket)_socket_).getWantClientAuth(); |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Configures the socket to use client (or server) mode in its first |
||||||
|
* handshake. |
||||||
|
* @param isClientMode The use client mode flag. |
||||||
|
*/ |
||||||
|
public void setUseClientMode(boolean isClientMode) { |
||||||
|
this.isClientMode = isClientMode; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns true if the socket is set to use client mode |
||||||
|
* in its first handshake. |
||||||
|
* When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns false. |
||||||
|
* @return true - If the socket should start its first handshake |
||||||
|
* in "client" mode. |
||||||
|
*/ |
||||||
|
public boolean getUseClientMode() { |
||||||
|
if (_socket_ instanceof SSLSocket) { |
||||||
|
return ((SSLSocket)_socket_).getUseClientMode(); |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Controls which particular cipher suites are enabled for use on this |
||||||
|
* connection. Called before server negotiation. |
||||||
|
* @param cipherSuites The cipher suites. |
||||||
|
*/ |
||||||
|
public void setEnabledCipherSuites(String[] cipherSuites) { |
||||||
|
suites = new String[cipherSuites.length]; |
||||||
|
System.arraycopy(cipherSuites, 0, suites, 0, cipherSuites.length); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the names of the cipher suites which could be enabled |
||||||
|
* for use on this connection. |
||||||
|
* When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns null. |
||||||
|
* @return An array of cipher suite names, or <code>null</code> |
||||||
|
*/ |
||||||
|
public String[] getEnabledCipherSuites() { |
||||||
|
if (_socket_ instanceof SSLSocket) { |
||||||
|
return ((SSLSocket)_socket_).getEnabledCipherSuites(); |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Controls which particular protocol versions are enabled for use on this |
||||||
|
* connection. I perform setting before a server negotiation. |
||||||
|
* @param protocolVersions The protocol versions. |
||||||
|
*/ |
||||||
|
public void setEnabledProtocols(String[] protocolVersions) { |
||||||
|
protocols = new String[protocolVersions.length]; |
||||||
|
System.arraycopy(protocolVersions, 0, protocols, 0, protocolVersions.length); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the names of the protocol versions which are currently |
||||||
|
* enabled for use on this connection. |
||||||
|
* When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns null. |
||||||
|
* @return An array of protocols, or <code>null</code> |
||||||
|
*/ |
||||||
|
public String[] getEnabledProtocols() { |
||||||
|
if (_socket_ instanceof SSLSocket) { |
||||||
|
return ((SSLSocket)_socket_).getEnabledProtocols(); |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* PBSZ command. pbsz value: 0 to (2^32)-1 decimal integer. |
||||||
|
* @param pbsz Protection Buffer Size. |
||||||
|
* @throws SSLException If the server reply code does not equal "200". |
||||||
|
* @throws IOException If an I/O error occurs while sending |
||||||
|
* the command. |
||||||
|
* @see #parsePBSZ(long) |
||||||
|
*/ |
||||||
|
public void execPBSZ(long pbsz) throws SSLException, IOException { |
||||||
|
if (pbsz < 0 || 4294967295L < pbsz) { // 32-bit unsigned number
|
||||||
|
throw new IllegalArgumentException(); |
||||||
|
} |
||||||
|
int status = sendCommand(CMD_PBSZ, String.valueOf(pbsz)); |
||||||
|
if (FTPReply.COMMAND_OK != status) { |
||||||
|
throw new SSLException(getReplyString()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* PBSZ command. pbsz value: 0 to (2^32)-1 decimal integer. |
||||||
|
* Issues the command and parses the response to return the negotiated value. |
||||||
|
* |
||||||
|
* @param pbsz Protection Buffer Size. |
||||||
|
* @throws SSLException If the server reply code does not equal "200". |
||||||
|
* @throws IOException If an I/O error occurs while sending |
||||||
|
* the command. |
||||||
|
* @return the negotiated value. |
||||||
|
* @see #execPBSZ(long) |
||||||
|
* @since 3.0 |
||||||
|
*/ |
||||||
|
public long parsePBSZ(long pbsz) throws SSLException, IOException { |
||||||
|
execPBSZ(pbsz); |
||||||
|
long minvalue = pbsz; |
||||||
|
String remainder = extractPrefixedData("PBSZ=", getReplyString()); |
||||||
|
if (remainder != null) { |
||||||
|
long replysz = Long.parseLong(remainder); |
||||||
|
if (replysz < minvalue) { |
||||||
|
minvalue = replysz; |
||||||
|
} |
||||||
|
} |
||||||
|
return minvalue; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* PROT command. |
||||||
|
* <ul> |
||||||
|
* <li>C - Clear</li> |
||||||
|
* <li>S - Safe(SSL protocol only)</li> |
||||||
|
* <li>E - Confidential(SSL protocol only)</li> |
||||||
|
* <li>P - Private</li> |
||||||
|
* </ul> |
||||||
|
* <b>N.B.</b> the method calls |
||||||
|
* {@link #setSocketFactory(javax.net.SocketFactory)} and |
||||||
|
* {@link #setServerSocketFactory(javax.net.ServerSocketFactory)} |
||||||
|
* |
||||||
|
* @param prot Data Channel Protection Level, if {@code null}, use {@link #DEFAULT_PROT}. |
||||||
|
* @throws SSLException If the server reply code does not equal {@code 200}. |
||||||
|
* @throws IOException If an I/O error occurs while sending |
||||||
|
* the command. |
||||||
|
*/ |
||||||
|
public void execPROT(String prot) throws SSLException, IOException { |
||||||
|
if (prot == null) { |
||||||
|
prot = DEFAULT_PROT; |
||||||
|
} |
||||||
|
if (!checkPROTValue(prot)) { |
||||||
|
throw new IllegalArgumentException(); |
||||||
|
} |
||||||
|
if (FTPReply.COMMAND_OK != sendCommand(CMD_PROT, prot)) { |
||||||
|
throw new SSLException(getReplyString()); |
||||||
|
} |
||||||
|
if (DEFAULT_PROT.equals(prot)) { |
||||||
|
setSocketFactory(null); |
||||||
|
setServerSocketFactory(null); |
||||||
|
} else { |
||||||
|
setSocketFactory(new FTPSSocketFactory(context)); |
||||||
|
setServerSocketFactory(new FTPSServerSocketFactory(context)); |
||||||
|
initSslContext(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Check the value that can be set in PROT Command value. |
||||||
|
* @param prot Data Channel Protection Level. |
||||||
|
* @return True - A set point is right / False - A set point is not right |
||||||
|
*/ |
||||||
|
private boolean checkPROTValue(String prot) { |
||||||
|
for (String element : PROT_COMMAND_VALUE) |
||||||
|
{ |
||||||
|
if (element.equals(prot)) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Send an FTP command. |
||||||
|
* A successful CCC (Clear Command Channel) command causes the underlying {@link SSLSocket} |
||||||
|
* instance to be assigned to a plain {@link Socket} |
||||||
|
* @param command The FTP command. |
||||||
|
* @return server reply. |
||||||
|
* @throws IOException If an I/O error occurs while sending the command. |
||||||
|
* @throws SSLException if a CCC command fails |
||||||
|
* @see FTP#sendCommand(String) |
||||||
|
*/ |
||||||
|
// Would like to remove this method, but that will break any existing clients that are using CCC
|
||||||
|
@Override |
||||||
|
public int sendCommand(String command, String args) throws IOException { |
||||||
|
int repCode = super.sendCommand(command, args); |
||||||
|
/* If CCC is issued, restore socket i/o streams to unsecured versions */ |
||||||
|
if (CMD_CCC.equals(command)) { |
||||||
|
if (FTPReply.COMMAND_OK == repCode) { |
||||||
|
_socket_.close(); |
||||||
|
_socket_ = plainSocket; |
||||||
|
_controlInput_ = new BufferedReader( |
||||||
|
new InputStreamReader( |
||||||
|
_socket_ .getInputStream(), getControlEncoding())); |
||||||
|
_controlOutput_ = new BufferedWriter( |
||||||
|
new OutputStreamWriter( |
||||||
|
_socket_.getOutputStream(), getControlEncoding())); |
||||||
|
} else { |
||||||
|
throw new SSLException(getReplyString()); |
||||||
|
} |
||||||
|
} |
||||||
|
return repCode; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a socket of the data connection. |
||||||
|
* Wrapped as an {@link SSLSocket}, which carries out handshake processing. |
||||||
|
* @param command The int representation of the FTP command to send. |
||||||
|
* @param arg The arguments to the FTP command. |
||||||
|
* If this parameter is set to null, then the command is sent with |
||||||
|
* no arguments. |
||||||
|
* @return corresponding to the established data connection. |
||||||
|
* Null is returned if an FTP protocol error is reported at any point |
||||||
|
* during the establishment and initialization of the connection. |
||||||
|
* @throws IOException If there is any problem with the connection. |
||||||
|
* @see FTPClient#_openDataConnection_(int, String) |
||||||
|
* @deprecated (3.3) Use {@link FTPClient#_openDataConnection_(FTPCmd, String)} instead |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
// Strictly speaking this is not needed, but it works round a Clirr bug
|
||||||
|
// So rather than invoke the parent code, we do it here
|
||||||
|
@Deprecated |
||||||
|
protected Socket _openDataConnection_(int command, String arg) |
||||||
|
throws IOException { |
||||||
|
return _openDataConnection_(FTPCommand.getCommand(command), arg); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a socket of the data connection. |
||||||
|
* Wrapped as an {@link SSLSocket}, which carries out handshake processing. |
||||||
|
* @param command The textual representation of the FTP command to send. |
||||||
|
* @param arg The arguments to the FTP command. |
||||||
|
* If this parameter is set to null, then the command is sent with |
||||||
|
* no arguments. |
||||||
|
* @return corresponding to the established data connection. |
||||||
|
* Null is returned if an FTP protocol error is reported at any point |
||||||
|
* during the establishment and initialization of the connection. |
||||||
|
* @throws IOException If there is any problem with the connection. |
||||||
|
* @see FTPClient#_openDataConnection_(int, String) |
||||||
|
* @since 3.2 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
protected Socket _openDataConnection_(String command, String arg) |
||||||
|
throws IOException { |
||||||
|
Socket socket = super._openDataConnection_(command, arg); |
||||||
|
_prepareDataSocket_(socket); |
||||||
|
if (socket instanceof SSLSocket) { |
||||||
|
SSLSocket sslSocket = (SSLSocket)socket; |
||||||
|
|
||||||
|
sslSocket.setUseClientMode(isClientMode); |
||||||
|
sslSocket.setEnableSessionCreation(isCreation); |
||||||
|
|
||||||
|
// server mode
|
||||||
|
if (!isClientMode) { |
||||||
|
sslSocket.setNeedClientAuth(isNeedClientAuth); |
||||||
|
sslSocket.setWantClientAuth(isWantClientAuth); |
||||||
|
} |
||||||
|
if (suites != null) { |
||||||
|
sslSocket.setEnabledCipherSuites(suites); |
||||||
|
} |
||||||
|
if (protocols != null) { |
||||||
|
sslSocket.setEnabledProtocols(protocols); |
||||||
|
} |
||||||
|
sslSocket.startHandshake(); |
||||||
|
} |
||||||
|
|
||||||
|
return socket; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Performs any custom initialization for a newly created SSLSocket (before |
||||||
|
* the SSL handshake happens). |
||||||
|
* Called by {@link #_openDataConnection_(int, String)} immediately |
||||||
|
* after creating the socket. |
||||||
|
* The default implementation is a no-op |
||||||
|
* @param socket the socket to set up |
||||||
|
* @throws IOException on error |
||||||
|
* @since 3.1 |
||||||
|
*/ |
||||||
|
protected void _prepareDataSocket_(Socket socket) |
||||||
|
throws IOException { |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the currently configured {@link TrustManager}. |
||||||
|
* |
||||||
|
* @return A TrustManager instance. |
||||||
|
*/ |
||||||
|
public TrustManager getTrustManager() { |
||||||
|
return trustManager; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Override the default {@link TrustManager} to use; if set to {@code null}, |
||||||
|
* the default TrustManager from the JVM will be used. |
||||||
|
* |
||||||
|
* @param trustManager The TrustManager implementation to set, may be {@code null} |
||||||
|
* @see org.apache.commons.net.util.TrustManagerUtils |
||||||
|
*/ |
||||||
|
public void setTrustManager(TrustManager trustManager) { |
||||||
|
this.trustManager = trustManager; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the currently configured {@link HostnameVerifier}. |
||||||
|
* The verifier is only used on client mode connections. |
||||||
|
* @return A HostnameVerifier instance. |
||||||
|
* @since 3.4 |
||||||
|
*/ |
||||||
|
public HostnameVerifier getHostnameVerifier() |
||||||
|
{ |
||||||
|
return hostnameVerifier; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Override the default {@link HostnameVerifier} to use. |
||||||
|
* The verifier is only used on client mode connections. |
||||||
|
* @param newHostnameVerifier The HostnameVerifier implementation to set or <code>null</code> to disable. |
||||||
|
* @since 3.4 |
||||||
|
*/ |
||||||
|
public void setHostnameVerifier(HostnameVerifier newHostnameVerifier) |
||||||
|
{ |
||||||
|
hostnameVerifier = newHostnameVerifier; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Return whether or not endpoint identification using the HTTPS algorithm |
||||||
|
* on Java 1.7+ is enabled. The default behaviour is for this to be disabled. |
||||||
|
* |
||||||
|
* This check is only performed on client mode connections. |
||||||
|
* |
||||||
|
* @return True if enabled, false if not. |
||||||
|
* @since 3.4 |
||||||
|
*/ |
||||||
|
public boolean isEndpointCheckingEnabled() |
||||||
|
{ |
||||||
|
return tlsEndpointChecking; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Automatic endpoint identification checking using the HTTPS algorithm |
||||||
|
* is supported on Java 1.7+. The default behaviour is for this to be disabled. |
||||||
|
* |
||||||
|
* This check is only performed on client mode connections. |
||||||
|
* |
||||||
|
* @param enable Enable automatic endpoint identification checking using the HTTPS algorithm on Java 1.7+. |
||||||
|
* @since 3.4 |
||||||
|
*/ |
||||||
|
public void setEndpointCheckingEnabled(boolean enable) |
||||||
|
{ |
||||||
|
tlsEndpointChecking = enable; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Closes the connection to the FTP server and restores |
||||||
|
* connection parameters to the default values. |
||||||
|
* <p> |
||||||
|
* Calls {@code setSocketFactory(null)} and {@code setServerSocketFactory(null)} |
||||||
|
* to reset the factories that may have been changed during the session, |
||||||
|
* e.g. by {@link #execPROT(String)} |
||||||
|
* @throws IOException If an error occurs while disconnecting. |
||||||
|
* @since 3.0 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void disconnect() throws IOException |
||||||
|
{ |
||||||
|
super.disconnect(); |
||||||
|
if (plainSocket != null) { |
||||||
|
plainSocket.close(); |
||||||
|
} |
||||||
|
setSocketFactory(null); |
||||||
|
setServerSocketFactory(null); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Send the AUTH command with the specified mechanism. |
||||||
|
* @param mechanism The mechanism name to send with the command. |
||||||
|
* @return server reply. |
||||||
|
* @throws IOException If an I/O error occurs while sending |
||||||
|
* the command. |
||||||
|
* @since 3.0 |
||||||
|
*/ |
||||||
|
public int execAUTH(String mechanism) throws IOException |
||||||
|
{ |
||||||
|
return sendCommand(CMD_AUTH, mechanism); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Send the ADAT command with the specified authentication data. |
||||||
|
* @param data The data to send with the command. |
||||||
|
* @return server reply. |
||||||
|
* @throws IOException If an I/O error occurs while sending |
||||||
|
* the command. |
||||||
|
* @since 3.0 |
||||||
|
*/ |
||||||
|
public int execADAT(byte[] data) throws IOException |
||||||
|
{ |
||||||
|
if (data != null) |
||||||
|
{ |
||||||
|
return sendCommand(CMD_ADAT, Base64.encodeBase64StringUnChunked(data)); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
return sendCommand(CMD_ADAT); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Send the CCC command to the server. |
||||||
|
* The CCC (Clear Command Channel) command causes the underlying {@link SSLSocket} instance to be assigned |
||||||
|
* to a plain {@link Socket} instances |
||||||
|
* @return server reply. |
||||||
|
* @throws IOException If an I/O error occurs while sending |
||||||
|
* the command. |
||||||
|
* @since 3.0 |
||||||
|
*/ |
||||||
|
public int execCCC() throws IOException |
||||||
|
{ |
||||||
|
int repCode = sendCommand(CMD_CCC); |
||||||
|
// This will be performed by sendCommand(String, String)
|
||||||
|
// if (FTPReply.isPositiveCompletion(repCode)) {
|
||||||
|
// _socket_.close();
|
||||||
|
// _socket_ = plainSocket;
|
||||||
|
// _controlInput_ = new BufferedReader(
|
||||||
|
// new InputStreamReader(
|
||||||
|
// _socket_.getInputStream(), getControlEncoding()));
|
||||||
|
// _controlOutput_ = new BufferedWriter(
|
||||||
|
// new OutputStreamWriter(
|
||||||
|
// _socket_.getOutputStream(), getControlEncoding()));
|
||||||
|
// }
|
||||||
|
return repCode; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Send the MIC command with the specified data. |
||||||
|
* @param data The data to send with the command. |
||||||
|
* @return server reply. |
||||||
|
* @throws IOException If an I/O error occurs while sending |
||||||
|
* the command. |
||||||
|
* @since 3.0 |
||||||
|
*/ |
||||||
|
public int execMIC(byte[] data) throws IOException |
||||||
|
{ |
||||||
|
if (data != null) |
||||||
|
{ |
||||||
|
return sendCommand(CMD_MIC, Base64.encodeBase64StringUnChunked(data)); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
return sendCommand(CMD_MIC, ""); // perhaps "=" or just sendCommand(String)?
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Send the CONF command with the specified data. |
||||||
|
* @param data The data to send with the command. |
||||||
|
* @return server reply. |
||||||
|
* @throws IOException If an I/O error occurs while sending |
||||||
|
* the command. |
||||||
|
* @since 3.0 |
||||||
|
*/ |
||||||
|
public int execCONF(byte[] data) throws IOException |
||||||
|
{ |
||||||
|
if (data != null) |
||||||
|
{ |
||||||
|
return sendCommand(CMD_CONF, Base64.encodeBase64StringUnChunked(data)); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
return sendCommand(CMD_CONF, ""); // perhaps "=" or just sendCommand(String)?
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Send the ENC command with the specified data. |
||||||
|
* @param data The data to send with the command. |
||||||
|
* @return server reply. |
||||||
|
* @throws IOException If an I/O error occurs while sending |
||||||
|
* the command. |
||||||
|
* @since 3.0 |
||||||
|
*/ |
||||||
|
public int execENC(byte[] data) throws IOException |
||||||
|
{ |
||||||
|
if (data != null) |
||||||
|
{ |
||||||
|
return sendCommand(CMD_ENC, Base64.encodeBase64StringUnChunked(data)); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
return sendCommand(CMD_ENC, ""); // perhaps "=" or just sendCommand(String)?
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Parses the given ADAT response line and base64-decodes the data. |
||||||
|
* @param reply The ADAT reply to parse. |
||||||
|
* @return the data in the reply, base64-decoded. |
||||||
|
* @since 3.0 |
||||||
|
*/ |
||||||
|
public byte[] parseADATReply(String reply) |
||||||
|
{ |
||||||
|
if (reply == null) { |
||||||
|
return null; |
||||||
|
} else { |
||||||
|
return Base64.decodeBase64(extractPrefixedData("ADAT=", reply)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Extract the data from a reply with a prefix, e.g. PBSZ=1234 => 1234 |
||||||
|
* @param prefix the prefix to find |
||||||
|
* @param reply where to find the prefix |
||||||
|
* @return the remainder of the string after the prefix, or null if the prefix was not present. |
||||||
|
*/ |
||||||
|
private String extractPrefixedData(String prefix, String reply) { |
||||||
|
int idx = reply.indexOf(prefix); |
||||||
|
if (idx == -1) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
// N.B. Cannot use trim before substring as leading space would affect the offset.
|
||||||
|
return reply.substring(idx+prefix.length()).trim(); |
||||||
|
} |
||||||
|
|
||||||
|
// DEPRECATED - for API compatibility only - DO NOT USE
|
||||||
|
|
||||||
|
/** @deprecated - not used - may be removed in a future release */ |
||||||
|
@Deprecated |
||||||
|
public static String KEYSTORE_ALGORITHM; |
||||||
|
|
||||||
|
/** @deprecated - not used - may be removed in a future release */ |
||||||
|
@Deprecated |
||||||
|
public static String TRUSTSTORE_ALGORITHM; |
||||||
|
|
||||||
|
/** @deprecated - not used - may be removed in a future release */ |
||||||
|
@Deprecated |
||||||
|
public static String PROVIDER; |
||||||
|
|
||||||
|
/** @deprecated - not used - may be removed in a future release */ |
||||||
|
@Deprecated |
||||||
|
public static String STORE_TYPE; |
||||||
|
|
||||||
|
} |
||||||
|
/* kate: indent-width 4; replace-tabs on; */ |
@ -0,0 +1,52 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.apache.commons.net.ftp; |
||||||
|
|
||||||
|
/** |
||||||
|
* FTPS-specific commands. |
||||||
|
* @since 2.0 |
||||||
|
* @deprecated 3.0 DO NOT USE |
||||||
|
*/ |
||||||
|
@Deprecated |
||||||
|
public final class FTPSCommand { |
||||||
|
public static final int AUTH = 0; |
||||||
|
public static final int ADAT = 1; |
||||||
|
public static final int PBSZ = 2; |
||||||
|
public static final int PROT = 3; |
||||||
|
public static final int CCC = 4; |
||||||
|
|
||||||
|
public static final int AUTHENTICATION_SECURITY_MECHANISM = AUTH; |
||||||
|
public static final int AUTHENTICATION_SECURITY_DATA = ADAT; |
||||||
|
public static final int PROTECTION_BUFFER_SIZE = PBSZ; |
||||||
|
public static final int DATA_CHANNEL_PROTECTION_LEVEL = PROT; |
||||||
|
public static final int CLEAR_COMMAND_CHANNEL = CCC; |
||||||
|
|
||||||
|
private static final String[] _commands = {"AUTH","ADAT","PBSZ","PROT","CCC"}; |
||||||
|
|
||||||
|
/** |
||||||
|
* Retrieve the FTPS command string corresponding to a specified |
||||||
|
* command code. |
||||||
|
* |
||||||
|
* @param command The command code. |
||||||
|
* @return The FTPS command string corresponding to a specified |
||||||
|
* command code. |
||||||
|
*/ |
||||||
|
public static final String getCommand(int command) { |
||||||
|
return _commands[command]; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,74 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.apache.commons.net.ftp; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.net.InetAddress; |
||||||
|
import java.net.ServerSocket; |
||||||
|
|
||||||
|
import javax.net.ServerSocketFactory; |
||||||
|
import javax.net.ssl.SSLContext; |
||||||
|
import javax.net.ssl.SSLServerSocket; |
||||||
|
|
||||||
|
/** |
||||||
|
* Server socket factory for FTPS connections. |
||||||
|
* @since 2.2 |
||||||
|
*/ |
||||||
|
public class FTPSServerSocketFactory extends ServerSocketFactory { |
||||||
|
|
||||||
|
/** Factory for secure socket factories */ |
||||||
|
private final SSLContext context; |
||||||
|
|
||||||
|
public FTPSServerSocketFactory(SSLContext context) { |
||||||
|
this.context = context; |
||||||
|
} |
||||||
|
|
||||||
|
// Override the default superclass method
|
||||||
|
@Override |
||||||
|
public ServerSocket createServerSocket() throws IOException { |
||||||
|
return init(this.context.getServerSocketFactory().createServerSocket()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public ServerSocket createServerSocket(int port) throws IOException { |
||||||
|
return init(this.context.getServerSocketFactory().createServerSocket(port)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public ServerSocket createServerSocket(int port, int backlog) throws IOException { |
||||||
|
return init(this.context.getServerSocketFactory().createServerSocket(port, backlog)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public ServerSocket createServerSocket(int port, int backlog, InetAddress ifAddress) throws IOException { |
||||||
|
return init(this.context.getServerSocketFactory().createServerSocket(port, backlog, ifAddress)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the socket so newly accepted connections will use SSL client mode. |
||||||
|
* |
||||||
|
* @param socket the SSLServerSocket to initialise |
||||||
|
* @return the socket |
||||||
|
* @throws ClassCastException if socket is not an instance of SSLServerSocket |
||||||
|
*/ |
||||||
|
public ServerSocket init(ServerSocket socket) { |
||||||
|
((SSLServerSocket) socket).setUseClientMode(true); |
||||||
|
return socket; |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,112 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.apache.commons.net.ftp; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.net.InetAddress; |
||||||
|
import java.net.Socket; |
||||||
|
import java.net.UnknownHostException; |
||||||
|
|
||||||
|
import javax.net.SocketFactory; |
||||||
|
import javax.net.ssl.SSLContext; |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* Implementation of org.apache.commons.net.SocketFactory |
||||||
|
* |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
public class FTPSSocketFactory extends SocketFactory { |
||||||
|
|
||||||
|
private final SSLContext context; |
||||||
|
|
||||||
|
public FTPSSocketFactory(SSLContext context) { |
||||||
|
this.context = context; |
||||||
|
} |
||||||
|
|
||||||
|
// Override the default implementation
|
||||||
|
@Override |
||||||
|
public Socket createSocket() throws IOException{ |
||||||
|
return this.context.getSocketFactory().createSocket(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Socket createSocket(String address, int port) throws UnknownHostException, IOException { |
||||||
|
return this.context.getSocketFactory().createSocket(address, port); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Socket createSocket(InetAddress address, int port) throws IOException { |
||||||
|
return this.context.getSocketFactory().createSocket(address, port); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Socket createSocket(String address, int port, InetAddress localAddress, int localPort) |
||||||
|
throws UnknownHostException, IOException { |
||||||
|
return this.context.getSocketFactory().createSocket(address, port, localAddress, localPort); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { |
||||||
|
return this.context.getSocketFactory().createSocket(address, port, localAddress, localPort); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// DEPRECATED METHODS - for API compatibility only - DO NOT USE
|
||||||
|
|
||||||
|
/** @param port the port |
||||||
|
* @return the socket |
||||||
|
* @throws IOException on error |
||||||
|
* @deprecated (2.2) use {@link FTPSServerSocketFactory#createServerSocket(int) instead} */ |
||||||
|
@Deprecated |
||||||
|
public java.net.ServerSocket createServerSocket(int port) throws IOException { |
||||||
|
return this.init(this.context.getServerSocketFactory().createServerSocket(port)); |
||||||
|
} |
||||||
|
|
||||||
|
/** @param port the port |
||||||
|
* @param backlog the backlog |
||||||
|
* @return the socket |
||||||
|
* @throws IOException on error |
||||||
|
* @deprecated (2.2) use {@link FTPSServerSocketFactory#createServerSocket(int, int) instead} */ |
||||||
|
@Deprecated |
||||||
|
public java.net.ServerSocket createServerSocket(int port, int backlog) throws IOException { |
||||||
|
return this.init(this.context.getServerSocketFactory().createServerSocket(port, backlog)); |
||||||
|
} |
||||||
|
|
||||||
|
/** @param port the port |
||||||
|
* @param backlog the backlog |
||||||
|
* @param ifAddress the interface
|
||||||
|
* @return the socket |
||||||
|
* @throws IOException on error |
||||||
|
* @deprecated (2.2) use {@link FTPSServerSocketFactory#createServerSocket(int, int, InetAddress) instead} */ |
||||||
|
@Deprecated |
||||||
|
public java.net.ServerSocket createServerSocket(int port, int backlog, InetAddress ifAddress) throws IOException { |
||||||
|
return this.init(this.context.getServerSocketFactory().createServerSocket(port, backlog, ifAddress)); |
||||||
|
} |
||||||
|
|
||||||
|
/** @param socket the socket |
||||||
|
* @return the socket |
||||||
|
* @throws IOException on error |
||||||
|
* @deprecated (2.2) use {@link FTPSServerSocketFactory#init(java.net.ServerSocket)} */ |
||||||
|
@Deprecated |
||||||
|
public java.net.ServerSocket init(java.net.ServerSocket socket) throws IOException { |
||||||
|
((javax.net.ssl.SSLServerSocket) socket).setUseClientMode(true); |
||||||
|
return socket; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,60 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.apache.commons.net.ftp; |
||||||
|
|
||||||
|
import java.security.cert.CertificateException; |
||||||
|
import java.security.cert.X509Certificate; |
||||||
|
|
||||||
|
import javax.net.ssl.X509TrustManager; |
||||||
|
|
||||||
|
/** |
||||||
|
* Do not use. |
||||||
|
* @since 2.0 |
||||||
|
* @deprecated 3.0 use |
||||||
|
* {@link org.apache.commons.net.util.TrustManagerUtils#getValidateServerCertificateTrustManager() |
||||||
|
* TrustManagerUtils#getValidateServerCertificateTrustManager()} instead |
||||||
|
*/ |
||||||
|
@Deprecated |
||||||
|
public class FTPSTrustManager implements X509TrustManager |
||||||
|
{ |
||||||
|
private static final X509Certificate[] EMPTY_X509CERTIFICATE_ARRAY = new X509Certificate[]{}; |
||||||
|
|
||||||
|
/** |
||||||
|
* No-op |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void checkClientTrusted(X509Certificate[] certificates, String authType) |
||||||
|
{ |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void checkServerTrusted(X509Certificate[] certificates, String authType) throws CertificateException |
||||||
|
{ |
||||||
|
for (X509Certificate certificate : certificates) |
||||||
|
{ |
||||||
|
certificate.checkValidity(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public X509Certificate[] getAcceptedIssuers() |
||||||
|
{ |
||||||
|
return EMPTY_X509CERTIFICATE_ARRAY; |
||||||
|
} |
||||||
|
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue