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