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; */