View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.net.ftp;
19  
20  import java.io.BufferedReader;
21  import java.io.BufferedWriter;
22  import java.io.IOException;
23  import java.io.InputStreamReader;
24  import java.io.OutputStreamWriter;
25  import java.net.Socket;
26  
27  import javax.net.ssl.HostnameVerifier;
28  import javax.net.ssl.KeyManager;
29  import javax.net.ssl.SSLContext;
30  import javax.net.ssl.SSLException;
31  import javax.net.ssl.SSLHandshakeException;
32  import javax.net.ssl.SSLSocket;
33  import javax.net.ssl.SSLSocketFactory;
34  import javax.net.ssl.TrustManager;
35  
36  import org.apache.commons.net.util.Base64;
37  import org.apache.commons.net.util.SSLContextUtils;
38  import org.apache.commons.net.util.SSLSocketUtils;
39  import org.apache.commons.net.util.TrustManagerUtils;
40  
41  /**
42   * FTP over SSL processing. If desired, the JVM property -Djavax.net.debug=all can be used to
43   * see wire-level SSL details.
44   *
45   * Warning: the hostname is not verified against the certificate by default, use
46   * {@link #setHostnameVerifier(HostnameVerifier)} or {@link #setEndpointCheckingEnabled(boolean)}
47   * (on Java 1.7+) to enable verification. Verification is only performed on client mode connections.
48   * @version $Id: FTPSClient.java 1697433 2015-08-24 15:11:40Z sebb $
49   * @since 2.0
50   */
51  public class FTPSClient extends FTPClient {
52  
53  // From http://www.iana.org/assignments/port-numbers
54  
55  //    ftps-data   989/tcp    ftp protocol, data, over TLS/SSL
56  //    ftps-data   989/udp    ftp protocol, data, over TLS/SSL
57  //    ftps        990/tcp    ftp protocol, control, over TLS/SSL
58  //    ftps        990/udp    ftp protocol, control, over TLS/SSL
59  
60      public static final int DEFAULT_FTPS_DATA_PORT = 989;
61      public static final int DEFAULT_FTPS_PORT = 990;
62  
63      /** The value that I can set in PROT command  (C = Clear, P = Protected) */
64      private static final String[] PROT_COMMAND_VALUE = {"C","E","S","P"};
65      /** Default PROT Command */
66      private static final String DEFAULT_PROT = "C";
67      /** Default secure socket protocol name, i.e. TLS */
68      private static final String DEFAULT_PROTOCOL = "TLS";
69  
70      /** The AUTH (Authentication/Security Mechanism) command. */
71      private static final String CMD_AUTH = "AUTH";
72      /**  The ADAT (Authentication/Security Data) command. */
73      private static final String CMD_ADAT = "ADAT";
74      /**  The PROT (Data Channel Protection Level) command. */
75      private static final String CMD_PROT = "PROT";
76      /**  The PBSZ (Protection Buffer Size) command. */
77      private static final String CMD_PBSZ = "PBSZ";
78      /**  The MIC (Integrity Protected Command) command. */
79      private static final String CMD_MIC = "MIC";
80      /**  The CONF (Confidentiality Protected Command) command. */
81      private static final String CMD_CONF = "CONF";
82      /**  The ENC (Privacy Protected Command) command. */
83      private static final String CMD_ENC = "ENC";
84      /**  The CCC (Clear Command Channel) command. */
85      private static final String CMD_CCC = "CCC";
86  
87      /** The security mode. (True - Implicit Mode / False - Explicit Mode) */
88      private final boolean isImplicit;
89      /** The secure socket protocol to be used, e.g. SSL/TLS. */
90      private final String protocol;
91      /** The AUTH Command value */
92      private String auth = DEFAULT_PROTOCOL;
93      /** The context object. */
94      private SSLContext context;
95      /** The socket object. */
96      private Socket plainSocket;
97      /** Controls whether a new SSL session may be established by this socket. Default true. */
98      private boolean isCreation = true;
99      /** 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      * @exception 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         setSocketFactory(null);
751         setServerSocketFactory(null);
752     }
753 
754     /**
755      * Send the AUTH command with the specified mechanism.
756      * @param mechanism The mechanism name to send with the command.
757      * @return server reply.
758      * @throws IOException If an I/O error occurs while sending
759      * the command.
760      * @since 3.0
761      */
762     public int execAUTH(String mechanism) throws IOException
763     {
764         return sendCommand(CMD_AUTH, mechanism);
765     }
766 
767     /**
768      * Send the ADAT command with the specified authentication data.
769      * @param data The data to send with the command.
770      * @return server reply.
771      * @throws IOException If an I/O error occurs while sending
772      * the command.
773      * @since 3.0
774      */
775     public int execADAT(byte[] data) throws IOException
776     {
777         if (data != null)
778         {
779             return sendCommand(CMD_ADAT, Base64.encodeBase64StringUnChunked(data));
780         }
781         else
782         {
783             return sendCommand(CMD_ADAT);
784         }
785     }
786 
787     /**
788      * Send the CCC command to the server.
789      * The CCC (Clear Command Channel) command causes the underlying {@link SSLSocket} instance  to be assigned
790      * to a plain {@link Socket} instances
791      * @return server reply.
792      * @throws IOException If an I/O error occurs while sending
793      * the command.
794      * @since 3.0
795      */
796     public int execCCC() throws IOException
797     {
798         int repCode = sendCommand(CMD_CCC);
799 // This will be performed by sendCommand(String, String)
800 //        if (FTPReply.isPositiveCompletion(repCode)) {
801 //            _socket_.close();
802 //            _socket_ = plainSocket;
803 //            _controlInput_ = new BufferedReader(
804 //                new InputStreamReader(
805 //                    _socket_.getInputStream(), getControlEncoding()));
806 //            _controlOutput_ = new BufferedWriter(
807 //                new OutputStreamWriter(
808 //                    _socket_.getOutputStream(), getControlEncoding()));
809 //        }
810         return repCode;
811     }
812 
813     /**
814      * Send the MIC command with the specified data.
815      * @param data The data to send with the command.
816      * @return server reply.
817      * @throws IOException If an I/O error occurs while sending
818      * the command.
819      * @since 3.0
820      */
821     public int execMIC(byte[] data) throws IOException
822     {
823         if (data != null)
824         {
825             return sendCommand(CMD_MIC, Base64.encodeBase64StringUnChunked(data));
826         }
827         else
828         {
829             return sendCommand(CMD_MIC, ""); // perhaps "=" or just sendCommand(String)?
830         }
831     }
832 
833     /**
834      * Send the CONF command with the specified data.
835      * @param data The data to send with the command.
836      * @return server reply.
837      * @throws IOException If an I/O error occurs while sending
838      * the command.
839      * @since 3.0
840      */
841     public int execCONF(byte[] data) throws IOException
842     {
843         if (data != null)
844         {
845             return sendCommand(CMD_CONF, Base64.encodeBase64StringUnChunked(data));
846         }
847         else
848         {
849             return sendCommand(CMD_CONF, ""); // perhaps "=" or just sendCommand(String)?
850         }
851     }
852 
853     /**
854      * Send the ENC command with the specified data.
855      * @param data The data to send with the command.
856      * @return server reply.
857      * @throws IOException If an I/O error occurs while sending
858      * the command.
859      * @since 3.0
860      */
861     public int execENC(byte[] data) throws IOException
862     {
863         if (data != null)
864         {
865             return sendCommand(CMD_ENC, Base64.encodeBase64StringUnChunked(data));
866         }
867         else
868         {
869             return sendCommand(CMD_ENC, ""); // perhaps "=" or just sendCommand(String)?
870         }
871     }
872 
873     /**
874      * Parses the given ADAT response line and base64-decodes the data.
875      * @param reply The ADAT reply to parse.
876      * @return the data in the reply, base64-decoded.
877      * @since 3.0
878      */
879     public byte[] parseADATReply(String reply)
880     {
881         if (reply == null) {
882             return null;
883         } else {
884             return Base64.decodeBase64(extractPrefixedData("ADAT=", reply));
885         }
886     }
887 
888     /**
889      * Extract the data from a reply with a prefix, e.g. PBSZ=1234 => 1234
890      * @param prefix the prefix to find
891      * @param reply where to find the prefix
892      * @return the remainder of the string after the prefix, or null if the prefix was not present.
893      */
894     private String extractPrefixedData(String prefix, String reply) {
895         int idx = reply.indexOf(prefix);
896         if (idx == -1) {
897             return null;
898         }
899         // N.B. Cannot use trim before substring as leading space would affect the offset.
900         return reply.substring(idx+prefix.length()).trim();
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; */