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