    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
    018package org.apache.commons.net.ftp;
    019
    020import java.io.BufferedReader;
    021import java.io.BufferedWriter;
    022import java.io.IOException;
    023import java.io.InputStreamReader;
    024import java.io.OutputStreamWriter;
    025import java.net.Socket;
    026import javax.net.ssl.KeyManager;
    027import javax.net.ssl.SSLContext;
    028import javax.net.ssl.SSLException;
    029import javax.net.ssl.SSLSocket;
    030import javax.net.ssl.SSLSocketFactory;
    031import javax.net.ssl.TrustManager;
    032
    033import org.apache.commons.net.util.Base64;
    034import org.apache.commons.net.util.SSLContextUtils;
    035import 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 1489533 2013-06-04 17:49:00Z sebb $
    042 * @since 2.0
    043 */
    044public 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     * @deprecated (3.3) Use {@link #_openDataConnection_(FTPCmd, String)} instead
    573     */
    574    @Override
    575    // Strictly speaking this is not needed, but it works round a Clirr bug
    576    // So rather than invoke the parent code, we do it here
    577    @Deprecated
    578    protected Socket _openDataConnection_(int command, String arg)
    579            throws IOException {
    580        return _openDataConnection_(FTPCommand.getCommand(command), arg);
    581    }
    582
    583   /**
    584     * Returns a socket of the data connection.
    585     * Wrapped as an {@link SSLSocket}, which carries out handshake processing.
    586     * @param command The textual representation of the FTP command to send.
    587     * @param arg The arguments to the FTP command.
    588     * If this parameter is set to null, then the command is sent with
    589     * no arguments.
    590     * @return corresponding to the established data connection.
    591     * Null is returned if an FTP protocol error is reported at any point
    592     * during the establishment and initialization of the connection.
    593     * @throws IOException If there is any problem with the connection.
    594     * @see FTPClient#_openDataConnection_(int, String)
    595     * @since 3.2
    596     */
    597    @Override
    598    protected Socket _openDataConnection_(String command, String arg)
    599            throws IOException {
    600        Socket socket = super._openDataConnection_(command, arg);
    601        _prepareDataSocket_(socket);
    602        if (socket instanceof SSLSocket) {
    603            SSLSocket sslSocket = (SSLSocket)socket;
    604
    605            sslSocket.setUseClientMode(isClientMode);
    606            sslSocket.setEnableSessionCreation(isCreation);
    607
    608            // server mode
    609            if (!isClientMode) {
    610                sslSocket.setNeedClientAuth(isNeedClientAuth);
    611                sslSocket.setWantClientAuth(isWantClientAuth);
    612            }
    613            if (suites != null) {
    614                sslSocket.setEnabledCipherSuites(suites);
    615            }
    616            if (protocols != null) {
    617                sslSocket.setEnabledProtocols(protocols);
    618            }
    619            sslSocket.startHandshake();
    620        }
    621
    622        return socket;
    623    }
    624
    625    /**
    626    * Performs any custom initialization for a newly created SSLSocket (before
    627    * the SSL handshake happens).
    628    * Called by {@link #_openDataConnection_(int, String)} immediately
    629    * after creating the socket.
    630    * The default implementation is a no-op
    631    * @throws IOException
    632    * @since 3.1
    633    */
    634    protected void _prepareDataSocket_(Socket socket)
    635            throws IOException {
    636    }
    637
    638    /**
    639     * Get the currently configured {@link TrustManager}.
    640     *
    641     * @return A TrustManager instance.
    642     */
    643    public TrustManager getTrustManager() {
    644        return trustManager;
    645    }
    646
    647    /**
    648     * Override the default {@link TrustManager} to use; if set to {@code null},
    649     * the default TrustManager from the JVM will be used.
    650     *
    651     * @param trustManager The TrustManager implementation to set, may be {@code null}
    652     * @see org.apache.commons.net.util.TrustManagerUtils
    653     */
    654    public void setTrustManager(TrustManager trustManager) {
    655        this.trustManager = trustManager;
    656    }
    657
    658    /**
    659     * Closes the connection to the FTP server and restores
    660     * connection parameters to the default values.
    661     * <p>
    662     * Calls {@code setSocketFactory(null)} and {@code setServerSocketFactory(null)}
    663     * to reset the factories that may have been changed during the session,
    664     * e.g. by {@link #execPROT(String)}
    665     * @exception IOException If an error occurs while disconnecting.
    666     * @since 3.0
    667     */
    668    @Override
    669    public void disconnect() throws IOException
    670    {
    671        super.disconnect();
    672        setSocketFactory(null);
    673        setServerSocketFactory(null);
    674    }
    675
    676    /**
    677     * Send the AUTH command with the specified mechanism.
    678     * @param mechanism The mechanism name to send with the command.
    679     * @return server reply.
    680     * @throws IOException If an I/O error occurs while sending
    681     * the command.
    682     * @since 3.0
    683     */
    684    public int execAUTH(String mechanism) throws IOException
    685    {
    686        return sendCommand(CMD_AUTH, mechanism);
    687    }
    688
    689    /**
    690     * Send the ADAT command with the specified authentication data.
    691     * @param data The data to send with the command.
    692     * @return server reply.
    693     * @throws IOException If an I/O error occurs while sending
    694     * the command.
    695     * @since 3.0
    696     */
    697    public int execADAT(byte[] data) throws IOException
    698    {
    699        if (data != null)
    700        {
    701            return sendCommand(CMD_ADAT, Base64.encodeBase64StringUnChunked(data));
    702        }
    703        else
    704        {
    705            return sendCommand(CMD_ADAT);
    706        }
    707    }
    708
    709    /**
    710     * Send the CCC command to the server.
    711     * The CCC (Clear Command Channel) command causes the underlying {@link SSLSocket} instance  to be assigned
    712     * to a plain {@link Socket} instances
    713     * @return server reply.
    714     * @throws IOException If an I/O error occurs while sending
    715     * the command.
    716     * @since 3.0
    717     */
    718    public int execCCC() throws IOException
    719    {
    720        int repCode = sendCommand(CMD_CCC);
    721// This will be performed by sendCommand(String, String)
    722//        if (FTPReply.isPositiveCompletion(repCode)) {
    723//            _socket_.close();
    724//            _socket_ = plainSocket;
    725//            _controlInput_ = new BufferedReader(
    726//                new InputStreamReader(
    727//                    _socket_.getInputStream(), getControlEncoding()));
    728//            _controlOutput_ = new BufferedWriter(
    729//                new OutputStreamWriter(
    730//                    _socket_.getOutputStream(), getControlEncoding()));
    731//        }
    732        return repCode;
    733    }
    734
    735    /**
    736     * Send the MIC command with the specified data.
    737     * @param data The data to send with the command.
    738     * @return server reply.
    739     * @throws IOException If an I/O error occurs while sending
    740     * the command.
    741     * @since 3.0
    742     */
    743    public int execMIC(byte[] data) throws IOException
    744    {
    745        if (data != null)
    746        {
    747            return sendCommand(CMD_MIC, Base64.encodeBase64StringUnChunked(data));
    748        }
    749        else
    750        {
    751            return sendCommand(CMD_MIC, ""); // perhaps "=" or just sendCommand(String)?
    752        }
    753    }
    754
    755    /**
    756     * Send the CONF command with the specified data.
    757     * @param data The data to send with the command.
    758     * @return server reply.
    759     * @throws IOException If an I/O error occurs while sending
    760     * the command.
    761     * @since 3.0
    762     */
    763    public int execCONF(byte[] data) throws IOException
    764    {
    765        if (data != null)
    766        {
    767            return sendCommand(CMD_CONF, Base64.encodeBase64StringUnChunked(data));
    768        }
    769        else
    770        {
    771            return sendCommand(CMD_CONF, ""); // perhaps "=" or just sendCommand(String)?
    772        }
    773    }
    774
    775    /**
    776     * Send the ENC command with the specified data.
    777     * @param data The data to send with the command.
    778     * @return server reply.
    779     * @throws IOException If an I/O error occurs while sending
    780     * the command.
    781     * @since 3.0
    782     */
    783    public int execENC(byte[] data) throws IOException
    784    {
    785        if (data != null)
    786        {
    787            return sendCommand(CMD_ENC, Base64.encodeBase64StringUnChunked(data));
    788        }
    789        else
    790        {
    791            return sendCommand(CMD_ENC, ""); // perhaps "=" or just sendCommand(String)?
    792        }
    793    }
    794
    795    /**
    796     * Parses the given ADAT response line and base64-decodes the data.
    797     * @param reply The ADAT reply to parse.
    798     * @return the data in the reply, base64-decoded.
    799     * @since 3.0
    800     */
    801    public byte[] parseADATReply(String reply)
    802    {
    803        if (reply == null) {
    804            return null;
    805        } else {
    806            return Base64.decodeBase64(extractPrefixedData("ADAT=", reply));
    807        }
    808    }
    809
    810    /**
    811     * Extract the data from a reply with a prefix, e.g. PBSZ=1234 => 1234
    812     * @param prefix the prefix to find
    813     * @param reply where to find the prefix
    814     * @return the remainder of the string after the prefix, or null if the prefix was not present.
    815     */
    816    private String extractPrefixedData(String prefix, String reply) {
    817        int idx = reply.indexOf(prefix);
    818        if (idx == -1) {
    819            return null;
    820        }
    821        // N.B. Cannot use trim before substring as leading space would affect the offset.
    822        return reply.substring(idx+prefix.length()).trim();
    823    }
    824
    825    // DEPRECATED - for API compatibility only - DO NOT USE
    826
    827    /** @deprecated - not used - may be removed in a future release */
    828    @Deprecated
    829    public static String KEYSTORE_ALGORITHM;
    830
    831    /** @deprecated - not used - may be removed in a future release */
    832    @Deprecated
    833    public static String TRUSTSTORE_ALGORITHM;
    834
    835    /** @deprecated - not used - may be removed in a future release */
    836    @Deprecated
    837    public static String PROVIDER;
    838
    839    /** @deprecated - not used - may be removed in a future release */
    840    @Deprecated
    841    public static String STORE_TYPE;
    842
    843}
    844/* kate: indent-width 4; replace-tabs on; */