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 1448087 2013-02-20 11:20:07Z 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; */