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