You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by ng...@apache.org on 2008/08/27 16:58:53 UTC

svn commit: r689495 [1/2] - in /mina/ftpserver/trunk: core/src/main/java/org/apache/ftpserver/config/spring/ core/src/main/java/org/apache/ftpserver/filesystem/ core/src/main/java/org/apache/ftpserver/ftpletcontainer/ core/src/main/java/org/apache/ftps...

Author: ngn
Date: Wed Aug 27 07:58:52 2008
New Revision: 689495

URL: http://svn.apache.org/viewvc?rev=689495&view=rev
Log:
Password encryption now moved to a strategy (FTPSERVER-167)
Improved unit tests for user manager configuration

Added:
    mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/ftpletcontainer/
    mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/ftpletcontainer/DefaultFtpletContainer.java   (with props)
    mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/ftpletcontainer/FtpletContainer.java   (with props)
    mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/internal/
    mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/usermanager/ClearTextPasswordEncryptor.java   (with props)
    mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/usermanager/Md5PasswordEncryptor.java   (with props)
    mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/usermanager/PasswordEncryptor.java   (with props)
    mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/usermanager/SaltedPasswordEncryptor.java   (with props)
    mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/util/LocalizedFtpReply.java   (with props)
    mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/config/spring/DbUserManagerConfigTest.java   (contents, props changed)
      - copied, changed from r686637, mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/config/spring/XmlDbUserManagerConfigTest.java
    mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/config/spring/FileUserManagerConfigTest.java   (with props)
    mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/config/spring/SpringConfigTestTemplate.java   (with props)
    mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/usermanager/ClearTextDbUserManagerTest.java   (with props)
    mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/usermanager/ClearTextPasswordEncryptorTest.java   (with props)
    mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/usermanager/Md5PasswordEncryptorTest.java   (with props)
    mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/usermanager/SaltedPasswordEncryptorTest.java   (with props)
    mina/ftpserver/trunk/core/src/test/resources/dbusermanagertest-cleartext-hsql.sql
Removed:
    mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/config/spring/XmlDbUserManagerConfigTest.java
    mina/ftpserver/trunk/core/src/test/resources/spring-config/config-spring-db-user-manager.xml
Modified:
    mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/config/spring/UserManagerBeanDefinitionParser.java
    mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/filesystem/NativeFileObject.java
    mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/usermanager/DbUserManager.java
    mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/usermanager/PropertiesUserManager.java
    mina/ftpserver/trunk/core/src/main/resources/org/apache/ftpserver/config/spring/ftpserver-1.0.xsd
    mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/clienttests/ClientTestTemplate.java
    mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/usermanager/DbUserManagerTest.java
    mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/usermanager/PropertiesUserManagerTest.java
    mina/ftpserver/trunk/core/src/test/resources/dbusermanagertest-hsql.sql
    mina/ftpserver/trunk/ftplet-api/pom.xml
    mina/ftpserver/trunk/ftplet-api/src/main/java/org/apache/ftpserver/ftplet/User.java

Modified: mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/config/spring/UserManagerBeanDefinitionParser.java
URL: http://svn.apache.org/viewvc/mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/config/spring/UserManagerBeanDefinitionParser.java?rev=689495&r1=689494&r2=689495&view=diff
==============================================================================
--- mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/config/spring/UserManagerBeanDefinitionParser.java (original)
+++ mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/config/spring/UserManagerBeanDefinitionParser.java Wed Aug 27 07:58:52 2008
@@ -20,8 +20,11 @@
 package org.apache.ftpserver.config.spring;
 
 import org.apache.ftpserver.ftplet.UserManager;
+import org.apache.ftpserver.usermanager.ClearTextPasswordEncryptor;
 import org.apache.ftpserver.usermanager.DbUserManager;
+import org.apache.ftpserver.usermanager.Md5PasswordEncryptor;
 import org.apache.ftpserver.usermanager.PropertiesUserManager;
+import org.apache.ftpserver.usermanager.SaltedPasswordEncryptor;
 import org.springframework.beans.factory.support.BeanDefinitionBuilder;
 import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
 import org.springframework.beans.factory.xml.ParserContext;
@@ -51,12 +54,22 @@
     protected void doParse(final Element element,
             final ParserContext parserContext,
             final BeanDefinitionBuilder builder) {
-        if (getBeanClass(element) == PropertiesUserManager.class) {
-            builder.addPropertyValue("propFile", element.getAttribute("file"));
-            if (StringUtils.hasText(element.getAttribute("encrypt-passwords"))
-                    && element.getAttribute("encrypt-passwords").equals("true")) {
-                builder.addPropertyValue("encryptPasswords", true);
+        
+        // common for both user managers
+        if (StringUtils.hasText(element.getAttribute("encrypt-passwords"))) {
+            String encryptionStrategy = element.getAttribute("encrypt-passwords"); 
+            
+            if(encryptionStrategy.equals("true") || encryptionStrategy.equals("md5")) {
+                builder.addPropertyValue("passwordEncryptor", new Md5PasswordEncryptor());
+            } else if(encryptionStrategy.equals("salted")) {
+                builder.addPropertyValue("passwordEncryptor", new SaltedPasswordEncryptor());
+            } else {
+                builder.addPropertyValue("passwordEncryptor", new ClearTextPasswordEncryptor());
             }
+        }
+        
+        if (getBeanClass(element) == PropertiesUserManager.class) {
+            builder.addPropertyValue("file", element.getAttribute("file"));
             builder.setInitMethodName("configure");
         } else {
             Element dsElm = SpringUtil.getChildElement(element,

Modified: mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/filesystem/NativeFileObject.java
URL: http://svn.apache.org/viewvc/mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/filesystem/NativeFileObject.java?rev=689495&r1=689494&r2=689495&view=diff
==============================================================================
--- mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/filesystem/NativeFileObject.java (original)
+++ mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/filesystem/NativeFileObject.java Wed Aug 27 07:58:52 2008
@@ -32,7 +32,10 @@
 
 import org.apache.ftpserver.ftplet.FileObject;
 import org.apache.ftpserver.ftplet.User;
+import org.apache.ftpserver.usermanager.BaseUser;
 import org.apache.ftpserver.usermanager.WriteRequest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * This class wraps native file object.
@@ -42,6 +45,8 @@
  */
 public class NativeFileObject implements FileObject {
 
+    private final Logger LOG = LoggerFactory.getLogger(NativeFileObject.class);
+    
     // the file name with respect to the user root.
     // The path separator character will be '/' and
     // it will always begin with '/'.
@@ -189,13 +194,19 @@
      * Check file write permission.
      */
     public boolean hasWritePermission() {
+        LOG.debug("Checking authorization for " + getFullName());
         if (user.authorize(new WriteRequest(getFullName())) == null) {
+            LOG.debug("Not authorized");
             return false;
         }
 
+        LOG.debug("Checking if file exists");
         if (file.exists()) {
+            LOG.debug("Checking can write: " + file.canWrite());
             return file.canWrite();
         }
+        
+        LOG.debug("Authorized");
         return true;
     }
 

Added: mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/ftpletcontainer/DefaultFtpletContainer.java
URL: http://svn.apache.org/viewvc/mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/ftpletcontainer/DefaultFtpletContainer.java?rev=689495&view=auto
==============================================================================
--- mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/ftpletcontainer/DefaultFtpletContainer.java (added)
+++ mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/ftpletcontainer/DefaultFtpletContainer.java Wed Aug 27 07:58:52 2008
@@ -0,0 +1,206 @@
+/*
+ * 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.ftpserver.ftpletcontainer;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.ftpserver.ftplet.FtpException;
+import org.apache.ftpserver.ftplet.FtpRequest;
+import org.apache.ftpserver.ftplet.FtpSession;
+import org.apache.ftpserver.ftplet.Ftplet;
+import org.apache.ftpserver.ftplet.FtpletContext;
+import org.apache.ftpserver.ftplet.FtpletResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This ftplet calls other ftplet methods and returns appropriate return value.
+ *
+ * @author The Apache MINA Project (dev@mina.apache.org)
+ * @version $Rev$, $Date$
+ */
+public class DefaultFtpletContainer implements FtpletContainer {
+
+    private final Logger LOG = LoggerFactory
+            .getLogger(DefaultFtpletContainer.class);
+
+    private FtpletContext ftpletContext;
+    
+    private Map<String, Ftplet> ftplets = new ConcurrentHashMap<String, Ftplet>();
+
+    public synchronized void dispose() {
+
+        for (Entry<String, Ftplet> entry : ftplets.entrySet()) {
+            try {
+                entry.getValue().destroy();
+            } catch (Exception ex) {
+                LOG.error(entry.getKey() + " :: FtpletHandler.destroy()", ex);
+            }
+        }
+        ftplets.clear();
+    }
+
+    public synchronized void addFtplet(String name, Ftplet ftplet) throws FtpException {
+        if (getFtplet(name) != null) {
+            throw new IllegalArgumentException("Ftplet with name \"" + name
+                    + "\" already registred with container");
+        }
+
+        ftplets.put(name, ftplet);
+        
+        if(ftpletContext != null) {
+            ftplet.init(ftpletContext);
+        }
+    }
+
+    public synchronized Ftplet removeFtplet(String name) {
+        Ftplet ftplet = ftplets.get(name);
+
+        if (ftplet != null) {
+            ftplets.remove(name);
+            return ftplet;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Get Ftplet for the given name.
+     */
+    public synchronized Ftplet getFtplet(String name) {
+        if (name == null) {
+            return null;
+        }
+
+        return ftplets.get(name);
+    }
+
+    public synchronized void init(FtpletContext ftpletContext) throws FtpException {
+        this.ftpletContext = ftpletContext;
+        
+        // initialize Ftplets already added
+
+        for (Entry<String, Ftplet> entry : ftplets.entrySet()) {
+            entry.getValue().init(ftpletContext);
+        }
+    }
+
+    /**
+     * @see FtpletContainer#getFtplets()
+     */
+    public synchronized Map<String, Ftplet> getFtplets() {
+        return ftplets;
+    }
+
+    /**
+     * @see FtpletContainer#setFtplets(Map)
+     */
+    public synchronized void setFtplets(Map<String, Ftplet> ftplets) {
+        this.ftplets = ftplets;
+    }
+
+    /**
+     * Destroy all ftplets.
+     */
+    public void destroy() {
+        dispose();
+    }
+
+    /**
+     * Call ftplet onConnect.
+     */
+    public FtpletResult onConnect(FtpSession session) throws FtpException,
+            IOException {
+        FtpletResult retVal = FtpletResult.DEFAULT;
+        for (Entry<String, Ftplet> entry : ftplets.entrySet()) {
+            retVal = entry.getValue().onConnect(session);
+            if (retVal == null) {
+                retVal = FtpletResult.DEFAULT;
+            }
+
+            // proceed only if the return value is FtpletResult.DEFAULT
+            if (retVal != FtpletResult.DEFAULT) {
+                break;
+            }
+        }
+        return retVal;
+    }
+
+    /**
+     * Call ftplet onDisconnect.
+     */
+    public FtpletResult onDisconnect(FtpSession session) throws FtpException,
+            IOException {
+        FtpletResult retVal = FtpletResult.DEFAULT;
+        for (Entry<String, Ftplet> entry : ftplets.entrySet()) {
+
+            retVal = entry.getValue().onDisconnect(session);
+            if (retVal == null) {
+                retVal = FtpletResult.DEFAULT;
+            }
+
+            // proceed only if the return value is FtpletResult.DEFAULT
+            if (retVal != FtpletResult.DEFAULT) {
+                break;
+            }
+        }
+        return retVal;
+    }
+
+    public FtpletResult afterCommand(FtpSession session, FtpRequest request)
+            throws FtpException, IOException {
+        FtpletResult retVal = FtpletResult.DEFAULT;
+        for (Entry<String, Ftplet> entry : ftplets.entrySet()) {
+
+            retVal = entry.getValue().afterCommand(session, request);
+            if (retVal == null) {
+                retVal = FtpletResult.DEFAULT;
+            }
+
+            // proceed only if the return value is FtpletResult.DEFAULT
+            if (retVal != FtpletResult.DEFAULT) {
+                break;
+            }
+        }
+        return retVal;
+    }
+
+    public FtpletResult beforeCommand(FtpSession session, FtpRequest request)
+            throws FtpException, IOException {
+        FtpletResult retVal = FtpletResult.DEFAULT;
+        for (Entry<String, Ftplet> entry : ftplets.entrySet()) {
+
+            retVal = entry.getValue().beforeCommand(session, request);
+            if (retVal == null) {
+                retVal = FtpletResult.DEFAULT;
+            }
+
+            // proceed only if the return value is FtpletResult.DEFAULT
+            if (retVal != FtpletResult.DEFAULT) {
+                break;
+            }
+        }
+        return retVal;
+    }
+
+}

Propchange: mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/ftpletcontainer/DefaultFtpletContainer.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/ftpletcontainer/FtpletContainer.java
URL: http://svn.apache.org/viewvc/mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/ftpletcontainer/FtpletContainer.java?rev=689495&view=auto
==============================================================================
--- mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/ftpletcontainer/FtpletContainer.java (added)
+++ mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/ftpletcontainer/FtpletContainer.java Wed Aug 27 07:58:52 2008
@@ -0,0 +1,88 @@
+/*
+ * 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.ftpserver.ftpletcontainer;
+
+import java.util.Map;
+
+import org.apache.ftpserver.ftplet.FtpException;
+import org.apache.ftpserver.ftplet.Ftplet;
+
+/**
+ * Interface describing an Ftplet container. Ftplet containers extend the
+ * {@link Ftplet} interface and forward any events to the Ftplets hosted by the
+ * container.
+ *
+ * @author The Apache MINA Project (dev@mina.apache.org)
+ * @version $Rev$, $Date$
+ */
+public interface FtpletContainer extends Ftplet {
+
+    /**
+     * Add an {@link Ftplet} to the container.
+     * 
+     * @param name
+     *            The name of the Ftplet to be added
+     * @param ftplet
+     *            The Ftplet
+     * @throws FtpException 
+     * @throws IllegalArgumentException
+     *             If an Ftplet with the same name already exist within the
+     *             container
+     */
+    void addFtplet(String name, Ftplet ftplet) throws FtpException;
+
+    /**
+     * Remove the {@link Ftplet} identified by the name (as provided in the
+     * {@link #addFtplet(String, Ftplet)} method.
+     * 
+     * @param name
+     *            The name of the Ftplet to be removed
+     * @return The removed Ftplet if found, or null if the name is unknown to
+     *         the container.
+     */
+    Ftplet removeFtplet(String name);
+
+    /**
+     * Retrive the {@link Ftplet} identified by the name (as provided in the
+     * {@link #addFtplet(String, Ftplet)} method.
+     * 
+     * @param name
+     *            The name of the Ftplet to retrive
+     * @return The Ftplet if found, or null if the name is unknown to the
+     *         container.
+     */
+    Ftplet getFtplet(String name);
+
+    /**
+     * Retrive all Ftplets registered with this container
+     * 
+     * @return A map of all Ftplets with their name as the key
+     */
+    Map<String, Ftplet> getFtplets();
+
+    /**
+     * Set the Ftplets for this container. Will remove all previously registred
+     * Ftplets in this container
+     * 
+     * @param ftplets
+     *            A map of all Ftplets with their name as the key
+     */
+    void setFtplets(Map<String, Ftplet> ftplets);
+}
\ No newline at end of file

Propchange: mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/ftpletcontainer/FtpletContainer.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/usermanager/ClearTextPasswordEncryptor.java
URL: http://svn.apache.org/viewvc/mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/usermanager/ClearTextPasswordEncryptor.java?rev=689495&view=auto
==============================================================================
--- mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/usermanager/ClearTextPasswordEncryptor.java (added)
+++ mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/usermanager/ClearTextPasswordEncryptor.java Wed Aug 27 07:58:52 2008
@@ -0,0 +1,54 @@
+/*
+ * 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.ftpserver.usermanager;
+
+
+
+/**
+ * Password encryptor that does no encryption, that is, keps the
+ * password in clear text
+ *
+ * @author The Apache MINA Project (dev@mina.apache.org)
+ * @version $Rev$, $Date$
+ */
+public class ClearTextPasswordEncryptor implements PasswordEncryptor {
+
+    /**
+     * Returns the clear text password
+     */
+    public String encrypt(String password) {
+        return password;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean matches(String passwordToCheck, String storedPassword) {
+        if(storedPassword == null) {
+            throw new NullPointerException("storedPassword can not be null");
+        }
+        if(passwordToCheck == null) {
+            throw new NullPointerException("passwordToCheck can not be null");
+        }
+        
+        return passwordToCheck.equals(storedPassword);
+    }
+
+}

Propchange: mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/usermanager/ClearTextPasswordEncryptor.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/usermanager/DbUserManager.java
URL: http://svn.apache.org/viewvc/mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/usermanager/DbUserManager.java?rev=689495&r1=689494&r2=689495&view=diff
==============================================================================
--- mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/usermanager/DbUserManager.java (original)
+++ mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/usermanager/DbUserManager.java Wed Aug 27 07:58:52 2008
@@ -75,6 +75,8 @@
     // used for lazy init.
     private boolean configured = false;
 
+    private PasswordEncryptor passwordEncryptor = new Md5PasswordEncryptor();
+    
     /**
      * Retrive the data source used by the user manager
      * 
@@ -428,7 +430,7 @@
             // create sql query
             HashMap<String, Object> map = new HashMap<String, Object>();
             map.put(ATTR_LOGIN, escapeString(user.getName()));
-            map.put(ATTR_PASSWORD, escapeString(getPassword(user)));
+            map.put(ATTR_PASSWORD, escapeString(passwordEncryptor.encrypt(user.getPassword())));
 
             String home = user.getHomeDirectory();
             if (home == null) {
@@ -649,64 +651,6 @@
     }
 
     /**
-     * Get user password.
-     * 
-     * <pre>
-     * If the password value is not null
-     *    password = new password 
-     * else 
-     *   if user does exist
-     *     password = old password
-     *   else 
-     *     password = &quot;&quot;
-     * </pre>
-     */
-    private synchronized String getPassword(User user) throws SQLException {
-
-        String password = user.getPassword();
-        if (password != null) {
-            return password;
-        }
-
-        // create sql query
-        HashMap<String, Object> map = new HashMap<String, Object>();
-        map.put(ATTR_LOGIN, escapeString(user.getName()));
-        String sql = StringUtils.replaceString(selectUserStmt, map);
-        LOG.info(sql);
-
-        // execute query
-        Statement stmt = null;
-        ResultSet rs = null;
-        try {
-            stmt = createConnection().createStatement();
-            rs = stmt.executeQuery(sql);
-            if (rs.next()) {
-                password = rs.getString(ATTR_PASSWORD);
-            }
-        } finally {
-            if (rs != null) {
-                try {
-                    rs.close();
-                } catch (Exception ex) {
-                    LOG.error("DbUserManager.getPassword()", ex);
-                }
-            }
-            if (stmt != null) {
-                try {
-                    stmt.close();
-                } catch (Exception ex) {
-                    LOG.error("DbUserManager.getPassword()", ex);
-                }
-            }
-        }
-
-        if (password == null) {
-            password = "";
-        }
-        return password;
-    }
-
-    /**
      * User authentication.
      */
     public synchronized User authenticate(Authentication authentication)
@@ -734,7 +678,6 @@
                 // create the sql query
                 HashMap<String, Object> map = new HashMap<String, Object>();
                 map.put(ATTR_LOGIN, escapeString(user));
-                map.put(ATTR_PASSWORD, escapeString(password));
                 String sql = StringUtils.replaceString(authenticateStmt, map);
                 LOG.info(sql);
 
@@ -743,7 +686,13 @@
                 rs = stmt.executeQuery(sql);
                 if (rs.next()) {
                     try {
-                        return getUserByName(user);
+                        String storedPassword = rs.getString(ATTR_PASSWORD);
+                        if(passwordEncryptor.matches(password, storedPassword)) {
+                            return getUserByName(user);
+                        } else {
+                            throw new AuthenticationFailedException(
+                                    "Authentication failed");
+                        }
                     } catch (FtpException e) {
                         throw new AuthenticationFailedException(
                                 "Authentication failed", e);
@@ -819,4 +768,24 @@
         }
         return valBuf.toString();
     }
+    
+
+    
+    /**
+     * Retrieve the password encryptor used for this user manager
+     * @return The password encryptor. Default to {@link Md5PasswordEncryptor}
+     *  if no other has been provided
+     */    
+    public PasswordEncryptor getPasswordEncryptor() {
+        return passwordEncryptor;
+    }
+
+
+    /**
+     * Set the password encryptor to use for this user manager
+     * @param passwordEncryptor The password encryptor
+     */
+    public void setPasswordEncryptor(PasswordEncryptor passwordEncryptor) {
+        this.passwordEncryptor = passwordEncryptor;
+    }
 }
\ No newline at end of file

Added: mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/usermanager/Md5PasswordEncryptor.java
URL: http://svn.apache.org/viewvc/mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/usermanager/Md5PasswordEncryptor.java?rev=689495&view=auto
==============================================================================
--- mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/usermanager/Md5PasswordEncryptor.java (added)
+++ mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/usermanager/Md5PasswordEncryptor.java Wed Aug 27 07:58:52 2008
@@ -0,0 +1,55 @@
+/*
+ * 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.ftpserver.usermanager;
+
+import org.apache.ftpserver.util.EncryptUtils;
+
+
+/**
+ * Password encryptor that hashes the password using MD5. Please note that this form 
+ * of encryption is sensitive to lookup attacks.
+ * 
+ * @author The Apache MINA Project (dev@mina.apache.org)
+ * @version $Rev$, $Date$
+ */
+public class Md5PasswordEncryptor implements PasswordEncryptor {
+
+    /**
+     * Hashes the password using MD5
+     */
+    public String encrypt(String password) {
+        return EncryptUtils.encryptMD5(password);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean matches(String passwordToCheck, String storedPassword) {
+        if(storedPassword == null) {
+            throw new NullPointerException("storedPassword can not be null");
+        }
+        if(passwordToCheck == null) {
+            throw new NullPointerException("passwordToCheck can not be null");
+        }
+        
+        return encrypt(passwordToCheck).equalsIgnoreCase(storedPassword);
+    }
+
+}

Propchange: mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/usermanager/Md5PasswordEncryptor.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/usermanager/PasswordEncryptor.java
URL: http://svn.apache.org/viewvc/mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/usermanager/PasswordEncryptor.java?rev=689495&view=auto
==============================================================================
--- mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/usermanager/PasswordEncryptor.java (added)
+++ mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/usermanager/PasswordEncryptor.java Wed Aug 27 07:58:52 2008
@@ -0,0 +1,47 @@
+/*
+ * 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.ftpserver.usermanager;
+
+
+/**
+ * Strategy used for encrypting and matching encrypted passwords.
+ * The purpose is to make the password encryption possible to extend.
+ *
+ * @author The Apache MINA Project (dev@mina.apache.org)
+ * @version $Rev$, $Date$
+ */
+public interface PasswordEncryptor {
+
+    /**
+     * Encrypts the password
+     * @param password The clear text password
+     * @return The encrypted password
+     */
+    String encrypt(String password); 
+
+    /**
+     * Matches an encrypted password with that stored
+     * @param passwordToCheck The encrypted password to check
+     * @param storedPassword The stored password
+     * @return true if the password match
+     */
+    boolean matches(String passwordToCheck, String storedPassword);
+    
+}

Propchange: mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/usermanager/PasswordEncryptor.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/usermanager/PropertiesUserManager.java
URL: http://svn.apache.org/viewvc/mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/usermanager/PropertiesUserManager.java?rev=689495&r1=689494&r2=689495&view=diff
==============================================================================
--- mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/usermanager/PropertiesUserManager.java (original)
+++ mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/usermanager/PropertiesUserManager.java Wed Aug 27 07:58:52 2008
@@ -36,7 +36,6 @@
 import org.apache.ftpserver.ftplet.FtpException;
 import org.apache.ftpserver.ftplet.User;
 import org.apache.ftpserver.util.BaseProperties;
-import org.apache.ftpserver.util.EncryptUtils;
 import org.apache.ftpserver.util.IoUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -61,9 +60,18 @@
 
     private File userDataFile = new File("./res/user.gen");
 
-    private boolean isPasswordEncrypt = true;
-
     private boolean isConfigured = false;
+    
+    private PasswordEncryptor passwordEncryptor = new Md5PasswordEncryptor();
+
+    
+    /**
+     * Retrieve the file used to load and store users
+     * @return The file
+     */
+    public File getFile() {
+        return userDataFile;
+    }
 
     /**
      * Set the file used to store and read users. Must be set before
@@ -72,7 +80,7 @@
      * @param propFile
      *            A file containing users
      */
-    public void setPropFile(File propFile) {
+    public void setFile(File propFile) {
         if (isConfigured) {
             throw new IllegalStateException("Must be called before configure()");
         }
@@ -80,40 +88,27 @@
         this.userDataFile = propFile;
     }
 
+    
     /**
-     * If true is returned, passwords will be stored as hashes rather than in
-     * clear text. Default is true.
-     * 
-     * @return True if passwords are stored as hashes.
-     */
-    public boolean isEncryptPassword() {
-        return isPasswordEncrypt;
+     * Retrieve the password encryptor used for this user manager
+     * @return The password encryptor. Default to {@link Md5PasswordEncryptor}
+     *  if no other has been provided
+     */    
+    public PasswordEncryptor getPasswordEncryptor() {
+        return passwordEncryptor;
     }
 
-    /**
-     * If set to true, passwords will be stored as a hash to ensure that it can
-     * not be retrived from the user file. Must be set before
-     * {@link #configure()} is called.
-     * 
-     * @param encryptPassword
-     *            True to store a hash of the passwords, false to store the
-     *            passwords in clear text.
-     */
-    public void setEncryptPasswords(boolean encryptPassword) {
-        if (isConfigured) {
-            throw new IllegalStateException("Must be called before configure()");
-        }
-
-        this.isPasswordEncrypt = encryptPassword;
-    }
 
     /**
-     * @deprecated Use {@link #setEncryptPasswords(boolean)}
+     * Set the password encryptor to use for this user manager
+     * @param passwordEncryptor The password encryptor
      */
-    public void setPropPasswordEncrypt(boolean encryptPassword) {
-        setEncryptPasswords(encryptPassword);
+    public void setPasswordEncryptor(PasswordEncryptor passwordEncryptor) {
+        this.passwordEncryptor = passwordEncryptor;
     }
 
+
+
     /**
      * Lazy init the user manager
      */
@@ -303,14 +298,9 @@
         String password = usr.getPassword();
 
         if (password != null) {
-            if (isPasswordEncrypt) {
-                password = EncryptUtils.encryptMD5(password);
-            }
+            password = passwordEncryptor.encrypt(password);
         } else {
-            String blankPassword = "";
-            if (isPasswordEncrypt) {
-                blankPassword = EncryptUtils.encryptMD5("");
-            }
+            String blankPassword = passwordEncryptor.encrypt("");
 
             if (doesExist(name)) {
                 String key = PREFIX + name + '.' + ATTR_PASSWORD;
@@ -424,12 +414,15 @@
                 password = "";
             }
 
-            String passVal = userDataProp.getProperty(PREFIX + user + '.'
+            String storedPassword = userDataProp.getProperty(PREFIX + user + '.'
                     + ATTR_PASSWORD);
-            if (isPasswordEncrypt) {
-                password = EncryptUtils.encryptMD5(password);
+            
+            if(storedPassword == null) {
+                // user does not exist
+                throw new AuthenticationFailedException("Authentication failed");
             }
-            if (password.equals(passVal)) {
+
+            if (passwordEncryptor.matches(password, storedPassword)) {
                 return getUserByName(user);
             } else {
                 throw new AuthenticationFailedException("Authentication failed");

Added: mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/usermanager/SaltedPasswordEncryptor.java
URL: http://svn.apache.org/viewvc/mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/usermanager/SaltedPasswordEncryptor.java?rev=689495&view=auto
==============================================================================
--- mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/usermanager/SaltedPasswordEncryptor.java (added)
+++ mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/usermanager/SaltedPasswordEncryptor.java Wed Aug 27 07:58:52 2008
@@ -0,0 +1,85 @@
+/*
+ * 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.ftpserver.usermanager;
+
+import java.security.SecureRandom;
+
+import org.apache.ftpserver.util.EncryptUtils;
+
+/**
+ * Password encryptor that hashes a salt together with the password using MD5. 
+ * Using a salt protects against birthday attacks. 
+ * The hashing is also made in iterations, making lookup attacks much harder.
+ *
+ * The algorithm is based on the principles described in
+ * http://www.jasypt.org/howtoencryptuserpasswords.html
+ * 
+ * @author The Apache MINA Project (dev@mina.apache.org)
+ * @version $Rev$, $Date$
+ */
+public class SaltedPasswordEncryptor implements PasswordEncryptor {
+
+    private SecureRandom rnd = new SecureRandom();
+
+    private static final int MAX_SEED = 99999999;
+    private static final int HASH_ITERATIONS = 1000;
+
+    private String encrypt(String password, String salt) {
+        String hash = salt + password;
+        for (int i = 0; i < HASH_ITERATIONS; i++) {
+            hash = EncryptUtils.encryptMD5(hash);
+        }
+        return salt + ":" + hash;
+    }
+
+    /**
+     * Encrypts the password using a salt concatenated with the password 
+     * and a series of MD5 steps.
+     */
+    public String encrypt(String password) {
+        String seed = Integer.toString(rnd.nextInt(MAX_SEED));
+
+        return encrypt(password, seed);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean matches(String passwordToCheck, String storedPassword) {
+        if (storedPassword == null) {
+            throw new NullPointerException("storedPassword can not be null");
+        }
+        if (passwordToCheck == null) {
+            throw new NullPointerException("passwordToCheck can not be null");
+        }
+
+        // look for divider for hash
+        int divider = storedPassword.indexOf(':');
+        
+        if(divider < 1) {
+            throw new IllegalArgumentException("stored password does not contain salt");
+        }
+        
+        String storedSalt = storedPassword.substring(0, divider);
+        
+        return encrypt(passwordToCheck, storedSalt).equalsIgnoreCase(storedPassword);
+    }
+
+}

Propchange: mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/usermanager/SaltedPasswordEncryptor.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/util/LocalizedFtpReply.java
URL: http://svn.apache.org/viewvc/mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/util/LocalizedFtpReply.java?rev=689495&view=auto
==============================================================================
--- mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/util/LocalizedFtpReply.java (added)
+++ mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/util/LocalizedFtpReply.java Wed Aug 27 07:58:52 2008
@@ -0,0 +1,499 @@
+/*
+ * 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.ftpserver.util;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+
+import org.apache.ftpserver.ftplet.DefaultFtpReply;
+import org.apache.ftpserver.ftplet.FileSystemView;
+import org.apache.ftpserver.ftplet.FtpReply;
+import org.apache.ftpserver.ftplet.FtpRequest;
+import org.apache.ftpserver.ftplet.FtpStatistics;
+import org.apache.ftpserver.interfaces.FtpIoSession;
+import org.apache.ftpserver.interfaces.FtpServerContext;
+import org.apache.ftpserver.interfaces.MessageResource;
+
+/**
+ * FTP reply translator.
+ *
+ * @author The Apache MINA Project (dev@mina.apache.org)
+ * @version $Rev$, $Date$
+ */
+public class LocalizedFtpReply extends DefaultFtpReply {
+
+    private static final String CLIENT_ACCESS_TIME = "client.access.time";
+
+    private static final String CLIENT_CON_TIME = "client.con.time";
+
+    private static final String CLIENT_DIR = "client.dir";
+
+    private static final String CLIENT_HOME = "client.home";
+
+    private static final String CLIENT_IP = "client.ip";
+
+    private static final String CLIENT_LOGIN_NAME = "client.login.name";
+
+    private static final String CLIENT_LOGIN_TIME = "client.login.time";
+
+    private static final String OUTPUT_CODE = "output.code";
+
+    private static final String OUTPUT_MSG = "output.msg";
+
+    private static final String REQUEST_ARG = "request.arg";
+
+    private static final String REQUEST_CMD = "request.cmd";
+
+    private static final String REQUEST_LINE = "request.line";
+
+    private static final String SERVER_IP = "server.ip";
+
+    private static final String SERVER_PORT = "server.port";
+
+    private static final String STAT_CON_CURR = "stat.con.curr";
+
+    private static final String STAT_CON_TOTAL = "stat.con.total";
+
+    private static final String STAT_DIR_CREATE_COUNT = "stat.dir.create.count";
+
+    private static final String STAT_DIR_DELETE_COUNT = "stat.dir.delete.count";
+
+    private static final String STAT_FILE_DELETE_COUNT = "stat.file.delete.count";
+
+    private static final String STAT_FILE_DOWNLOAD_BYTES = "stat.file.download.bytes";
+
+    private static final String STAT_FILE_DOWNLOAD_COUNT = "stat.file.download.count";
+
+    private static final String STAT_FILE_UPLOAD_BYTES = "stat.file.upload.bytes";
+
+    private static final String STAT_FILE_UPLOAD_COUNT = "stat.file.upload.count";
+
+    private static final String STAT_LOGIN_ANON_CURR = "stat.login.anon.curr";
+
+    private static final String STAT_LOGIN_ANON_TOTAL = "stat.login.anon.total";
+
+    private static final String STAT_LOGIN_CURR = "stat.login.curr";
+
+    private static final String STAT_LOGIN_TOTAL = "stat.login.total";
+
+    private static final String STAT_START_TIME = "stat.start.time";
+    
+    public static FtpReply translate(FtpIoSession session, FtpRequest request,
+            FtpServerContext context, int code, String subId, String basicMsg) {
+        String msg = translateMessage(session, request, context, code, subId,
+                basicMsg);
+
+        return new LocalizedFtpReply(code, msg);
+    }
+
+    private static String translateMessage(FtpIoSession session,
+            FtpRequest request, FtpServerContext context, int code,
+            String subId, String basicMsg) {
+        MessageResource resource = context.getMessageResource();
+        String lang = session.getLanguage();
+
+        String msg = null;
+        if (resource != null) {
+            msg = resource.getMessage(code, subId, lang);
+        }
+        if (msg == null) {
+            msg = "";
+        }
+        msg = replaceVariables(session, request, context, code, basicMsg, msg);
+
+        return msg;
+    }
+
+    /**
+     * Replace server variables.
+     */
+    private static String replaceVariables(FtpIoSession session,
+            FtpRequest request, FtpServerContext context, int code,
+            String basicMsg, String str) {
+
+        int startIndex = 0;
+        int openIndex = str.indexOf('{', startIndex);
+        if (openIndex == -1) {
+            return str;
+        }
+
+        int closeIndex = str.indexOf('}', startIndex);
+        if ((closeIndex == -1) || (openIndex > closeIndex)) {
+            return str;
+        }
+
+        StringBuffer sb = new StringBuffer(128);
+        sb.append(str.substring(startIndex, openIndex));
+        while (true) {
+            String varName = str.substring(openIndex + 1, closeIndex);
+            sb.append(getVariableValue(session, request, context, code,
+                    basicMsg, varName));
+
+            startIndex = closeIndex + 1;
+            openIndex = str.indexOf('{', startIndex);
+            if (openIndex == -1) {
+                sb.append(str.substring(startIndex));
+                break;
+            }
+
+            closeIndex = str.indexOf('}', startIndex);
+            if ((closeIndex == -1) || (openIndex > closeIndex)) {
+                sb.append(str.substring(startIndex));
+                break;
+            }
+            sb.append(str.substring(startIndex, openIndex));
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Get the variable value.
+     */
+    private static String getVariableValue(FtpIoSession session,
+            FtpRequest request, FtpServerContext context, int code,
+            String basicMsg, String varName) {
+
+        String varVal = null;
+
+        // all output variables
+        if (varName.startsWith("output.")) {
+            varVal = getOutputVariableValue(session, code, basicMsg, varName);
+        }
+
+        // all server variables
+        else if (varName.startsWith("server.")) {
+            varVal = getServerVariableValue(session, varName);
+        }
+
+        // all request variables
+        else if (varName.startsWith("request.")) {
+            varVal = getRequestVariableValue(session, request, varName);
+        }
+
+        // all statistical variables
+        else if (varName.startsWith("stat.")) {
+            varVal = getStatisticalVariableValue(session, context, varName);
+        }
+
+        // all client variables
+        else if (varName.startsWith("client.")) {
+            varVal = getClientVariableValue(session, varName);
+        }
+
+        if (varVal == null) {
+            varVal = "";
+        }
+        return varVal;
+    }
+
+    /**
+     * Get client variable value.
+     */
+    private static String getClientVariableValue(FtpIoSession session,
+            String varName) {
+
+        String varVal = null;
+
+        // client ip
+        if (varName.equals(CLIENT_IP)) {
+            if (session.getRemoteAddress() instanceof InetSocketAddress) {
+                InetSocketAddress remoteSocketAddress = (InetSocketAddress) session
+                        .getRemoteAddress();
+                varVal = remoteSocketAddress.getAddress().getHostAddress();
+            }
+
+        }
+
+        // client connection time
+        else if (varName.equals(CLIENT_CON_TIME)) {
+            varVal = DateUtils.getISO8601Date(session.getCreationTime());
+        }
+
+        // client login name
+        else if (varName.equals(CLIENT_LOGIN_NAME)) {
+            if (session.getUser() != null) {
+                varVal = session.getUser().getName();
+            }
+        }
+
+        // client login time
+        else if (varName.equals(CLIENT_LOGIN_TIME)) {
+            varVal = DateUtils.getISO8601Date(session.getLoginTime().getTime());
+        }
+
+        // client last access time
+        else if (varName.equals(CLIENT_ACCESS_TIME)) {
+            varVal = DateUtils.getISO8601Date(session.getLastAccessTime()
+                    .getTime());
+        }
+
+        // client home
+        else if (varName.equals(CLIENT_HOME)) {
+            varVal = session.getUser().getHomeDirectory();
+        }
+
+        // client directory
+        else if (varName.equals(CLIENT_DIR)) {
+            FileSystemView fsView = session.getFileSystemView();
+            if (fsView != null) {
+                try {
+                    varVal = fsView.getCurrentDirectory().getFullName();
+                } catch (Exception ex) {
+                    varVal = "";
+                }
+            }
+        }
+        return varVal;
+    }
+
+    /**
+     * Get output variable value.
+     */
+    private static String getOutputVariableValue(FtpIoSession session,
+            int code, String basicMsg, String varName) {
+        String varVal = null;
+
+        // output code
+        if (varName.equals(OUTPUT_CODE)) {
+            varVal = String.valueOf(code);
+        }
+
+        // output message
+        else if (varName.equals(OUTPUT_MSG)) {
+            varVal = basicMsg;
+        }
+
+        return varVal;
+    }
+
+    /**
+     * Get request variable value.
+     */
+    private static String getRequestVariableValue(FtpIoSession session,
+            FtpRequest request, String varName) {
+
+        String varVal = null;
+
+        if (request == null) {
+            return "";
+        }
+
+        // request line
+        if (varName.equals(REQUEST_LINE)) {
+            varVal = request.getRequestLine();
+        }
+
+        // request command
+        else if (varName.equals(REQUEST_CMD)) {
+            varVal = request.getCommand();
+        }
+
+        // request argument
+        else if (varName.equals(REQUEST_ARG)) {
+            varVal = request.getArgument();
+        }
+
+        return varVal;
+    }
+
+    /**
+     * Get server variable value.
+     */
+    private static String getServerVariableValue(FtpIoSession session,
+            String varName) {
+
+        String varVal = null;
+
+        SocketAddress localSocketAddress = session.getLocalAddress();
+
+        if (localSocketAddress instanceof InetSocketAddress) {
+            InetSocketAddress localInetSocketAddress = (InetSocketAddress) localSocketAddress;
+            // server address
+            if (varName.equals(SERVER_IP)) {
+
+                InetAddress addr = localInetSocketAddress.getAddress();
+
+                if (addr != null) {
+                    varVal = addr.getHostAddress();
+                }
+            }
+
+            // server port
+            else if (varName.equals(SERVER_PORT)) {
+                varVal = String.valueOf(localInetSocketAddress.getPort());
+            }
+        }
+
+        return varVal;
+    }
+
+    /**
+     * Get statistical connection variable value.
+     */
+    private static String getStatisticalConnectionVariableValue(
+            FtpIoSession session, FtpServerContext context, String varName) {
+        String varVal = null;
+        FtpStatistics stat = context.getFtpStatistics();
+
+        // total connection number
+        if (varName.equals(STAT_CON_TOTAL)) {
+            varVal = String.valueOf(stat.getTotalConnectionNumber());
+        }
+
+        // current connection number
+        else if (varName.equals(STAT_CON_CURR)) {
+            varVal = String.valueOf(stat.getCurrentConnectionNumber());
+        }
+
+        return varVal;
+    }
+
+    /**
+     * Get statistical directory variable value.
+     */
+    private static String getStatisticalDirectoryVariableValue(
+            FtpIoSession session, FtpServerContext context, String varName) {
+        String varVal = null;
+        FtpStatistics stat = context.getFtpStatistics();
+
+        // total directory created
+        if (varName.equals(STAT_DIR_CREATE_COUNT)) {
+            varVal = String.valueOf(stat.getTotalDirectoryCreated());
+        }
+
+        // total directory removed
+        else if (varName.equals(STAT_DIR_DELETE_COUNT)) {
+            varVal = String.valueOf(stat.getTotalDirectoryRemoved());
+        }
+
+        return varVal;
+    }
+
+    /**
+     * Get statistical file variable value.
+     */
+    private static String getStatisticalFileVariableValue(FtpIoSession session,
+            FtpServerContext context, String varName) {
+        String varVal = null;
+        FtpStatistics stat = context.getFtpStatistics();
+
+        // total number of file upload
+        if (varName.equals(STAT_FILE_UPLOAD_COUNT)) {
+            varVal = String.valueOf(stat.getTotalUploadNumber());
+        }
+
+        // total bytes uploaded
+        else if (varName.equals(STAT_FILE_UPLOAD_BYTES)) {
+            varVal = String.valueOf(stat.getTotalUploadSize());
+        }
+
+        // total number of file download
+        else if (varName.equals(STAT_FILE_DOWNLOAD_COUNT)) {
+            varVal = String.valueOf(stat.getTotalDownloadNumber());
+        }
+
+        // total bytes downloaded
+        else if (varName.equals(STAT_FILE_DOWNLOAD_BYTES)) {
+            varVal = String.valueOf(stat.getTotalDownloadSize());
+        }
+
+        // total number of files deleted
+        else if (varName.equals(STAT_FILE_DELETE_COUNT)) {
+            varVal = String.valueOf(stat.getTotalDeleteNumber());
+        }
+
+        return varVal;
+    }
+
+    /**
+     * Get statistical login variable value.
+     */
+    private static String getStatisticalLoginVariableValue(
+            FtpIoSession session, FtpServerContext context, String varName) {
+        String varVal = null;
+        FtpStatistics stat = context.getFtpStatistics();
+
+        // total login number
+        if (varName.equals(STAT_LOGIN_TOTAL)) {
+            varVal = String.valueOf(stat.getTotalLoginNumber());
+        }
+
+        // current login number
+        else if (varName.equals(STAT_LOGIN_CURR)) {
+            varVal = String.valueOf(stat.getCurrentLoginNumber());
+        }
+
+        // total anonymous login number
+        else if (varName.equals(STAT_LOGIN_ANON_TOTAL)) {
+            varVal = String.valueOf(stat.getTotalAnonymousLoginNumber());
+        }
+
+        // current anonymous login number
+        else if (varName.equals(STAT_LOGIN_ANON_CURR)) {
+            varVal = String.valueOf(stat.getCurrentAnonymousLoginNumber());
+        }
+
+        return varVal;
+    }
+
+    /**
+     * Get statistical variable value.
+     */
+    private static String getStatisticalVariableValue(FtpIoSession session,
+            FtpServerContext context, String varName) {
+
+        String varVal = null;
+        FtpStatistics stat = context.getFtpStatistics();
+
+        // server start time
+        if (varName.equals(STAT_START_TIME)) {
+            varVal = DateUtils.getISO8601Date(stat.getStartTime().getTime());
+        }
+
+        // connection statistical variables
+        else if (varName.startsWith("stat.con")) {
+            varVal = getStatisticalConnectionVariableValue(session, context,
+                    varName);
+        }
+
+        // login statistical variables
+        else if (varName.startsWith("stat.login.")) {
+            varVal = getStatisticalLoginVariableValue(session, context, varName);
+        }
+
+        // file statistical variable
+        else if (varName.startsWith("stat.file")) {
+            varVal = getStatisticalFileVariableValue(session, context, varName);
+        }
+
+        // directory statistical variable
+        else if (varName.startsWith("stat.dir.")) {
+            varVal = getStatisticalDirectoryVariableValue(session, context,
+                    varName);
+        }
+
+        return varVal;
+    }
+
+    private LocalizedFtpReply(int code, String message) {
+        super(code, message);
+    }   
+}

Propchange: mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/util/LocalizedFtpReply.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: mina/ftpserver/trunk/core/src/main/resources/org/apache/ftpserver/config/spring/ftpserver-1.0.xsd
URL: http://svn.apache.org/viewvc/mina/ftpserver/trunk/core/src/main/resources/org/apache/ftpserver/config/spring/ftpserver-1.0.xsd?rev=689495&r1=689494&r2=689495&view=diff
==============================================================================
--- mina/ftpserver/trunk/core/src/main/resources/org/apache/ftpserver/config/spring/ftpserver-1.0.xsd (original)
+++ mina/ftpserver/trunk/core/src/main/resources/org/apache/ftpserver/config/spring/ftpserver-1.0.xsd Wed Aug 27 07:58:52 2008
@@ -1,30 +1,25 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!--
- 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. -->  
-
-<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
-	targetNamespace="http://mina.apache.org/ftpserver/spring/v1"
-	xmlns="http://mina.apache.org/ftpserver/spring/v1"
-	xmlns:beans="http://www.springframework.org/schema/beans">
+	<!--
+		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.
+	-->
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+	elementFormDefault="qualified" targetNamespace="http://mina.apache.org/ftpserver/spring/v1"
+	xmlns="http://mina.apache.org/ftpserver/spring/v1" xmlns:beans="http://www.springframework.org/schema/beans">
 
 	<!-- Import the Spring beans XML schema -->
 	<xs:import namespace="http://www.springframework.org/schema/beans"
-		schemaLocation="http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"/>
+		schemaLocation="http://www.springframework.org/schema/beans/spring-beans-2.5.xsd" />
 
 	<!-- The main entry point, used for setting up an entire FTP server -->
 	<xs:element name="server">
@@ -33,30 +28,30 @@
 				<xs:element minOccurs="0" name="listeners">
 					<xs:complexType>
 						<xs:choice minOccurs="1" maxOccurs="unbounded">
-							<xs:element ref="nio-listener"/>
-							<xs:element ref="listener"/>
+							<xs:element ref="nio-listener" />
+							<xs:element ref="listener" />
 						</xs:choice>
 					</xs:complexType>
 				</xs:element>
-				<xs:element minOccurs="0" ref="ftplets"/>
+				<xs:element minOccurs="0" ref="ftplets" />
 				<xs:choice minOccurs="0" maxOccurs="1">
-					<xs:element minOccurs="0" ref="file-user-manager"/>
-					<xs:element minOccurs="0" ref="db-user-manager"/>
-					<xs:element minOccurs="0" ref="user-manager"/>
+					<xs:element minOccurs="0" ref="file-user-manager" />
+					<xs:element minOccurs="0" ref="db-user-manager" />
+					<xs:element minOccurs="0" ref="user-manager" />
 				</xs:choice>
 				<xs:choice minOccurs="0" maxOccurs="1">
-					<xs:element minOccurs="0" ref="native-filesystem"/>
-					<xs:element minOccurs="0" ref="filesystem"/>
+					<xs:element minOccurs="0" ref="native-filesystem" />
+					<xs:element minOccurs="0" ref="filesystem" />
 				</xs:choice>
-				<xs:element minOccurs="0" ref="commands"/>
-				<xs:element minOccurs="0" ref="messages"/>
+				<xs:element minOccurs="0" ref="commands" />
+				<xs:element minOccurs="0" ref="messages" />
 			</xs:sequence>
-			<xs:attribute name="id" type="xs:ID"/>
-            <xs:attribute name="max-logins" type="xs:int"/>
-            <xs:attribute name="max-anon-logins" type="xs:int"/>
-            <xs:attribute name="anon-enabled" type="xs:boolean"/>
-            <xs:attribute name="max-login-failures" type="xs:int"/>
-            <xs:attribute name="login-failure-delay" type="xs:int"/>
+			<xs:attribute name="id" type="xs:ID" />
+			<xs:attribute name="max-logins" type="xs:int" />
+			<xs:attribute name="max-anon-logins" type="xs:int" />
+			<xs:attribute name="anon-enabled" type="xs:boolean" />
+			<xs:attribute name="max-login-failures" type="xs:int" />
+			<xs:attribute name="login-failure-delay" type="xs:int" />
 		</xs:complexType>
 	</xs:element>
 
@@ -66,41 +61,40 @@
 			<xs:sequence>
 				<xs:element name="keystore">
 					<xs:complexType>
-						<xs:attribute name="file" use="required"/>
-						<xs:attribute name="password" use="required"/>
-						<xs:attribute name="type"/>
-						<xs:attribute name="algorithm"/>
-						<xs:attribute name="key-alias"/>
-						<xs:attribute name="key-password"/>
+						<xs:attribute name="file" use="required" />
+						<xs:attribute name="password" use="required" />
+						<xs:attribute name="type" />
+						<xs:attribute name="algorithm" />
+						<xs:attribute name="key-alias" />
+						<xs:attribute name="key-password" />
 					</xs:complexType>
 				</xs:element>
 				<xs:element minOccurs="0" name="truststore">
 					<xs:complexType>
-						<xs:attribute name="file" use="required"/>
-						<xs:attribute name="password"/>
-						<xs:attribute name="type"/>
-						<xs:attribute name="algorithm"/>
+						<xs:attribute name="file" use="required" />
+						<xs:attribute name="password" />
+						<xs:attribute name="type" />
+						<xs:attribute name="algorithm" />
 					</xs:complexType>
 				</xs:element>
 			</xs:sequence>
 			<xs:attribute name="client-authentication">
-             <xs:simpleType>
-                 <xs:restriction base="xs:string">
-                     <xs:enumeration value="NEED" />
-                     <xs:enumeration value="WANT" />
-                     <xs:enumeration value="NONE" />
-                 </xs:restriction>
-             </xs:simpleType>
-            </xs:attribute>			
-			
-			<xs:attribute name="enabled-ciphersuites" type="xs:string"/>
+				<xs:simpleType>
+					<xs:restriction base="xs:string">
+						<xs:enumeration value="NEED" />
+						<xs:enumeration value="WANT" />
+						<xs:enumeration value="NONE" />
+					</xs:restriction>
+				</xs:simpleType>
+			</xs:attribute>
+			<xs:attribute name="enabled-ciphersuites" type="xs:string" />
 			<xs:attribute name="protocol">
-			 <xs:simpleType>
-			     <xs:restriction base="xs:string">
-			         <xs:enumeration value="SSL" />
-			         <xs:enumeration value="TLS" />
-			     </xs:restriction>
-			 </xs:simpleType>
+				<xs:simpleType>
+					<xs:restriction base="xs:string">
+						<xs:enumeration value="SSL" />
+						<xs:enumeration value="TLS" />
+					</xs:restriction>
+				</xs:simpleType>
 			</xs:attribute>
 		</xs:complexType>
 	</xs:element>
@@ -109,43 +103,42 @@
 	<xs:element name="nio-listener">
 		<xs:complexType>
 			<xs:sequence>
-				<xs:element minOccurs="0" ref="ssl"/>
+				<xs:element minOccurs="0" ref="ssl" />
 				<xs:element minOccurs="0" name="data-connection">
 					<xs:complexType>
 						<xs:sequence>
-							<xs:element minOccurs="0" ref="ssl"/>
+							<xs:element minOccurs="0" ref="ssl" />
 							<xs:element minOccurs="0" name="active">
 								<xs:complexType>
-									<xs:attribute name="enabled" type="xs:boolean"/>
-									<xs:attribute name="local-address"/>
-									<xs:attribute name="local-port" type="xs:int"/>
-									<xs:attribute name="ip-check" type="xs:boolean"/>
+									<xs:attribute name="enabled" type="xs:boolean" />
+									<xs:attribute name="local-address" />
+									<xs:attribute name="local-port" type="xs:int" />
+									<xs:attribute name="ip-check" type="xs:boolean" />
 								</xs:complexType>
 							</xs:element>
 							<xs:element minOccurs="0" name="passive">
 								<xs:complexType>
-									<xs:attribute name="address"/>
-									<xs:attribute name="external-address"/>
-									<xs:attribute name="ports"/>
+									<xs:attribute name="address" />
+									<xs:attribute name="external-address" />
+									<xs:attribute name="ports" />
 								</xs:complexType>
 							</xs:element>
 						</xs:sequence>
-						<xs:attribute name="idle-timeout" type="xs:int"/>
+						<xs:attribute name="idle-timeout" type="xs:int" />
 					</xs:complexType>
-
 				</xs:element>
-				<xs:element minOccurs="0" name="blacklist" type="xs:string"/>
+				<xs:element minOccurs="0" name="blacklist" type="xs:string" />
 			</xs:sequence>
-			<xs:attribute name="name" use="required" type="xs:string"/>
-			<xs:attribute name="local-address"/>
-			<xs:attribute name="port" type="xs:int"/>
-			<xs:attribute name="idle-timeout" type="xs:int"/>
-			<xs:attribute name="implicit-ssl" type="xs:boolean"/>
+			<xs:attribute name="name" use="required" type="xs:string" />
+			<xs:attribute name="local-address" />
+			<xs:attribute name="port" type="xs:int" />
+			<xs:attribute name="idle-timeout" type="xs:int" />
+			<xs:attribute name="implicit-ssl" type="xs:boolean" />
 		</xs:complexType>
 	</xs:element>
 	
 	<!-- Extension element used for defining a custom listener -->
-	<xs:element name="listener" type="spring-bean-or-ref-with-name"/>
+	<xs:element name="listener" type="spring-bean-or-ref-with-name" />
 
 	<!-- Element used to configure Ftplets for used with the server -->
 	<xs:element name="ftplets">
@@ -155,11 +148,11 @@
 					<xs:complexType>
 						<xs:sequence>
 							<xs:choice maxOccurs="unbounded">
-								<xs:element ref="beans:bean"/>
-								<xs:element ref="beans:ref"/>
+								<xs:element ref="beans:bean" />
+								<xs:element ref="beans:ref" />
 							</xs:choice>
 						</xs:sequence>
-						<xs:attribute name="name" use="required" type="xs:string"/>
+						<xs:attribute name="name" use="required" type="xs:string" />
 					</xs:complexType>
 				</xs:element>
 			</xs:sequence>
@@ -169,40 +162,61 @@
 	<!-- Element used to configure a file based user manager -->
 	<xs:element name="file-user-manager">
 		<xs:complexType>
-			<xs:attribute name="file" use="required" type="xs:string"/>
-			<xs:attribute name="encrypt-passwords" type="xs:boolean"/>
+			<xs:attribute name="file" use="required" type="xs:string" />
+			<xs:attribute name="encrypt-passwords">
+				<xs:simpleType>
+					<xs:restriction base="xs:string">
+						<xs:enumeration value="clear"></xs:enumeration>
+						<!-- Deprecated, use "clear" -->
+						<xs:enumeration value="false"></xs:enumeration>
+						<xs:enumeration value="md5"></xs:enumeration>
+   						<!-- Deprecated, use "md5" -->
+   						<xs:enumeration value="true"></xs:enumeration>
+						<xs:enumeration value="salted"></xs:enumeration>
+					</xs:restriction>
+				</xs:simpleType>
+			</xs:attribute>
 		</xs:complexType>
 	</xs:element>
 	
 	<!-- Element used to configure a database based user manager -->
 	<xs:element name="db-user-manager">
 		<xs:complexType>
-			<xs:sequence>
-				<xs:element name="data-source" type="spring-bean-or-ref"/>
-				<xs:element name="insert-user" type="xs:string"/>
-				<xs:element name="update-user" type="xs:string"/>
-				<xs:element name="delete-user" type="xs:string"/>
-				<xs:element name="select-user" type="xs:string"/>
-				<xs:element name="select-all-users" type="xs:string"/>
-				<xs:element name="is-admin" type="xs:string"/>
-				<xs:element name="authenticate" type="xs:string"/>
+    		<xs:sequence>
+				<xs:element name="data-source" type="spring-bean-or-ref" />
+				<xs:element name="insert-user" type="xs:string" />
+				<xs:element name="update-user" type="xs:string" />
+				<xs:element name="delete-user" type="xs:string" />
+				<xs:element name="select-user" type="xs:string" />
+				<xs:element name="select-all-users" type="xs:string" />
+				<xs:element name="is-admin" type="xs:string" />
+				<xs:element name="authenticate" type="xs:string" />
 			</xs:sequence>
+            <xs:attribute name="encrypt-passwords">
+                <xs:simpleType>
+                    <xs:restriction base="xs:string">
+                        <xs:enumeration value="clear"></xs:enumeration>
+                        <xs:enumeration value="md5"></xs:enumeration>
+                        <xs:enumeration value="salted"></xs:enumeration>
+                    </xs:restriction>
+                </xs:simpleType>
+            </xs:attribute>
 		</xs:complexType>
 	</xs:element>
 
 	<!-- Extension element used for defining a custom user manager -->
-	<xs:element name="user-manager" type="spring-bean-or-ref"/>
+	<xs:element name="user-manager" type="spring-bean-or-ref" />
 
 	<!-- Element used to configure the default file system -->
 	<xs:element name="native-filesystem">
 		<xs:complexType>
-			<xs:attribute name="case-insensitive" type="xs:boolean"/>
-			<xs:attribute name="create-home" type="xs:boolean"/>
+			<xs:attribute name="case-insensitive" type="xs:boolean" />
+			<xs:attribute name="create-home" type="xs:boolean" />
 		</xs:complexType>
 	</xs:element>
 
 	<!-- Extension element used for defining a custom file system -->
-	<xs:element name="filesystem" type="spring-bean-or-ref"/>
+	<xs:element name="filesystem" type="spring-bean-or-ref" />
 
 	<!-- Element used to provide custom command implementations -->
 	<xs:element name="commands">
@@ -212,32 +226,31 @@
 					<xs:complexType>
 						<xs:sequence>
 							<xs:choice maxOccurs="unbounded">
-								<xs:element ref="beans:bean"/>
-								<xs:element ref="beans:ref"/>
+								<xs:element ref="beans:bean" />
+								<xs:element ref="beans:ref" />
 							</xs:choice>
 						</xs:sequence>
-						<xs:attribute name="name" use="required"/>
+						<xs:attribute name="name" use="required" />
 					</xs:complexType>
-
 				</xs:element>
 			</xs:sequence>
-			<xs:attribute name="use-default" type="xs:boolean"/>
+			<xs:attribute name="use-default" type="xs:boolean" />
 		</xs:complexType>
 	</xs:element>
 	
 	<!-- Element used to configure and localize messages -->
 	<xs:element name="messages">
 		<xs:complexType>
-			<xs:attribute name="languages"/>
-			<xs:attribute name="directory"/>
+			<xs:attribute name="languages" />
+			<xs:attribute name="directory" />
 		</xs:complexType>
 	</xs:element>
 
 	<!-- Reusable type used for extension elements -->
 	<xs:complexType name="spring-bean-or-ref">
 		<xs:choice>
-			<xs:element ref="beans:bean"/>
-			<xs:element ref="beans:ref"/>
+			<xs:element ref="beans:bean" />
+			<xs:element ref="beans:ref" />
 		</xs:choice>
 	</xs:complexType>
 	
@@ -245,12 +258,10 @@
 	<xs:complexType name="spring-bean-or-ref-with-name">
 		<xs:sequence>
 			<xs:choice>
-				<xs:element ref="beans:bean"/>
-				<xs:element ref="beans:ref"/>
+				<xs:element ref="beans:bean" />
+				<xs:element ref="beans:ref" />
 			</xs:choice>
-			
 		</xs:sequence>
-		<xs:attribute name="name" use="required" type="xs:string"/>
+		<xs:attribute name="name" use="required" type="xs:string" />
 	</xs:complexType>
-	
-</xs:schema>
+</xs:schema>
\ No newline at end of file

Modified: mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/clienttests/ClientTestTemplate.java
URL: http://svn.apache.org/viewvc/mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/clienttests/ClientTestTemplate.java?rev=689495&r1=689494&r2=689495&view=diff
==============================================================================
--- mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/clienttests/ClientTestTemplate.java (original)
+++ mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/clienttests/ClientTestTemplate.java Wed Aug 27 07:58:52 2008
@@ -32,6 +32,7 @@
 import org.apache.ftpserver.FtpServer;
 import org.apache.ftpserver.listener.nio.NioListener;
 import org.apache.ftpserver.test.TestUtil;
+import org.apache.ftpserver.usermanager.ClearTextPasswordEncryptor;
 import org.apache.ftpserver.usermanager.PropertiesUserManager;
 import org.apache.ftpserver.util.IoUtils;
 import org.slf4j.Logger;
@@ -87,8 +88,8 @@
 
         PropertiesUserManager userManager = new PropertiesUserManager();
         userManager.setAdminName("admin");
-        userManager.setEncryptPasswords(false);
-        userManager.setPropFile(USERS_FILE);
+        userManager.setPasswordEncryptor(new ClearTextPasswordEncryptor());
+        userManager.setFile(USERS_FILE);
         userManager.configure();
 
         context.setUserManager(userManager);

Copied: mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/config/spring/DbUserManagerConfigTest.java (from r686637, mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/config/spring/XmlDbUserManagerConfigTest.java)
URL: http://svn.apache.org/viewvc/mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/config/spring/DbUserManagerConfigTest.java?p2=mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/config/spring/DbUserManagerConfigTest.java&p1=mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/config/spring/XmlDbUserManagerConfigTest.java&r1=686637&r2=689495&rev=689495&view=diff
==============================================================================
--- mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/config/spring/XmlDbUserManagerConfigTest.java (original)
+++ mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/config/spring/DbUserManagerConfigTest.java Wed Aug 27 07:58:52 2008
@@ -19,13 +19,10 @@
 
 package org.apache.ftpserver.config.spring;
 
-import junit.framework.TestCase;
-
 import org.apache.ftpserver.FtpServer;
 import org.apache.ftpserver.usermanager.DbUserManager;
+import org.apache.ftpserver.usermanager.SaltedPasswordEncryptor;
 import org.hsqldb.jdbc.jdbcDataSource;
-import org.springframework.beans.factory.xml.XmlBeanFactory;
-import org.springframework.core.io.FileSystemResource;
 
 /**
 *
@@ -33,18 +30,29 @@
 * @version $Rev$, $Date$
 *
 */
-public class XmlDbUserManagerConfigTest extends TestCase {
+public class DbUserManagerConfigTest extends SpringConfigTestTemplate {
 
     public void test() throws Throwable {
-        XmlBeanFactory factory = new XmlBeanFactory(
-                new FileSystemResource(
-                        "src/test/resources/spring-config/config-spring-db-user-manager.xml"));
-
-        FtpServer server = (FtpServer) factory.getBean("server");
+        FtpServer server = createServer("<db-user-manager  encrypt-passwords=\"salted\">"
+                + "<data-source>"
+                + "    <beans:bean class=\"org.hsqldb.jdbc.jdbcDataSource\">"
+                + "        <beans:property name=\"database\" value=\"jdbc:hsqldb:mem:foo\" />"
+                + "        <beans:property name=\"user\" value=\"sa\" />"
+                + "        <beans:property name=\"password\" value=\"\" />"
+                + "    </beans:bean>" + "</data-source>"
+                + "<insert-user>INSERT USER</insert-user>"
+                + "<update-user>UPDATE USER</update-user>"
+                + "<delete-user>DELETE USER</delete-user>"
+                + "<select-user>SELECT USER</select-user>"
+                + "<select-all-users>SELECT ALL USERS</select-all-users>"
+                + "<is-admin>IS ADMIN</is-admin>"
+                + "<authenticate>AUTHENTICATE</authenticate>"
+                + "</db-user-manager>");
 
         DbUserManager um = (DbUserManager) server.getServerContext()
                 .getUserManager();
         assertTrue(um.getDataSource() instanceof jdbcDataSource);
+        assertTrue(um.getPasswordEncryptor() instanceof SaltedPasswordEncryptor);
 
         assertEquals("INSERT USER", um.getSqlUserInsert());
         assertEquals("UPDATE USER", um.getSqlUserUpdate());

Propchange: mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/config/spring/DbUserManagerConfigTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/config/spring/FileUserManagerConfigTest.java
URL: http://svn.apache.org/viewvc/mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/config/spring/FileUserManagerConfigTest.java?rev=689495&view=auto
==============================================================================
--- mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/config/spring/FileUserManagerConfigTest.java (added)
+++ mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/config/spring/FileUserManagerConfigTest.java Wed Aug 27 07:58:52 2008
@@ -0,0 +1,77 @@
+/*
+ * 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.ftpserver.config.spring;
+
+import org.apache.ftpserver.FtpServer;
+import org.apache.ftpserver.usermanager.ClearTextPasswordEncryptor;
+import org.apache.ftpserver.usermanager.Md5PasswordEncryptor;
+import org.apache.ftpserver.usermanager.PropertiesUserManager;
+import org.apache.ftpserver.usermanager.SaltedPasswordEncryptor;
+
+/**
+*
+* @author The Apache MINA Project (dev@mina.apache.org)
+* @version $Rev$, $Date$
+*
+*/
+public class FileUserManagerConfigTest extends SpringConfigTestTemplate {
+
+    private PropertiesUserManager createPropertiesUserManager(String config) {
+        FtpServer server = createServer(config);
+
+        return (PropertiesUserManager) server.getUserManager();
+    }
+
+    public void testFile() throws Throwable {
+        PropertiesUserManager um = createPropertiesUserManager("<file-user-manager file=\"/tmp/foo.users\" />");
+        assertEquals("/tmp/foo.users", um.getFile().getAbsolutePath());
+    }
+
+    public void testMd5PasswordEncryptor() throws Throwable {
+        PropertiesUserManager um = createPropertiesUserManager("<file-user-manager file=\"foo\" encrypt-passwords=\"md5\" />");
+
+        assertTrue(um.getPasswordEncryptor() instanceof Md5PasswordEncryptor);
+    }
+    
+    public void testTruePasswordEncryptor() throws Throwable {
+        PropertiesUserManager um = createPropertiesUserManager("<file-user-manager file=\"foo\" encrypt-passwords=\"true\" />");
+
+        assertTrue(um.getPasswordEncryptor() instanceof Md5PasswordEncryptor);
+    }
+
+    public void testNonePasswordEncryptor() throws Throwable {
+        PropertiesUserManager um = createPropertiesUserManager("<file-user-manager file=\"foo\" encrypt-passwords=\"clear\" />");
+
+        assertTrue(um.getPasswordEncryptor() instanceof ClearTextPasswordEncryptor);
+    }
+
+    public void testSaltedPasswordEncryptor() throws Throwable {
+        PropertiesUserManager um = createPropertiesUserManager("<file-user-manager file=\"foo\" encrypt-passwords=\"salted\" />");
+
+        assertTrue(um.getPasswordEncryptor() instanceof SaltedPasswordEncryptor);
+    }
+    
+    public void testFalsePasswordEncryptor() throws Throwable {
+        PropertiesUserManager um = createPropertiesUserManager("<file-user-manager file=\"foo\" encrypt-passwords=\"false\" />");
+
+        assertTrue(um.getPasswordEncryptor() instanceof ClearTextPasswordEncryptor);
+    }
+
+}

Propchange: mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/config/spring/FileUserManagerConfigTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/config/spring/SpringConfigTestTemplate.java
URL: http://svn.apache.org/viewvc/mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/config/spring/SpringConfigTestTemplate.java?rev=689495&view=auto
==============================================================================
--- mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/config/spring/SpringConfigTestTemplate.java (added)
+++ mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/config/spring/SpringConfigTestTemplate.java Wed Aug 27 07:58:52 2008
@@ -0,0 +1,53 @@
+/*
+ * 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.ftpserver.config.spring;
+
+import junit.framework.TestCase;
+
+import org.apache.ftpserver.FtpServer;
+import org.springframework.beans.factory.xml.XmlBeanFactory;
+import org.springframework.core.io.ByteArrayResource;
+
+/**
+*
+* @author The Apache MINA Project (dev@mina.apache.org)
+* @version $Rev$, $Date$
+*
+*/
+public abstract class SpringConfigTestTemplate extends TestCase {
+
+    protected FtpServer createServer(String config) {
+        String completeConfig = "<server id=\"server\" xmlns=\"http://mina.apache.org/ftpserver/spring/v1\" "
+            + "xmlns:beans=\"http://www.springframework.org/schema/beans\" " 
+            + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
+            + "xsi:schemaLocation=\" "
+            + "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd "
+            + "http://mina.apache.org/ftpserver/spring/v1 http://mina.apache.org/ftpserver/ftpserver-1.0.xsd "
+            + "\">"
+            + config
+            + "</server>";
+
+        XmlBeanFactory factory = new XmlBeanFactory(
+                new ByteArrayResource(completeConfig.getBytes()));
+        
+        return (FtpServer) factory.getBean("server");
+
+    }
+}

Propchange: mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/config/spring/SpringConfigTestTemplate.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/usermanager/ClearTextDbUserManagerTest.java
URL: http://svn.apache.org/viewvc/mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/usermanager/ClearTextDbUserManagerTest.java?rev=689495&view=auto
==============================================================================
--- mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/usermanager/ClearTextDbUserManagerTest.java (added)
+++ mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/usermanager/ClearTextDbUserManagerTest.java Wed Aug 27 07:58:52 2008
@@ -0,0 +1,53 @@
+/*
+ * 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.ftpserver.usermanager;
+
+import java.io.File;
+import java.io.FileReader;
+import java.sql.Connection;
+import java.sql.Statement;
+
+import org.apache.ftpserver.ftplet.FtpException;
+import org.apache.ftpserver.ftplet.UserManager;
+import org.apache.ftpserver.test.TestUtil;
+import org.apache.ftpserver.util.IoUtils;
+import org.hsqldb.jdbc.jdbcDataSource;
+
+/**
+*
+* @author The Apache MINA Project (dev@mina.apache.org)
+* @version $Rev$, $Date$
+*
+*/
+public class ClearTextDbUserManagerTest extends DbUserManagerTest {
+
+    protected File getInitSqlScript() {
+        return new File(TestUtil.getBaseDir(),
+            "src/test/resources/dbusermanagertest-cleartext-hsql.sql");  
+    }
+
+
+    protected UserManager createUserManager() throws FtpException {
+        DbUserManager manager = (DbUserManager) super.createUserManager();
+        manager.setPasswordEncryptor(new ClearTextPasswordEncryptor());
+        return manager;
+
+    }
+}

Propchange: mina/ftpserver/trunk/core/src/test/java/org/apache/ftpserver/usermanager/ClearTextDbUserManagerTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain