You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pekko.apache.org by fa...@apache.org on 2024/06/03 13:00:21 UTC

(pekko-connectors) branch main updated: reduce size of LegacyFtpsClient (#683)

This is an automated email from the ASF dual-hosted git repository.

fanningpj pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/pekko-connectors.git


The following commit(s) were added to refs/heads/main by this push:
     new 0050c1415 reduce size of LegacyFtpsClient (#683)
0050c1415 is described below

commit 0050c1415baca478ae586781268df54601a7850c
Author: PJ Fanning <pj...@users.noreply.github.com>
AuthorDate: Mon Jun 3 14:00:16 2024 +0100

    reduce size of LegacyFtpsClient (#683)
    
    * reduce size of LegacyFtpsClient
    
    * bin compat with pekko-connectors-ftp 1.0
    
    * more
    
    Update LegacyFtpsClient.java
    
    move package
---
 .../apache/commons/net/ftp/LegacyFtpsClient.java   | 208 +++++
 .../connectors/ftp/impl/LegacyFtpsClient.java      | 836 ---------------------
 .../ftpsclient.backwards.excludes                  |  26 -
 .../pekko/stream/connectors/ftp/impl/FtpLike.scala |   6 +-
 .../connectors/ftp/impl/FtpSourceFactory.scala     |   8 +-
 .../connectors/ftp/impl/FtpsOperations.scala       |   8 +-
 .../stream/connectors/ftp/javadsl/FtpApi.scala     |   4 +-
 .../apache/pekko/stream/connectors/ftp/model.scala |  10 +-
 .../stream/connectors/ftp/scaladsl/FtpApi.scala    |   4 +-
 9 files changed, 228 insertions(+), 882 deletions(-)

diff --git a/ftp/src/main/java/org/apache/commons/net/ftp/LegacyFtpsClient.java b/ftp/src/main/java/org/apache/commons/net/ftp/LegacyFtpsClient.java
new file mode 100644
index 000000000..28cfc63e2
--- /dev/null
+++ b/ftp/src/main/java/org/apache/commons/net/ftp/LegacyFtpsClient.java
@@ -0,0 +1,208 @@
+/*
+ * 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.Inet6Address;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+import javax.net.ssl.SSLSocket;
+
+/**
+ * This class is a workaround for code introduced in commons-net 3.9.0 that breaks
+ * FTPS support when HTTP proxies are used.
+ * See https://issues.apache.org/jira/browse/NET-718
+ * 
+ * Derived from https://github.com/apache/commons-net/blob/master/src/main/java/org/apache/commons/net/ftp/FTPSClient.java
+ * and https://github.com/apache/commons-net/blob/master/src/main/java/org/apache/commons/net/ftp/FTPClient.java
+ */
+public final class LegacyFtpsClient extends FTPSClient {
+
+    /** Default secure socket protocol name, i.e. TLS */
+    private static final String DEFAULT_PROTOCOL = "TLS";
+
+    /**
+     * Constructor for LegacyFtpsClient calls.
+     *
+     * Sets protocol to {@link #DEFAULT_PROTOCOL} - i.e. TLS
+     * @param isImplicit True - Implicit Mode / False - Explicit Mode
+     */
+    public LegacyFtpsClient(final boolean isImplicit) {
+        super(DEFAULT_PROTOCOL, isImplicit);
+    }
+
+    /**
+     * Not implemented. FTP.STREAM_TRANSFER_MODE is always used.
+     */
+    @Override
+    public boolean setFileTransferMode(final int mode) throws IOException {
+        return false;
+    }
+
+    /**
+     * 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_(final String command, final String arg) throws IOException {
+        final Socket socket = _openFTPDataConnection_(command, arg);
+        _prepareDataSocket_(socket);
+        if (socket instanceof SSLSocket) {
+            final SSLSocket sslSocket = (SSLSocket) socket;
+
+            sslSocket.setUseClientMode(isClientMode());
+            sslSocket.setEnableSessionCreation(isCreation());
+
+            // server mode
+            if (!isClientMode()) {
+                sslSocket.setNeedClientAuth(isNeedClientAuth());
+                sslSocket.setWantClientAuth(isWantClientAuth());
+            }
+            if (getSuites() != null) {
+                sslSocket.setEnabledCipherSuites(getSuites());
+            }
+            if (getProtocols() != null) {
+                sslSocket.setEnabledProtocols(getProtocols());
+            }
+            sslSocket.startHandshake();
+        }
+
+        return socket;
+    }
+
+    private Socket _openFTPDataConnection_(final String command, final String arg) throws IOException {
+        final int dataConnectionMode = getDataConnectionMode();
+        if (dataConnectionMode != ACTIVE_LOCAL_DATA_CONNECTION_MODE && dataConnectionMode != PASSIVE_LOCAL_DATA_CONNECTION_MODE) {
+            return null;
+        }
+        final boolean isInet6Address = getRemoteAddress() instanceof Inet6Address;
+        final Socket socket;
+        final int soTimeoutMillis = DurationUtils.toMillisInt(getDataTimeout());
+        if (dataConnectionMode == ACTIVE_LOCAL_DATA_CONNECTION_MODE) {
+            // if no activePortRange was set (correctly) -> getActivePort() = 0
+            // -> new ServerSocket(0) -> bind to any free local port
+            try (final ServerSocket server = _serverSocketFactory_.createServerSocket(getActivePort(), 1, getHostAddress())) {
+                // Try EPRT only if remote server is over IPv6, if not use PORT,
+                // because EPRT has no advantage over PORT on IPv4.
+                // It could even have the disadvantage,
+                // that EPRT will make the data connection fail, because
+                // today's intelligent NAT Firewalls are able to
+                // substitute IP addresses in the PORT command,
+                // but might not be able to recognize the EPRT command.
+                if (isInet6Address) {
+                    if (!FTPReply.isPositiveCompletion(eprt(getReportHostAddress(), server.getLocalPort()))) {
+                        return null;
+                    }
+                } else if (!FTPReply.isPositiveCompletion(port(getReportHostAddress(), server.getLocalPort()))) {
+                    return null;
+                }
+                if (getRestartOffset() > 0 && !restart(getRestartOffset())) {
+                    return null;
+                }
+                if (!FTPReply.isPositivePreliminary(sendCommand(command, arg))) {
+                    return null;
+                }
+                // For now, let's just use the data timeout value for waiting for
+                // the data connection. It may be desirable to let this be a
+                // separately configurable value. In any case, we really want
+                // to allow preventing the accept from blocking indefinitely.
+                if (soTimeoutMillis >= 0) {
+                    server.setSoTimeout(soTimeoutMillis);
+                }
+                socket = server.accept();
+                // Ensure the timeout is set before any commands are issued on the new socket
+                if (soTimeoutMillis >= 0) {
+                    socket.setSoTimeout(soTimeoutMillis);
+                }
+                if (getReceiveDataSocketBufferSize() > 0) {
+                    socket.setReceiveBufferSize(getReceiveDataSocketBufferSize());
+                }
+                if (getSendDataSocketBufferSize() > 0) {
+                    socket.setSendBufferSize(getSendDataSocketBufferSize());
+                }
+            }
+        } else {
+            // We must be in PASSIVE_LOCAL_DATA_CONNECTION_MODE
+            // Try EPSV command first on IPv6 - and IPv4 if enabled.
+            // When using IPv4 with NAT it has the advantage
+            // to work with more rare configurations.
+            // E.g. if FTP server has a static PASV address (external network)
+            // and the client is coming from another internal network.
+            // In that case the data connection after PASV command would fail,
+            // while EPSV would make the client succeed by taking just the port.
+            final boolean attemptEPSV = isUseEPSVwithIPv4() || isInet6Address;
+            if (attemptEPSV && epsv() == FTPReply.ENTERING_EPSV_MODE) {
+                _parseExtendedPassiveModeReply(_replyLines.get(0));
+            } 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));
+            }
+            socket = _socketFactory_.createSocket();
+            if (getReceiveDataSocketBufferSize() > 0) {
+                socket.setReceiveBufferSize(getReceiveDataSocketBufferSize());
+            }
+            if (getSendDataSocketBufferSize() > 0) {
+                socket.setSendBufferSize(getSendDataSocketBufferSize());
+            }
+            if (getPassiveLocalIPAddress() != null) {
+                socket.bind(new InetSocketAddress(getPassiveLocalIPAddress(), 0));
+            }
+            // For now, let's just use the data timeout value for waiting for
+            // the data connection. It may be desirable to let this be a
+            // separately configurable value. In any case, we really want
+            // to allow preventing the accept from blocking indefinitely.
+            if (soTimeoutMillis >= 0) {
+                socket.setSoTimeout(soTimeoutMillis);
+            }
+            socket.connect(new InetSocketAddress(getPassiveHost(), getPassivePort()), connectTimeout);
+            if (getRestartOffset() > 0 && !restart(getRestartOffset())) {
+                socket.close();
+                return null;
+            }
+            if (!FTPReply.isPositivePreliminary(sendCommand(command, arg))) {
+                socket.close();
+                return null;
+            }
+        }
+        if (isRemoteVerificationEnabled() && !verifyRemote(socket)) {
+            // Grab the host before we close the socket to avoid NET-663
+            final InetAddress socketHost = socket.getInetAddress();
+            socket.close();
+            throw new IOException(
+                    "Host attempting data connection " + socketHost.getHostAddress() + " is not same as server " + getRemoteAddress().getHostAddress());
+        }
+        return socket;
+    }
+
+}
diff --git a/ftp/src/main/java/org/apache/pekko/stream/connectors/ftp/impl/LegacyFtpsClient.java b/ftp/src/main/java/org/apache/pekko/stream/connectors/ftp/impl/LegacyFtpsClient.java
deleted file mode 100644
index db82e6f84..000000000
--- a/ftp/src/main/java/org/apache/pekko/stream/connectors/ftp/impl/LegacyFtpsClient.java
+++ /dev/null
@@ -1,836 +0,0 @@
-/*
- * 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.pekko.stream.connectors.ftp.impl;
-
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.util.Base64;
-
-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.ftp.*;
-import org.apache.commons.net.util.SSLContextUtils;
-import org.apache.commons.net.util.SSLSocketUtils;
-import org.apache.commons.net.util.TrustManagerUtils;
-
-/**
- * This class is a workaround for code introduced in commons-net 3.9.0 that breaks
- * FTPS support when HTTP proxies are used.
- * See https://issues.apache.org/jira/browse/NET-718
- * 
- * Derived from https://github.com/apache/commons-net/blob/master/src/main/java/org/apache/commons/net/ftp/FTPSClient.java
- */
-final class LegacyFtpsClient 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";
-
-    /** @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;
-    /** 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;
-
-    /** The want client auth flag. */
-    private boolean isWantClientAuth;
-
-    /** The cipher suites */
-    private String[] suites;
-
-    /** The protocol versions */
-    private String[] protocols;
-
-    /**
-     * 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;
-
-    /** The {@link HostnameVerifier} to use post-TLS, default null (i.e. no verification). */
-    private HostnameVerifier hostnameVerifier;
-
-    /** Use Java 1.7+ HTTPS Endpoint Identification Algorithm. */
-    private boolean tlsEndpointChecking;
-
-    /**
-     * Constructor for LegacyFtpsClient calls.
-     *
-     * Sets protocol to {@link #DEFAULT_PROTOCOL} - i.e. TLS - and security mode to explicit (isImplicit = false)
-     */
-    LegacyFtpsClient() {
-        this(false);
-    }
-
-    /**
-     * Constructor for LegacyFtpsClient calls.
-     *
-     * Sets protocol to {@link #DEFAULT_PROTOCOL} - i.e. TLS
-     * @param isImplicit True - Implicit Mode / False - Explicit Mode
-     */
-    LegacyFtpsClient(final boolean isImplicit) {
-        this(DEFAULT_PROTOCOL, isImplicit);
-    }
-
-    private LegacyFtpsClient(final String protocol, final boolean isImplicit) {
-        this.protocol = protocol;
-        this.isImplicit = isImplicit;
-        if (isImplicit) {
-            setDefaultPort(DEFAULT_FTPS_PORT);
-        }
-    }
-
-    /**
-     * 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 the connect() methods.
-     *
-     * @throws IOException If there is any problem with establishing the connection.
-     * @see org.apache.commons.net.SocketClient#_connectAction_()
-     */
-    @Override
-    protected void _connectAction_() throws IOException {
-        // Implicit mode.
-        if (isImplicit) {
-            applySocketAttributes();
-            sslNegotiation();
-        }
-        super._connectAction_();
-        // Explicit mode.
-        if (!isImplicit) {
-            execAUTH();
-            sslNegotiation();
-        }
-    }
-
-    /**
-     * 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_(final int command, final 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_(final String command, final String arg) throws IOException {
-        final Socket socket = super._openDataConnection_(command, arg);
-        _prepareDataSocket_(socket);
-        if (socket instanceof SSLSocket) {
-            final 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_(final Socket socket) throws IOException {
-    }
-
-    /**
-     * 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(final String prot) {
-        for (final String element : PROT_COMMAND_VALUE) {
-            if (element.equals(prot)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Create SSL socket from plain socket.
-     *
-     * @param socket
-     * @return SSL Socket
-     * @throws IOException
-     */
-    private SSLSocket createSSLSocket(final Socket socket) throws IOException {
-        if (socket != null) {
-            final SSLSocketFactory f = context.getSocketFactory();
-            return (SSLSocket) f.createSocket(socket, _hostname_, socket.getPort(), false);
-        }
-        return null;
-    }
-
-    /**
-     * 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 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(final byte[] data) throws IOException {
-        if (data != null) {
-            return sendCommand(CMD_ADAT, Base64.getEncoder().encodeToString(data));
-        }
-        return sendCommand(CMD_ADAT);
-    }
-
-    /**
-     * AUTH command.
-     *
-     * @throws SSLException If the server reply code equals neither "234" nor "334".
-     * @throws IOException  If an I/O error occurs while either sending the command.
-     */
-    protected void execAUTH() throws SSLException, IOException {
-        final 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());
-        }
-    }
-
-    /**
-     * 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(final String mechanism) throws IOException {
-        return sendCommand(CMD_AUTH, mechanism);
-    }
-
-    /**
-     * 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 {
-        final 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 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(final byte[] data) throws IOException {
-        if (data != null) {
-            return sendCommand(CMD_CONF, Base64.getEncoder().encodeToString(data));
-        }
-        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(final byte[] data) throws IOException {
-        if (data != null) {
-            return sendCommand(CMD_ENC, Base64.getEncoder().encodeToString(data));
-        }
-        return sendCommand(CMD_ENC, ""); // perhaps "=" or just sendCommand(String)?
-    }
-
-    /**
-     * 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(final byte[] data) throws IOException {
-        if (data != null) {
-            return sendCommand(CMD_MIC, Base64.getEncoder().encodeToString(data));
-        }
-        return sendCommand(CMD_MIC, ""); // perhaps "=" or just sendCommand(String)?
-    }
-
-    /**
-     * 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(final long pbsz) throws SSLException, IOException {
-        if (pbsz < 0 || 4294967295L < pbsz) { // 32-bit unsigned number
-            throw new IllegalArgumentException();
-        }
-        final int status = sendCommand(CMD_PBSZ, String.valueOf(pbsz));
-        if (FTPReply.COMMAND_OK != status) {
-            throw new SSLException(getReplyString());
-        }
-    }
-
-    /**
-     * 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();
-        }
-    }
-
-    /**
-     * 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(final String prefix, final String reply) {
-        final 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();
-    }
-
-    /**
-     * Return AUTH command use value.
-     *
-     * @return AUTH command use value.
-     */
-    public String getAuthValue() {
-        return this.auth;
-    }
-
-    /**
-     * 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;
-    }
-
-    /**
-     * 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;
-    }
-
-    /**
-     * 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;
-    }
-
-    /**
-     * 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;
-    }
-
-    /**
-     * Gets the {@link KeyManager} instance.
-     *
-     * @return The {@link KeyManager} instance
-     */
-    private KeyManager getKeyManager() {
-        return keyManager;
-    }
-
-    /**
-     * 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;
-    }
-
-    /**
-     * Get the currently configured {@link TrustManager}.
-     *
-     * @return A TrustManager instance.
-     */
-    public TrustManager getTrustManager() {
-        return trustManager;
-    }
-
-    /**
-     * 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;
-    }
-
-    /**
-     * 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;
-    }
-
-    /**
-     * Performs a lazy init of the SSL context
-     *
-     * @throws IOException
-     */
-    private void initSslContext() throws IOException {
-        if (context == null) {
-            context = SSLContextUtils.createSSLContext(protocol, getKeyManager(), getTrustManager());
-        }
-    }
-
-    /**
-     * Return whether or not endpoint identification using the HTTPS algorithm on Java 1.7+ is enabled. The default behavior 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;
-    }
-
-    /**
-     * 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(final String reply) {
-        if (reply == null) {
-            return null;
-        }
-        return Base64.getDecoder().decode(extractPrefixedData("ADAT=", reply));
-    }
-
-    /**
-     * 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(final long pbsz) throws SSLException, IOException {
-        execPBSZ(pbsz);
-        long minvalue = pbsz;
-        final String remainder = extractPrefixedData("PBSZ=", getReplyString());
-        if (remainder != null) {
-            final long replysz = Long.parseLong(remainder);
-            if (replysz < minvalue) {
-                minvalue = replysz;
-            }
-        }
-        return minvalue;
-    }
-
-    /**
-     * 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 org.apache.commons.net.ftp.FTP#sendCommand(String)
-     */
-    // Would like to remove this method, but that will break any existing clients that are using CCC
-    @Override
-    public int sendCommand(final String command, final String args) throws IOException {
-        final 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) {
-                throw new SSLException(getReplyString());
-            }
-            _socket_.close();
-            _socket_ = plainSocket;
-            _controlInput_ = new BufferedReader(new InputStreamReader(_socket_.getInputStream(), getControlEncoding()));
-            _controlOutput_ = new BufferedWriter(new OutputStreamWriter(_socket_.getOutputStream(), getControlEncoding()));
-        }
-        return repCode;
-    }
-
-    /**
-     * Set AUTH command use value. This processing is done before connected processing.
-     *
-     * @param auth AUTH command use value.
-     */
-    public void setAuthValue(final String auth) {
-        this.auth = auth;
-    }
-
-    /**
-     * Controls which particular cipher suites are enabled for use on this connection. Called before server negotiation.
-     *
-     * @param cipherSuites The cipher suites.
-     */
-    public void setEnabledCipherSuites(final String[] cipherSuites) {
-        suites = cipherSuites.clone();
-    }
-
-    /**
-     * 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(final String[] protocolVersions) {
-        protocols = protocolVersions.clone();
-    }
-
-    /**
-     * Controls whether a new SSL session may be established by this socket.
-     *
-     * @param isCreation The established socket flag.
-     */
-    public void setEnabledSessionCreation(final boolean isCreation) {
-        this.isCreation = isCreation;
-    }
-
-    /**
-     * Automatic endpoint identification checking using the HTTPS algorithm is supported on Java 1.7+. The default behavior 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(final boolean enable) {
-        tlsEndpointChecking = enable;
-    }
-
-    /**
-     * 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(final HostnameVerifier newHostnameVerifier) {
-        hostnameVerifier = newHostnameVerifier;
-    }
-
-    /**
-     * Set a {@link KeyManager} to use
-     *
-     * @param keyManager The KeyManager implementation to set.
-     * @see org.apache.commons.net.util.KeyManagerUtils
-     */
-    public void setKeyManager(final KeyManager keyManager) {
-        this.keyManager = keyManager;
-    }
-
-    // DEPRECATED - for API compatibility only - DO NOT USE
-
-    /**
-     * Configures the socket to require client authentication.
-     *
-     * @param isNeedClientAuth The need client auth flag.
-     */
-    public void setNeedClientAuth(final boolean isNeedClientAuth) {
-        this.isNeedClientAuth = isNeedClientAuth;
-    }
-
-    /**
-     * 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(final TrustManager trustManager) {
-        this.trustManager = trustManager;
-    }
-
-    /**
-     * Configures the socket to use client (or server) mode in its first handshake.
-     *
-     * @param isClientMode The use client mode flag.
-     */
-    public void setUseClientMode(final boolean isClientMode) {
-        this.isClientMode = isClientMode;
-    }
-
-    /**
-     * 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(final boolean isWantClientAuth) {
-        this.isWantClientAuth = isWantClientAuth;
-    }
-
-    /**
-     * 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();
-        final SSLSocket socket = createSSLSocket(_socket_);
-        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 && (hostnameVerifier != null && !hostnameVerifier.verify(_hostname_, socket.getSession()))) {
-            throw new SSLHandshakeException("Hostname doesn't match certificate");
-        }
-    }
-
-}
-/* kate: indent-width 4; replace-tabs on; */
diff --git a/ftp/src/main/mima-filters/1.1.x.backwards.excludes/ftpsclient.backwards.excludes b/ftp/src/main/mima-filters/1.1.x.backwards.excludes/ftpsclient.backwards.excludes
deleted file mode 100644
index c9cfab14f..000000000
--- a/ftp/src/main/mima-filters/1.1.x.backwards.excludes/ftpsclient.backwards.excludes
+++ /dev/null
@@ -1,26 +0,0 @@
-# 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.
-
-# fork FTPSClient due to issue with latest proxy support 
-ProblemFilters.exclude[IncompatibleSignatureProblem]("org.apache.pekko.stream.connectors.ftp.FtpsSettings.configureConnection")
-ProblemFilters.exclude[IncompatibleSignatureProblem]("org.apache.pekko.stream.connectors.ftp.FtpsSettings.withConfigureConnection")
-ProblemFilters.exclude[IncompatibleSignatureProblem]("org.apache.pekko.stream.connectors.ftp.FtpsSettings.withConfigureConnectionConsumer")
-ProblemFilters.exclude[IncompatibleSignatureProblem]("org.apache.pekko.stream.connectors.ftp.FtpsSettings.this")
-ProblemFilters.exclude[IncompatibleSignatureProblem]("org.apache.pekko.stream.connectors.ftp.javadsl.Ftps.ftpLike")
-ProblemFilters.exclude[IncompatibleSignatureProblem]("org.apache.pekko.stream.connectors.ftp.javadsl.Ftps.ftpClient")
-ProblemFilters.exclude[IncompatibleSignatureProblem]("org.apache.pekko.stream.connectors.ftp.scaladsl.Ftps.ftpLike")
-ProblemFilters.exclude[IncompatibleSignatureProblem]("org.apache.pekko.stream.connectors.ftp.scaladsl.Ftps.ftpClient")
diff --git a/ftp/src/main/scala/org/apache/pekko/stream/connectors/ftp/impl/FtpLike.scala b/ftp/src/main/scala/org/apache/pekko/stream/connectors/ftp/impl/FtpLike.scala
index de00ea53b..216d7a597 100644
--- a/ftp/src/main/scala/org/apache/pekko/stream/connectors/ftp/impl/FtpLike.scala
+++ b/ftp/src/main/scala/org/apache/pekko/stream/connectors/ftp/impl/FtpLike.scala
@@ -15,7 +15,7 @@ package org.apache.pekko.stream.connectors.ftp
 package impl
 
 import net.schmizz.sshj.SSHClient
-import org.apache.commons.net.ftp.FTPClient
+import org.apache.commons.net.ftp.{ FTPClient, FTPSClient }
 
 import scala.collection.immutable
 import scala.util.Try
@@ -78,8 +78,8 @@ object FtpLike {
   // type class instances
   implicit val ftpLikeInstance: FtpLike[FTPClient, FtpSettings] with RetrieveOffset with FtpOperations =
     new FtpLike[FTPClient, FtpSettings] with RetrieveOffset with FtpOperations
-  implicit val ftpsLikeInstance: FtpLike[FTPClient, FtpsSettings] with RetrieveOffset with FtpsOperations =
-    new FtpLike[FTPClient, FtpsSettings] with RetrieveOffset with FtpsOperations
+  implicit val ftpsLikeInstance: FtpLike[FTPSClient, FtpsSettings] with RetrieveOffset with FtpsOperations =
+    new FtpLike[FTPSClient, FtpsSettings] with RetrieveOffset with FtpsOperations
   implicit val sFtpLikeInstance
       : FtpLike[SSHClient, SftpSettings] with RetrieveOffset with SftpOperations with UnconfirmedReads =
     new FtpLike[SSHClient, SftpSettings] with RetrieveOffset with SftpOperations with UnconfirmedReads
diff --git a/ftp/src/main/scala/org/apache/pekko/stream/connectors/ftp/impl/FtpSourceFactory.scala b/ftp/src/main/scala/org/apache/pekko/stream/connectors/ftp/impl/FtpSourceFactory.scala
index dc27c43a5..e9d57b53d 100644
--- a/ftp/src/main/scala/org/apache/pekko/stream/connectors/ftp/impl/FtpSourceFactory.scala
+++ b/ftp/src/main/scala/org/apache/pekko/stream/connectors/ftp/impl/FtpSourceFactory.scala
@@ -21,7 +21,7 @@ import pekko.stream.connectors.ftp.FtpCredentials
 import pekko.stream.connectors.ftp._
 
 import net.schmizz.sshj.SSHClient
-import org.apache.commons.net.ftp.{ FTPClient, FTPSClient }
+import org.apache.commons.net.ftp.{ FTPClient, FTPSClient, LegacyFtpsClient }
 
 /**
  * INTERNAL API
@@ -159,13 +159,13 @@ private[ftp] trait FtpSource extends FtpSourceFactory[FTPClient, FtpSettings] {
  * INTERNAL API
  */
 @InternalApi
-private[ftp] trait FtpsSource extends FtpSourceFactory[FTPClient, FtpsSettings] {
+private[ftp] trait FtpsSource extends FtpSourceFactory[FTPSClient, FtpsSettings] {
   protected final val FtpsBrowserSourceName = "FtpsBrowserSource"
   protected final val FtpsIOSourceName = "FtpsIOSource"
   protected final val FtpsDirectorySource = "FtpsDirectorySource"
   protected final val FtpsIOSinkName = "FtpsIOSink"
 
-  protected val ftpClient: FtpsSettings => FTPClient = settings => {
+  protected val ftpClient: FtpsSettings => FTPSClient = settings => {
     if (settings.useUpdatedFtpsClient) {
       new FTPSClient(settings.useFtpsImplicit)
     } else {
@@ -262,7 +262,7 @@ private[ftp] trait FtpSourceParams extends FtpSource with FtpDefaultSettings {
 @InternalApi
 private[ftp] trait FtpsSourceParams extends FtpsSource with FtpsDefaultSettings {
   type S = FtpsSettings
-  protected[this] val ftpLike: FtpLike[FTPClient, S] = FtpLike.ftpsLikeInstance
+  protected[this] val ftpLike: FtpLike[FTPSClient, S] = FtpLike.ftpsLikeInstance
 }
 
 /**
diff --git a/ftp/src/main/scala/org/apache/pekko/stream/connectors/ftp/impl/FtpsOperations.scala b/ftp/src/main/scala/org/apache/pekko/stream/connectors/ftp/impl/FtpsOperations.scala
index f457fec62..84adb0e10 100644
--- a/ftp/src/main/scala/org/apache/pekko/stream/connectors/ftp/impl/FtpsOperations.scala
+++ b/ftp/src/main/scala/org/apache/pekko/stream/connectors/ftp/impl/FtpsOperations.scala
@@ -16,7 +16,7 @@ package org.apache.pekko.stream.connectors.ftp.impl
 import org.apache.pekko
 import pekko.annotation.InternalApi
 import pekko.stream.connectors.ftp.{ FtpAuthenticationException, FtpsSettings }
-import org.apache.commons.net.ftp.{ FTP, FTPClient, FTPSClient }
+import org.apache.commons.net.ftp.{ FTP, FTPSClient, LegacyFtpsClient }
 
 import scala.util.Try
 
@@ -25,9 +25,9 @@ import scala.util.Try
  */
 @InternalApi
 private[ftp] trait FtpsOperations extends CommonFtpOperations {
-  self: FtpLike[FTPClient, FtpsSettings] =>
+  self: FtpLike[FTPSClient, FtpsSettings] =>
 
-  def connect(connectionSettings: FtpsSettings)(implicit ftpClient: FTPClient): Try[Handler] =
+  def connect(connectionSettings: FtpsSettings)(implicit ftpClient: FTPSClient): Try[Handler] =
     Try {
       connectionSettings.proxy.foreach(ftpClient.setProxy)
 
@@ -69,7 +69,7 @@ private[ftp] trait FtpsOperations extends CommonFtpOperations {
       ftpClient
     }
 
-  def disconnect(handler: Handler)(implicit ftpClient: FTPClient): Unit =
+  def disconnect(handler: Handler)(implicit ftpClient: FTPSClient): Unit =
     if (ftpClient.isConnected) {
       ftpClient.logout()
       ftpClient.disconnect()
diff --git a/ftp/src/main/scala/org/apache/pekko/stream/connectors/ftp/javadsl/FtpApi.scala b/ftp/src/main/scala/org/apache/pekko/stream/connectors/ftp/javadsl/FtpApi.scala
index f94ebd9ad..95f34c4e1 100644
--- a/ftp/src/main/scala/org/apache/pekko/stream/connectors/ftp/javadsl/FtpApi.scala
+++ b/ftp/src/main/scala/org/apache/pekko/stream/connectors/ftp/javadsl/FtpApi.scala
@@ -27,7 +27,7 @@ import pekko.util.ByteString
 import pekko.util.FunctionConverters._
 import pekko.{ Done, NotUsed }
 import net.schmizz.sshj.SSHClient
-import org.apache.commons.net.ftp.FTPClient
+import org.apache.commons.net.ftp.{ FTPClient, FTPSClient }
 
 @DoNotInherit
 sealed trait FtpApi[FtpClient, S <: RemoteFileSettings] { self: FtpSourceFactory[FtpClient, S] =>
@@ -374,7 +374,7 @@ object Ftp extends FtpApi[FTPClient, FtpSettings] with FtpSourceParams {
   }
 
 }
-object Ftps extends FtpApi[FTPClient, FtpsSettings] with FtpsSourceParams {
+object Ftps extends FtpApi[FTPSClient, FtpsSettings] with FtpsSourceParams {
   def ls(host: String): Source[FtpFile, NotUsed] = ls(host, basePath = "")
   def ls(host: String, basePath: String): Source[FtpFile, NotUsed] = ls(basePath, defaultSettings(host))
 
diff --git a/ftp/src/main/scala/org/apache/pekko/stream/connectors/ftp/model.scala b/ftp/src/main/scala/org/apache/pekko/stream/connectors/ftp/model.scala
index f81410333..67425930e 100644
--- a/ftp/src/main/scala/org/apache/pekko/stream/connectors/ftp/model.scala
+++ b/ftp/src/main/scala/org/apache/pekko/stream/connectors/ftp/model.scala
@@ -20,7 +20,7 @@ import javax.net.ssl.TrustManager
 import java.nio.file.attribute.PosixFilePermission
 
 import org.apache.pekko.annotation.{ DoNotInherit, InternalApi }
-import org.apache.commons.net.ftp.FTPClient
+import org.apache.commons.net.ftp.{ FTPClient, FTPSClient }
 
 /**
  * FTP remote file descriptor.
@@ -188,7 +188,7 @@ final class FtpsSettings private (
     val useFtpsImplicit: Boolean,
     val autodetectUTF8: Boolean,
     val useUpdatedFtpsClient: Boolean,
-    val configureConnection: FTPClient => Unit,
+    val configureConnection: FTPSClient => Unit,
     val proxy: Option[Proxy],
     val keyManager: Option[KeyManager],
     val trustManager: Option[TrustManager]) extends FtpFileSettings {
@@ -213,7 +213,7 @@ final class FtpsSettings private (
    * Scala API:
    * Sets the configure connection callback.
    */
-  def withConfigureConnection(value: FTPClient => Unit): FtpsSettings =
+  def withConfigureConnection(value: FTPSClient => Unit): FtpsSettings =
     copy(configureConnection = value)
 
   /**
@@ -221,7 +221,7 @@ final class FtpsSettings private (
    * Sets the configure connection callback.
    */
   def withConfigureConnectionConsumer(
-      configureConnection: java.util.function.Consumer[FTPClient]): FtpsSettings =
+      configureConnection: java.util.function.Consumer[FTPSClient]): FtpsSettings =
     copy(configureConnection = configureConnection.accept)
 
   private def copy(
@@ -233,7 +233,7 @@ final class FtpsSettings private (
       useFtpsImplicit: Boolean = useFtpsImplicit,
       autodetectUTF8: Boolean = autodetectUTF8,
       useUpdatedFtpsClient: Boolean = useUpdatedFtpsClient,
-      configureConnection: FTPClient => Unit = configureConnection,
+      configureConnection: FTPSClient => Unit = configureConnection,
       proxy: Option[Proxy] = proxy,
       keyManager: Option[KeyManager] = keyManager,
       trustManager: Option[TrustManager] = trustManager): FtpsSettings = new FtpsSettings(
diff --git a/ftp/src/main/scala/org/apache/pekko/stream/connectors/ftp/scaladsl/FtpApi.scala b/ftp/src/main/scala/org/apache/pekko/stream/connectors/ftp/scaladsl/FtpApi.scala
index 59fe8e84d..9cbceb951 100644
--- a/ftp/src/main/scala/org/apache/pekko/stream/connectors/ftp/scaladsl/FtpApi.scala
+++ b/ftp/src/main/scala/org/apache/pekko/stream/connectors/ftp/scaladsl/FtpApi.scala
@@ -23,7 +23,7 @@ import pekko.stream.scaladsl.{ Sink, Source }
 import pekko.util.ByteString
 import pekko.{ Done, NotUsed }
 import net.schmizz.sshj.SSHClient
-import org.apache.commons.net.ftp.FTPClient
+import org.apache.commons.net.ftp.{ FTPClient, FTPSClient }
 
 import scala.concurrent.Future
 
@@ -274,7 +274,7 @@ object Ftp extends FtpApi[FTPClient, FtpSettings] with FtpSourceParams {
 
 }
 
-object Ftps extends FtpApi[FTPClient, FtpsSettings] with FtpsSourceParams {
+object Ftps extends FtpApi[FTPSClient, FtpsSettings] with FtpsSourceParams {
   def ls(host: String): Source[FtpFile, NotUsed] = ls(host, basePath = "")
 
   def ls(host: String, basePath: String): Source[FtpFile, NotUsed] = ls(basePath, defaultSettings(host))


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@pekko.apache.org
For additional commands, e-mail: commits-help@pekko.apache.org