    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.pop3;
    019
    020    import java.io.IOException;
    021    import java.net.Socket;
    022
    023    import javax.net.ssl.KeyManager;
    024    import javax.net.ssl.SSLContext;
    025    import javax.net.ssl.SSLException;
    026    import javax.net.ssl.SSLSocket;
    027    import javax.net.ssl.SSLSocketFactory;
    028    import javax.net.ssl.TrustManager;
    029
    030    import org.apache.commons.net.util.SSLContextUtils;
    031
    032    /**
    033     * POP3 over SSL processing. Copied from FTPSClient.java and modified to suit POP3.
    034     * If implicit mode is selected (NOT the default), SSL/TLS negotiation starts right
    035     * after the connection has been established. In explicit mode (the default), SSL/TLS
    036     * negotiation starts when the user calls execTLS() and the server accepts the command.
    037     * Implicit usage:
    038     *               POP3SClient c = new POP3SClient(true);
    039     *               c.connect("127.0.0.1", 995);
    040     * Explicit usage:
    041     *               POP3SClient c = new POP3SClient();
    042     *               c.connect("127.0.0.1", 110);
    043     *               if (c.execTLS()) { /rest of the commands here/ }
    044     * @since 3.0
    045     */
    046    public class POP3SClient extends POP3Client
    047    {
    048        // from http://www.iana.org/assignments/port-numbers
    049
    050        //    pop3s           995/tcp    pop3 protocol over TLS/SSL (was spop3)
    051        //    pop3s           995/udp    pop3 protocol over TLS/SSL (was spop3)
    052
    053        private static final int DEFAULT_POP3S_PORT = 995;
    054
    055        /** Default secure socket protocol name, like TLS */
    056        private static final String DEFAULT_PROTOCOL = "TLS";
    057
    058        /** The security mode. True - Implicit Mode / False - Explicit Mode. */
    059        private final boolean isImplicit;
    060        /** The secure socket protocol to be used, like SSL/TLS. */
    061        private final String protocol;
    062        /** The context object. */
    063        private SSLContext context = null;
    064        /** The cipher suites. SSLSockets have a default set of these anyway,
    065            so no initialization required. */
    066        private String[] suites = null;
    067        /** The protocol versions. */
    068        private String[] protocols = //null;
    069            null;//{"SSLv2", "SSLv3", "TLSv1", "TLSv1.1", "SSLv2Hello"};
    070
    071        /** The FTPS {@link TrustManager} implementation, default null. */
    072        private TrustManager trustManager = null;
    073
    074        /** The {@link KeyManager}, default null. */
    075        private KeyManager keyManager = null;
    076
    077        /**
    078         * Constructor for POP3SClient, using {@link #DEFAULT_PROTOCOL} i.e. TLS
    079         * Sets security mode to explicit.
    080         */
    081        public POP3SClient()
    082        {
    083            this(DEFAULT_PROTOCOL, false);
    084        }
    085
    086        /**
    087         * Constructor for POP3SClient, using {@link #DEFAULT_PROTOCOL} i.e. TLS
    088         * @param implicit The security mode, {@code true} for implicit, {@code false} for explicit
    089         */
    090        public POP3SClient(boolean implicit)
    091        {
    092            this(DEFAULT_PROTOCOL, implicit);
    093        }
    094
    095        /**
    096         * Constructor for POP3SClient.
    097         * Sets security mode to explicit.
    098         * @param proto the protocol.
    099         */
    100        public POP3SClient(String proto)
    101        {
    102            this(proto, false);
    103        }
    104
    105        /**
    106         * Constructor for POP3SClient.
    107         * @param proto the protocol.
    108         * @param implicit The security mode, {@code true} for implicit, {@code false} for explicit
    109         */
    110        public POP3SClient(String proto, boolean implicit)
    111        {
    112            this(proto, implicit, null);
    113        }
    114
    115        /**
    116         * Constructor for POP3SClient.
    117         * Sets the default port to {@link #DEFAULT_POP3S_PORT} - 995 - if using implicit mode
    118         * @param proto the protocol.
    119         * @param implicit The security mode, {@code true} for implicit, {@code false} for explicit
    120         * @param ctx the context to be used
    121         */
    122        public POP3SClient(String proto, boolean implicit, SSLContext ctx)
    123        {
    124            super();
    125            protocol = proto;
    126            isImplicit = implicit;
    127            context = ctx;
    128            if (isImplicit) {
    129                setDefaultPort(DEFAULT_POP3S_PORT);
    130            }
    131        }
    132
    133        /**
    134         * Constructor for POP3SClient, using {@link #DEFAULT_PROTOCOL} i.e. TLS
    135         * @param implicit The security mode, {@code true} for implicit, {@code false} for explicit
    136         * @param ctx A pre-configured SSL Context.
    137         */
    138        public POP3SClient(boolean implicit, SSLContext ctx)
    139        {
    140            this(DEFAULT_PROTOCOL, implicit, ctx);
    141        }
    142
    143        /**
    144         * Constructor for POP3SClient, using {@link #DEFAULT_PROTOCOL} - TLS - and isImplicit = false
    145         * @param context A pre-configured SSL Context.
    146         * @see #POP3SClient(boolean, SSLContext)
    147         */
    148        public POP3SClient(SSLContext context)
    149        {
    150            this(false, context);
    151        }
    152
    153        /**
    154         * Because there are so many connect() methods,
    155         * the _connectAction_() method is provided as a means of performing
    156         * some action immediately after establishing a connection,
    157         * rather than reimplementing all of the connect() methods.
    158         * @throws IOException If it is thrown by _connectAction_().
    159         * @see org.apache.commons.net.SocketClient#_connectAction_()
    160         */
    161        @Override
    162        protected void _connectAction_() throws IOException
    163        {
    164            // Implicit mode.
    165            if (isImplicit) performSSLNegotiation();
    166            super._connectAction_();
    167            // Explicit mode - don't do anything. The user calls execTLS()
    168        }
    169
    170        /**
    171         * Performs a lazy init of the SSL context.
    172         * @throws IOException When could not initialize the SSL context.
    173         */
    174        private void initSSLContext() throws IOException
    175        {
    176            if (context == null)
    177            {
    178                context = SSLContextUtils.createSSLContext(protocol, getKeyManager(), getTrustManager());
    179            }
    180        }
    181
    182        /**
    183         * SSL/TLS negotiation. Acquires an SSL socket of a
    184         * connection and carries out handshake processing.
    185         * @throws IOException If server negotiation fails.
    186         */
    187        private void performSSLNegotiation() throws IOException
    188        {
    189            initSSLContext();
    190
    191            SSLSocketFactory ssf = context.getSocketFactory();
    192            String ip = getRemoteAddress().getHostAddress();
    193            int port = getRemotePort();
    194            SSLSocket socket =
    195                (SSLSocket) ssf.createSocket(_socket_, ip, port, true);
    196            socket.setEnableSessionCreation(true);
    197            socket.setUseClientMode(true);
    198
    199            if (protocols != null) socket.setEnabledProtocols(protocols);
    200            if (suites != null) socket.setEnabledCipherSuites(suites);
    201            socket.startHandshake();
    202
    203            _socket_ = socket;
    204            _input_ = socket.getInputStream();
    205            _output_ = socket.getOutputStream();
    206        }
    207
    208        /**
    209         * Get the {@link KeyManager} instance.
    210         * @return The current {@link KeyManager} instance.
    211         */
    212        private KeyManager getKeyManager()
    213        {
    214            return keyManager;
    215        }
    216
    217        /**
    218         * Set a {@link KeyManager} to use.
    219         * @param newKeyManager The KeyManager implementation to set.
    220         * @see org.apache.commons.net.util.KeyManagerUtils
    221         */
    222        public void setKeyManager(KeyManager newKeyManager)
    223        {
    224            keyManager = newKeyManager;
    225        }
    226
    227        /**
    228         * Controls which particular cipher suites are enabled for use on this
    229         * connection. Called before server negotiation.
    230         * @param cipherSuites The cipher suites.
    231         */
    232        public void setEnabledCipherSuites(String[] cipherSuites)
    233        {
    234            suites = new String[cipherSuites.length];
    235            System.arraycopy(cipherSuites, 0, suites, 0, cipherSuites.length);
    236        }
    237
    238        /**
    239         * Returns the names of the cipher suites which could be enabled
    240         * for use on this connection.
    241         * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns null.
    242         * @return An array of cipher suite names, or <code>null</code>.
    243         */
    244        public String[] getEnabledCipherSuites()
    245        {
    246            if (_socket_ instanceof SSLSocket)
    247            {
    248                return ((SSLSocket)_socket_).getEnabledCipherSuites();
    249            }
    250            return null;
    251        }
    252
    253        /**
    254         * Controls which particular protocol versions are enabled for use on this
    255         * connection. I perform setting before a server negotiation.
    256         * @param protocolVersions The protocol versions.
    257         */
    258        public void setEnabledProtocols(String[] protocolVersions)
    259        {
    260            protocols = new String[protocolVersions.length];
    261            System.arraycopy(protocolVersions, 0, protocols, 0, protocolVersions.length);
    262        }
    263
    264        /**
    265         * Returns the names of the protocol versions which are currently
    266         * enabled for use on this connection.
    267         * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns null.
    268         * @return An array of protocols, or <code>null</code>.
    269         */
    270        public String[] getEnabledProtocols()
    271        {
    272            if (_socket_ instanceof SSLSocket)
    273            {
    274                return ((SSLSocket)_socket_).getEnabledProtocols();
    275            }
    276            return null;
    277        }
    278
    279        /**
    280         * The TLS command execution.
    281         * @throws SSLException If the server reply code is not positive.
    282         * @throws IOException If an I/O error occurs while sending
    283         * the command or performing the negotiation.
    284         * @return TRUE if the command and negotiation succeeded.
    285         */
    286        public boolean execTLS() throws SSLException, IOException
    287        {
    288            if (sendCommand("STLS") != POP3Reply.OK)
    289            {
    290                return false;
    291                //throw new SSLException(getReplyString());
    292            }
    293            performSSLNegotiation();
    294            return true;
    295        }
    296
    297        /**
    298         * Get the currently configured {@link TrustManager}.
    299         * @return A TrustManager instance.
    300         */
    301        public TrustManager getTrustManager()
    302        {
    303            return trustManager;
    304        }
    305
    306        /**
    307         * Override the default {@link TrustManager} to use.
    308         * @param newTrustManager The TrustManager implementation to set.
    309         * @see org.apache.commons.net.util.TrustManagerUtils
    310         */
    311        public void setTrustManager(TrustManager newTrustManager)
    312        {
    313            trustManager = newTrustManager;
    314        }
    315    }
    316
    317    /* kate: indent-width 4; replace-tabs on; */