View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.net.pop3;
19  
20  import java.io.BufferedWriter;
21  import java.io.IOException;
22  import java.io.InputStreamReader;
23  import java.io.OutputStreamWriter;
24  
25  import javax.net.ssl.KeyManager;
26  import javax.net.ssl.SSLContext;
27  import javax.net.ssl.SSLException;
28  import javax.net.ssl.SSLSocket;
29  import javax.net.ssl.SSLSocketFactory;
30  import javax.net.ssl.TrustManager;
31  
32  import org.apache.commons.net.io.CRLFLineReader;
33  import org.apache.commons.net.util.SSLContextUtils;
34  
35  /**
36   * POP3 over SSL processing. Copied from FTPSClient.java and modified to suit POP3.
37   * If implicit mode is selected (NOT the default), SSL/TLS negotiation starts right
38   * after the connection has been established. In explicit mode (the default), SSL/TLS
39   * negotiation starts when the user calls execTLS() and the server accepts the command.
40   * Implicit usage:
41   *               POP3SClient c = new POP3SClient(true);
42   *               c.connect("127.0.0.1", 995);
43   * Explicit usage:
44   *               POP3SClient c = new POP3SClient();
45   *               c.connect("127.0.0.1", 110);
46   *               if (c.execTLS()) { /rest of the commands here/ }
47   * @since 3.0
48   */
49  public class POP3SClient extends POP3Client
50  {
51      // from http://www.iana.org/assignments/port-numbers
52  
53      //    pop3s           995/tcp    pop3 protocol over TLS/SSL (was spop3)
54      //    pop3s           995/udp    pop3 protocol over TLS/SSL (was spop3)
55  
56      private static final int DEFAULT_POP3S_PORT = 995;
57  
58      /** Default secure socket protocol name, like TLS */
59      private static final String DEFAULT_PROTOCOL = "TLS";
60  
61      /** The security mode. True - Implicit Mode / False - Explicit Mode. */
62      private final boolean isImplicit;
63      /** The secure socket protocol to be used, like SSL/TLS. */
64      private final String protocol;
65      /** The context object. */
66      private SSLContext context = null;
67      /** The cipher suites. SSLSockets have a default set of these anyway,
68          so no initialization required. */
69      private String[] suites = null;
70      /** The protocol versions. */
71      private String[] protocols = //null;
72          null;//{"SSLv2", "SSLv3", "TLSv1", "TLSv1.1", "SSLv2Hello"};
73  
74      /** The FTPS {@link TrustManager} implementation, default null. */
75      private TrustManager trustManager = null;
76  
77      /** The {@link KeyManager}, default null. */
78      private KeyManager keyManager = null;
79  
80      /**
81       * Constructor for POP3SClient, using {@link #DEFAULT_PROTOCOL} i.e. TLS
82       * Sets security mode to explicit.
83       */
84      public POP3SClient()
85      {
86          this(DEFAULT_PROTOCOL, false);
87      }
88  
89      /**
90       * Constructor for POP3SClient, using {@link #DEFAULT_PROTOCOL} i.e. TLS
91       * @param implicit The security mode, {@code true} for implicit, {@code false} for explicit
92       */
93      public POP3SClient(boolean implicit)
94      {
95          this(DEFAULT_PROTOCOL, implicit);
96      }
97  
98      /**
99       * 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; */