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