    001    /*
    002     * Licensed to the Apache Software Foundation (ASF) under one or more
    003     * contributor license agreements.  See the NOTICE file distributed with
    004     * this work for additional information regarding copyright ownership.
    005     * The ASF licenses this file to You under the Apache License, Version 2.0
    006     * (the "License"); you may not use this file except in compliance with
    007     * the License.  You may obtain a copy of the License at
    008     *
    009     *      http://www.apache.org/licenses/LICENSE-2.0
    010     *
    011     * Unless required by applicable law or agreed to in writing, software
    012     * distributed under the License is distributed on an "AS IS" BASIS,
    013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014     * See the License for the specific language governing permissions and
    015     * limitations under the License.
    016     */
    017
    018    package org.apache.commons.net.ftp;
    019
    020    import java.io.BufferedReader;
    021    import java.io.BufferedWriter;
    022    import java.io.IOException;
    023    import java.io.InputStreamReader;
    024    import java.io.OutputStreamWriter;
    025    import java.net.Socket;
    026    import javax.net.ssl.KeyManager;
    027    import javax.net.ssl.SSLContext;
    028    import javax.net.ssl.SSLException;
    029    import javax.net.ssl.SSLSocket;
    030    import javax.net.ssl.SSLSocketFactory;
    031    import javax.net.ssl.TrustManager;
    032
    033    import org.apache.commons.net.util.Base64;
    034    import org.apache.commons.net.util.SSLContextUtils;
    035    import org.apache.commons.net.util.TrustManagerUtils;
    036
    037    /**
    038     * FTP over SSL processing. If desired, the JVM property -Djavax.net.debug=all can be used to
    039     * see wire-level SSL details.
    040     *
    041     * @version $Id: FTPSClient.java 1407341 2012-11-09 01:31:00Z ggregory $
    042     * @since 2.0
    043     */
    044    public class FTPSClient extends FTPClient {
    045
    046    // From http://www.iana.org/assignments/port-numbers
    047
    048    //    ftps-data   989/tcp    ftp protocol, data, over TLS/SSL
    049    //    ftps-data   989/udp    ftp protocol, data, over TLS/SSL
    050    //    ftps        990/tcp    ftp protocol, control, over TLS/SSL
    051    //    ftps        990/udp    ftp protocol, control, over TLS/SSL
    052
    053        public static final int DEFAULT_FTPS_DATA_PORT = 989;
    054        public static final int DEFAULT_FTPS_PORT = 990;
    055
    056        /** The value that I can set in PROT command  (C = Clear, P = Protected) */
    057        private static final String[] PROT_COMMAND_VALUE = {"C","E","S","P"};
    058        /** Default PROT Command */
    059        private static final String DEFAULT_PROT = "C";
    060        /** Default secure socket protocol name, i.e. TLS */
    061        private static final String DEFAULT_PROTOCOL = "TLS";
    062
    063        /** The AUTH (Authentication/Security Mechanism) command. */
    064        private static final String CMD_AUTH = "AUTH";
    065        /**  The ADAT (Authentication/Security Data) command. */
    066        private static final String CMD_ADAT = "ADAT";
    067        /**  The PROT (Data Channel Protection Level) command. */
    068        private static final String CMD_PROT = "PROT";
    069        /**  The PBSZ (Protection Buffer Size) command. */
    070        private static final String CMD_PBSZ = "PBSZ";
    071        /**  The MIC (Integrity Protected Command) command. */
    072        private static final String CMD_MIC = "MIC";
    073        /**  The CONF (Confidentiality Protected Command) command. */
    074        private static final String CMD_CONF = "CONF";
    075        /**  The ENC (Privacy Protected Command) command. */
    076        private static final String CMD_ENC = "ENC";
    077        /**  The CCC (Clear Command Channel) command. */
    078        private static final String CMD_CCC = "CCC";
    079
    080        /** The security mode. (True - Implicit Mode / False - Explicit Mode) */
    081        private final boolean isImplicit;
    082        /** The secure socket protocol to be used, e.g. SSL/TLS. */
    083        private final String protocol;
    084        /** The AUTH Command value */
    085        private String auth = DEFAULT_PROTOCOL;
    086        /** The context object. */
    087        private SSLContext context;
    088        /** The socket object. */
    089        private Socket plainSocket;
    090        /** Controls whether a new SSL session may be established by this socket. Default true. */
    091        private boolean isCreation = true;
    092        /** The use client mode flag. */
    093        private boolean isClientMode = true;
    094        /** The need client auth flag. */
    095        private boolean isNeedClientAuth = false;
    096        /** The want client auth flag. */
    097        private boolean isWantClientAuth = false;
    098        /** The cipher suites */
    099        private String[] suites = null;
    100        /** The protocol versions */
    101        private String[] protocols = null;
    102
    103        /** The FTPS {@link TrustManager} implementation, default validate only: {@link TrustManagerUtils#getValidateServerCertificateTrustManager()}. */
    104        private TrustManager trustManager = TrustManagerUtils.getValidateServerCertificateTrustManager();
    105
    106        /** The {@link KeyManager}, default null (i.e. use system default). */
    107        private KeyManager keyManager = null;
    108
    109        /**
    110         * Constructor for FTPSClient, calls {@link #FTPSClient(String, boolean)}.
    111         *
    112         * Sets protocol to {@link #DEFAULT_PROTOCOL} - i.e. TLS - and security mode to explicit (isImplicit = false)
    113         */
    114        public FTPSClient() {
    115            this(DEFAULT_PROTOCOL, false);
    116        }
    117
    118        /**
    119         * Constructor for FTPSClient, using {@link #DEFAULT_PROTOCOL} - i.e. TLS
    120         * Calls {@link #FTPSClient(String, boolean)}
    121         * @param isImplicit The security mode (Implicit/Explicit).
    122         */
    123        public FTPSClient(boolean isImplicit) {
    124            this(DEFAULT_PROTOCOL, isImplicit);
    125        }
    126
    127        /**
    128         * Constructor for FTPSClient, using explict mode, calls {@link #FTPSClient(String, boolean)}.
    129         *
    130         * @param protocol the protocol to use
    131         */
    132        public FTPSClient(String protocol) {
    133            this(protocol, false);
    134        }
    135
    136        /**
    137         * Constructor for FTPSClient allowing specification of protocol
    138         * and security mode. If isImplicit is true, the port is set to
    139         * {@link #DEFAULT_FTPS_PORT} i.e. 990.
    140         * The default TrustManager is set from {@link TrustManagerUtils#getValidateServerCertificateTrustManager()}
    141         * @param protocol the protocol
    142         * @param isImplicit The security mode(Implicit/Explicit).
    143         */
    144        public FTPSClient(String protocol, boolean isImplicit) {
    145            super();
    146            this.protocol = protocol;
    147            this.isImplicit = isImplicit;
    148            if (isImplicit) {
    149                setDefaultPort(DEFAULT_FTPS_PORT);
    150            }
    151        }
    152
    153        /**
    154         * Constructor for FTPSClient, using {@link #DEFAULT_PROTOCOL} - i.e. TLS
    155         * The default TrustManager is set from {@link TrustManagerUtils#getValidateServerCertificateTrustManager()}
    156         * @param isImplicit The security mode(Implicit/Explicit).
    157         * @param context A pre-configured SSL Context
    158         */
    159        public FTPSClient(boolean isImplicit, SSLContext context) {
    160            this(DEFAULT_PROTOCOL, isImplicit);
    161            this.context = context;
    162        }
    163
    164        /**
    165         * Constructor for FTPSClient, using {@link #DEFAULT_PROTOCOL} - i.e. TLS
    166         * and isImplicit {@code false}
    167         * Calls {@link #FTPSClient(boolean, SSLContext)}
    168         * @param context A pre-configured SSL Context
    169         */
    170        public FTPSClient(SSLContext context) {
    171            this(false, context);
    172        }
    173
    174
    175        /**
    176         * Set AUTH command use value.
    177         * This processing is done before connected processing.
    178         * @param auth AUTH command use value.
    179         */
    180        public void setAuthValue(String auth) {
    181            this.auth = auth;
    182        }
    183
    184        /**
    185         * Return AUTH command use value.
    186         * @return AUTH command use value.
    187         */
    188        public String getAuthValue() {
    189            return this.auth;
    190        }
    191
    192
    193        /**
    194         * Because there are so many connect() methods,
    195         * the _connectAction_() method is provided as a means of performing
    196         * some action immediately after establishing a connection,
    197         * rather than reimplementing all of the connect() methods.
    198         * @throws IOException If it throw by _connectAction_.
    199         * @see org.apache.commons.net.SocketClient#_connectAction_()
    200         */
    201        @Override
    202        protected void _connectAction_() throws IOException {
    203            // Implicit mode.
    204            if (isImplicit) {
    205                sslNegotiation();
    206            }
    207            super._connectAction_();
    208            // Explicit mode.
    209            if (!isImplicit) {
    210                execAUTH();
    211                sslNegotiation();
    212            }
    213        }
    214
    215        /**
    216         * AUTH command.
    217         * @throws SSLException If it server reply code not equal "234" and "334".
    218         * @throws IOException If an I/O error occurs while either sending
    219         * the command.
    220         */
    221        protected void execAUTH() throws SSLException, IOException {
    222            int replyCode = sendCommand(CMD_AUTH, auth);
    223            if (FTPReply.SECURITY_MECHANISM_IS_OK == replyCode) {
    224                // replyCode = 334
    225                // I carry out an ADAT command.
    226            } else if (FTPReply.SECURITY_DATA_EXCHANGE_COMPLETE != replyCode) {
    227                throw new SSLException(getReplyString());
    228            }
    229        }
    230
    231        /**
    232         * Performs a lazy init of the SSL context
    233         * @throws IOException
    234         */
    235        private void initSslContext() throws IOException {
    236            if (context == null) {
    237                context = SSLContextUtils.createSSLContext(protocol, getKeyManager(), getTrustManager());
    238            }
    239        }
    240
    241        /**
    242         * SSL/TLS negotiation. Acquires an SSL socket of a control
    243         * connection and carries out handshake processing.
    244         * @throws IOException If server negotiation fails
    245         */
    246        protected void sslNegotiation() throws IOException {
    247            plainSocket = _socket_;
    248            initSslContext();
    249
    250            SSLSocketFactory ssf = context.getSocketFactory();
    251            String ip = _socket_.getInetAddress().getHostAddress();
    252            int port = _socket_.getPort();
    253            SSLSocket socket =
    254                (SSLSocket) ssf.createSocket(_socket_, ip, port, false);
    255            socket.setEnableSessionCreation(isCreation);
    256            socket.setUseClientMode(isClientMode);
    257            // server mode
    258            if (!isClientMode) {
    259                socket.setNeedClientAuth(isNeedClientAuth);
    260                socket.setWantClientAuth(isWantClientAuth);
    261            }
    262
    263            if (protocols != null) {
    264                socket.setEnabledProtocols(protocols);
    265            }
    266            if (suites != null) {
    267                socket.setEnabledCipherSuites(suites);
    268            }
    269            socket.startHandshake();
    270
    271            _socket_ = socket;
    272            _controlInput_ = new BufferedReader(new InputStreamReader(
    273                    socket .getInputStream(), getControlEncoding()));
    274            _controlOutput_ = new BufferedWriter(new OutputStreamWriter(
    275                    socket.getOutputStream(), getControlEncoding()));
    276        }
    277
    278        /**
    279         * Get the {@link KeyManager} instance.
    280         * @return The {@link KeyManager} instance
    281         */
    282        private KeyManager getKeyManager() {
    283            return keyManager;
    284        }
    285
    286        /**
    287        * Set a {@link KeyManager} to use
    288        *
    289        * @param keyManager The KeyManager implementation to set.
    290        * @see org.apache.commons.net.util.KeyManagerUtils
    291        */
    292        public void setKeyManager(KeyManager keyManager) {
    293            this.keyManager = keyManager;
    294        }
    295
    296        /**
    297         * Controls whether a new SSL session may be established by this socket.
    298         * @param isCreation The established socket flag.
    299         */
    300        public void setEnabledSessionCreation(boolean isCreation) {
    301            this.isCreation = isCreation;
    302        }
    303
    304        /**
    305         * Returns true if new SSL sessions may be established by this socket.
    306         * When the underlying {@link Socket} instance is not SSL-enabled (i.e. an
    307         * instance of {@link SSLSocket} with {@link SSLSocket}{@link #getEnableSessionCreation()}) enabled,
    308         * this returns False.
    309         * @return true - Indicates that sessions may be created;
    310         * this is the default.
    311         * false - indicates that an existing session must be resumed.
    312         */
    313        public boolean getEnableSessionCreation() {
    314            if (_socket_ instanceof SSLSocket) {
    315                return ((SSLSocket)_socket_).getEnableSessionCreation();
    316            }
    317            return false;
    318        }
    319
    320        /**
    321         * Configures the socket to require client authentication.
    322         * @param isNeedClientAuth The need client auth flag.
    323         */
    324        public void setNeedClientAuth(boolean isNeedClientAuth) {
    325            this.isNeedClientAuth = isNeedClientAuth;
    326        }
    327
    328        /**
    329         * Returns true if the socket will require client authentication.
    330         * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns false.
    331         * @return true - If the server mode socket should request
    332         * that the client authenticate itself.
    333         */
    334        public boolean getNeedClientAuth() {
    335            if (_socket_ instanceof SSLSocket) {
    336                return ((SSLSocket)_socket_).getNeedClientAuth();
    337            }
    338            return false;
    339        }
    340
    341        /**
    342         * Configures the socket to request client authentication,
    343         * but only if such a request is appropriate to the cipher
    344         * suite negotiated.
    345         * @param isWantClientAuth The want client auth flag.
    346         */
    347        public void setWantClientAuth(boolean isWantClientAuth) {
    348            this.isWantClientAuth = isWantClientAuth;
    349        }
    350
    351        /**
    352         * Returns true if the socket will request client authentication.
    353         * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns false.
    354         * @return true - If the server mode socket should request
    355         * that the client authenticate itself.
    356         */
    357        public boolean getWantClientAuth() {
    358            if (_socket_ instanceof SSLSocket) {
    359                return ((SSLSocket)_socket_).getWantClientAuth();
    360            }
    361            return false;
    362        }
    363
    364        /**
    365         * Configures the socket to use client (or server) mode in its first
    366         * handshake.
    367         * @param isClientMode The use client mode flag.
    368         */
    369        public void setUseClientMode(boolean isClientMode) {
    370            this.isClientMode = isClientMode;
    371        }
    372
    373        /**
    374         * Returns true if the socket is set to use client mode
    375         * in its first handshake.
    376         * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns false.
    377         * @return true - If the socket should start its first handshake
    378         * in "client" mode.
    379         */
    380        public boolean getUseClientMode() {
    381            if (_socket_ instanceof SSLSocket) {
    382                return ((SSLSocket)_socket_).getUseClientMode();
    383            }
    384            return false;
    385        }
    386
    387        /**
    388         * Controls which particular cipher suites are enabled for use on this
    389         * connection. Called before server negotiation.
    390         * @param cipherSuites The cipher suites.
    391         */
    392        public void setEnabledCipherSuites(String[] cipherSuites) {
    393            suites = new String[cipherSuites.length];
    394            System.arraycopy(cipherSuites, 0, suites, 0, cipherSuites.length);
    395        }
    396
    397        /**
    398         * Returns the names of the cipher suites which could be enabled
    399         * for use on this connection.
    400         * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns null.
    401         * @return An array of cipher suite names, or <code>null</code>
    402         */
    403        public String[] getEnabledCipherSuites() {
    404            if (_socket_ instanceof SSLSocket) {
    405                return ((SSLSocket)_socket_).getEnabledCipherSuites();
    406            }
    407            return null;
    408        }
    409
    410        /**
    411         * Controls which particular protocol versions are enabled for use on this
    412         * connection. I perform setting before a server negotiation.
    413         * @param protocolVersions The protocol versions.
    414         */
    415        public void setEnabledProtocols(String[] protocolVersions) {
    416            protocols = new String[protocolVersions.length];
    417            System.arraycopy(protocolVersions, 0, protocols, 0, protocolVersions.length);
    418        }
    419
    420        /**
    421         * Returns the names of the protocol versions which are currently
    422         * enabled for use on this connection.
    423         * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns null.
    424         * @return An array of protocols, or <code>null</code>
    425         */
    426        public String[] getEnabledProtocols() {
    427            if (_socket_ instanceof SSLSocket) {
    428                return ((SSLSocket)_socket_).getEnabledProtocols();
    429            }
    430            return null;
    431        }
    432
    433        /**
    434         * PBSZ command. pbsz value: 0 to (2^32)-1 decimal integer.
    435         * @param pbsz Protection Buffer Size.
    436         * @throws SSLException If the server reply code does not equal "200".
    437         * @throws IOException If an I/O error occurs while sending
    438         * the command.
    439         * @see #parsePBSZ(long)
    440         */
    441        public void execPBSZ(long pbsz) throws SSLException, IOException {
    442            if (pbsz < 0 || 4294967295L < pbsz) { // 32-bit unsigned number
    443                throw new IllegalArgumentException();
    444            }
    445            int status = sendCommand(CMD_PBSZ, String.valueOf(pbsz));
    446            if (FTPReply.COMMAND_OK != status) {
    447                throw new SSLException(getReplyString());
    448            }
    449        }
    450
    451        /**
    452         * PBSZ command. pbsz value: 0 to (2^32)-1 decimal integer.
    453         * Issues the command and parses the response to return the negotiated value.
    454         *
    455         * @param pbsz Protection Buffer Size.
    456         * @throws SSLException If the server reply code does not equal "200".
    457         * @throws IOException If an I/O error occurs while sending
    458         * the command.
    459         * @return the negotiated value.
    460         * @see #execPBSZ(long)
    461         * @since 3.0
    462         */
    463        public long parsePBSZ(long pbsz) throws SSLException, IOException {
    464            execPBSZ(pbsz);
    465            long minvalue = pbsz;
    466            String remainder = extractPrefixedData("PBSZ=", getReplyString());
    467            if (remainder != null) {
    468                long replysz = Long.parseLong(remainder);
    469                if (replysz < minvalue) {
    470                    minvalue = replysz;
    471                }
    472            }
    473            return minvalue;
    474        }
    475
    476        /**
    477         * PROT command.
    478         * <ul>
    479         * <li>C - Clear</li>
    480         * <li>S - Safe(SSL protocol only)</li>
    481         * <li>E - Confidential(SSL protocol only)</li>
    482         * <li>P - Private</li>
    483         * </ul>
    484         * <b>N.B.</b> the method calls
    485         *  {@link #setSocketFactory(javax.net.SocketFactory)} and
    486         *  {@link #setServerSocketFactory(javax.net.ServerSocketFactory)}
    487         *
    488         * @param prot Data Channel Protection Level, if {@code null}, use {@link #DEFAULT_PROT}.
    489         * @throws SSLException If the server reply code does not equal  {@code 200}.
    490         * @throws IOException If an I/O error occurs while sending
    491         * the command.
    492         */
    493        public void execPROT(String prot) throws SSLException, IOException {
    494            if (prot == null) {
    495                prot = DEFAULT_PROT;
    496            }
    497            if (!checkPROTValue(prot)) {
    498                throw new IllegalArgumentException();
    499            }
    500            if (FTPReply.COMMAND_OK != sendCommand(CMD_PROT, prot)) {
    501                throw new SSLException(getReplyString());
    502            }
    503            if (DEFAULT_PROT.equals(prot)) {
    504                setSocketFactory(null);
    505                setServerSocketFactory(null);
    506            } else {
    507                setSocketFactory(new FTPSSocketFactory(context));
    508                setServerSocketFactory(new FTPSServerSocketFactory(context));
    509                initSslContext();
    510            }
    511        }
    512
    513        /**
    514         * Check the value that can be set in PROT Command value.
    515         * @param prot Data Channel Protection Level.
    516         * @return True - A set point is right / False - A set point is not right
    517         */
    518        private boolean checkPROTValue(String prot) {
    519            for (String element : PROT_COMMAND_VALUE)
    520            {
    521                if (element.equals(prot)) {
    522                    return true;
    523                }
    524            }
    525            return false;
    526        }
    527
    528        /**
    529         * Send an FTP command.
    530         * A successful CCC (Clear Command Channel) command causes the underlying {@link SSLSocket}
    531         * instance to be assigned to a plain {@link Socket}
    532         * @param command The FTP command.
    533         * @return server reply.
    534         * @throws IOException If an I/O error occurs while sending the command.
    535         * @throws SSLException if a CCC command fails
    536         * @see org.apache.commons.net.ftp.FTP#sendCommand(java.lang.String)
    537         */
    538        // Would like to remove this method, but that will break any existing clients that are using CCC
    539        @Override
    540        public int sendCommand(String command, String args) throws IOException {
    541            int repCode = super.sendCommand(command, args);
    542            /* If CCC is issued, restore socket i/o streams to unsecured versions */
    543            if (CMD_CCC.equals(command)) {
    544                if (FTPReply.COMMAND_OK == repCode) {
    545                    _socket_.close();
    546                    _socket_ = plainSocket;
    547                    _controlInput_ = new BufferedReader(
    548                        new InputStreamReader(
    549                            _socket_ .getInputStream(), getControlEncoding()));
    550                    _controlOutput_ = new BufferedWriter(
    551                        new OutputStreamWriter(
    552                            _socket_.getOutputStream(), getControlEncoding()));
    553                } else {
    554                    throw new SSLException(getReplyString());
    555                }
    556            }
    557            return repCode;
    558        }
    559
    560        /**
    561         * Returns a socket of the data connection.
    562         * Wrapped as an {@link SSLSocket}, which carries out handshake processing.
    563         * @param command The int representation of the FTP command to send.
    564         * @param arg The arguments to the FTP command.
    565         * If this parameter is set to null, then the command is sent with
    566         * no arguments.
    567         * @return corresponding to the established data connection.
    568         * Null is returned if an FTP protocol error is reported at any point
    569         * during the establishment and initialization of the connection.
    570         * @throws IOException If there is any problem with the connection.
    571         * @see FTPClient#_openDataConnection_(int, String)
    572         */
    573        @Override
    574        // Strictly speaking this is not needed, but it works round a Clirr bug
    575        // So rather than invoke the parent code, we do it here
    576        protected Socket _openDataConnection_(int command, String arg)
    577                throws IOException {
    578            return _openDataConnection_(FTPCommand.getCommand(command), arg);
    579        }
    580
    581       /**
    582         * Returns a socket of the data connection.
    583         * Wrapped as an {@link SSLSocket}, which carries out handshake processing.
    584         * @param command The textual representation of the FTP command to send.
    585         * @param arg The arguments to the FTP command.
    586         * If this parameter is set to null, then the command is sent with
    587         * no arguments.
    588         * @return corresponding to the established data connection.
    589         * Null is returned if an FTP protocol error is reported at any point
    590         * during the establishment and initialization of the connection.
    591         * @throws IOException If there is any problem with the connection.
    592         * @see FTPClient#_openDataConnection_(int, String)
    593         * @since 3.2
    594         */
    595        @Override
    596        protected Socket _openDataConnection_(String command, String arg)
    597                throws IOException {
    598            Socket socket = super._openDataConnection_(command, arg);
    599            _prepareDataSocket_(socket);
    600            if (socket instanceof SSLSocket) {
    601                SSLSocket sslSocket = (SSLSocket)socket;
    602
    603                sslSocket.setUseClientMode(isClientMode);
    604                sslSocket.setEnableSessionCreation(isCreation);
    605
    606                // server mode
    607                if (!isClientMode) {
    608                    sslSocket.setNeedClientAuth(isNeedClientAuth);
    609                    sslSocket.setWantClientAuth(isWantClientAuth);
    610                }
    611                if (suites != null) {
    612                    sslSocket.setEnabledCipherSuites(suites);
    613                }
    614                if (protocols != null) {
    615                    sslSocket.setEnabledProtocols(protocols);
    616                }
    617                sslSocket.startHandshake();
    618            }
    619
    620            return socket;
    621        }
    622
    623        /**
    624        * Performs any custom initialization for a newly created SSLSocket (before
    625        * the SSL handshake happens).
    626        * Called by {@link #_openDataConnection_(int, String)} immediately
    627        * after creating the socket.
    628        * The default implementation is a no-op
    629        * @throws IOException
    630        * @since 3.1
    631        */
    632        protected void _prepareDataSocket_(Socket socket)
    633                throws IOException {
    634        }
    635
    636        /**
    637         * Get the currently configured {@link TrustManager}.
    638         *
    639         * @return A TrustManager instance.
    640         */
    641        public TrustManager getTrustManager() {
    642            return trustManager;
    643        }
    644
    645        /**
    646         * Override the default {@link TrustManager} to use; if set to {@code null},
    647         * the default TrustManager from the JVM will be used.
    648         *
    649         * @param trustManager The TrustManager implementation to set, may be {@code null}
    650         * @see org.apache.commons.net.util.TrustManagerUtils
    651         */
    652        public void setTrustManager(TrustManager trustManager) {
    653            this.trustManager = trustManager;
    654        }
    655
    656        /**
    657         * Closes the connection to the FTP server and restores
    658         * connection parameters to the default values.
    659         * <p>
    660         * Calls {@code setSocketFactory(null)} and {@code setServerSocketFactory(null)}
    661         * to reset the factories that may have been changed during the session,
    662         * e.g. by {@link #execPROT(String)}
    663         * @exception IOException If an error occurs while disconnecting.
    664         * @since 3.0
    665         */
    666        @Override
    667        public void disconnect() throws IOException
    668        {
    669            super.disconnect();
    670            setSocketFactory(null);
    671            setServerSocketFactory(null);
    672        }
    673
    674        /**
    675         * Send the AUTH command with the specified mechanism.
    676         * @param mechanism The mechanism name to send with the command.
    677         * @return server reply.
    678         * @throws IOException If an I/O error occurs while sending
    679         * the command.
    680         * @since 3.0
    681         */
    682        public int execAUTH(String mechanism) throws IOException
    683        {
    684            return sendCommand(CMD_AUTH, mechanism);
    685        }
    686
    687        /**
    688         * Send the ADAT command with the specified authentication data.
    689         * @param data The data to send with the command.
    690         * @return server reply.
    691         * @throws IOException If an I/O error occurs while sending
    692         * the command.
    693         * @since 3.0
    694         */
    695        public int execADAT(byte[] data) throws IOException
    696        {
    697            if (data != null)
    698            {
    699                return sendCommand(CMD_ADAT, Base64.encodeBase64StringUnChunked(data));
    700            }
    701            else
    702            {
    703                return sendCommand(CMD_ADAT);
    704            }
    705        }
    706
    707        /**
    708         * Send the CCC command to the server.
    709         * The CCC (Clear Command Channel) command causes the underlying {@link SSLSocket} instance  to be assigned
    710         * to a plain {@link Socket} instances
    711         * @return server reply.
    712         * @throws IOException If an I/O error occurs while sending
    713         * the command.
    714         * @since 3.0
    715         */
    716        public int execCCC() throws IOException
    717        {
    718            int repCode = sendCommand(CMD_CCC);
    719    // This will be performed by sendCommand(String, String)
    720    //        if (FTPReply.isPositiveCompletion(repCode)) {
    721    //            _socket_.close();
    722    //            _socket_ = plainSocket;
    723    //            _controlInput_ = new BufferedReader(
    724    //                new InputStreamReader(
    725    //                    _socket_.getInputStream(), getControlEncoding()));
    726    //            _controlOutput_ = new BufferedWriter(
    727    //                new OutputStreamWriter(
    728    //                    _socket_.getOutputStream(), getControlEncoding()));
    729    //        }
    730            return repCode;
    731        }
    732
    733        /**
    734         * Send the MIC command with the specified data.
    735         * @param data The data to send with the command.
    736         * @return server reply.
    737         * @throws IOException If an I/O error occurs while sending
    738         * the command.
    739         * @since 3.0
    740         */
    741        public int execMIC(byte[] data) throws IOException
    742        {
    743            if (data != null)
    744            {
    745                return sendCommand(CMD_MIC, Base64.encodeBase64StringUnChunked(data));
    746            }
    747            else
    748            {
    749                return sendCommand(CMD_MIC, ""); // perhaps "=" or just sendCommand(String)?
    750            }
    751        }
    752
    753        /**
    754         * Send the CONF command with the specified data.
    755         * @param data The data to send with the command.
    756         * @return server reply.
    757         * @throws IOException If an I/O error occurs while sending
    758         * the command.
    759         * @since 3.0
    760         */
    761        public int execCONF(byte[] data) throws IOException
    762        {
    763            if (data != null)
    764            {
    765                return sendCommand(CMD_CONF, Base64.encodeBase64StringUnChunked(data));
    766            }
    767            else
    768            {
    769                return sendCommand(CMD_CONF, ""); // perhaps "=" or just sendCommand(String)?
    770            }
    771        }
    772
    773        /**
    774         * Send the ENC command with the specified data.
    775         * @param data The data to send with the command.
    776         * @return server reply.
    777         * @throws IOException If an I/O error occurs while sending
    778         * the command.
    779         * @since 3.0
    780         */
    781        public int execENC(byte[] data) throws IOException
    782        {
    783            if (data != null)
    784            {
    785                return sendCommand(CMD_ENC, Base64.encodeBase64StringUnChunked(data));
    786            }
    787            else
    788            {
    789                return sendCommand(CMD_ENC, ""); // perhaps "=" or just sendCommand(String)?
    790            }
    791        }
    792
    793        /**
    794         * Parses the given ADAT response line and base64-decodes the data.
    795         * @param reply The ADAT reply to parse.
    796         * @return the data in the reply, base64-decoded.
    797         * @since 3.0
    798         */
    799        public byte[] parseADATReply(String reply)
    800        {
    801            if (reply == null) {
    802                return null;
    803            } else {
    804                return Base64.decodeBase64(extractPrefixedData("ADAT=", reply));
    805            }
    806        }
    807
    808        /**
    809         * Extract the data from a reply with a prefix, e.g. PBSZ=1234 => 1234
    810         * @param prefix the prefix to find
    811         * @param reply where to find the prefix
    812         * @return the remainder of the string after the prefix, or null if the prefix was not present.
    813         */
    814        private String extractPrefixedData(String prefix, String reply) {
    815            int idx = reply.indexOf(prefix);
    816            if (idx == -1) {
    817                return null;
    818            }
    819            // N.B. Cannot use trim before substring as leading space would affect the offset.
    820            return reply.substring(idx+prefix.length()).trim();
    821        }
    822
    823        // DEPRECATED - for API compatibility only - DO NOT USE
    824
    825        /** @deprecated - not used - may be removed in a future release */
    826        @Deprecated
    827        public static String KEYSTORE_ALGORITHM;
    828
    829        /** @deprecated - not used - may be removed in a future release */
    830        @Deprecated
    831        public static String TRUSTSTORE_ALGORITHM;
    832
    833        /** @deprecated - not used - may be removed in a future release */
    834        @Deprecated
    835        public static String PROVIDER;
    836
    837        /** @deprecated - not used - may be removed in a future release */
    838        @Deprecated
    839        public static String STORE_TYPE;
    840
    841    }
    842    /* kate: indent-width 4; replace-tabs on; */