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