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