/*
* JBoss, Home of Professional Open Source.
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
package org.teiid.resource.adapter.ftp;
import static org.teiid.core.util.Assertion.*;
import static org.apache.commons.net.ftp.FTPClient.ACTIVE_LOCAL_DATA_CONNECTION_MODE;
import static org.apache.commons.net.ftp.FTPClient.PASSIVE_LOCAL_DATA_CONNECTION_MODE;
import static org.apache.commons.net.ftp.FTP.ASCII_FILE_TYPE;
import static org.apache.commons.net.ftp.FTP.EBCDIC_FILE_TYPE;
import static org.apache.commons.net.ftp.FTP.BINARY_FILE_TYPE;
import static org.apache.commons.net.ftp.FTP.LOCAL_FILE_TYPE;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.util.Arrays;
import java.util.Map;
import javax.net.ssl.KeyManager;
import javax.net.ssl.TrustManager;
import javax.resource.ResourceException;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPClientConfig;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.commons.net.ftp.FTPSClient;
import org.apache.commons.net.util.KeyManagerUtils;
import org.apache.commons.net.util.TrustManagerUtils;
import org.teiid.core.BundleUtil;
import org.teiid.core.TeiidRuntimeException;
import org.teiid.core.util.StringUtil;
import org.teiid.resource.spi.BasicConnectionFactory;
import org.teiid.resource.spi.BasicManagedConnectionFactory;
public class FtpManagedConnectionFactory extends BasicManagedConnectionFactory {
private static final long serialVersionUID = -687763504336137294L;
public static final BundleUtil UTIL = BundleUtil.getBundleUtil(FtpManagedConnectionFactory.class);
private String parentDirectory;
private String fileMapping;
protected FTPClientConfig config;
protected String username;
protected String host;
protected String password;
protected Integer port = FTP.DEFAULT_PORT;
protected Integer bufferSize = 2048;
protected Integer clientMode = ACTIVE_LOCAL_DATA_CONNECTION_MODE;
protected Integer fileType = BINARY_FILE_TYPE;
protected String controlEncoding = FTP.DEFAULT_CONTROL_ENCODING;
private Integer connectTimeout;
private Integer defaultTimeout;
private Integer dataTimeout;
private Boolean isFtps = false;
private Boolean useClientMode;
private Boolean sessionCreation;
private String authValue;
private String certificate;
private TrustManager trustManager;
private String[] cipherSuites;
private String[] protocols;
private String keyPath;
private String keyPassword;
private KeyManager keyManager;
private Boolean needClientAuth;
private Boolean wantsClientAuth;
private Boolean implicit = false;
private String execProt = "P"; //$NON-NLS-1$
private String protocol;
public FTPClientConfig getConfig() {
return config;
}
public String getParentDirectory() {
return parentDirectory;
}
public void setParentDirectory(String parentDirectory) {
isNotNull(parentDirectory, UTIL.getString("parentdirectory_not_null"));//$NON-NLS-1$
this.parentDirectory = parentDirectory;
}
public String getFileMapping() {
return fileMapping;
}
public void setFileMapping(String fileMapping) {
this.fileMapping = fileMapping;
}
public void setConfig(FTPClientConfig config) {
isNotNull(config, UTIL.getString("ftp_client_config"));//$NON-NLS-1$
this.config = config;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
isNotNull(username, UTIL.getString("ftp_client_username"));//$NON-NLS-1$
this.username = username;
}
public String getHost() {
return host;
}
public void setHost(String host) {
assertTrue(host!= null && host.length() > 0, UTIL.getString("ftp_client_host"));//$NON-NLS-1$
this.host = host;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
isNotNull(password, UTIL.getString("ftp_client_password"));//$NON-NLS-1$
this.password = password;
}
public Integer getPort() {
return port;
}
public void setPort(Integer port) {
assertTrue(port > 0, UTIL.getString("ftp_client_port"));//$NON-NLS-1$
this.port = port;
}
public Integer getBufferSize() {
return bufferSize;
}
public void setBufferSize(Integer bufferSize) {
this.bufferSize = bufferSize;
}
public Integer getClientMode() {
return clientMode;
}
public void setClientMode(Integer clientMode) {
assertTrue(clientMode == ACTIVE_LOCAL_DATA_CONNECTION_MODE || clientMode == PASSIVE_LOCAL_DATA_CONNECTION_MODE, UTIL.getString("ftp_client_clientMode", clientMode));//$NON-NLS-1$
this.clientMode = clientMode;
}
public Integer getFileType() {
return fileType;
}
/**
* File types defined by {@link org.apache.commons.net.ftp.FTP} constants:
* <ul>
* <li>{@link org.apache.commons.net.ftp.FTP#ASCII_FILE_TYPE}</li>
* <li>{@link org.apache.commons.net.ftp.FTP#EBCDIC_FILE_TYPE}</li>
* <li>{@link org.apache.commons.net.ftp.FTP#BINARY_FILE_TYPE}</li>
* <li>{@link org.apache.commons.net.ftp.FTP#LOCAL_FILE_TYPE}</li>
* </ul>
* @param fileType The file type.
*/
public void setFileType(Integer fileType) {
assertTrue(fileType == ASCII_FILE_TYPE || fileType == EBCDIC_FILE_TYPE || fileType == BINARY_FILE_TYPE || fileType == LOCAL_FILE_TYPE);
this.fileType = fileType;
}
public String getControlEncoding() {
return controlEncoding;
}
public void setControlEncoding(String controlEncoding) {
isNotNull(controlEncoding);
this.controlEncoding = controlEncoding;
}
public Integer getConnectTimeout() {
return connectTimeout;
}
public void setConnectTimeout(Integer connectTimeout) {
this.connectTimeout = connectTimeout;
}
public Integer getDefaultTimeout() {
return defaultTimeout;
}
public void setDefaultTimeout(Integer defaultTimeout) {
this.defaultTimeout = defaultTimeout;
}
public Integer getDataTimeout() {
return dataTimeout;
}
public void setDataTimeout(Integer dataTimeout) {
this.dataTimeout = dataTimeout;
}
public Boolean getIsFtps() {
return isFtps;
}
public void setIsFtps(Boolean isFtps) {
this.isFtps = isFtps;
}
public Boolean getUseClientMode() {
return useClientMode;
}
public void setUseClientMode(Boolean useClientMode) {
this.useClientMode = useClientMode;
}
public Boolean getSessionCreation() {
return sessionCreation;
}
public void setSessionCreation(Boolean sessionCreation) {
this.sessionCreation = sessionCreation;
}
public String getAuthValue() {
return authValue;
}
public void setAuthValue(String authValue) {
isNotNull(authValue);
this.authValue = authValue;
}
public String getCertificate() {
return certificate;
}
public void setCertificate(String certificate) {
this.certificate = certificate;
if(this.certificate != null && Files.exists(Paths.get(this.certificate))) {
try {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); //$NON-NLS-1$
InputStream in = Files.newInputStream(Paths.get(this.certificate));
Certificate cert = certificateFactory.generateCertificate(in);
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
keyStore.setCertificateEntry("alias", cert); //$NON-NLS-1$
trustManager = TrustManagerUtils.getDefaultTrustManager(keyStore);
} catch (IOException | GeneralSecurityException e) {
throw new TeiidRuntimeException(UTIL.getString("ftp_certificate_path", certificate, e)); //$NON-NLS-1$
}
}
}
public String getCipherSuites() {
return formStringFromArray(cipherSuites);
}
private String formStringFromArray(String[] cipherSuites) {
String result = ""; //$NON-NLS-1$
for(String str : cipherSuites) {
result = result + str + ","; //$NON-NLS-1$
}
return result.substring(0, result.length() - 1);
}
public void setCipherSuites(String cipherSuites) {
assertTrue(cipherSuites != null && cipherSuites.length() > 0, UTIL.getString("ftp_client_invalid_array", cipherSuites, "cipherSuites"));//$NON-NLS-1$ //$NON-NLS-12$
this.cipherSuites = cipherSuites.split(","); //$NON-NLS-1$
}
public String getProtocols() {
return formStringFromArray(protocols);
}
public void setProtocols(String protocols) {
assertTrue(protocols != null && protocols.length() > 0, UTIL.getString("ftp_client_invalid_array", protocols, "protocols"));//$NON-NLS-1$ //$NON-NLS-12$
this.protocols = protocols.split(","); //$NON-NLS-1$
}
public String getKeyPath() {
return keyPath;
}
public void setKeyPath(String keyPath) {
this.keyPath = keyPath;
if(this.keyPath != null && Files.exists(Paths.get(this.keyPath))) {
if(this.keyPassword == null){
this.keyPassword = ""; //$NON-NLS-1$
}
try {
this.keyManager = KeyManagerUtils.createClientKeyManager(Paths.get(this.keyPath).toFile(), this.keyPassword);
} catch (IOException | GeneralSecurityException e) {
throw new TeiidRuntimeException(UTIL.getString("ftp_ketstore_path", this.keyPath, e)); //$NON-NLS-1$
}
}
}
public String getKeyPassword() {
return keyPassword;
}
public void setKeyPassword(String keyPassword) {
this.keyPassword = keyPassword;
}
public Boolean getNeedClientAuth() {
return needClientAuth;
}
public void setNeedClientAuth(Boolean needClientAuth) {
this.needClientAuth = needClientAuth;
}
public Boolean getWantsClientAuth() {
return wantsClientAuth;
}
public void setWantsClientAuth(Boolean wantsClientAuth) {
this.wantsClientAuth = wantsClientAuth;
}
public Boolean isImplicit() {
return implicit;
}
public void setImplicit(Boolean implicit) {
this.implicit = implicit;
}
public String getExecProt() {
return execProt;
}
public void setExecProt(String execProt) {
this.execProt = execProt;
}
public String getProtocol() {
return protocol;
}
public void setProtocol(String protocol) {
// TODO: add more validation for a valid protocol
assertTrue(protocol != null && protocol.length() > 0 && !protocol.contains(" ")); //$NON-NLS-1$
this.protocol = protocol;
}
@SuppressWarnings("serial")
@Override
public BasicConnectionFactory<FtpFileConnectionImpl> createConnectionFactory()throws ResourceException {
final Map<String, String> map = StringUtil.valueOf(this.fileMapping, Map.class);
return new BasicConnectionFactory<FtpFileConnectionImpl>() {
@Override
public FtpFileConnectionImpl getConnection() throws ResourceException {
try {
return new FtpFileConnectionImpl(createClient(), parentDirectory, map);
} catch (IOException e) {
throw new ResourceException(e);
}
}};
}
protected FTPClient createClient() throws IOException, ResourceException {
FTPClient client = createClientInstance();
beforeConnectProcessing(client);
client.connect(this.host, this.port);
if (!FTPReply.isPositiveCompletion(client.getReplyCode())) {
throw new ResourceException(UTIL.getString("ftp_connect_failed", this.host, this.port)); //$NON-NLS-1$
}
if (!client.login(this.username, this.password)) {
throw new IllegalStateException(UTIL.getString("ftp_login_failed", client.getReplyString())); //$NON-NLS-1$
}
afterConnectProcessing(client);
return client;
}
private FTPClient createClientInstance() {
if(this.isFtps) {
if(this.getProtocol() != null) {
return new FTPSClient(this.protocol, this.implicit);
}
return new FTPSClient(this.implicit);
}
return new FTPClient();
}
private void beforeConnectProcessing(FTPClient client) throws IOException {
client.configure(this.config);
if (this.connectTimeout != null) {
client.setConnectTimeout(this.connectTimeout);
}
if (this.defaultTimeout != null) {
client.setDefaultTimeout(this.defaultTimeout);
}
if (this.dataTimeout != null) {
client.setDataTimeout(this.dataTimeout);
}
client.setControlEncoding(this.controlEncoding);
if(this.isFtps){
FTPSClient ftpsClient = (FTPSClient) client;
ftpsClient.execPBSZ(0);
ftpsClient.execPROT(this.execProt);
}
}
private void afterConnectProcessing(FTPClient client) throws IOException {
if (this.parentDirectory == null) {
throw new IOException(UTIL.getString("parentdirectory_not_set")); //$NON-NLS-1$
}
if(!client.changeWorkingDirectory(this.getParentDirectory())){
throw new IOException(UTIL.getString("ftp_dir_not_exist", this.getParentDirectory())); //$NON-NLS-1$
}
updateClientMode(client);
client.setFileType(this.fileType);
client.setBufferSize(this.bufferSize);
if(this.isFtps) {
FTPSClient ftpsClient = (FTPSClient) client;
if(this.getAuthValue() != null) {
ftpsClient.setAuthValue(this.authValue);
}
if (this.trustManager != null) {
ftpsClient.setTrustManager(this.trustManager);
}
if (this.cipherSuites != null) {
ftpsClient.setEnabledCipherSuites(this.cipherSuites);
}
if (this.protocols != null) {
ftpsClient.setEnabledProtocols(this.protocols);
}
if (this.sessionCreation != null) {
ftpsClient.setEnabledSessionCreation(this.sessionCreation);
}
if (this.useClientMode != null) {
ftpsClient.setUseClientMode(this.useClientMode);
}
if (this.sessionCreation != null) {
ftpsClient.setEnabledSessionCreation(this.sessionCreation);
}
if (this.keyManager != null) {
ftpsClient.setKeyManager(this.keyManager);
}
if (this.needClientAuth != null) {
ftpsClient.setNeedClientAuth(this.needClientAuth);
}
if (this.wantsClientAuth != null) {
ftpsClient.setWantClientAuth(this.wantsClientAuth);
}
}
}
private void updateClientMode(FTPClient client) {
switch (this.clientMode) {
case ACTIVE_LOCAL_DATA_CONNECTION_MODE:
client.enterLocalActiveMode();
break;
case PASSIVE_LOCAL_DATA_CONNECTION_MODE:
client.enterLocalPassiveMode();
break;
default:
break;
}
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((this.parentDirectory == null) ? 0 : this.parentDirectory.hashCode());
result = prime * result + ((this.username == null) ? 0 : this.username.hashCode());
result = prime * result + ((this.password == null) ? 0 : this.password.hashCode());
result = prime * result + ((this.host == null) ? 0 : this.host.hashCode());
result = prime * result + ((this.controlEncoding == null) ? 0 : this.controlEncoding.hashCode());
result = prime * result + ((this.connectTimeout == null) ? 0 : this.connectTimeout.hashCode());
result = prime * result + ((this.dataTimeout == null) ? 0 : this.dataTimeout.hashCode());
result = prime * result + ((this.defaultTimeout == null) ? 0 : this.defaultTimeout.hashCode());
result = prime * result + this.port;
result = prime * result + this.fileType;
result = prime * result + this.clientMode;
result = prime * result + this.bufferSize;
if(this.isFtps) {
result = prime * result + ((this.certificate == null) ? 0 : this.certificate.hashCode());
result = prime * result + ((this.keyPath == null) ? 0 : this.keyPath.hashCode());
result = prime * result + ((this.keyPassword == null) ? 0 : this.keyPassword.hashCode());
result = prime * result + ((this.authValue == null) ? 0 : this.authValue.hashCode());
result = prime * result + ((this.protocol == null) ? 0 : this.protocol.hashCode());
result = prime * result + ((this.execProt == null) ? 0 : this.execProt.hashCode());
result = prime * result + ((this.useClientMode == null) ? 0 : this.useClientMode.hashCode());
result = prime * result + ((this.sessionCreation == null) ? 0 : this.sessionCreation.hashCode());
result = prime * result + ((this.needClientAuth == null) ? 0 : this.needClientAuth.hashCode());
result = prime * result + ((this.wantsClientAuth == null) ? 0 : this.wantsClientAuth.hashCode());
result = prime * result + ((this.implicit) ? 1231 : 1237);
result = prime * result + ((this.cipherSuites == null) ? 0 : Arrays.hashCode(this.cipherSuites));
result = prime * result + ((this.protocols == null) ? 0 : Arrays.hashCode(this.protocols));
}
return result;
}
@Override
public boolean equals(Object obj) {
if(this == obj) {
return true;
}
if(null == obj) {
return false;
}
if(getClass() != obj.getClass()) {
return false;
}
FtpManagedConnectionFactory other = (FtpManagedConnectionFactory) obj;
if (!checkEquals(this.parentDirectory, other.parentDirectory)) {
return false;
}
if (!checkEquals(this.username, other.username)) {
return false;
}
if (!checkEquals(this.password, other.password)) {
return false;
}
if (!checkEquals(this.host, other.host)) {
return false;
}
if (!checkEquals(this.controlEncoding, other.controlEncoding)) {
return false;
}
if(this.connectTimeout != other.connectTimeout) {
return false;
}
if(this.dataTimeout != other.dataTimeout) {
return false;
}
if(this.defaultTimeout != other.defaultTimeout) {
return false;
}
if(this.port != other.port) {
return false;
}
if(this.fileType != other.fileType) {
return false;
}
if(this.clientMode != other.clientMode) {
return false;
}
if(this.bufferSize != other.bufferSize) {
return false;
}
if(this.isFtps) {
if (!checkEquals(this.certificate, other.certificate)) {
return false;
}
if (!checkEquals(this.keyPath, other.keyPath)) {
return false;
}
if (!checkEquals(this.keyPassword, other.keyPassword)) {
return false;
}
if (!checkEquals(this.authValue, other.authValue)) {
return false;
}
if (!checkEquals(this.protocol, other.protocol)) {
return false;
}
if (!checkEquals(this.execProt, other.execProt)) {
return false;
}
if(!this.useClientMode.equals(other.useClientMode)) {
return false;
}
if(!this.sessionCreation.equals(other.sessionCreation)) {
return false;
}
if(!this.needClientAuth.equals(other.needClientAuth)) {
return false;
}
if(!this.wantsClientAuth.equals(other.wantsClientAuth)) {
return false;
}
if(this.implicit != other.implicit) {
return false;
}
if(!Arrays.equals(this.cipherSuites, other.cipherSuites)) {
return false;
}
if(!Arrays.equals(this.protocols, other.protocols)) {
return false;
}
}
return true;
}
}