package com.openfarmanager.android.core.network.ftp;
import android.database.Cursor;
import com.openfarmanager.android.App;
import com.openfarmanager.android.R;
import com.openfarmanager.android.core.DataStorageHelper;
import com.openfarmanager.android.core.dbadapters.NetworkAccountDbAdapter;
import com.openfarmanager.android.core.network.NetworkApi;
import com.openfarmanager.android.filesystem.FileProxy;
import com.openfarmanager.android.filesystem.FileSystemScanner;
import com.openfarmanager.android.filesystem.FtpFile;
import com.openfarmanager.android.model.NetworkAccount;
import com.openfarmanager.android.model.NetworkEnum;
import com.openfarmanager.android.model.exeptions.FtpDirectoryDeleteException;
import com.openfarmanager.android.model.exeptions.InAppAuthException;
import com.openfarmanager.android.model.exeptions.NetworkException;
import static com.openfarmanager.android.utils.Extensions.tryParse;
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;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
/**
* @author Vlad Namashko
*/
public class FtpAPI implements NetworkApi {
public static final String FTP_SERVER = "server";
public static final String FTP_PORT = "port";
public static final String FTP_MODE = "mode";
public static final String FTP_USER = "user";
public static final String FTP_PASSWORD = "password";
private FTPClient mFtpClient;
private FtpAccount mCurrentAccount;
public void connectAndSave(String server, int port, boolean activeMode,
String user, String password) throws InAppAuthException {
connect(server, port, activeMode, user, password);
// save account to db
JSONObject authData = new JSONObject();
try {
authData.put(FTP_SERVER, server);
authData.put(FTP_PORT, port);
authData.put(FTP_MODE, activeMode);
authData.put(FTP_USER, user);
authData.put(FTP_PASSWORD, password);
long currentAccountId = NetworkAccountDbAdapter.insert(
server + "(" + user + ")",
NetworkEnum.FTP.ordinal(), authData.toString());
List<NetworkAccount> accounts = parseAccounts(NetworkAccountDbAdapter.getAccountById(currentAccountId));
if (accounts.size() == 1) {
mCurrentAccount = (FtpAccount) accounts.get(0);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
public void connect(FtpAccount ftpAccount) throws InAppAuthException {
mCurrentAccount = ftpAccount;
connect(ftpAccount.getServer(), ftpAccount.getPort(), ftpAccount.getMode(),
ftpAccount.getUser(), ftpAccount.getPassword());
}
public void connect(String server, int port, boolean activeMode,
String user, String password) throws InAppAuthException {
mFtpClient = new FTPClient();
try {
mFtpClient.connect(server, port);
} catch (Exception e) {
e.printStackTrace();
throw new InAppAuthException(App.sInstance.getString(R.string.error_wrong_ftp_host));
}
try {
if (!activeMode) {
mFtpClient.enterRemotePassiveMode();
}
mFtpClient.login(user, password);
//mFtpClient.setFileType(FTP.BINARY_FILE_TYPE);
} catch (Exception e) {
e.printStackTrace();
throw new InAppAuthException(App.sInstance.getString(R.string.error_ftp_io));
}
if (!FTPReply.isPositiveCompletion(mFtpClient.getReplyCode())) {
try {
mFtpClient.disconnect();
} catch (Exception ignore) {}
throw new InAppAuthException(App.sInstance.getString(R.string.error_wrong_credentials));
}
String charset = App.sInstance.getSettings().getCharset(server);
if (charset != null) {
mFtpClient.setControlEncoding(charset);
}
}
public List<FileProxy> getDirectoryFiles(String path) throws NetworkException {
List<FileProxy> files = new ArrayList<FileProxy>();
try {
if (!path.equals(mFtpClient.printWorkingDirectory())) {
mFtpClient.changeWorkingDirectory(path);
}
FTPFile[] ftpFiles = mFtpClient.listFiles();
for (FTPFile ftpFile : ftpFiles) {
files.add(new FtpFile(path, ftpFile));
}
FileSystemScanner.sInstance.sort(files);
} catch (SocketTimeoutException e) {
throw NetworkException.handleNetworkException(e);
} catch (IOException e) {
if ("FTPConnection closed".equals(e.getMessage())) {
throw NetworkException.handleNetworkException(e);
}
} catch (Exception e) {
e.printStackTrace();
//throw NetworkException.handleNetworkException(e);
}
return files;
}
public void setCharset(Charset charset) {
mFtpClient.setControlEncoding(charset.name());
App.sInstance.getSettings().saveCharset(mCurrentAccount.getServer(), charset.name());
}
public int getAuthorizedAccountsCount() {
return NetworkAccountDbAdapter.count(NetworkEnum.FTP.ordinal());
}
@Override
public List<NetworkAccount> getAuthorizedAccounts() {
return parseAccounts(NetworkAccountDbAdapter.getAccounts(NetworkEnum.FTP.ordinal()));
}
private List<NetworkAccount> parseAccounts(Cursor cursor) {
List<NetworkAccount> accounts = new ArrayList<NetworkAccount>();
if (cursor == null) {
return accounts;
}
try {
int idxId = cursor.getColumnIndex(NetworkAccountDbAdapter.Columns.ID);
int idxUserName = cursor.getColumnIndex(NetworkAccountDbAdapter.Columns.USER_NAME);
int idxAuthData = cursor.getColumnIndex(NetworkAccountDbAdapter.Columns.AUTH_DATA);
while(cursor.moveToNext()) {
String authData = cursor.getString(idxAuthData);
try {
JSONObject data = new JSONObject(authData);
FtpAccount account = new FtpAccount(cursor.getLong(idxId), cursor.getString(idxUserName),
data.getString(FTP_SERVER), tryParse(data.getString(FTP_PORT), 21),
tryParse(data.getString(FTP_MODE), false), data.getString(FTP_USER), data.getString(FTP_PASSWORD));
accounts.add(account);
} catch (JSONException e) {
e.printStackTrace();
}
}
} finally {
cursor.close();
DataStorageHelper.closeDatabase();
}
return accounts;
}
public FTPClient client() {
return mFtpClient;
}
@Override
public NetworkAccount newAccount() {
return new FtpAccount(-1, App.sInstance.getResources().getString(com.openfarmanager.android.R.string.btn_new),
null, 0, false, null, null);
}
@Override
public NetworkAccount getCurrentNetworkAccount() {
return mCurrentAccount;
}
@Override
public void delete(final FileProxy file) throws Exception {
if (file.isDirectory()) {
if (!mFtpClient.removeDirectory(file.getFullPath())) {
if (App.sInstance.getSettings().isFtpAllowRecursiveDelete()) {
for (FileProxy proxy : getDirectoryFiles(file.getFullPath())) {
delete(proxy);
}
mFtpClient.removeDirectory(file.getFullPath());
} else {
throw new FtpDirectoryDeleteException();
}
}
} else {
mFtpClient.deleteFile(file.getFullPath());
}
}
@Override
public String createDirectory(String baseDirectory, String newDirectoryName) throws Exception {
String path = baseDirectory + "/" + newDirectoryName;
mFtpClient.makeDirectory(path);
return path;
}
@Override
public List<FileProxy> search(String path, String query) {
throw new RuntimeException();
}
@Override
public boolean rename(FileProxy srcFile, String s) throws Exception {
String fullPath = srcFile.getFullPath();
String workingDir = fullPath.substring(0, fullPath.lastIndexOf('/') + 1);
mFtpClient.changeWorkingDirectory(workingDir);
mFtpClient.rename(fullPath.substring(fullPath.lastIndexOf('/') + 1), s.substring(s.lastIndexOf('/') + 1));
return true;
}
public static class FtpAccount extends NetworkAccount {
private String mServer;
private int mPort;
private boolean mMode;
private String mUser;
private String mPassword;
public FtpAccount(long id, String userName, JSONObject data) throws JSONException {
this(id, userName, data.getString(FTP_SERVER), tryParse(data.getString(FTP_PORT), 21),
tryParse(data.getString(FTP_MODE), false), data.getString(FTP_USER), data.getString(FTP_PASSWORD));
}
public FtpAccount(long id, String userName, String server, int port, boolean mode, String user, String password) {
mId = id;
mUserName = userName;
mServer = server;
mPort = port;
mMode = mode;
mUser = user;
mPassword = password;
}
public String getServer() {
return mServer;
}
public int getPort() {
return mPort;
}
public boolean getMode() {
return mMode;
}
public String getUser() {
return mUser;
}
public String getPassword() {
return mPassword;
}
@Override
public NetworkEnum getNetworkType() {
return NetworkEnum.FTP;
}
}
}