You are viewing a plain text version of this content. The canonical link for it is here.
Posted to scm@geronimo.apache.org by sa...@apache.org on 2014/11/25 22:39:49 UTC

svn commit: r1641706 - in /geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider: ./ src/main/java/org/apache/geronimo/javamail/store/imap/connection/ src/main/java/org/apache/geronimo/javamail/store/pop3/connection/ src/main/jav...

Author: salyh
Date: Tue Nov 25 21:39:49 2014
New Revision: 1641706

URL: http://svn.apache.org/r1641706
Log:
Fix GERONIMO-5429 GERONIMO-5430 GERONIMO-5873, fixed also APOP authentication for POP3, POP3 store can now use STARTTLS

Modified:
    geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/pom.xml
    geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/connection/IMAPConnection.java
    geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/connection/POP3Connection.java
    geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPConnection.java
    geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/util/MailConnection.java
    geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/util/ProtocolProperties.java

Modified: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/pom.xml
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/pom.xml?rev=1641706&r1=1641705&r2=1641706&view=diff
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/pom.xml (original)
+++ geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/pom.xml Tue Nov 25 21:39:49 2014
@@ -104,7 +104,8 @@
                             javax.net.ssl*;resolution:="optional",
                             javax.security.sasl*;resolution:="optional",
                             javax.security.auth.callback*;resolution:="optional",
-                            org.apache.geronimo.mail.james.mime4j.codec
+                            org.apache.geronimo.mail.james.mime4j.codec,
+                            javax.net
                         </Import-Package>
                     </instructions>
                     <unpackBundle>true</unpackBundle>

Modified: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/connection/IMAPConnection.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/connection/IMAPConnection.java?rev=1641706&r1=1641705&r2=1641706&view=diff
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/connection/IMAPConnection.java (original)
+++ geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/connection/IMAPConnection.java Tue Nov 25 21:39:49 2014
@@ -141,30 +141,43 @@ public class IMAPConnection extends Mail
 
             // make sure we process these now
             processPendingResponses();
-
-            // if we're not already using an SSL connection, and we have permission to issue STARTTLS, AND
-            // the server supports this, then switch to TLS mode before continuing.
-            if (!sslConnection && props.getBooleanProperty(MAIL_STARTTLS_ENABLE, false) && hasCapability(CAPABILITY_STARTTLS)) {
-                // if the server supports TLS, then use it for the connection.
-                // on our connection.
-
-                // tell the server of our intention to start a TLS session
-                sendSimpleCommand("STARTTLS");
-
-                // The connection is then handled by the superclass level.
-                getConnectedTLSSocket();
-
-                // create the special reader for pulling the responses.
-                reader = new IMAPResponseStream(inputStream);
-
-                // the IMAP spec states that the capability response is independent of login state or
-                // user, but I'm not sure I believe that to be the case.  It doesn't hurt to refresh
-                // the information again after establishing a secure connection.
-                getCapability();
-                // and we need to repeat this check.
-                if (extractResponse("PREAUTH") != null) {
-                    preAuthorized = true;
+            
+            boolean requireTLS = props.getBooleanProperty(MAIL_STARTTLS_REQUIRED, false);
+            boolean enableTLS = props.getBooleanProperty(MAIL_STARTTLS_ENABLE, false);
+            boolean serverSupportsTLS = hasCapability(CAPABILITY_STARTTLS);
+
+            // if we're not already using an SSL connection, and we have permission to issue STARTTLS or its even required
+            // try to setup a SSL connection
+            if (!sslConnection && (enableTLS || requireTLS)) {
+                
+                //if the server does not support TLS check if its required.
+                //If true then throw an error, if not establish a non SSL connection
+                if(requireTLS && !serverSupportsTLS) {
+                    throw new MessagingException("Server doesn't support required transport level security");
+                } else if (serverSupportsTLS){
+                    // tell the server of our intention to start a TLS session
+                    sendSimpleCommand("STARTTLS");
+    
+                    // The connection is then handled by the superclass level.
+                    getConnectedTLSSocket();
+    
+                    // create the special reader for pulling the responses.
+                    reader = new IMAPResponseStream(inputStream);
+    
+                    // the IMAP spec states that the capability response is independent of login state or
+                    // user, but I'm not sure I believe that to be the case.  It doesn't hurt to refresh
+                    // the information again after establishing a secure connection.
+                    getCapability();
+                    // and we need to repeat this check.
+                    if (extractResponse("PREAUTH") != null) {
+                        preAuthorized = true;
+                    }
+                } else {
+                    if (debug) {
+                        debugOut("STARTTLS is enabled but not required and server does not support it. So we establish a connection without transport level security");
+                    }
                 }
+                
             }
 
             // damn, no login required.

Modified: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/connection/POP3Connection.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/connection/POP3Connection.java?rev=1641706&r1=1641705&r2=1641706&view=diff
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/connection/POP3Connection.java (original)
+++ geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/pop3/connection/POP3Connection.java Tue Nov 25 21:39:49 2014
@@ -37,6 +37,7 @@ import javax.mail.internet.InternetHeade
 
 import org.apache.geronimo.javamail.authentication.AuthenticatorFactory;
 import org.apache.geronimo.javamail.authentication.ClientAuthenticator;
+import org.apache.geronimo.javamail.store.imap.connection.IMAPResponseStream;
 import org.apache.geronimo.javamail.store.pop3.POP3Constants;
 import org.apache.geronimo.javamail.util.CommandFailedException;
 import org.apache.geronimo.javamail.util.InvalidCommandException;
@@ -57,7 +58,7 @@ public class POP3Connection extends Mail
     static final protected String MAIL_AUTH_ENABLED = "auth.enable";
     static final protected String MAIL_RESET_QUIT = "rsetbeforequit";
     static final protected String MAIL_DISABLE_TOP = "disabletop";
-    static final protected String MAIL_FORGET_TOP = "forgettopheaders";
+    //static final protected String MAIL_FORGET_TOP = "forgettopheaders"; //TODO forgettopheaders
 
     // the initial greeting string, which might be required for APOP authentication.
     protected String greeting;
@@ -71,27 +72,22 @@ public class POP3Connection extends Mail
     protected PrintWriter writer;
     // this connection was closed unexpectedly
     protected boolean closed;
-    // indicates whether this conneciton is currently logged in.  Once
+    // indicates whether this connection is currently logged in.  Once
     // we send a QUIT, we're finished.
     protected boolean loggedIn;
     // indicates whether we need to avoid using the TOP command
     // when retrieving headers
     protected boolean topDisabled = false;
+    // is TLS enabled on our part?
+    protected boolean useTLS = false;
+    // is TLS required on our part?
+    protected boolean requireTLS = false;
 
     /**
      * Normal constructor for an POP3Connection() object.
      *
-     * @param store    The store we're associated with (source of parameter values).
-     * @param host     The target host name of the IMAP server.
-     * @param port     The target listening port of the server.  Defaults to 119 if
-     *                 the port is specified as -1.
-     * @param username The login user name (can be null unless authentication is
-     *                 required).
-     * @param password Password associated with the userid account.  Can be null if
-     *                 authentication is not required.
-     * @param sslConnection
-     *                 True if this is targetted as an SSLConnection.
-     * @param debug    The session debug flag.
+     * @param props  The protocol properties abstraction containing our
+     *               property modifiers.
      */
     public POP3Connection(ProtocolProperties props) {
         super(props);
@@ -100,6 +96,11 @@ public class POP3Connection extends Mail
         authEnabled = props.getBooleanProperty(MAIL_AUTH_ENABLED, false);
         apopEnabled = props.getBooleanProperty(MAIL_APOP_ENABLED, false);
         topDisabled = props.getBooleanProperty(MAIL_DISABLE_TOP, false);
+        // and also check for TLS enablement.
+        useTLS = props.getBooleanProperty(MAIL_STARTTLS_ENABLE, false);
+        // and also check if TLS is required.
+        requireTLS = props.getBooleanProperty(MAIL_STARTTLS_REQUIRED, false);
+       
     }
 
 
@@ -121,7 +122,35 @@ public class POP3Connection extends Mail
             getConnection();
             // consume the welcome line
             getWelcome();
-
+            
+            // if we're not already using an SSL connection, and we have permission to issue STARTTLS or its even required
+            // try to setup a SSL connection
+            if (!sslConnection && (useTLS || requireTLS)) {
+                
+                    // tell the server of our intention to start a TLS session
+                    POP3Response starttlsResponse = null;
+                    try {
+                        starttlsResponse = sendCommand("STLS");
+                    } catch (CommandFailedException e) {
+                       
+                    }
+                    
+                    //if the server does not support TLS check if its required.
+                    //If true then throw an error, if not establish a non SSL connection
+                    if(requireTLS && (starttlsResponse == null || starttlsResponse.isError())) {
+                        throw new MessagingException("Server doesn't support required transport level security");
+                    } else if(starttlsResponse != null && starttlsResponse.getStatus() == POP3Response.OK) {
+                     // The connection is then handled by the superclass level.
+                        getConnectedTLSSocket();
+                    } else {
+                        if (debug) {
+                            debugOut("STARTTLS is enabled but not required and server does not support it. So we establish a connection without transport level security");
+                        }
+                    }
+            }
+            
+            getConnection();
+            
             // go login with the server
             if (login())
             {
@@ -154,7 +183,7 @@ public class POP3Connection extends Mail
             throw new MessagingException("Unable to obtain a connection to the POP3 server", e);
         }
 
-        // The POp3 protocol is inherently a string-based protocol, so we get
+        // The POP3 protocol is inherently a string-based protocol, so we get
         // string readers/writers for the connection streams.  Note that we explicitly
         // set the encoding to ensure that an inappropriate native encoding is not picked up.
         try {
@@ -210,7 +239,7 @@ public class POP3Connection extends Mail
     }
 
     /**
-     * Test if the connnection has been forcibly closed.
+     * Test if the connection has been forcibly closed.
      *
      * @return True if the server disconnected the connection.
      */
@@ -259,7 +288,7 @@ public class POP3Connection extends Mail
         byte[] data = null;
 
         String line;
-        MIMEInputReader source = new MIMEInputReader(reader);
+        //MIMEInputReader source = new MIMEInputReader(reader); //TODO unused
 
         try {
             line = reader.readLine();
@@ -570,7 +599,7 @@ public class POP3Connection extends Mail
             throw new MessagingException("Unable to create MD5 digest", e);
         }
         // this will throw an exception if it gives an error failure
-        sendCommand("APOP " + username + " " + Hex.encode(digest));
+        sendCommand("APOP " + username + " " + new String(Hex.encode(digest)));
         // no exception, we must have passed
         return true;
     }

Modified: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPConnection.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPConnection.java?rev=1641706&r1=1641705&r2=1641706&view=diff
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPConnection.java (original)
+++ geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/transport/smtp/SMTPConnection.java Tue Nov 25 21:39:49 2014
@@ -64,6 +64,7 @@ public class SMTPConnection extends Mail
     protected static final String MAIL_SMTP_ALLOW8BITMIME = "allow8bitmime";
     protected static final String MAIL_SMTP_REPORT_SUCCESS = "reportsuccess";
     protected static final String MAIL_SMTP_STARTTLS_ENABLE = "starttls.enable";
+    protected static final String MAIL_SMTP_STARTTLS_REQUIRED = "starttls.required";
     protected static final String MAIL_SMTP_AUTH = "auth";
     protected static final String MAIL_SMTP_FROM = "from";
     protected static final String MAIL_SMTP_DSN_RET = "dsn.ret";
@@ -83,6 +84,8 @@ public class SMTPConnection extends Mail
     protected boolean serverTLS = false;
     // is TLS enabled on our part?
     protected boolean useTLS = false;
+    // is TLS required on our part?
+    protected boolean requireTLS = false;
     // should we use 8BITMIME encoding if supported by the server?
     protected boolean use8bit = false;
 
@@ -98,6 +101,8 @@ public class SMTPConnection extends Mail
         reportSuccess = props.getBooleanProperty(MAIL_SMTP_REPORT_SUCCESS, false);
         // and also check for TLS enablement.
         useTLS = props.getBooleanProperty(MAIL_SMTP_STARTTLS_ENABLE, false);
+        // and also check if TLS is required.
+        requireTLS = props.getBooleanProperty(MAIL_SMTP_STARTTLS_REQUIRED, false);
         // and also check for 8bitmime support
         use8bit = props.getBooleanProperty(MAIL_SMTP_ALLOW8BITMIME, false);
     }
@@ -804,23 +809,31 @@ public class SMTPConnection extends Mail
             sendHelo();
         }
 
-        if (useTLS) {
-            // if we've been told to use TLS, and this server doesn't support
-            // it, then this is a failure
-            if (!serverTLS) {
+        if (useTLS || requireTLS) {
+            // if we've been told to use TLS
+            // if its not required and server does not support it we establish an unsecure connection
+            //see GERONIMO-5873 and GERONIMO-5430
+            if (requireTLS && !serverTLS) {
+                // if we've been told to use TLS, and this server doesn't support
+                // it, then this is a failure
                 throw new MessagingException("Server doesn't support required transport level security");
-            }
-            // if the server supports TLS, then use it for the connection.
-            // on our connection.
-            getConnectedTLSSocket();
-
-            // some servers (gmail is one that I know of) only send a STARTTLS
-            // extension message on the
-            // first EHLO command. Now that we have the TLS handshaking
-            // established, we need to send a
-            // second EHLO message to retrieve the AUTH records from the server.
-            if (!sendEhlo()) {
-                throw new MessagingException("Failure sending EHLO command to SMTP server");
+            } else if (serverTLS){
+                // if the server supports TLS, then use it for the connection.
+                // on our connection.
+                getConnectedTLSSocket();
+
+                // some servers (gmail is one that I know of) only send a STARTTLS
+                // extension message on the
+                // first EHLO command. Now that we have the TLS handshaking
+                // established, we need to send a
+                // second EHLO message to retrieve the AUTH records from the server.
+                if (!sendEhlo()) {
+                    throw new MessagingException("Failure sending EHLO command to SMTP server");
+                }
+            } else {
+                if (debug) {
+                    debugOut("STARTTLS is enabled but not required and server does not support it. So we establish a connection without transport level security");
+                }
             }
         }
 
@@ -928,6 +941,27 @@ public class SMTPConnection extends Mail
         useTLS = start;
     }
 
+    
+    /**
+     * Return the current requireTLS property.
+     * 
+     * @return The current requireTLS property.
+     */
+    public boolean getRequireTLS() {
+        return requireTLS;
+    }
+
+
+    /**
+     * Set a new value for the requireTLS property.
+     * 
+     * @param require
+     *                  The new setting.
+     */
+    public void setRequireTLS(boolean require) {
+        requireTLS = require;
+    }
+
 
     /**
      * Process an extension string passed back as the EHLP response.

Modified: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/util/MailConnection.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/util/MailConnection.java?rev=1641706&r1=1641705&r2=1641706&view=diff
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/util/MailConnection.java (original)
+++ geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/util/MailConnection.java Tue Nov 25 21:39:49 2014
@@ -17,32 +17,43 @@
 
 package org.apache.geronimo.javamail.util;
 
-import java.io.IOException; 
-import java.io.InputStream; 
-import java.io.OutputStream; 
-import java.io.PrintStream; 
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.net.InetAddress;
-import java.net.Socket; 
+import java.net.Socket;
 import java.net.UnknownHostException;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
-import java.util.StringTokenizer; 
+import java.util.StringTokenizer;
 
 import javax.mail.MessagingException;
 import javax.mail.Session;
+import javax.net.SocketFactory;
+import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLSocket;
-
-import org.apache.geronimo.javamail.authentication.ClientAuthenticator; 
-import org.apache.geronimo.javamail.authentication.CramMD5Authenticator; 
-import org.apache.geronimo.javamail.authentication.DigestMD5Authenticator; 
-import org.apache.geronimo.javamail.authentication.LoginAuthenticator; 
-import org.apache.geronimo.javamail.authentication.PlainAuthenticator; 
-import org.apache.geronimo.javamail.authentication.SASLAuthenticator; 
-import org.apache.geronimo.javamail.util.CommandFailedException;      
-import org.apache.geronimo.javamail.util.InvalidCommandException;      
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+
+import org.apache.geronimo.javamail.authentication.ClientAuthenticator;
+import org.apache.geronimo.javamail.authentication.CramMD5Authenticator;
+import org.apache.geronimo.javamail.authentication.DigestMD5Authenticator;
+import org.apache.geronimo.javamail.authentication.LoginAuthenticator;
+import org.apache.geronimo.javamail.authentication.PlainAuthenticator;
+import org.apache.geronimo.javamail.authentication.SASLAuthenticator;
 
 /**
  * Base class for all mail Store/Transport connection.  Centralizes management
@@ -59,10 +70,10 @@ public class MailConnection {
     /**
      * property keys for protocol properties.
      */
-    protected static final String MAIL_AUTH = "auth";
     protected static final String MAIL_PORT = "port";
     protected static final String MAIL_LOCALHOST = "localhost";
     protected static final String MAIL_STARTTLS_ENABLE = "starttls.enable";
+    protected static final String MAIL_STARTTLS_REQUIRED = "starttls.required";
     protected static final String MAIL_SSL_ENABLE = "ssl.enable";
     protected static final String MAIL_TIMEOUT = "timeout";
     protected static final String MAIL_SASL_ENABLE = "sasl.enable";
@@ -71,11 +82,19 @@ public class MailConnection {
     protected static final String MAIL_SASL_MECHANISMS = "sasl.mechanisms";
     protected static final String MAIL_PLAIN_DISABLE = "auth.plain.disable";
     protected static final String MAIL_LOGIN_DISABLE = "auth.login.disable";
+    
+    protected static final String MAIL_FACTORY = "socketFactory"; //GERONIMO-5429
     protected static final String MAIL_FACTORY_CLASS = "socketFactory.class";
     protected static final String MAIL_FACTORY_FALLBACK = "socketFactory.fallback";
     protected static final String MAIL_FACTORY_PORT = "socketFactory.port";
+
+    protected static final String MAIL_SSL_FACTORY = "ssl.socketFactory"; //GERONIMO-5429
+    protected static final String MAIL_SSL_FACTORY_CLASS = "ssl.socketFactory.class";
+    protected static final String MAIL_SSL_FACTORY_PORT = "ssl.socketFactory.port";
     protected static final String MAIL_SSL_PROTOCOLS = "ssl.protocols";
     protected static final String MAIL_SSL_CIPHERSUITES = "ssl.ciphersuites";
+    protected static final String MAIL_SSL_TRUST = "ssl.trust";
+
     protected static final String MAIL_LOCALADDRESS = "localaddress";
     protected static final String MAIL_LOCALPORT = "localport";
     protected static final String MAIL_ENCODE_TRACE = "encodetrace";
@@ -167,6 +186,11 @@ public class MailConnection {
         // initialize our debug settings from the session 
         debug = session.getDebug(); 
         debugStream = session.getDebugOut();
+        
+        String mailSSLEnable = props.getProperty(MAIL_SSL_ENABLE);
+        if(mailSSLEnable != null) {
+            this.sslConnection = Boolean.valueOf(mailSSLEnable);
+        }
     }
     
     
@@ -200,7 +224,7 @@ public class MailConnection {
             }
         }
     	
-    	// Before we do anything, let's make sure that we succesfully received a host
+    	// Before we do anything, let's make sure that we successfully received a host
     	if ( host == null ) {
     		host = DEFAULT_MAIL_HOST;
     	}
@@ -292,62 +316,84 @@ public class MailConnection {
     protected void getConnectedSocket() throws IOException {
         debugOut("Attempting plain socket connection to server " + serverHost + ":" + serverPort);
 
-        // check the properties that control how we connect. 
-        getConnectionProperties(); 
-
-        // the socket factory can be specified via a session property.  By default, we just directly
-        // instantiate a socket without using a factory.
-        String socketFactory = props.getProperty(MAIL_FACTORY_CLASS);
-
-        // make sure the socket is nulled out to start 
+        // make sure this is null 
         socket = null;
-
-        // if there is no socket factory defined (normal), we just create a socket directly.
-        if (socketFactory == null) {
-            socket = new Socket(serverHost, serverPort, localAddress, localPort);
+        
+        createSocket(false);
+        
+        // if we have a timeout value, set that before returning 
+        if (timeout >= 0) {
+            socket.setSoTimeout(timeout);
+        }
+    }
+    
+    private boolean createSocketFromFactory(boolean ssl, boolean layer) throws IOException {
+        
+        String socketFactoryClass = props.getProperty(ssl?MAIL_SSL_FACTORY_CLASS:MAIL_FACTORY_CLASS);
+        
+        if(socketFactoryClass == null) {
+            return false;
         }
+        
+        // we'll try this with potentially two different factories if we're allowed to fall back.
+        boolean fallback = props.getBooleanProperty(MAIL_FACTORY_FALLBACK, false);
+        int socketFactoryPort = props.getIntProperty(ssl?MAIL_SSL_FACTORY_PORT:MAIL_FACTORY_PORT, -1);
+        Integer portArg = new Integer(socketFactoryPort == -1 ? serverPort : socketFactoryPort);
+        
+        debugOut("Creating "+(ssl?"":"non-")+"SSL socket using factory " + socketFactoryClass+ " listening on port "+portArg);
 
-        else {
+        while (true) {
             try {
-                int socketFactoryPort = props.getIntProperty(MAIL_FACTORY_PORT, -1);
-
-                // we choose the port used by the socket based on overrides.
-                Integer portArg = new Integer(socketFactoryPort == -1 ? serverPort : socketFactoryPort);
-
+                
                 // use the current context loader to resolve this.
                 ClassLoader loader = Thread.currentThread().getContextClassLoader();
-                Class factoryClass = loader.loadClass(socketFactory);
+                Class factoryClass = loader.loadClass(socketFactoryClass);
 
                 // done indirectly, we need to invoke the method using reflection.
                 // This retrieves a factory instance.
-                Method getDefault = factoryClass.getMethod("getDefault", new Class[0]);
-                Object defFactory = getDefault.invoke(new Object(), new Object[0]);
-
+                //Method getDefault = factoryClass.getMethod("getDefault", new Class[0]); //TODO check instantiation of socket factory
+                Object defFactory = factoryClass.newInstance();// getDefault.invoke(new Object(), new Object[0]);
                 // now that we have the factory, there are two different createSocket() calls we use,
                 // depending on whether we have a localAddress override.
 
-                if (localAddress != null) {
+                if (localAddress != null && !layer) {
                     // retrieve the createSocket(String, int, InetAddress, int) method.
                     Class[] createSocketSig = new Class[] { String.class, Integer.TYPE, InetAddress.class, Integer.TYPE };
                     Method createSocket = factoryClass.getMethod("createSocket", createSocketSig);
 
                     Object[] createSocketArgs = new Object[] { serverHost, portArg, localAddress, new Integer(localPort) };
                     socket = (Socket)createSocket.invoke(defFactory, createSocketArgs);
+                    break; 
                 }
                 else {
-                    // retrieve the createSocket(String, int) method.
-                    Class[] createSocketSig = new Class[] { String.class, Integer.TYPE };
-                    Method createSocket = factoryClass.getMethod("createSocket", createSocketSig);
-
-                    Object[] createSocketArgs = new Object[] { serverHost, portArg };
-                    socket = (Socket)createSocket.invoke(defFactory, createSocketArgs);
+                    if(layer) {
+                     // retrieve the createSocket(String, int) method.
+                        Class[] createSocketSig = new Class[] { Socket.class, String.class, Integer.TYPE, Boolean.TYPE };
+                        Method createSocket = factoryClass.getMethod("createSocket", createSocketSig);
+
+                        Object[] createSocketArgs = new Object[] { socket, serverHost, new Integer(serverPort), Boolean.TRUE };
+                        socket = (Socket)createSocket.invoke(defFactory, createSocketArgs);
+                        break; 
+                    } else {
+                     // retrieve the createSocket(String, int) method.
+                        Class[] createSocketSig = new Class[] { String.class, Integer.TYPE };
+                        Method createSocket = factoryClass.getMethod("createSocket", createSocketSig);
+
+                        Object[] createSocketArgs = new Object[] { serverHost, portArg };
+                        socket = (Socket)createSocket.invoke(defFactory, createSocketArgs);
+                        break; 
+                    }
+                    
+                    
                 }
             } catch (Throwable e) {
-                // if a socket factor is specified, then we may need to fall back to a default.  This behavior
-                // is controlled by (surprise) more session properties.
-                if (props.getBooleanProperty(MAIL_FACTORY_FALLBACK, false)) {
-                    debugOut("First plain socket attempt failed, falling back to default factory", e);
-                    socket = new Socket(serverHost, serverPort, localAddress, localPort);
+                // if we're allowed to fallback, then use the default factory and try this again.  We only
+                // allow this to happen once.
+                if (fallback) {
+                    debugOut("First attempt at creating "+(ssl?"":"non-")+"SSL socket failed, falling back to default factory");
+                    socketFactoryClass = ssl?"javax.net.ssl.SSLSocketFactory":"javax.net.SocketFactory";
+                    fallback = false;
+                    continue;
                 }
                 // we have an exception.  We're going to throw an IOException, which may require unwrapping
                 // or rewrapping the exception.
@@ -357,8 +403,7 @@ public class MailConnection {
                         e = ((InvocationTargetException)e).getTargetException();
                     }
 
-                    debugOut("Plain socket creation failure", e);
-
+                    debugOut("Failure creating "+(ssl?"":"non-")+"SSL socket", e);
                     // throw this as an IOException, with the original exception attached.
                     IOException ioe = new IOException("Error connecting to " + serverHost + ", " + serverPort);
                     ioe.initCause(e);
@@ -366,11 +411,108 @@ public class MailConnection {
                 }
             }
         }
-        // if we have a timeout value, set that before returning 
-        if (timeout >= 0) {
-            socket.setSoTimeout(timeout);
+        
+        return true;
+    }
+    
+    private void createSocketFromFactory(SocketFactory sf, boolean layer) throws IOException {
+        
+        if(sf instanceof SSLSocketFactory && layer) {
+            socket = ((SSLSocketFactory) sf).createSocket(socket, serverHost, serverPort, true);
+            return;
+        }
+        
+        if (localAddress != null) {
+            socket = sf.createSocket(serverHost, serverPort, localAddress, localPort);
+        } else
+        {
+            socket = sf.createSocket(serverHost, serverPort);
+        }
+    }
+    
+    private boolean createSocketFromConfiguredFactoryInstance(boolean ssl, boolean layer) throws IOException {
+        
+        
+        
+        if (ssl) {
+            Object sfProp = props.getPropertyAsObject(MAIL_SSL_FACTORY);
+            if (sfProp != null && sfProp instanceof SSLSocketFactory) {
+                createSocketFromFactory((SSLSocketFactory) sfProp, layer);
+                debugOut("Creating "+(ssl?"":"non-")+"SSL "+(layer?"layered":"non-layered")+" socket using a instance of factory " + sfProp.getClass()+ " listening");
+                return true;
+            }
+        } else {
+            Object sfProp = props.getPropertyAsObject(MAIL_FACTORY);
+            if (sfProp != null && sfProp instanceof SocketFactory) {
+                createSocketFromFactory((SocketFactory) sfProp, layer);
+                debugOut("Creating "+(ssl?"":"non-")+"SSL "+(layer?"layered":"non-layered")+" socket using a instance of factory " + sfProp.getClass()+ " listening");
+                return true;
+            }
+        }
+        
+        return false;
+    }
+    
+    private void createSSLSocketFromSSLContext(boolean layer) throws IOException{
+        
+        debugOut("Creating "+(layer?"layered ":"non-layered ")+"SSL socket using SSL Context");
+
+        
+        try {
+            SSLContext sslcontext = SSLContext.getInstance("TLS");
+            
+            String sslTrust = props.getProperty(MAIL_SSL_TRUST);
+            
+            TrustManager trustManager = null;
+            
+            if(sslTrust != null) {
+                if(sslTrust.equals("*")) {
+                    trustManager = new SSLTrustManager(null, true); //trust all
+                } else
+                {
+                   String[] trustedHosts = sslTrust.split("\\s+");
+                   trustManager = new SSLTrustManager(trustedHosts, false); //trust some
+                   
+                   if(serverHost == null || serverHost.isEmpty() || !Arrays.asList(trustedHosts).contains(serverHost)) {
+                       throw new IOException("Server is not trusted: " + serverHost);
+                   }
+                   
+                }
+            } else {
+                trustManager = new SSLTrustManager(null, false); //default
+                
+            }
+             
+            sslcontext.init(null, new TrustManager[]{trustManager}, null);
+            
+            createSocketFromFactory(sslcontext.getSocketFactory(), layer);
+        } catch (KeyManagementException e) {
+            //cannot happen
+            throw new IOException(e);
+        } catch (NoSuchAlgorithmException e) {
+            //cannot happen
+            throw new IOException(e);
         }
     }
+    
+    private void createSocket(boolean ssl) throws IOException {
+        
+        if(createSocketFromConfiguredFactoryInstance(ssl, false)) {
+            return;
+        }
+
+        if(createSocketFromFactory(ssl, false)) {
+            return;
+        }
+        
+        if(!ssl) {
+            createSocketFromFactory(SocketFactory.getDefault(), false);
+            return;
+        }
+        
+          
+        createSSLSocketFromSSLContext(false);
+    }
 
 
     /**
@@ -382,78 +524,12 @@ public class MailConnection {
         debugOut("Attempting SSL socket connection to server " + serverHost + ":" + serverPort);
         // the socket factory can be specified via a protocol property, a session property, and if all else
         // fails (which it usually does), we fall back to the standard factory class.
-        String socketFactory = props.getProperty(MAIL_FACTORY_CLASS, "javax.net.ssl.SSLSocketFactory");
 
         // make sure this is null 
         socket = null;
+        
+        createSocket(true);
 
-        // we'll try this with potentially two different factories if we're allowed to fall back.
-        boolean fallback = props.getBooleanProperty(MAIL_FACTORY_FALLBACK, false);
-
-        while (true) {
-            try {
-                debugOut("Creating SSL socket using factory " + socketFactory);
-
-                int socketFactoryPort = props.getIntProperty(MAIL_FACTORY_PORT, -1);
-
-                // we choose the port used by the socket based on overrides.
-                Integer portArg = new Integer(socketFactoryPort == -1 ? serverPort : socketFactoryPort);
-
-                // use the current context loader to resolve this.
-                ClassLoader loader = Thread.currentThread().getContextClassLoader();
-                Class factoryClass = loader.loadClass(socketFactory);
-
-                // done indirectly, we need to invoke the method using reflection.
-                // This retrieves a factory instance.
-                Method getDefault = factoryClass.getMethod("getDefault", new Class[0]);
-                Object defFactory = getDefault.invoke(new Object(), new Object[0]);
-
-                // now that we have the factory, there are two different createSocket() calls we use,
-                // depending on whether we have a localAddress override.
-
-                if (localAddress != null) {
-                    // retrieve the createSocket(String, int, InetAddress, int) method.
-                    Class[] createSocketSig = new Class[] { String.class, Integer.TYPE, InetAddress.class, Integer.TYPE };
-                    Method createSocket = factoryClass.getMethod("createSocket", createSocketSig);
-
-                    Object[] createSocketArgs = new Object[] { serverHost, portArg, localAddress, new Integer(localPort) };
-                    socket = (Socket)createSocket.invoke(defFactory, createSocketArgs);
-                    break; 
-                }
-                else {
-                    // retrieve the createSocket(String, int) method.
-                    Class[] createSocketSig = new Class[] { String.class, Integer.TYPE };
-                    Method createSocket = factoryClass.getMethod("createSocket", createSocketSig);
-
-                    Object[] createSocketArgs = new Object[] { serverHost, portArg };
-                    socket = (Socket)createSocket.invoke(defFactory, createSocketArgs);
-                    break; 
-                }
-            } catch (Throwable e) {
-                // if we're allowed to fallback, then use the default factory and try this again.  We only
-                // allow this to happen once.
-                if (fallback) {
-                    debugOut("First attempt at creating SSL socket failed, falling back to default factory");
-                    socketFactory = "javax.net.ssl.SSLSocketFactory";
-                    fallback = false;
-                    continue;
-                }
-                // we have an exception.  We're going to throw an IOException, which may require unwrapping
-                // or rewrapping the exception.
-                else {
-                    // we have an exception from the reflection, so unwrap the base exception
-                    if (e instanceof InvocationTargetException) {
-                        e = ((InvocationTargetException)e).getTargetException();
-                    }
-
-                    debugOut("Failure creating SSL socket", e);
-                    // throw this as an IOException, with the original exception attached.
-                    IOException ioe = new IOException("Error connecting to " + serverHost + ", " + serverPort);
-                    ioe.initCause(e);
-                    throw ioe;
-                }
-            }
-        }
         // and set the timeout value 
         if (timeout >= 0) {
             socket.setSoTimeout(timeout);
@@ -497,44 +573,34 @@ public class MailConnection {
      	try {
 
             // we use the same target and port as the current connection.
-            String host = socket.getInetAddress().getHostName();
-            int port = socket.getPort();
+            serverHost = socket.getInetAddress().getHostName();
+            serverPort = socket.getPort();
 
             // the socket factory can be specified via a session property.  By default, we use
             // the native SSL factory.
-            String socketFactory = props.getProperty(MAIL_FACTORY_CLASS, "javax.net.ssl.SSLSocketFactory");
-
-            // use the current context loader to resolve this.
-            ClassLoader loader = Thread.currentThread().getContextClassLoader();
-            Class factoryClass = loader.loadClass(socketFactory);
-
-            // done indirectly, we need to invoke the method using reflection.
-            // This retrieves a factory instance.
-            Method getDefault = factoryClass.getMethod("getDefault", new Class[0]);
-            Object defFactory = getDefault.invoke(new Object(), new Object[0]);
-
-            // now we need to invoke createSocket()
-            Class[] createSocketSig = new Class[] { Socket.class, String.class, Integer.TYPE, Boolean.TYPE };
-            Method createSocket = factoryClass.getMethod("createSocket", createSocketSig);
-
-            Object[] createSocketArgs = new Object[] { socket, host, new Integer(port), Boolean.TRUE };
-
-            // and finally create the socket
-            Socket sslSocket = (Socket)createSocket.invoke(defFactory, createSocketArgs);
+            if(createSocketFromConfiguredFactoryInstance(true, true)) {
+                debugOut("TLS socket factory configured as instance"); 
+            } else if(createSocketFromFactory(true, true)) {
+                debugOut("TLS socket factory configured as class"); 
+            } else {
+                debugOut("TLS socket factory from SSLContext"); 
+                createSSLSocketFromSSLContext(true);
+            }
 
             // if this is an instance of SSLSocket (very common), try setting the protocol to be
             // "TLSv1".  If this is some other class because of a factory override, we'll just have to
             // accept that things will work.
-            if (sslSocket instanceof SSLSocket) {
-                ((SSLSocket)sslSocket).setEnabledProtocols(new String[] {"TLSv1"} );
-                ((SSLSocket)sslSocket).setUseClientMode(true);
-                debugOut("Initiating STARTTLS handshake"); 
-                ((SSLSocket)sslSocket).startHandshake();
+            if (socket instanceof SSLSocket) {
+                String[] suites = ((SSLSocket)socket).getSupportedCipherSuites();
+                ((SSLSocket)socket).setEnabledCipherSuites(suites);
+                ((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1"} );
+                ((SSLSocket)socket).setUseClientMode(true);
+                debugOut("Initiating STARTTLS handshake");
+                ((SSLSocket)socket).startHandshake();
+            } else {
+                throw new IOException("Socket is not an instance of SSLSocket, maybe wrong configured ssl factory?");
             }
 
-
-            // this is our active socket now
-            socket = sslSocket;
             getConnectionStreams(); 
             debugOut("TLS connection established"); 
      	}
@@ -838,4 +904,57 @@ public class MailConnection {
     public void setLocalHost(String localHost) {
         this.localHost = localHost;
     }
+    
+    private class SSLTrustManager implements X509TrustManager {
+        
+        private final X509TrustManager defaultTrustManager;
+        
+        private final boolean trustAll;
+        private final String[] trustedHosts;
+
+        SSLTrustManager(String[] trustedHosts, boolean trustAll) throws IOException{
+            super();
+            this.trustAll = trustAll;
+            this.trustedHosts = trustedHosts;
+            
+            try {
+                TrustManagerFactory defaultTrustManagerFactory = TrustManagerFactory.getInstance("X509");
+                defaultTrustManagerFactory.init((KeyStore)null);
+                defaultTrustManager = (X509TrustManager) defaultTrustManagerFactory.getTrustManagers()[0];
+            } catch (NoSuchAlgorithmException e) {
+                //cannot happen
+                throw new IOException(e);
+            } catch (KeyStoreException e) {
+                //cannot happen
+                throw new IOException(e);
+            }
+            
+        }
+
+        /* (non-Javadoc)
+         * @see javax.net.ssl.X509TrustManager#checkClientTrusted(java.security.cert.X509Certificate[], java.lang.String)
+         */
+        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+            defaultTrustManager.checkClientTrusted(chain, authType);
+            
+        }
+
+        /* (non-Javadoc)
+         * @see javax.net.ssl.X509TrustManager#checkServerTrusted(java.security.cert.X509Certificate[], java.lang.String)
+         */
+        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+            if (!trustAll || trustedHosts != null) {
+                defaultTrustManager.checkServerTrusted(chain, authType);
+            }
+            
+        }
+
+        /* (non-Javadoc)
+         * @see javax.net.ssl.X509TrustManager#getAcceptedIssuers()
+         */
+        public X509Certificate[] getAcceptedIssuers() {
+            return defaultTrustManager.getAcceptedIssuers();
+        }
+        
+    }
 }

Modified: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/util/ProtocolProperties.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/util/ProtocolProperties.java?rev=1641706&r1=1641705&r2=1641706&view=diff
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/util/ProtocolProperties.java (original)
+++ geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/util/ProtocolProperties.java Tue Nov 25 21:39:49 2014
@@ -45,7 +45,7 @@ public class ProtocolProperties {
         this.protocol = protocol; 
         this.sslConnection = sslConnection; 
         this.defaultPort = defaultPort; 
-        // this helps avoid a lot of concatentates when retrieving properties. 
+        // this helps avoid a lot of concatenates when retrieving properties. 
         protocolPrefix = "mail." + protocol + ".";
     }
     
@@ -104,6 +104,21 @@ public class ProtocolProperties {
         String fullName = protocolPrefix + name;
         return session.getProperty(fullName);
     }
+    
+    /**
+     * Get a property (as object) associated with this mail protocol.
+     *
+     * @param name   The name of the property.
+     *
+     * @return The property value (returns null if the property has not been set).
+     */
+    public Object getPropertyAsObject(String name) {
+        // the name we're given is the least qualified part of the name.  
+        // We construct the full property name
+        // using the protocol
+        String fullName = protocolPrefix + name;
+        return session.getProperties().get(fullName);
+    }
 
     /**
      * Get a property associated with this mail session.  Returns