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