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