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 = false;
102    /** The want client auth flag. */
103    private boolean isWantClientAuth = false;
104    /** The cipher suites */
105    private String[] suites = null;
106    /** The protocol versions */
107    private String[] protocols = null;
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 = null;
116
117    /** The {@link HostnameVerifier} to use post-TLS, default null (i.e. no verification). */
118    private HostnameVerifier hostnameVerifier = null;
119
120    /** Use Java 1.7+ HTTPS Endpoint Identification Algorithim. */
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        super();
160        this.protocol = protocol;
161        this.isImplicit = isImplicit;
162        if (isImplicit) {
163            setDefaultPort(DEFAULT_FTPS_PORT);
164        }
165    }
166
167    /**
168     * Constructor for FTPSClient, using {@link #DEFAULT_PROTOCOL} - i.e. TLS
169     * The default TrustManager is set from {@link TrustManagerUtils#getValidateServerCertificateTrustManager()}
170     * @param isImplicit The security mode(Implicit/Explicit).
171     * @param context A pre-configured SSL Context
172     */
173    public FTPSClient(final boolean isImplicit, final SSLContext context) {
174        this(DEFAULT_PROTOCOL, isImplicit);
175        this.context = context;
176    }
177
178    /**
179     * Constructor for FTPSClient, using {@link #DEFAULT_PROTOCOL} - i.e. TLS
180     * and isImplicit {@code false}
181     * Calls {@link #FTPSClient(boolean, SSLContext)}
182     * @param context A pre-configured SSL Context
183     */
184    public FTPSClient(final SSLContext context) {
185        this(false, context);
186    }
187
188
189    /**
190     * Set AUTH command use value.
191     * This processing is done before connected processing.
192     * @param auth AUTH command use value.
193     */
194    public void setAuthValue(final String auth) {
195        this.auth = auth;
196    }
197
198    /**
199     * Return AUTH command use value.
200     * @return AUTH command use value.
201     */
202    public String getAuthValue() {
203        return this.auth;
204    }
205
206
207    /**
208     * Because there are so many connect() methods,
209     * the _connectAction_() method is provided as a means of performing
210     * some action immediately after establishing a connection,
211     * rather than reimplementing all of the connect() methods.
212     * @throws IOException If it throw by _connectAction_.
213     * @see org.apache.commons.net.SocketClient#_connectAction_()
214     */
215    @Override
216    protected void _connectAction_() throws IOException {
217        // Implicit mode.
218        if (isImplicit) {
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 = (SSLSocket)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(socket.getInetAddress().getHostAddress(), 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 = new String[cipherSuites.length];
416        System.arraycopy(cipherSuites, 0, suites, 0, cipherSuites.length);
417    }
418
419    /**
420     * Returns the names of the cipher suites which could be enabled
421     * for use on this connection.
422     * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns null.
423     * @return An array of cipher suite names, or <code>null</code>
424     */
425    public String[] getEnabledCipherSuites() {
426        if (_socket_ instanceof SSLSocket) {
427            return ((SSLSocket)_socket_).getEnabledCipherSuites();
428        }
429        return null;
430    }
431
432    /**
433     * Controls which particular protocol versions are enabled for use on this
434     * connection. I perform setting before a server negotiation.
435     * @param protocolVersions The protocol versions.
436     */
437    public void setEnabledProtocols(final String[] protocolVersions) {
438        protocols = new String[protocolVersions.length];
439        System.arraycopy(protocolVersions, 0, protocols, 0, protocolVersions.length);
440    }
441
442    /**
443     * Returns the names of the protocol versions which are currently
444     * enabled for use on this connection.
445     * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns null.
446     * @return An array of protocols, or <code>null</code>
447     */
448    public String[] getEnabledProtocols() {
449        if (_socket_ instanceof SSLSocket) {
450            return ((SSLSocket)_socket_).getEnabledProtocols();
451        }
452        return null;
453    }
454
455    /**
456     * PBSZ command. pbsz value: 0 to (2^32)-1 decimal integer.
457     * @param pbsz Protection Buffer Size.
458     * @throws SSLException If the server reply code does not equal "200".
459     * @throws IOException If an I/O error occurs while sending
460     * the command.
461     * @see #parsePBSZ(long)
462     */
463    public void execPBSZ(final long pbsz) throws SSLException, IOException {
464        if (pbsz < 0 || 4294967295L < pbsz) { // 32-bit unsigned number
465            throw new IllegalArgumentException();
466        }
467        final int status = sendCommand(CMD_PBSZ, String.valueOf(pbsz));
468        if (FTPReply.COMMAND_OK != status) {
469            throw new SSLException(getReplyString());
470        }
471    }
472
473    /**
474     * PBSZ command. pbsz value: 0 to (2^32)-1 decimal integer.
475     * Issues the command and parses the response to return the negotiated value.
476     *
477     * @param pbsz Protection Buffer Size.
478     * @throws SSLException If the server reply code does not equal "200".
479     * @throws IOException If an I/O error occurs while sending
480     * the command.
481     * @return the negotiated value.
482     * @see #execPBSZ(long)
483     * @since 3.0
484     */
485    public long parsePBSZ(final long pbsz) throws SSLException, IOException {
486        execPBSZ(pbsz);
487        long minvalue = pbsz;
488        final String remainder = extractPrefixedData("PBSZ=", getReplyString());
489        if (remainder != null) {
490            final long replysz = Long.parseLong(remainder);
491            if (replysz < minvalue) {
492                minvalue = replysz;
493            }
494        }
495        return minvalue;
496    }
497
498    /**
499     * PROT command.
500     * <ul>
501     * <li>C - Clear</li>
502     * <li>S - Safe(SSL protocol only)</li>
503     * <li>E - Confidential(SSL protocol only)</li>
504     * <li>P - Private</li>
505     * </ul>
506     * <b>N.B.</b> the method calls
507     *  {@link #setSocketFactory(javax.net.SocketFactory)} and
508     *  {@link #setServerSocketFactory(javax.net.ServerSocketFactory)}
509     *
510     * @param prot Data Channel Protection Level, if {@code null}, use {@link #DEFAULT_PROT}.
511     * @throws SSLException If the server reply code does not equal  {@code 200}.
512     * @throws IOException If an I/O error occurs while sending
513     * the command.
514     */
515    public void execPROT(String prot) throws SSLException, IOException {
516        if (prot == null) {
517            prot = DEFAULT_PROT;
518        }
519        if (!checkPROTValue(prot)) {
520            throw new IllegalArgumentException();
521        }
522        if (FTPReply.COMMAND_OK != sendCommand(CMD_PROT, prot)) {
523            throw new SSLException(getReplyString());
524        }
525        if (DEFAULT_PROT.equals(prot)) {
526            setSocketFactory(null);
527            setServerSocketFactory(null);
528        } else {
529            setSocketFactory(new FTPSSocketFactory(context));
530            setServerSocketFactory(new FTPSServerSocketFactory(context));
531            initSslContext();
532        }
533    }
534
535    /**
536     * Check the value that can be set in PROT Command value.
537     * @param prot Data Channel Protection Level.
538     * @return True - A set point is right / False - A set point is not right
539     */
540    private boolean checkPROTValue(final String prot) {
541        for (final String element : PROT_COMMAND_VALUE)
542        {
543            if (element.equals(prot)) {
544                return true;
545            }
546        }
547        return false;
548    }
549
550    /**
551     * Send an FTP command.
552     * A successful CCC (Clear Command Channel) command causes the underlying {@link SSLSocket}
553     * instance to be assigned to a plain {@link Socket}
554     * @param command The FTP command.
555     * @return server reply.
556     * @throws IOException If an I/O error occurs while sending the command.
557     * @throws SSLException if a CCC command fails
558     * @see org.apache.commons.net.ftp.FTP#sendCommand(java.lang.String)
559     */
560    // Would like to remove this method, but that will break any existing clients that are using CCC
561    @Override
562    public int sendCommand(final String command, final String args) throws IOException {
563        final int repCode = super.sendCommand(command, args);
564        /* If CCC is issued, restore socket i/o streams to unsecured versions */
565        if (CMD_CCC.equals(command)) {
566            if (FTPReply.COMMAND_OK == repCode) {
567                _socket_.close();
568                _socket_ = plainSocket;
569                _controlInput_ = new BufferedReader(
570                    new InputStreamReader(
571                        _socket_ .getInputStream(), getControlEncoding()));
572                _controlOutput_ = new BufferedWriter(
573                    new OutputStreamWriter(
574                        _socket_.getOutputStream(), getControlEncoding()));
575            } else {
576                throw new SSLException(getReplyString());
577            }
578        }
579        return repCode;
580    }
581
582    /**
583     * Returns a socket of the data connection.
584     * Wrapped as an {@link SSLSocket}, which carries out handshake processing.
585     * @param command The int representation of the FTP command to send.
586     * @param arg The arguments to the FTP command.
587     * If this parameter is set to null, then the command is sent with
588     * no arguments.
589     * @return corresponding to the established data connection.
590     * Null is returned if an FTP protocol error is reported at any point
591     * during the establishment and initialization of the connection.
592     * @throws IOException If there is any problem with the connection.
593     * @see FTPClient#_openDataConnection_(int, String)
594     * @deprecated (3.3) Use {@link FTPClient#_openDataConnection_(FTPCmd, String)} instead
595     */
596    @Override
597    // Strictly speaking this is not needed, but it works round a Clirr bug
598    // So rather than invoke the parent code, we do it here
599    @Deprecated
600    protected Socket _openDataConnection_(final int command, final String arg)
601            throws IOException {
602        return _openDataConnection_(FTPCommand.getCommand(command), arg);
603    }
604
605   /**
606     * Returns a socket of the data connection.
607     * Wrapped as an {@link SSLSocket}, which carries out handshake processing.
608     * @param command The textual representation of the FTP command to send.
609     * @param arg The arguments to the FTP command.
610     * If this parameter is set to null, then the command is sent with
611     * no arguments.
612     * @return corresponding to the established data connection.
613     * Null is returned if an FTP protocol error is reported at any point
614     * during the establishment and initialization of the connection.
615     * @throws IOException If there is any problem with the connection.
616     * @see FTPClient#_openDataConnection_(int, String)
617     * @since 3.2
618     */
619    @Override
620    protected Socket _openDataConnection_(final String command, final String arg)
621            throws IOException {
622        final Socket socket = super._openDataConnection_(command, arg);
623        _prepareDataSocket_(socket);
624        if (socket instanceof SSLSocket) {
625            final SSLSocket sslSocket = (SSLSocket)socket;
626
627            sslSocket.setUseClientMode(isClientMode);
628            sslSocket.setEnableSessionCreation(isCreation);
629
630            // server mode
631            if (!isClientMode) {
632                sslSocket.setNeedClientAuth(isNeedClientAuth);
633                sslSocket.setWantClientAuth(isWantClientAuth);
634            }
635            if (suites != null) {
636                sslSocket.setEnabledCipherSuites(suites);
637            }
638            if (protocols != null) {
639                sslSocket.setEnabledProtocols(protocols);
640            }
641            sslSocket.startHandshake();
642        }
643
644        return socket;
645    }
646
647    /**
648    * Performs any custom initialization for a newly created SSLSocket (before
649    * the SSL handshake happens).
650    * Called by {@link #_openDataConnection_(int, String)} immediately
651    * after creating the socket.
652    * The default implementation is a no-op
653     * @param socket the socket to set up
654    * @throws IOException on error
655    * @since 3.1
656    */
657    protected void _prepareDataSocket_(final Socket socket)
658            throws IOException {
659    }
660
661    /**
662     * Get the currently configured {@link TrustManager}.
663     *
664     * @return A TrustManager instance.
665     */
666    public TrustManager getTrustManager() {
667        return trustManager;
668    }
669
670    /**
671     * Override the default {@link TrustManager} to use; if set to {@code null},
672     * the default TrustManager from the JVM will be used.
673     *
674     * @param trustManager The TrustManager implementation to set, may be {@code null}
675     * @see org.apache.commons.net.util.TrustManagerUtils
676     */
677    public void setTrustManager(final TrustManager trustManager) {
678        this.trustManager = trustManager;
679    }
680
681    /**
682     * Get the currently configured {@link HostnameVerifier}.
683     * The verifier is only used on client mode connections.
684     * @return A HostnameVerifier instance.
685     * @since 3.4
686     */
687    public HostnameVerifier getHostnameVerifier()
688    {
689        return hostnameVerifier;
690    }
691
692    /**
693     * Override the default {@link HostnameVerifier} to use.
694     * The verifier is only used on client mode connections.
695     * @param newHostnameVerifier The HostnameVerifier implementation to set or <code>null</code> to disable.
696     * @since 3.4
697     */
698    public void setHostnameVerifier(final HostnameVerifier newHostnameVerifier)
699    {
700        hostnameVerifier = newHostnameVerifier;
701    }
702
703    /**
704     * Return whether or not endpoint identification using the HTTPS algorithm
705     * on Java 1.7+ is enabled. The default behavior is for this to be disabled.
706     *
707     * This check is only performed on client mode connections.
708     *
709     * @return True if enabled, false if not.
710     * @since 3.4
711     */
712    public boolean isEndpointCheckingEnabled()
713    {
714        return tlsEndpointChecking;
715    }
716
717    /**
718     * Automatic endpoint identification checking using the HTTPS algorithm
719     * is supported on Java 1.7+. The default behavior is for this to be disabled.
720     *
721     * This check is only performed on client mode connections.
722     *
723     * @param enable Enable automatic endpoint identification checking using the HTTPS algorithm on Java 1.7+.
724     * @since 3.4
725     */
726    public void setEndpointCheckingEnabled(final boolean enable)
727    {
728        tlsEndpointChecking = enable;
729    }
730
731    /**
732     * Closes the connection to the FTP server and restores
733     * connection parameters to the default values.
734     * <p>
735     * Calls {@code setSocketFactory(null)} and {@code setServerSocketFactory(null)}
736     * to reset the factories that may have been changed during the session,
737     * e.g. by {@link #execPROT(String)}
738     * @throws IOException If an error occurs while disconnecting.
739     * @since 3.0
740     */
741    @Override
742    public void disconnect() throws IOException
743    {
744        super.disconnect();
745        if (plainSocket != null) {
746            plainSocket.close();
747        }
748        setSocketFactory(null);
749        setServerSocketFactory(null);
750    }
751
752    /**
753     * Send the AUTH command with the specified mechanism.
754     * @param mechanism The mechanism name to send with the command.
755     * @return server reply.
756     * @throws IOException If an I/O error occurs while sending
757     * the command.
758     * @since 3.0
759     */
760    public int execAUTH(final String mechanism) throws IOException
761    {
762        return sendCommand(CMD_AUTH, mechanism);
763    }
764
765    /**
766     * Send the ADAT command with the specified authentication data.
767     * @param data The data to send with the command.
768     * @return server reply.
769     * @throws IOException If an I/O error occurs while sending
770     * the command.
771     * @since 3.0
772     */
773    public int execADAT(final byte[] data) throws IOException
774    {
775        if (data != null)
776        {
777            return sendCommand(CMD_ADAT, Base64.encodeBase64StringUnChunked(data));
778        }
779        return sendCommand(CMD_ADAT);
780    }
781
782    /**
783     * Send the CCC command to the server.
784     * The CCC (Clear Command Channel) command causes the underlying {@link SSLSocket} instance  to be assigned
785     * to a plain {@link Socket} instances
786     * @return server reply.
787     * @throws IOException If an I/O error occurs while sending
788     * the command.
789     * @since 3.0
790     */
791    public int execCCC() throws IOException
792    {
793        final int repCode = sendCommand(CMD_CCC);
794// This will be performed by sendCommand(String, String)
795//        if (FTPReply.isPositiveCompletion(repCode)) {
796//            _socket_.close();
797//            _socket_ = plainSocket;
798//            _controlInput_ = new BufferedReader(
799//                new InputStreamReader(
800//                    _socket_.getInputStream(), getControlEncoding()));
801//            _controlOutput_ = new BufferedWriter(
802//                new OutputStreamWriter(
803//                    _socket_.getOutputStream(), getControlEncoding()));
804//        }
805        return repCode;
806    }
807
808    /**
809     * Send the MIC command with the specified data.
810     * @param data The data to send with the command.
811     * @return server reply.
812     * @throws IOException If an I/O error occurs while sending
813     * the command.
814     * @since 3.0
815     */
816    public int execMIC(final byte[] data) throws IOException
817    {
818        if (data != null)
819        {
820            return sendCommand(CMD_MIC, Base64.encodeBase64StringUnChunked(data));
821        }
822        return sendCommand(CMD_MIC, ""); // perhaps "=" or just sendCommand(String)?
823    }
824
825    /**
826     * Send the CONF command with the specified data.
827     * @param data The data to send with the command.
828     * @return server reply.
829     * @throws IOException If an I/O error occurs while sending
830     * the command.
831     * @since 3.0
832     */
833    public int execCONF(final byte[] data) throws IOException
834    {
835        if (data != null)
836        {
837            return sendCommand(CMD_CONF, Base64.encodeBase64StringUnChunked(data));
838        }
839        return sendCommand(CMD_CONF, ""); // perhaps "=" or just sendCommand(String)?
840    }
841
842    /**
843     * Send the ENC command with the specified data.
844     * @param data The data to send with the command.
845     * @return server reply.
846     * @throws IOException If an I/O error occurs while sending
847     * the command.
848     * @since 3.0
849     */
850    public int execENC(final byte[] data) throws IOException
851    {
852        if (data != null)
853        {
854            return sendCommand(CMD_ENC, Base64.encodeBase64StringUnChunked(data));
855        }
856        return sendCommand(CMD_ENC, ""); // perhaps "=" or just sendCommand(String)?
857    }
858
859    /**
860     * Parses the given ADAT response line and base64-decodes the data.
861     * @param reply The ADAT reply to parse.
862     * @return the data in the reply, base64-decoded.
863     * @since 3.0
864     */
865    public byte[] parseADATReply(final String reply)
866    {
867        if (reply == null) {
868            return null;
869        }
870        return Base64.decodeBase64(extractPrefixedData("ADAT=", reply));
871    }
872
873    /**
874     * Extract the data from a reply with a prefix, e.g. PBSZ=1234 => 1234
875     * @param prefix the prefix to find
876     * @param reply where to find the prefix
877     * @return the remainder of the string after the prefix, or null if the prefix was not present.
878     */
879    private String extractPrefixedData(final String prefix, final String reply) {
880        final int idx = reply.indexOf(prefix);
881        if (idx == -1) {
882            return null;
883        }
884        // N.B. Cannot use trim before substring as leading space would affect the offset.
885        return reply.substring(idx+prefix.length()).trim();
886    }
887
888    /**
889     * Create SSL socket from plain socket.
890     *
891     * @param socket
892     * @return SSL Sockect
893     * @throws IOException
894     */
895    private Socket createSSLSocket(final Socket socket) throws IOException {
896        if (socket != null) {
897            final SSLSocketFactory f = context.getSocketFactory();
898            return f.createSocket(socket, socket.getInetAddress().getHostAddress(), socket.getPort(), false);
899        }
900        return null;
901    }
902
903    // DEPRECATED - for API compatibility only - DO NOT USE
904
905    /** @deprecated - not used - may be removed in a future release */
906    @Deprecated
907    public static String KEYSTORE_ALGORITHM;
908
909    /** @deprecated - not used - may be removed in a future release */
910    @Deprecated
911    public static String TRUSTSTORE_ALGORITHM;
912
913    /** @deprecated - not used - may be removed in a future release */
914    @Deprecated
915    public static String PROVIDER;
916
917    /** @deprecated - not used - may be removed in a future release */
918    @Deprecated
919    public static String STORE_TYPE;
920
921}
922/* kate: indent-width 4; replace-tabs on; */