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