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 1230368 2012-01-12 02:00:49Z 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) {
201                sslNegotiation();
202            }
203            super._connectAction_();
204            // Explicit mode.
205            if (!isImplicit) {
206                execAUTH();
207                sslNegotiation();
208            }
209        }
210    
211        /**
212         * AUTH command.
213         * @throws SSLException If it server reply code not equal "234" and "334".
214         * @throws IOException If an I/O error occurs while either sending
215         * the command.
216         */
217        protected void execAUTH() throws SSLException, IOException {
218            int replyCode = sendCommand(CMD_AUTH, auth);
219            if (FTPReply.SECURITY_MECHANISM_IS_OK == replyCode) {
220                // replyCode = 334
221                // I carry out an ADAT command.
222            } else if (FTPReply.SECURITY_DATA_EXCHANGE_COMPLETE != replyCode) {
223                throw new SSLException(getReplyString());
224            }
225        }
226    
227        /**
228         * Performs a lazy init of the SSL context
229         * @throws IOException
230         */
231        private void initSslContext() throws IOException {
232            if (context == null) {
233                context = SSLContextUtils.createSSLContext(protocol, getKeyManager(), getTrustManager());
234            }
235        }
236    
237        /**
238         * SSL/TLS negotiation. Acquires an SSL socket of a control
239         * connection and carries out handshake processing.
240         * @throws IOException If server negotiation fails
241         */
242        protected void sslNegotiation() throws IOException {
243            plainSocket = _socket_;
244            initSslContext();
245    
246            SSLSocketFactory ssf = context.getSocketFactory();
247            String ip = _socket_.getInetAddress().getHostAddress();
248            int port = _socket_.getPort();
249            SSLSocket socket =
250                (SSLSocket) ssf.createSocket(_socket_, ip, port, false);
251            socket.setEnableSessionCreation(isCreation);
252            socket.setUseClientMode(isClientMode);
253            // server mode
254            if (!isClientMode) {
255                socket.setNeedClientAuth(isNeedClientAuth);
256                socket.setWantClientAuth(isWantClientAuth);
257            }
258    
259            if (protocols != null) {
260                socket.setEnabledProtocols(protocols);
261            }
262            if (suites != null) {
263                socket.setEnabledCipherSuites(suites);
264            }
265            socket.startHandshake();
266    
267            _socket_ = socket;
268            _controlInput_ = new BufferedReader(new InputStreamReader(
269                    socket .getInputStream(), getControlEncoding()));
270            _controlOutput_ = new BufferedWriter(new OutputStreamWriter(
271                    socket.getOutputStream(), getControlEncoding()));
272        }
273    
274        /**
275         * Get the {@link KeyManager} instance.
276         * @return The {@link KeyManager} instance
277         */
278        private KeyManager getKeyManager() {
279            return keyManager;
280        }
281    
282        /**
283        * Set a {@link KeyManager} to use
284        *
285        * @param keyManager The KeyManager implementation to set.
286        * @see org.apache.commons.net.util.KeyManagerUtils
287        */
288        public void setKeyManager(KeyManager keyManager) {
289            this.keyManager = keyManager;
290        }
291    
292        /**
293         * Controls whether a new SSL session may be established by this socket.
294         * @param isCreation The established socket flag.
295         */
296        public void setEnabledSessionCreation(boolean isCreation) {
297            this.isCreation = isCreation;
298        }
299    
300        /**
301         * Returns true if new SSL sessions may be established by this socket.
302         * When the underlying {@link Socket} instance is not SSL-enabled (i.e. an
303         * instance of {@link SSLSocket} with {@link SSLSocket}{@link #getEnableSessionCreation()}) enabled,
304         * this returns False.
305         * @return true - Indicates that sessions may be created;
306         * this is the default.
307         * false - indicates that an existing session must be resumed.
308         */
309        public boolean getEnableSessionCreation() {
310            if (_socket_ instanceof SSLSocket) {
311                return ((SSLSocket)_socket_).getEnableSessionCreation();
312            }
313            return false;
314        }
315    
316        /**
317         * Configures the socket to require client authentication.
318         * @param isNeedClientAuth The need client auth flag.
319         */
320        public void setNeedClientAuth(boolean isNeedClientAuth) {
321            this.isNeedClientAuth = isNeedClientAuth;
322        }
323    
324        /**
325         * Returns true if the socket will require client authentication.
326         * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns false.
327         * @return true - If the server mode socket should request
328         * that the client authenticate itself.
329         */
330        public boolean getNeedClientAuth() {
331            if (_socket_ instanceof SSLSocket) {
332                return ((SSLSocket)_socket_).getNeedClientAuth();
333            }
334            return false;
335        }
336    
337        /**
338         * Configures the socket to request client authentication,
339         * but only if such a request is appropriate to the cipher
340         * suite negotiated.
341         * @param isWantClientAuth The want client auth flag.
342         */
343        public void setWantClientAuth(boolean isWantClientAuth) {
344            this.isWantClientAuth = isWantClientAuth;
345        }
346    
347        /**
348         * Returns true if the socket will request client authentication.
349         * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns false.
350         * @return true - If the server mode socket should request
351         * that the client authenticate itself.
352         */
353        public boolean getWantClientAuth() {
354            if (_socket_ instanceof SSLSocket) {
355                return ((SSLSocket)_socket_).getWantClientAuth();
356            }
357            return false;
358        }
359    
360        /**
361         * Configures the socket to use client (or server) mode in its first
362         * handshake.
363         * @param isClientMode The use client mode flag.
364         */
365        public void setUseClientMode(boolean isClientMode) {
366            this.isClientMode = isClientMode;
367        }
368    
369        /**
370         * Returns true if the socket is set to use client mode
371         * in its first handshake.
372         * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns false.
373         * @return true - If the socket should start its first handshake
374         * in "client" mode.
375         */
376        public boolean getUseClientMode() {
377            if (_socket_ instanceof SSLSocket) {
378                return ((SSLSocket)_socket_).getUseClientMode();
379            }
380            return false;
381        }
382    
383        /**
384         * Controls which particular cipher suites are enabled for use on this
385         * connection. Called before server negotiation.
386         * @param cipherSuites The cipher suites.
387         */
388        public void setEnabledCipherSuites(String[] cipherSuites) {
389            suites = new String[cipherSuites.length];
390            System.arraycopy(cipherSuites, 0, suites, 0, cipherSuites.length);
391        }
392    
393        /**
394         * Returns the names of the cipher suites which could be enabled
395         * for use on this connection.
396         * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns null.
397         * @return An array of cipher suite names, or <code>null</code>
398         */
399        public String[] getEnabledCipherSuites() {
400            if (_socket_ instanceof SSLSocket) {
401                return ((SSLSocket)_socket_).getEnabledCipherSuites();
402            }
403            return null;
404        }
405    
406        /**
407         * Controls which particular protocol versions are enabled for use on this
408         * connection. I perform setting before a server negotiation.
409         * @param protocolVersions The protocol versions.
410         */
411        public void setEnabledProtocols(String[] protocolVersions) {
412            protocols = new String[protocolVersions.length];
413            System.arraycopy(protocolVersions, 0, protocols, 0, protocolVersions.length);
414        }
415    
416        /**
417         * Returns the names of the protocol versions which are currently
418         * enabled for use on this connection.
419         * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns null.
420         * @return An array of protocols, or <code>null</code>
421         */
422        public String[] getEnabledProtocols() {
423            if (_socket_ instanceof SSLSocket) {
424                return ((SSLSocket)_socket_).getEnabledProtocols();
425            }
426            return null;
427        }
428    
429        /**
430         * PBSZ command. pbsz value: 0 to (2^32)-1 decimal integer.
431         * @param pbsz Protection Buffer Size.
432         * @throws SSLException If the server reply code does not equal "200".
433         * @throws IOException If an I/O error occurs while sending
434         * the command.
435         * @see #parsePBSZ(long)
436         */
437        public void execPBSZ(long pbsz) throws SSLException, IOException {
438            if (pbsz < 0 || 4294967295L < pbsz) { // 32-bit unsigned number
439                throw new IllegalArgumentException();
440            }
441            int status = sendCommand(CMD_PBSZ, String.valueOf(pbsz));
442            if (FTPReply.COMMAND_OK != status) {
443                throw new SSLException(getReplyString());
444            }
445        }
446    
447        /**
448         * PBSZ command. pbsz value: 0 to (2^32)-1 decimal integer.
449         * Issues the command and parses the response to return the negotiated value.
450         * 
451         * @param pbsz Protection Buffer Size.
452         * @throws SSLException If the server reply code does not equal "200".
453         * @throws IOException If an I/O error occurs while sending
454         * the command.
455         * @return the negotiated value.
456         * @see #execPBSZ(long)
457         * @since 3.0
458         */
459        public long parsePBSZ(long pbsz) throws SSLException, IOException {
460            execPBSZ(pbsz);
461            long minvalue = pbsz;
462            String remainder = extractPrefixedData("PBSZ=", getReplyString());
463            if (remainder != null) {
464                long replysz = Long.parseLong(remainder);
465                if (replysz < minvalue) {
466                    minvalue = replysz;
467                }
468            }
469            return minvalue;
470        }
471        
472        /**
473         * PROT command.<br/>
474         * C - Clear<br/>
475         * S - Safe(SSL protocol only)<br/>
476         * E - Confidential(SSL protocol only)<br/>
477         * P - Private
478         * <p>
479         * <b>N.B.</b> the method calls
480         *  {@link #setSocketFactory(javax.net.SocketFactory)} and
481         *  {@link #setServerSocketFactory(javax.net.ServerSocketFactory)}
482         *
483         * @param prot Data Channel Protection Level.
484         * @throws SSLException If the server reply code does not equal "200".
485         * @throws IOException If an I/O error occurs while sending
486         * the command.
487         */
488        public void execPROT(String prot) throws SSLException, IOException {
489            if (prot == null) {
490                prot = DEFAULT_PROT;
491            }
492            if (!checkPROTValue(prot)) {
493                throw new IllegalArgumentException();
494            }
495            if (FTPReply.COMMAND_OK != sendCommand(CMD_PROT, prot)) {
496                throw new SSLException(getReplyString());
497            }
498            if (DEFAULT_PROT.equals(prot)) {
499                setSocketFactory(null);
500                setServerSocketFactory(null);
501            } else {
502                setSocketFactory(new FTPSSocketFactory(context));
503                setServerSocketFactory(new FTPSServerSocketFactory(context));
504                initSslContext();
505            }
506        }
507    
508        /**
509         * Check the value that can be set in PROT Command value.
510         * @param prot Data Channel Protection Level.
511         * @return True - A set point is right / False - A set point is not right
512         */
513        private boolean checkPROTValue(String prot) {
514            for (int p = 0; p < PROT_COMMAND_VALUE.length; p++) {
515                if (PROT_COMMAND_VALUE[p].equals(prot)) {
516                    return true;
517                }
518            }
519            return false;
520        }
521    
522        /**
523         * Send an FTP command.
524         * A successful CCC (Clear Command Channel) command causes the underlying {@link SSLSocket} 
525         * instance to be assigned to a plain {@link Socket}
526         * @param command The FTP command.
527         * @return server reply.
528         * @throws IOException If an I/O error occurs while sending the command.
529         * @throws SSLException if a CCC command fails
530         * @see org.apache.commons.net.ftp.FTP#sendCommand(java.lang.String)
531         */
532        // Would like to remove this method, but that will break any existing clients that are using CCC
533        @Override
534        public int sendCommand(String command, String args) throws IOException {
535            int repCode = super.sendCommand(command, args);
536            /* If CCC is issued, restore socket i/o streams to unsecured versions */
537            if (CMD_CCC.equals(command)) {
538                if (FTPReply.COMMAND_OK == repCode) {
539                    _socket_.close();
540                    _socket_ = plainSocket;
541                    _controlInput_ = new BufferedReader(
542                        new InputStreamReader(
543                            _socket_ .getInputStream(), getControlEncoding()));
544                    _controlOutput_ = new BufferedWriter(
545                        new OutputStreamWriter(
546                            _socket_.getOutputStream(), getControlEncoding()));
547                } else {
548                    throw new SSLException(getReplyString());
549                }
550            }
551            return repCode;
552        }
553    
554        /**
555         * Returns a socket of the data connection.
556         * Wrapped as an {@link SSLSocket}, which carries out handshake processing.
557         * @param command The textual representation of the FTP command to send.
558         * @param arg The arguments to the FTP command.
559         * If this parameter is set to null, then the command is sent with
560         * no arguments.
561         * @return corresponding to the established data connection.
562         * Null is returned if an FTP protocol error is reported at any point
563         * during the establishment and initialization of the connection.
564         * @throws IOException If there is any problem with the connection.
565         * @see FTPClient#_openDataConnection_(int, String)
566         */
567        @Override
568        protected Socket _openDataConnection_(int command, String arg)
569                throws IOException {
570            Socket socket = super._openDataConnection_(command, arg);
571            _prepareDataSocket_(socket);
572            if (socket instanceof SSLSocket) {
573                SSLSocket sslSocket = (SSLSocket)socket;
574    
575                sslSocket.setUseClientMode(isClientMode);
576                sslSocket.setEnableSessionCreation(isCreation);
577    
578                // server mode
579                if (!isClientMode) {
580                    sslSocket.setNeedClientAuth(isNeedClientAuth);
581                    sslSocket.setWantClientAuth(isWantClientAuth);
582                }
583                if (suites != null) {
584                    sslSocket.setEnabledCipherSuites(suites);
585                }
586                if (protocols != null) {
587                    sslSocket.setEnabledProtocols(protocols);
588                }
589                sslSocket.startHandshake();
590            }
591    
592            return socket;
593        }
594    
595        /**
596        * Performs any custom initialization for a newly created SSLSocket (before
597        * the SSL handshake happens).
598        * Called by {@link #_openDataConnection_(int, String)} immediately 
599        * after creating the socket.
600        * The default implementation is a no-op
601        * @throws IOException 
602        */
603        protected void _prepareDataSocket_(Socket socket)
604                throws IOException {
605        }
606    
607        /**
608         * Get the currently configured {@link TrustManager}.
609         *
610         * @return A TrustManager instance.
611         */
612        public TrustManager getTrustManager() {
613            return trustManager;
614        }
615    
616        /**
617         * Override the default {@link TrustManager} to use.
618         *
619         * @param trustManager The TrustManager implementation to set.
620        * @see org.apache.commons.net.util.TrustManagerUtils
621         */
622        public void setTrustManager(TrustManager trustManager) {
623            this.trustManager = trustManager;
624        }
625    
626        /**
627         * Closes the connection to the FTP server and restores
628         * connection parameters to the default values.
629         * <p>
630         * Calls {@code setSocketFactory(null)} and {@code setServerSocketFactory(null)}
631         * to reset the factories that may have been changed during the session,
632         * e.g. by {@link #execPROT(String)}
633         * @exception IOException If an error occurs while disconnecting.
634         * @since 3.0
635         */
636        @Override
637        public void disconnect() throws IOException
638        {
639            super.disconnect();
640            setSocketFactory(null);
641            setServerSocketFactory(null);
642        }
643    
644        /**
645         * Send the AUTH command with the specified mechanism.
646         * @param mechanism The mechanism name to send with the command.
647         * @return server reply.
648         * @throws IOException If an I/O error occurs while sending
649         * the command.
650         * @since 3.0
651         */
652        public int execAUTH(String mechanism) throws IOException
653        {
654            return sendCommand(CMD_AUTH, mechanism);
655        }
656    
657        /**
658         * Send the ADAT command with the specified authentication data.
659         * @param data The data to send with the command.
660         * @return server reply.
661         * @throws IOException If an I/O error occurs while sending
662         * the command.
663         * @since 3.0
664         */
665        public int execADAT(byte[] data) throws IOException
666        {
667            if (data != null)
668            {
669                return sendCommand(CMD_ADAT, new String(Base64.encodeBase64(data)));
670            }
671            else
672            {
673                return sendCommand(CMD_ADAT);
674            }
675        }
676    
677        /**
678         * Send the CCC command to the server.
679         * The CCC (Clear Command Channel) command causes the underlying {@link SSLSocket} instance  to be assigned
680         * to a plain {@link Socket} instances
681         * @return server reply.
682         * @throws IOException If an I/O error occurs while sending
683         * the command.
684         * @since 3.0
685         */
686        public int execCCC() throws IOException
687        {
688            int repCode = sendCommand(CMD_CCC);
689    // This will be performed by sendCommand(String, String)
690    //        if (FTPReply.isPositiveCompletion(repCode)) {
691    //            _socket_.close();
692    //            _socket_ = plainSocket;
693    //            _controlInput_ = new BufferedReader(
694    //                new InputStreamReader(
695    //                    _socket_.getInputStream(), getControlEncoding()));
696    //            _controlOutput_ = new BufferedWriter(
697    //                new OutputStreamWriter(
698    //                    _socket_.getOutputStream(), getControlEncoding()));
699    //        }
700            return repCode;
701        }
702    
703        /**
704         * Send the MIC command with the specified data.
705         * @param data The data to send with the command.
706         * @return server reply.
707         * @throws IOException If an I/O error occurs while sending
708         * the command.
709         * @since 3.0
710         */
711        public int execMIC(byte[] data) throws IOException
712        {
713            if (data != null)
714            {
715                return sendCommand(CMD_MIC, new String(Base64.encodeBase64(data)));
716            }
717            else
718            {
719                return sendCommand(CMD_MIC, ""); // perhaps "=" or just sendCommand(String)?
720            }
721        }
722    
723        /**
724         * Send the CONF command with the specified data.
725         * @param data The data to send with the command.
726         * @return server reply.
727         * @throws IOException If an I/O error occurs while sending
728         * the command.
729         * @since 3.0
730         */
731        public int execCONF(byte[] data) throws IOException
732        {
733            if (data != null)
734            {
735                return sendCommand(CMD_CONF, new String(Base64.encodeBase64(data)));
736            }
737            else
738            {
739                return sendCommand(CMD_CONF, ""); // perhaps "=" or just sendCommand(String)?
740            }
741        }
742    
743        /**
744         * Send the ENC command with the specified data.
745         * @param data The data to send with the command.
746         * @return server reply.
747         * @throws IOException If an I/O error occurs while sending
748         * the command.
749         * @since 3.0
750         */
751        public int execENC(byte[] data) throws IOException
752        {
753            if (data != null)
754            {
755                return sendCommand(CMD_ENC, new String(Base64.encodeBase64(data)));
756            }
757            else
758            {
759                return sendCommand(CMD_ENC, ""); // perhaps "=" or just sendCommand(String)?
760            }
761        }
762    
763        /**
764         * Parses the given ADAT response line and base64-decodes the data.
765         * @param reply The ADAT reply to parse.
766         * @return the data in the reply, base64-decoded.
767         * @since 3.0
768         */
769        public byte[] parseADATReply(String reply)
770        {
771            if (reply == null) {
772                return null;
773            } else {
774                return Base64.decodeBase64(extractPrefixedData("ADAT=", reply));
775            }
776        }
777    
778        /**
779         * Extract the data from a reply with a prefix, e.g. PBSZ=1234 => 1234
780         * @param prefix the prefix to find
781         * @param reply where to find the prefix
782         * @return the remainder of the string after the prefix, or null if the prefix was not present.
783         */
784        private String extractPrefixedData(String prefix, String reply) {
785            int idx = reply.indexOf(prefix);
786            if (idx == -1) { 
787                return null;
788            }
789            // N.B. Cannot use trim before substring as leading space would affect the offset.
790            return reply.substring(idx+prefix.length()).trim();
791        }
792    
793        // DEPRECATED - for API compatibility only - DO NOT USE
794    
795        /** @deprecated - not used - may be removed in a future release */
796        @Deprecated
797        public static String KEYSTORE_ALGORITHM;
798    
799        /** @deprecated - not used - may be removed in a future release */
800        @Deprecated
801        public static String TRUSTSTORE_ALGORITHM;
802    
803        /** @deprecated - not used - may be removed in a future release */
804        @Deprecated
805        public static String PROVIDER;
806    
807        /** @deprecated - not used - may be removed in a future release */
808        @Deprecated
809        public static String STORE_TYPE;
810    
811    }
812    /* kate: indent-width 4; replace-tabs on; */