Author: norman
Date: Sun Sep 27 17:38:54 2009
New Revision: 819359

URL: http://svn.apache.org/viewvc?rev=819359&view=rev
Log:
First draft of generic STARTTLS support + SMTPServer STARTTLS support 
(JAMES-290)

Added:
    james/server/trunk/phoenix-deployment/src/main/
    james/server/trunk/phoenix-deployment/src/main/config/
    james/server/trunk/phoenix-deployment/src/main/java/
    james/server/trunk/phoenix-deployment/src/main/resources/
    
james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/core/esmtp/StartTlsCmdHandler.java
    james/server/trunk/smtpserver-function/src/test/resources/test_keystore   
(with props)
Modified:
    
james/server/trunk/avalon-socket-library/src/main/java/org/apache/james/socket/AbstractProtocolServer.java
    
james/server/trunk/avalon-socket-library/src/main/java/org/apache/james/socket/JamesConnectionBridge.java
    
james/server/trunk/avalon-socket-library/src/main/java/org/apache/james/socket/ProtocolContext.java
    james/server/trunk/phoenix-deployment/src/conf/james-config.xml
    
james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/SMTPConfiguration.java
    
james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/SMTPHandler.java
    
james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/SMTPServer.java
    
james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/SMTPSession.java
    
james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/core/CoreCmdHandlerLoader.java
    
james/server/trunk/smtpserver-function/src/test/java/org/apache/james/smtpserver/BaseFakeSMTPSession.java
    
james/server/trunk/smtpserver-function/src/test/java/org/apache/james/smtpserver/SMTPServerTest.java
    
james/server/trunk/smtpserver-function/src/test/java/org/apache/james/smtpserver/SMTPTestConfiguration.java
    
james/server/trunk/smtpserver-function/src/test/java/org/apache/james/smtpserver/ValidRcptHandlerTest.java

Modified: 
james/server/trunk/avalon-socket-library/src/main/java/org/apache/james/socket/AbstractProtocolServer.java
URL: 
http://svn.apache.org/viewvc/james/server/trunk/avalon-socket-library/src/main/java/org/apache/james/socket/AbstractProtocolServer.java?rev=819359&r1=819358&r2=819359&view=diff
==============================================================================
--- 
james/server/trunk/avalon-socket-library/src/main/java/org/apache/james/socket/AbstractProtocolServer.java
 (original)
+++ 
james/server/trunk/avalon-socket-library/src/main/java/org/apache/james/socket/AbstractProtocolServer.java
 Sun Sep 27 17:38:54 2009
@@ -25,11 +25,15 @@
 import java.net.InetAddress;
 import java.net.ServerSocket;
 import java.net.UnknownHostException;
+import java.security.KeyStore;
 import java.security.Provider;
 import java.security.Security;
 import java.util.concurrent.atomic.AtomicLong;
 
 import javax.annotation.PostConstruct;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
 
 import 
org.apache.avalon.cornerstone.services.connection.AbstractHandlerFactory;
 import org.apache.avalon.cornerstone.services.connection.ConnectionHandler;
@@ -53,6 +57,7 @@
 import org.apache.avalon.framework.service.Serviceable;
 import org.apache.excalibur.thread.ThreadPool;
 import org.apache.james.api.dnsservice.DNSService;
+import org.apache.james.services.FileSystem;
 
 /**
  * Server which creates connection handlers. All new James service must
@@ -199,8 +204,14 @@
      */
     private String streamDumpDir = null;
 
-    
-    
+    private FileSystem fSystem;
+       private SSLSocketFactory factory;
+
+       private String keystore;
+
+       private String secret;    
+     
+       private boolean useStartTLS;
     /**
      * Gets the DNS Service.
      * @return the dnsServer
@@ -221,6 +232,10 @@
         this.connectionManager = connectionManager;
     }
 
+    public void setFileSystem(FileSystem fSystem) {
+       this.fSystem = fSystem;
+    }
+    
     /**
      * @see 
org.apache.avalon.framework.service.Serviceable#service(ServiceManager)
      */
@@ -231,6 +246,7 @@
             
(JamesConnectionManager)componentManager.lookup(JamesConnectionManager.ROLE);
         setConnectionManager(connectionManager);
         dnsService = (DNSService) comp.lookup(DNSService.ROLE);
+        fSystem= (FileSystem) comp.lookup(FileSystem.ROLE);
     }
 
     /**
@@ -282,6 +298,7 @@
         } else {
             serverSocketType = confSocketType;
         }
+     
 
         StringBuilder infoBuffer;
         threadGroup = conf.getChild("threadGroup").getValue(null);
@@ -376,6 +393,20 @@
             .append(" per IP connections for " +getServiceType());
         logger.info(infoBuffer.toString());
         
+               Configuration tlsConfig = conf.getChild("startTLS");
+               if (tlsConfig != null) {
+                       useStartTLS = tlsConfig.getAttributeAsBoolean("enable", 
false);
+               System.err.println("config=" + useStartTLS);
+
+                       if (useStartTLS) {
+                               keystore = 
tlsConfig.getChild("keystore").getValue(null);
+                               if (keystore == null) {
+                                       throw new 
ConfigurationException("keystore needs to get configured");
+                               }
+                               secret = 
tlsConfig.getChild("secret").getValue("");
+                               loadJCEProviders(conf, getLogger());
+                       }
+               }
     }
 
     private void loadJCEProviders(Configuration conf, final Logger logger) 
throws ConfigurationException {
@@ -479,7 +510,7 @@
         // keeping these looked up services locally, because they are only 
needed beyond initialization
         ThreadManager threadManager = (ThreadManager) 
componentManager.lookup(ThreadManager.ROLE);
         SocketManager socketManager = (SocketManager) 
componentManager.lookup(SocketManager.ROLE);
-        
+       
         initializeThreadPool(threadManager);
 
         initializeServerSocket(socketManager);
@@ -494,10 +525,48 @@
 
         theWatchdogFactory = getWatchdogFactory();
 
+        if (useStartTLS) {
+               initStartTLS();
+        }
         // Allow subclasses to perform initialisation
         doInit();
     }
     
+    private void initStartTLS() throws Exception {
+       KeyStore ks = null;
+               KeyManagerFactory kmf = null;
+               SSLContext sslcontext = null;
+
+               // This loads the key material, and initialises the
+               // SSLSocketFactory
+               // This should be done once!!
+               // Note: in order to load SunJCE provider the jre/lib/ext 
should be
+               // added
+               // to the java.ext.dirs see the note in run.sh script
+               try {
+                       // just to see SunJCE is loaded
+                       Provider[] provs = Security.getProviders();
+                       for (int i = 0; i < provs.length; i++)
+                               getLogger().debug("Provider[" + i + "]=" + 
provs[i].getName());
+
+                       char[] passphrase = secret.toCharArray();
+                       ks = KeyStore.getInstance("JKS");
+                       ks.load(fSystem.getResource(keystore), passphrase);
+                       kmf = KeyManagerFactory.getInstance("SunX509", 
"SunJSSE");
+                       kmf.init(ks, passphrase);
+                       sslcontext = SSLContext.getInstance("TLS", "SunJSSE");
+                       sslcontext.init(kmf.getKeyManagers(), null, null);
+               } catch (Exception e) {
+                       getLogger().error("Exception accessing keystore: " + e);
+                       throw e;
+               }
+               factory = sslcontext.getSocketFactory();
+               // just to see the list of supported ciphers
+               String[] ss = factory.getSupportedCipherSuites();
+               getLogger().debug("list of supported ciphers");
+               for (int i = 0; i < ss.length; i++)
+                       getLogger().debug(ss[i]);
+    }
     
     /**
      * Hook for subclasses to perform an required initialisation
@@ -843,9 +912,15 @@
             serviceShortNameString = serviceType;
         }
         final String name = serviceShortNameString + "Handler-" + 
handlerCount.getAndAdd(1);
-        final JamesConnectionBridge delegatingJamesHandler = 
-            new JamesConnectionBridge(newProtocolHandlerInstance(), 
dnsService, name, getLogger());
+        final JamesConnectionBridge delegatingJamesHandler;
+        
+        if (useStartTLS) {
+               delegatingJamesHandler = new 
JamesConnectionBridge(newProtocolHandlerInstance(), dnsService, name, 
getLogger(), factory);
+        } else {
+            delegatingJamesHandler = new 
JamesConnectionBridge(newProtocolHandlerInstance(), dnsService, name, 
getLogger());
+        }
         return delegatingJamesHandler;
+        
     }
     
     protected abstract ProtocolHandler newProtocolHandlerInstance();
@@ -858,5 +933,10 @@
         return JamesConnectionBridge.class;
     }
 
+
+    public boolean useStartTLS() {
+       return useStartTLS;
+    }
+    
 }
 

Modified: 
james/server/trunk/avalon-socket-library/src/main/java/org/apache/james/socket/JamesConnectionBridge.java
URL: 
http://svn.apache.org/viewvc/james/server/trunk/avalon-socket-library/src/main/java/org/apache/james/socket/JamesConnectionBridge.java?rev=819359&r1=819358&r2=819359&view=diff
==============================================================================
--- 
james/server/trunk/avalon-socket-library/src/main/java/org/apache/james/socket/JamesConnectionBridge.java
 (original)
+++ 
james/server/trunk/avalon-socket-library/src/main/java/org/apache/james/socket/JamesConnectionBridge.java
 Sun Sep 27 17:38:54 2009
@@ -31,6 +31,9 @@
 import java.net.Socket;
 import java.net.SocketException;
 
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+
 import org.apache.avalon.cornerstone.services.connection.ConnectionHandler;
 import org.apache.avalon.excalibur.pool.Poolable;
 import org.apache.avalon.framework.container.ContainerUtil;
@@ -118,6 +121,15 @@
     
     private final Log log;
     
+    private SSLSocketFactory factory;
+    private boolean secureEnabled = false;
+    
+    public JamesConnectionBridge(final ProtocolHandler delegated, final 
DNSService dnsServer, final String name, 
+            final Logger logger, final SSLSocketFactory factory) {
+       this(delegated, dnsServer, name, logger);
+        this.factory = factory;
+    }
+    
     public JamesConnectionBridge(final ProtocolHandler delegated, final 
DNSService dnsServer, final String name, 
             final Logger logger) {
         this.protocolHandler = delegated;
@@ -146,20 +158,8 @@
             synchronized (this) {
                 handlerThread = Thread.currentThread();
             }
-            in = new BufferedInputStream(socket.getInputStream(), 
DEFAULT_INPUT_BUFFER_SIZE);
-            outs = new BufferedOutputStream(socket.getOutputStream(), 
DEFAULT_OUTPUT_BUFFER_SIZE);
-            // enable tcp dump for debug
-            if (tcplogprefix != null) {
-                outs = new SplitOutputStream(outs, new 
FileOutputStream(tcplogprefix+"out"));
-                in = new CopyInputStream(in, new 
FileOutputStream(tcplogprefix+"in"));
-            }
-            
-            // An ASCII encoding can be used because all transmissions other
-            // that those in the message body command are guaranteed
-            // to be ASCII
-            inReader = new CRLFTerminatedReader(in, "ASCII");
-            
-            out = new InternetPrintWriter(outs, true);
+
+            connectStreams(socket);
         } catch (RuntimeException e) {
             StringBuilder exceptionBuffer = 
                 new StringBuilder(256)
@@ -198,7 +198,34 @@
         }
     }
 
-    /**
+
+       private SSLSocket secureSocket(Socket socket) throws IOException {
+               SSLSocket sslsock = (SSLSocket) factory.createSocket(socket, 
socket
+                               .getInetAddress().getHostName(), 
socket.getPort(), true);
+               sslsock.setUseClientMode(false);
+
+               return sslsock;
+       }
+
+       
+    private void connectStreams(Socket socket) throws IOException {
+        in = new BufferedInputStream(socket.getInputStream(), 
DEFAULT_INPUT_BUFFER_SIZE);
+        outs = new BufferedOutputStream(socket.getOutputStream(), 
DEFAULT_OUTPUT_BUFFER_SIZE);
+        // enable tcp dump for debug
+        if (tcplogprefix != null) {
+            outs = new SplitOutputStream(outs, new 
FileOutputStream(tcplogprefix+"out"));
+            in = new CopyInputStream(in, new 
FileOutputStream(tcplogprefix+"in"));
+        }
+        
+        // An ASCII encoding can be used because all transmissions other
+        // that those in the message body command are guaranteed
+        // to be ASCII
+        inReader = new CRLFTerminatedReader(in, "ASCII");
+        
+        out = new InternetPrintWriter(outs, true);
+       }
+
+       /**
      * The method clean up and close the allocated resources
      */
     private void cleanHandler() {
@@ -435,6 +462,7 @@
         protocolHandler.resetHandler();
         remoteHost = null;
         remoteIP = null;
+        secureEnabled = false;
     }
 
     /**
@@ -500,4 +528,24 @@
     public void execute() {
         idleClose();
     }
+
+    /**
+     * @see org.apache.james.socket.ProtocolContext#isSecure()
+     */
+       public boolean isSecure() {
+               return secureEnabled;
+       }
+
+       /**
+        * @see org.apache.james.socket.ProtocolContext#secure()
+        */
+       public void secure() throws IOException {
+               if (factory == null) {
+                       throw new UnsupportedOperationException("StartTLS not 
supported");
+               }
+               this.secureEnabled = true;
+               
+               socket = secureSocket(socket);
+               connectStreams(socket);
+       }
 }

Modified: 
james/server/trunk/avalon-socket-library/src/main/java/org/apache/james/socket/ProtocolContext.java
URL: 
http://svn.apache.org/viewvc/james/server/trunk/avalon-socket-library/src/main/java/org/apache/james/socket/ProtocolContext.java?rev=819359&r1=819358&r2=819359&view=diff
==============================================================================
--- 
james/server/trunk/avalon-socket-library/src/main/java/org/apache/james/socket/ProtocolContext.java
 (original)
+++ 
james/server/trunk/avalon-socket-library/src/main/java/org/apache/james/socket/ProtocolContext.java
 Sun Sep 27 17:38:54 2009
@@ -20,6 +20,7 @@
 
 package org.apache.james.socket;
 
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.PrintWriter;
@@ -98,4 +99,17 @@
      * @return not null
      */
     public Log getLogger();
+    
+    /**
+     * Secure the current socket using tls/ssl
+     * @throws IOException 
+     */
+    public void secure() throws IOException;
+    
+    /**
+     * Return if the current socket is using tls/ssl
+     * 
+     * @return isSecure
+     */
+    public boolean isSecure();
 }
\ No newline at end of file

Modified: james/server/trunk/phoenix-deployment/src/conf/james-config.xml
URL: 
http://svn.apache.org/viewvc/james/server/trunk/phoenix-deployment/src/conf/james-config.xml?rev=819359&r1=819358&r2=819359&view=diff
==============================================================================
--- james/server/trunk/phoenix-deployment/src/conf/james-config.xml (original)
+++ james/server/trunk/phoenix-deployment/src/conf/james-config.xml Sun Sep 27 
17:38:54 2009
@@ -963,12 +963,27 @@
       <!--
       <useTLS>true</useTLS>
       -->
+      
       <!-- Use provider elements to specify additional JCE providers.
          The jars should be put into $JAMES_HOME/lib.
            For example, Uncomment this if you want to use 
            BouncyCastle JCE (http://www.bouncycastle.org)
       <provider>org.bouncycastle.jce.provider.BouncyCastleProvider</provider> 
-->
 
+      <!-- Set enable to true to support STARTTLS.
+           To use this you need to copy sunjce_provider.jar to /path/james/lib 
directory.
+       -->
+      
+      <startTLS enable="false">
+      
+        <!-- To create a new keystore execute:
+        keytool -genkey -alias james -keyalg RSA -keystore 
/path/to/james/conf/keystore
+         -->
+        <keystore>file://conf/keystore</keystore>
+        <secret></secret>
+        <provider>org.bouncycastle.jce.provider.BouncyCastleProvider</provider>
+      </startTLS>
+      
       <handler>
          <!-- This is the name used by the server to identify itself in the 
SMTP -->
          <!-- protocol.  If autodetect is TRUE, the server will discover its 
-->
@@ -1648,5 +1663,4 @@
          <min-spare-threads>20</min-spare-threads>
       </thread-group>
    </thread-manager>
-
 </config>

Modified: 
james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/SMTPConfiguration.java
URL: 
http://svn.apache.org/viewvc/james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/SMTPConfiguration.java?rev=819359&r1=819358&r2=819359&view=diff
==============================================================================
--- 
james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/SMTPConfiguration.java
 (original)
+++ 
james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/SMTPConfiguration.java
 Sun Sep 27 17:38:54 2009
@@ -89,5 +89,7 @@
      * @return true or false
      */
     boolean useAddressBracketsEnforcement();
+    
+    boolean isStartTLSSupported();
 
 }

Modified: 
james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/SMTPHandler.java
URL: 
http://svn.apache.org/viewvc/james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/SMTPHandler.java?rev=819359&r1=819358&r2=819359&view=diff
==============================================================================
--- 
james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/SMTPHandler.java
 (original)
+++ 
james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/SMTPHandler.java
 Sun Sep 27 17:38:54 2009
@@ -398,4 +398,16 @@
     public Log getLogger() {
         return context.getLogger();
     }
+
+       public boolean isTLSStarted() {
+               return context.isSecure();
+       }
+
+       public void secure() throws IOException {
+               context.secure();
+       }
+
+       public boolean isStartTLSSupported() {
+               return getConfigurationData().isStartTLSSupported();
+       }
 }

Modified: 
james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/SMTPServer.java
URL: 
http://svn.apache.org/viewvc/james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/SMTPServer.java?rev=819359&r1=819358&r2=819359&view=diff
==============================================================================
--- 
james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/SMTPServer.java
 (original)
+++ 
james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/SMTPServer.java
 Sun Sep 27 17:38:54 2009
@@ -119,8 +119,7 @@
     = new SMTPHandlerConfigurationDataImpl();
 
     private boolean addressBracketsEnforcement = true;
-
-
+    
     /**
      * Gets the current instance loader.
      * @return the loader
@@ -227,7 +226,6 @@
             smtpGreeting = 
handlerConfiguration.getChild("smtpGreeting").getValue(null);
 
             addressBracketsEnforcement = 
handlerConfiguration.getChild("addressBracketsEnforcement").getValueAsBoolean(true);
-
         } else {
             // TODO Remove this in next not backwards compatible release!
             if (hello == null) 
mailetcontext.setAttribute(Constants.HELLO_NAME, "localhost");
@@ -344,6 +342,10 @@
             }
             return authRequired;
         }
+
+               public boolean isStartTLSSupported() {
+                       return SMTPServer.this.useStartTLS();
+               }
         
         //TODO: IF we create here an interface to get DNSServer
         //      we should access it from the SMTPHandlers
@@ -355,5 +357,5 @@
         final SMTPHandler theHandler = new SMTPHandler(handlerChain, 
theConfigData);
         return theHandler;
     }
-
+    
 }

Modified: 
james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/SMTPSession.java
URL: 
http://svn.apache.org/viewvc/james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/SMTPSession.java?rev=819359&r1=819358&r2=819359&view=diff
==============================================================================
--- 
james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/SMTPSession.java
 (original)
+++ 
james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/SMTPSession.java
 Sun Sep 27 17:38:54 2009
@@ -19,6 +19,7 @@
 
 package org.apache.james.smtpserver;
 
+import java.io.IOException;
 import java.util.Map;
 
 import org.apache.commons.logging.Log;
@@ -188,5 +189,12 @@
      * @return log, not null
      */
     Log getLogger();
+    
+    boolean isStartTLSSupported();
+    
+    boolean isTLSStarted();
+
+    void secure() throws IOException;
+
 }
 

Modified: 
james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/core/CoreCmdHandlerLoader.java
URL: 
http://svn.apache.org/viewvc/james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/core/CoreCmdHandlerLoader.java?rev=819359&r1=819358&r2=819359&view=diff
==============================================================================
--- 
james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/core/CoreCmdHandlerLoader.java
 (original)
+++ 
james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/core/CoreCmdHandlerLoader.java
 Sun Sep 27 17:38:54 2009
@@ -25,6 +25,7 @@
 import org.apache.james.smtpserver.core.esmtp.AuthCmdHandler;
 import org.apache.james.smtpserver.core.esmtp.EhloCmdHandler;
 import org.apache.james.smtpserver.core.esmtp.MailSizeEsmtpExtension;
+import org.apache.james.smtpserver.core.esmtp.StartTlsCmdHandler;
 
 import java.util.LinkedList;
 import java.util.List;
@@ -54,7 +55,7 @@
     private final String AUTHREQUIREDTORELAY = 
AuthRequiredToRelayRcptHook.class.getName();
     private final String SENDERAUTHIDENTITYVERIFICATION = 
SenderAuthIdentifyVerificationRcptHook.class.getName();
     private final String DATALINEMESSAGEHOOKHANDLER = 
DataLineMessageHookHandler.class.getName();
-   
+    private final String STARTTLSHANDLER = StartTlsCmdHandler.class.getName();
     /**
      * @see org.apache.james.smtpserver.HandlersPackage#getHandlers()
      */
@@ -82,7 +83,7 @@
         commands.add(SENDERAUTHIDENTITYVERIFICATION);
         commands.add(POSTMASTERABUSEHOOK);
         commands.add(DATALINEMESSAGEHOOKHANDLER);
-        
+        commands.add(STARTTLSHANDLER);
         return commands;
     }
 }

Added: 
james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/core/esmtp/StartTlsCmdHandler.java
URL: 
http://svn.apache.org/viewvc/james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/core/esmtp/StartTlsCmdHandler.java?rev=819359&view=auto
==============================================================================
--- 
james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/core/esmtp/StartTlsCmdHandler.java
 (added)
+++ 
james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/core/esmtp/StartTlsCmdHandler.java
 Sun Sep 27 17:38:54 2009
@@ -0,0 +1,104 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.smtpserver.core.esmtp;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.james.dsn.DSNStatus;
+import org.apache.james.smtpserver.CommandHandler;
+import org.apache.james.smtpserver.SMTPResponse;
+import org.apache.james.smtpserver.SMTPRetCode;
+import org.apache.james.smtpserver.SMTPSession;
+
+/**
+ * Handles STARTTLS command
+ */
+public class StartTlsCmdHandler implements CommandHandler, EhloExtension {
+       /**
+        * The name of the command handled by the command handler
+        */
+       private final static String COMMAND_NAME = "STARTTLS";
+
+       /**
+        * @see org.apache.james.smtpserver.CommandHandler#getImplCommands()
+        */
+       public Collection<String> getImplCommands() {
+               Collection<String> commands = new ArrayList<String>();
+               commands.add(COMMAND_NAME);
+               return commands;
+       }
+
+       /**
+        * Handler method called upon receipt of a STARTTLS command. Resets
+        * message-specific, but not authenticated user, state.
+        * 
+        * @see 
org.apache.james.smtpserver.CommandHandler#onCommand(org.apache.james.smtpserver.SMTPSession,
+        *      java.lang.String, java.lang.String)
+        */
+       public SMTPResponse onCommand(SMTPSession session, String command,
+                       String parameters) {
+               SMTPResponse response = null;
+               if (session.isStartTLSSupported()) {
+                       if (session.isTLSStarted()) {
+                               response = new SMTPResponse("500", 
DSNStatus.getStatus(DSNStatus.PERMANENT, DSNStatus.DELIVERY_INVALID_CMD) + " 
TLS already active RFC2487 5.2");
+                       } else {
+                               if ((parameters == null) || 
(parameters.length() == 0)) {
+                                       response = new SMTPResponse("220", 
DSNStatus.getStatus(DSNStatus.SUCCESS, DSNStatus.UNDEFINED_STATUS) + " Ready to 
start TLS");
+                               } else {
+                                       response = new SMTPResponse("501 "+ 
DSNStatus.getStatus(DSNStatus.PERMANENT, DSNStatus.DELIVERY_INVALID_ARG) + " 
Syntax error (no parameters allowed) with STARTTLS command");
+                               }
+                       }
+                       try {
+                               if (!session.isTLSStarted()) {
+                                       session.secure();
+                                       // force reset
+                                       session.resetState();
+                               }
+                       } catch (IOException e) {
+                               response = new 
SMTPResponse(SMTPRetCode.LOCAL_ERROR,"Temporary error while trying to start 
TLS");
+                       }
+               } else {
+               StringBuilder result = new StringBuilder();
+               result.append(DSNStatus.getStatus(DSNStatus.PERMANENT, 
DSNStatus.DELIVERY_INVALID_CMD))
+                             .append(" Command ")
+                             .append(command)
+                             .append(" unrecognized.");
+               response =  new 
SMTPResponse(SMTPRetCode.SYNTAX_ERROR_COMMAND_UNRECOGNIZED, result);
+               }
+               return response;
+       }
+
+       /**
+        * @see 
org.apache.james.smtpserver.core.esmtp.EhloExtension#getImplementedEsmtpFeatures(org.apache.james.smtpserver.SMTPSession)
+        */
+       public List<String> getImplementedEsmtpFeatures(SMTPSession session) {
+               List<String> esmtpextensions = new ArrayList<String>();
+               // SMTP STARTTLS
+               if (!session.isTLSStarted() && session.isStartTLSSupported()) {
+                       esmtpextensions.add("STARTTLS");
+               }
+               return esmtpextensions;
+
+       }
+
+}

Modified: 
james/server/trunk/smtpserver-function/src/test/java/org/apache/james/smtpserver/BaseFakeSMTPSession.java
URL: 
http://svn.apache.org/viewvc/james/server/trunk/smtpserver-function/src/test/java/org/apache/james/smtpserver/BaseFakeSMTPSession.java?rev=819359&r1=819358&r2=819359&view=diff
==============================================================================
--- 
james/server/trunk/smtpserver-function/src/test/java/org/apache/james/smtpserver/BaseFakeSMTPSession.java
 (original)
+++ 
james/server/trunk/smtpserver-function/src/test/java/org/apache/james/smtpserver/BaseFakeSMTPSession.java
 Sun Sep 27 17:38:54 2009
@@ -20,6 +20,7 @@
 
 package org.apache.james.smtpserver;
 
+import java.io.IOException;
 import java.util.Map;
 
 import org.apache.commons.logging.Log;
@@ -178,4 +179,17 @@
     public Log getLogger() {
         return log;
     }
+
+       public boolean isStartTLSSupported() {
+               return getConfigurationData().isStartTLSSupported();
+       }
+
+       public boolean isTLSStarted() {
+        throw new UnsupportedOperationException("Unimplemented Stub Method");
+       }
+
+       public void secure() throws IOException {
+        throw new UnsupportedOperationException("Unimplemented Stub Method");
+
+       }
 }

Modified: 
james/server/trunk/smtpserver-function/src/test/java/org/apache/james/smtpserver/SMTPServerTest.java
URL: 
http://svn.apache.org/viewvc/james/server/trunk/smtpserver-function/src/test/java/org/apache/james/smtpserver/SMTPServerTest.java?rev=819359&r1=819358&r2=819359&view=diff
==============================================================================
--- 
james/server/trunk/smtpserver-function/src/test/java/org/apache/james/smtpserver/SMTPServerTest.java
 (original)
+++ 
james/server/trunk/smtpserver-function/src/test/java/org/apache/james/smtpserver/SMTPServerTest.java
 Sun Sep 27 17:38:54 2009
@@ -46,6 +46,7 @@
 import org.apache.commons.net.smtp.SMTPReply;
 import org.apache.james.api.dnsservice.DNSService;
 import org.apache.james.api.user.UsersRepository;
+import org.apache.james.services.FileSystem;
 import org.apache.james.services.MailServer;
 import org.apache.james.socket.JamesConnectionManager;
 import org.apache.james.socket.SimpleConnectionManager;
@@ -53,6 +54,7 @@
 import org.apache.james.test.mock.avalon.MockSocketManager;
 import org.apache.james.test.mock.avalon.MockStore;
 import org.apache.james.test.mock.avalon.MockThreadManager;
+import org.apache.james.test.mock.james.MockFileSystem;
 import org.apache.james.test.mock.james.MockMailServer;
 import org.apache.james.test.util.Util;
 import org.apache.james.userrepository.MockUsersRepository;
@@ -151,7 +153,6 @@
     private MockUsersRepository m_usersRepository = new MockUsersRepository();
     private FakeLoader m_serviceManager;
     private AlterableDNSServer m_dnsServer;
-
     public SMTPServerTest() {
         super("SMTPServerTest");
         m_smtpListenerPort = Util.getNonPrivilegedPort();
@@ -214,6 +215,8 @@
         m_serviceManager.put(DNSService.ROLE, m_dnsServer);
         m_serviceManager.put("dnsserver", m_dnsServer);
         m_serviceManager.put(Store.ROLE, new MockStore());
+        m_serviceManager.put(FileSystem.ROLE, new MockFileSystem());
+    
         return m_serviceManager;
     }
 
@@ -250,6 +253,35 @@
         assertNotNull("mail received by mail server", 
m_mailServer.getLastMail());
     }
 
+    public void testStartTLSInEHLO() throws Exception {
+       m_testConfiguration.setStartTLS();
+        finishSetUp(m_testConfiguration);
+        
+        SMTPClient smtpProtocol = new SMTPClient();
+        smtpProtocol.connect("127.0.0.1", m_smtpListenerPort);
+
+        // no message there, yet
+        assertNull("no mail received by mail server", 
m_mailServer.getLastMail());
+
+        smtpProtocol.sendCommand("EHLO "+InetAddress.getLocalHost());
+        String[] capabilityRes = smtpProtocol.getReplyStrings();
+        
+        List capabilitieslist = new ArrayList();
+        for (int i = 1; i < capabilityRes.length; i++) {
+            capabilitieslist.add(capabilityRes[i].substring(4));
+        }
+        
+        assertEquals("capabilities", 4, capabilitieslist.size());
+        assertTrue("capabilities present PIPELINING", 
capabilitieslist.contains("PIPELINING"));
+        assertTrue("capabilities present ENHANCEDSTATUSCODES", 
capabilitieslist.contains("ENHANCEDSTATUSCODES"));
+        assertTrue("capabilities present 8BITMIME", 
capabilitieslist.contains("8BITMIME"));
+        assertTrue("capabilities present STARTTLS", 
capabilitieslist.contains("STARTTLS"));
+    
+        smtpProtocol.quit();
+        smtpProtocol.disconnect();
+
+    }
+    
     /**
      * TODO: Understand why this fails!
      * 

Modified: 
james/server/trunk/smtpserver-function/src/test/java/org/apache/james/smtpserver/SMTPTestConfiguration.java
URL: 
http://svn.apache.org/viewvc/james/server/trunk/smtpserver-function/src/test/java/org/apache/james/smtpserver/SMTPTestConfiguration.java?rev=819359&r1=819358&r2=819359&view=diff
==============================================================================
--- 
james/server/trunk/smtpserver-function/src/test/java/org/apache/james/smtpserver/SMTPTestConfiguration.java
 (original)
+++ 
james/server/trunk/smtpserver-function/src/test/java/org/apache/james/smtpserver/SMTPTestConfiguration.java
 Sun Sep 27 17:38:54 2009
@@ -29,6 +29,7 @@
 import 
org.apache.james.smtpserver.core.filter.fastfail.ResolvableEhloHeloHandler;
 import 
org.apache.james.smtpserver.core.filter.fastfail.ReverseEqualsEhloHeloHandler;
 import 
org.apache.james.smtpserver.core.filter.fastfail.ValidSenderDomainHandler;
+import org.apache.james.test.mock.util.AttrValConfiguration;
 import org.apache.james.test.util.Util;
 
 public class SMTPTestConfiguration extends DefaultConfiguration {
@@ -50,6 +51,7 @@
     private int m_maxRcpt = 0;
     private boolean m_useRBL = false;
     private boolean m_addressBracketsEnforcement = true;
+       private boolean m_startTLS = false;
 
     
     public SMTPTestConfiguration(int smtpListenerPort) {
@@ -140,7 +142,9 @@
         this.m_addressBracketsEnforcement = addressBracketsEnforcement;
     }
     
-
+    public void setStartTLS() {
+       m_startTLS  = true;
+    }
     public void init() throws ConfigurationException {
 
         setAttribute("enabled", true);
@@ -157,6 +161,13 @@
         handlerConfig.addChild(Util.getValuedConfiguration("authRequired", 
m_authorizingMode));
         
handlerConfig.addChild(Util.getValuedConfiguration("heloEhloEnforcement", 
m_heloEhloEnforcement+""));
         
handlerConfig.addChild(Util.getValuedConfiguration("addressBracketsEnforcement",
 m_addressBracketsEnforcement+""));
+        
+        DefaultConfiguration tlsConfig = new DefaultConfiguration("startTLS");
+        tlsConfig.setAttribute("enable", m_startTLS);
+        tlsConfig.addChild(new 
AttrValConfiguration("keystore","file://conf/test_keystore"));
+        tlsConfig.addChild(Util.getValuedConfiguration("secret", "jamestest"));
+        addChild(tlsConfig);
+        
         if (m_verifyIdentity) 
handlerConfig.addChild(Util.getValuedConfiguration("verifyIdentity", "" + 
m_verifyIdentity));
  
         DefaultConfiguration config = new DefaultConfiguration("handlerchain");

Modified: 
james/server/trunk/smtpserver-function/src/test/java/org/apache/james/smtpserver/ValidRcptHandlerTest.java
URL: 
http://svn.apache.org/viewvc/james/server/trunk/smtpserver-function/src/test/java/org/apache/james/smtpserver/ValidRcptHandlerTest.java?rev=819359&r1=819358&r2=819359&view=diff
==============================================================================
--- 
james/server/trunk/smtpserver-function/src/test/java/org/apache/james/smtpserver/ValidRcptHandlerTest.java
 (original)
+++ 
james/server/trunk/smtpserver-function/src/test/java/org/apache/james/smtpserver/ValidRcptHandlerTest.java
 Sun Sep 27 17:38:54 2009
@@ -34,11 +34,9 @@
 import org.apache.james.api.vut.ErrorMappingException;
 import org.apache.james.api.vut.VirtualUserTable;
 import org.apache.james.api.vut.VirtualUserTableStore;
-import org.apache.james.services.MailServer;
 import org.apache.james.smtpserver.core.filter.fastfail.ValidRcptHandler;
 import org.apache.james.smtpserver.hook.HookReturnCode;
 import org.apache.james.test.mock.avalon.MockLogger;
-import org.apache.james.test.mock.avalon.MockServiceManager;
 import org.apache.james.test.mock.james.MockVirtualUserTableStore;
 import org.apache.james.userrepository.MockUsersRepository;
 import org.apache.mailet.MailAddress;
@@ -50,7 +48,6 @@
     private final static String INVALID_USER = "invalid";
     private final static String USER1 = "user1";
     private final static String USER2 = "user2";
-    private MockServiceManager serviceMan;
     
     UsersRepository users;
     ValidRcptHandler handler;
@@ -106,10 +103,6 @@
                 throw new UnsupportedOperationException("Unimplemented Stub 
Method");
             }
 
-            public MailServer getMailServer() {
-                throw new UnsupportedOperationException("Unimplemented Stub 
Method");
-            }
-
             public long getMaxMessageSize() {
                 throw new UnsupportedOperationException("Unimplemented Stub 
Method");
             }
@@ -122,10 +115,6 @@
                 throw new UnsupportedOperationException("Unimplemented Stub 
Method");
             }
 
-            public UsersRepository getUsersRepository() {
-                
-                return users;
-            }
 
             public boolean isRelayingAllowed(String remoteIP) {
                 throw new UnsupportedOperationException("Unimplemented Stub 
Method");
@@ -142,6 +131,10 @@
                        public boolean isAuthRequired(String remoteIP) {
                 throw new UnsupportedOperationException("Unimplemented Stub 
Method");
                        }
+
+                       public boolean isStartTLSSupported() {
+                               return false;
+                       }
         };
     
         return conf;

Added: james/server/trunk/smtpserver-function/src/test/resources/test_keystore
URL: 
http://svn.apache.org/viewvc/james/server/trunk/smtpserver-function/src/test/resources/test_keystore?rev=819359&view=auto
==============================================================================
Binary file - no diff available.

Propchange: 
james/server/trunk/smtpserver-function/src/test/resources/test_keystore
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream



---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to