pgoldstein    2002/08/17 11:33:28

  Modified:    src/java/org/apache/james/userrepository
                        AbstractJdbcUsersRepository.java
                        AbstractUsersRepository.java DefaultJamesUser.java
                        JamesUsersJdbcRepository.java
                        ListUsersJdbcRepository.java
                        UsersFileRepository.java UsersLDAPRepository.java
  Added:       src/java/org/apache/james/userrepository package.html
  Log:
  Added extensive comments.
  
  Revision  Changes    Path
  1.9       +46 -8     
jakarta-james/src/java/org/apache/james/userrepository/AbstractJdbcUsersRepository.java
  
  Index: AbstractJdbcUsersRepository.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-james/src/java/org/apache/james/userrepository/AbstractJdbcUsersRepository.java,v
  retrieving revision 1.8
  retrieving revision 1.9
  diff -u -r1.8 -r1.9
  --- AbstractJdbcUsersRepository.java  14 Aug 2002 02:11:01 -0000      1.8
  +++ AbstractJdbcUsersRepository.java  17 Aug 2002 18:33:28 -0000      1.9
  @@ -31,7 +31,7 @@
   import java.util.*;
   
   /**
  - * An abstract base class for creating UserRepository implementation
  + * An abstract base class for creating UserRepository implementations
    * which use a database for persistence.
    * 
    * To implement a new UserRepository using by extending this class,
  @@ -56,11 +56,19 @@
   public abstract class AbstractJdbcUsersRepository extends AbstractUsersRepository
       implements Component, Contextualizable, Composable, Configurable, Initializable
   {
  +    /**
  +     * The Avalon context used by the instance
  +     */
       protected Context context;
  +
       protected Map m_sqlParameters;
  +
       private String m_sqlFileName;
  +
       private String m_datasourceName;
  +
       private DataSourceSelector m_datasources;
  +
       private DataSourceComponent m_datasource;
   
       // Fetches all Users from the db.
  @@ -81,13 +89,27 @@
       // The JDBCUtil helper class
       private JDBCUtil theJDBCUtil;
   
  +    /**
  +     * Pass the Context to the component.
  +     * This method is called after the setLogger()
  +     * method and before any other method.
  +     *
  +     * @param context the context
  +     * @throws ContextException if context is invalid
  +     */
       public void contextualize(final Context context)
               throws ContextException {
           this.context = context;
       }
   
       /**
  -     * Compose the repository with the DataSourceSelector component.
  +     * Pass the <code>ComponentManager</code> to the <code>composer</code>.
  +     * The instance uses the specified <code>ComponentManager</code> to 
  +     * acquire the components it needs for execution.
  +     *
  +     * @param componentManager The <code>ComponentManager</code> which this
  +     *                <code>Composable</code> uses.
  +     * @throws ComponentException if an error occurs
        */
       public void compose( final ComponentManager componentManager )
           throws ComponentException
  @@ -202,6 +224,7 @@
        *     and performing paramter substitution,
        * 3) Initialises the database with the required tables, if necessary.
        * 
  +     * @throws Exception if an error occurs
        */
       public void initialize() throws Exception 
       {
  @@ -337,7 +360,7 @@
       }
   
       //
  -    // Superclass methods - overridden in AbstractUsersRepository
  +    // Superclass methods - overridden from AbstractUsersRepository
       //
       /**
        * Returns a list populated with all of the Users in the repository.
  @@ -377,6 +400,8 @@
       /**
        * Adds a user to the underlying Repository.
        * The user name must not clash with an existing user.
  +     *
  +     * @param user the user to be added
        */
       protected void doAddUser(User user) {
           Connection conn = openConnection();
  @@ -403,7 +428,10 @@
   
       /**
        * Removes a user from the underlying repository.
  -     * If the user doesn't exist, returns ok.
  +     * If the user doesn't exist this method doesn't throw
  +     * an exception.
  +     *
  +     * @param user the user to be removed
        */
       protected void doRemoveUser(User user) {
           String username = user.getUserName();
  @@ -428,6 +456,8 @@
   
       /**
        * Updates a user record to match the supplied User.
  +     *
  +     * @param user the updated user record
        */
       protected void doUpdateUser(User user)
       {
  @@ -454,6 +484,11 @@
        * If the specified SQL statement has been defined, this method
        * overrides the basic implementation in AbstractUsersRepository
        * to increase performance.
  +     *
  +     * @param name the name of the user being retrieved
  +     * @param ignoreCase whether the name is regarded as case-insensitive
  +     *
  +     * @return the user being retrieved, null if the user doesn't exist
        */
       protected User getUserByName(String name, boolean ignoreCase)
       {
  @@ -508,7 +543,7 @@
        * 
        * @param rsUsers A ResultSet with a User record in the current row.
        * @return A User instance
  -     * @exception SQLException
  +     * @throws SQLException
        *                   if an exception occurs reading from the ResultSet
        */
       protected abstract User readUserFromResultSet(ResultSet rsUsers)
  @@ -523,7 +558,7 @@
        * @param user       a User instance, which should be an implementation class 
which
        *                   is handled by this Repostory implementation.
        * @param userInsert a PreparedStatement initialised with SQL taken from the 
"insert" SQL definition.
  -     * @exception SQLException
  +     * @throws SQLException
        *                   if an exception occurs while setting parameter values.
        */
       protected abstract void setUserForInsertStatement(User user, 
  @@ -539,7 +574,7 @@
        * @param user       a User instance, which should be an implementation class 
which
        *                   is handled by this Repostory implementation.
        * @param userUpdate a PreparedStatement initialised with SQL taken from the 
"update" SQL definition.
  -     * @exception SQLException
  +     * @throws SQLException
        *                   if an exception occurs while setting parameter values.
        */
       protected abstract void setUserForUpdateStatement(User user, 
  @@ -547,7 +582,10 @@
           throws SQLException;
   
       /**
  -     * Opens a connection, handling exceptions.
  +     * Opens a connection, throwing a runtime exception if a SQLException is
  +     * encountered in the process.
  +     *
  +     * @return the new connection
        */
       private Connection openConnection()
       {
  
  
  
  1.7       +40 -1     
jakarta-james/src/java/org/apache/james/userrepository/AbstractUsersRepository.java
  
  Index: AbstractUsersRepository.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-james/src/java/org/apache/james/userrepository/AbstractUsersRepository.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- AbstractUsersRepository.java      7 Aug 2002 23:24:27 -0000       1.6
  +++ AbstractUsersRepository.java      17 Aug 2002 18:33:28 -0000      1.7
  @@ -79,6 +79,11 @@
        * Gets a user by name, ignoring case if specified.
        * This implementation gets the entire set of users,
        * and scrolls through searching for one matching <code>name</code>.
  +     *
  +     * @param name the name of the user being retrieved
  +     * @param ignoreCase whether the name is regarded as case-insensitive
  +     *
  +     * @return the user being retrieved, null if the user doesn't exist
        */
       protected User getUserByName(String name, boolean ignoreCase)
       {
  @@ -104,6 +109,8 @@
        * Adds a user to the repository with the specified User object.
        * Users names must be unique-case-insensitive in the repository.
        *
  +     * @param user the user to be added
  +     *
        * @return true if succesful, false otherwise
        * @since James 1.2.2
        */
  @@ -122,6 +129,9 @@
       /**
        * Adds a user to the repository with the specified attributes.  In current
        * implementations, the Object attributes is generally a String password.
  +     *
  +     * @param name the name of the user to be added
  +     * @param attributes the password value as a String
        */
       public void addUser(String name, Object attributes) 
       {
  @@ -142,6 +152,8 @@
        * Update the repository with the specified user object. A user object
        * with this username must already exist.
        *
  +     * @param user the user to be updated
  +     *
        * @return true if successful.
        */
       public boolean updateUser(User user)
  @@ -158,6 +170,8 @@
   
       /**
        * Removes a user from the repository
  +     *
  +     * @param user the user to be removed
        */
       public void removeUser(String name)
       {
  @@ -181,6 +195,10 @@
        * Get the user object with the specified user name.  Return null if no
        * such user.
        *
  +     * @param name the name of the user to retrieve
  +     *
  +     * @return the user if found, null otherwise
  +     *
        * @since James 1.2.2
        */
       public User getUserByName(String name)
  @@ -192,6 +210,10 @@
        * Get the user object with the specified user name. Match user naems on
        * a case insensitive basis.  Return null if no such user.
        *
  +     * @param name the name of the user to retrieve
  +     *
  +     * @return the user if found, null otherwise
  +     *
        * @since James 1.2.2
        */
       public User getUserByNameCaseInsensitive(String name)
  @@ -202,6 +224,10 @@
       /**
        * Returns the user name of the user matching name on an equalsIgnoreCase
        * basis. Returns null if no match.
  +     *
  +     * @param name the name of the user to retrieve
  +     *
  +     * @return the correct case sensitive name of the user
        */
       public String getRealName(String name)
       {
  @@ -239,16 +265,27 @@
        * this typically means "check the password" where a String password is passed
        * as the Object attributes.
        *
  +     * @param name the name of the user to be tested
  +     * @param attributes the password to be tested
  +     *
  +     * @throws UnsupportedOperationException always, as this method should not be 
used
  +     *
        * @deprecated As of James 1.2.2, use {@link #test(String, String) test(String 
name, String password)}
        */
       public boolean test(String name, Object attributes)
       {
  -        throw new RuntimeException("Improper use of deprecated method - read 
javadocs");
  +        throw new UnsupportedOperationException("Improper use of deprecated method 
- read javadocs");
       }
   
       /**
        * Test if user with name 'name' has password 'password'.
        *
  +     * @param name the name of the user to be tested
  +     * @param password the password to be tested
  +     *
  +     * @return true if the test is successful, false if the
  +     *              password is incorrect or the user doesn't
  +     *              exist
        * @since James 1.2.2
        */
       public boolean test(String name, String password)
  @@ -264,6 +301,8 @@
   
       /**
        * Returns a count of the users in the repository.
  +     *
  +     * @return the number of users in the repository
        */
       public int countUsers()
       {
  
  
  
  1.3       +11 -7     
jakarta-james/src/java/org/apache/james/userrepository/DefaultJamesUser.java
  
  Index: DefaultJamesUser.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-james/src/java/org/apache/james/userrepository/DefaultJamesUser.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- DefaultJamesUser.java     18 Jan 2002 02:48:39 -0000      1.2
  +++ DefaultJamesUser.java     17 Aug 2002 18:33:28 -0000      1.3
  @@ -39,13 +39,17 @@
   
   
       /**
  -     * Call initialize when creating a new instance.
  +     * Initialize the component. Initialization includes
  +     * allocating any resources required throughout the
  +     * components lifecycle.
  +     *
  +     * @throws Exception if an error occurs
        */
       public void initialize() {
  -     forwarding = false;
  -     forwardingDestination = null;
  -     aliasing = false;
  -     alias = "";
  +        forwarding = false;
  +        forwardingDestination = null;
  +        aliasing = false;
  +        alias = "";
       }
   
       public void setForwarding(boolean forward) {
  
  
  
  1.4       +3 -6      
jakarta-james/src/java/org/apache/james/userrepository/JamesUsersJdbcRepository.java
  
  Index: JamesUsersJdbcRepository.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-james/src/java/org/apache/james/userrepository/JamesUsersJdbcRepository.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- JamesUsersJdbcRepository.java     7 Aug 2002 23:24:27 -0000       1.3
  +++ JamesUsersJdbcRepository.java     17 Aug 2002 18:33:28 -0000      1.4
  @@ -62,21 +62,18 @@
   
       protected void setUserForInsertStatement(User user, 
                                                PreparedStatement userInsert) 
  -        throws SQLException 
  -    {
  +        throws SQLException {
           setUserForStatement(user, userInsert, false);
       }
   
       protected void setUserForUpdateStatement(User user, 
                                                PreparedStatement userUpdate) 
  -        throws SQLException 
  -    {
  +        throws SQLException {
           setUserForStatement(user, userUpdate, true);
       }
   
       private void setUserForStatement(User user, PreparedStatement stmt,
  -                                     boolean userNameLast) throws SQLException
  -    {
  +                                     boolean userNameLast) throws SQLException {
           // Determine column offsets to use, based on username column pos.
           int nameIndex = 1;
           int colOffset = 1;
  
  
  
  1.3       +4 -6      
jakarta-james/src/java/org/apache/james/userrepository/ListUsersJdbcRepository.java
  
  Index: ListUsersJdbcRepository.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-james/src/java/org/apache/james/userrepository/ListUsersJdbcRepository.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- ListUsersJdbcRepository.java      18 Jan 2002 02:48:39 -0000      1.2
  +++ ListUsersJdbcRepository.java      17 Aug 2002 18:33:28 -0000      1.3
  @@ -22,24 +22,22 @@
    */
   public class ListUsersJdbcRepository extends AbstractJdbcUsersRepository
   {
  -    protected User readUserFromResultSet(ResultSet rsUsers) throws SQLException 
  -    {
  +    protected User readUserFromResultSet(ResultSet rsUsers) throws SQLException {
           // Get the username, and build a DefaultUser with it.
           String username = rsUsers.getString(1);
           DefaultUser user = new DefaultUser(username, "SHA");
           return user;
       }
  +
       protected void setUserForInsertStatement(User user, 
                                                PreparedStatement userInsert) 
  -        throws SQLException 
  -    {
  +        throws SQLException {
           userInsert.setString(1, user.getUserName());
       }
   
       protected void setUserForUpdateStatement(User user, 
                                                PreparedStatement userUpdate) 
  -        throws SQLException 
  -    {
  +        throws SQLException {
           throw new UnsupportedOperationException("Can't update a List User - " +
                                                   "only has a single attribute.");
       }
  
  
  
  1.7       +78 -57    
jakarta-james/src/java/org/apache/james/userrepository/UsersFileRepository.java
  
  Index: UsersFileRepository.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-james/src/java/org/apache/james/userrepository/UsersFileRepository.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- UsersFileRepository.java  7 Aug 2002 23:24:27 -0000       1.6
  +++ UsersFileRepository.java  17 Aug 2002 18:33:28 -0000      1.7
  @@ -54,6 +54,12 @@
       private ObjectRepository or;
       private String destination;
   
  +    /**
  +     * Pass the <code>Configuration</code> to the instance.
  +     *
  +     * @param configuration the class configurations.
  +     * @throws ConfigurationException if an error occurs
  +     */
       public void configure( final Configuration configuration )
           throws ConfigurationException {
   
  @@ -64,13 +70,21 @@
           }
       }
   
  +    /**
  +     * Pass the <code>ComponentManager</code> to the <code>composer</code>.
  +     * The instance uses the specified <code>ComponentManager</code> to 
  +     * acquire the components it needs for execution.
  +     *
  +     * @param componentManager The <code>ComponentManager</code> which this
  +     *                <code>Composable</code> uses.
  +     * @throws ComponentException if an error occurs
  +     */
       public void compose( final ComponentManager componentManager )
           throws ComponentException {
   
  -     try {
  +        try {
               store = (Store)componentManager.
                   lookup( "org.apache.avalon.cornerstone.services.store.Store" );
  -
           } catch (Exception e) {
               final String message = "Failed to retrieve Store component:" + 
e.getMessage();
               getLogger().error( message, e );
  @@ -78,6 +92,13 @@
           }
       }
   
  +    /**
  +     * Initialize the component. Initialization includes
  +     * allocating any resources required throughout the
  +     * components lifecycle.
  +     *
  +     * @throws Exception if an error occurs
  +     */
       public void initialize()
           throws Exception {
   
  @@ -113,80 +134,80 @@
       }
   
       public synchronized boolean addUser(User user) {
  -     String username = user.getUserName();
  -     if (contains(username)) {
  -         return false;
  -     }
  +        String username = user.getUserName();
  +        if (contains(username)) {
  +            return false;
  +        }
           try {
               or.put(username, user);
           } catch (Exception e) {
               throw new RuntimeException("Exception caught while storing user: " + e 
);
           }
  -     return true;
  +        return true;
       }
   
       public synchronized void addUser(String name, Object attributes) {
  -     if (attributes instanceof String)
  -        {
  -         User newbie = new DefaultUser(name, "SHA");
  +        if (attributes instanceof String) {
  +            User newbie = new DefaultUser(name, "SHA");
               newbie.setPassword( (String) attributes);
  -         addUser(newbie);
  -     }
  -        else
  -        {
  +            addUser(newbie);
  +        }
  +        else {
               throw new RuntimeException("Improper use of deprecated method" 
                                          + " - use addUser(User user)");
           }
       }
   
       public synchronized User getUserByName(String name) {
  -     if (contains(name)) {
  +        if (contains(name)) {
               try {
                   return (User)or.get(name);
               } catch (Exception e) {
                   throw new RuntimeException("Exception while retrieving user: "
                                              + e.getMessage());
               }
  -     } else {
  -         return null;
  -     }
  +        } else {
  +            return null;
  +        }
       }
   
       public User getUserByNameCaseInsensitive(String name) {
  -     String realName = getRealName(name);
  -     if (realName == null ) {
  -          throw new RuntimeException("No such user");
  -     }
  -     return getUserByName(realName);
  +        String realName = getRealName(name);
  +        // TODO: This clause is in violation of the contract for the
  +        //       interface - class should return false if the user
  +        //       doesn't exist
  +        if (realName == null ) {
  +            throw new RuntimeException("No such user");
  +        }
  +        return getUserByName(realName);
       }
   
       public String getRealName(String name) {
           Iterator it = list();
  -     while (it.hasNext()) {
  -         String temp = (String) it.next();
  -         if (name.equalsIgnoreCase(temp)) {
  -             return temp;
  -         }
  -     }
  -     return null;
  -    }
  -    public Object getAttributes(String name) {
  -       
  -        throw new RuntimeException("Improper use of deprecated method - read 
javadocs");
  -        
  +        while (it.hasNext()) {
  +            String temp = (String) it.next();
  +            if (name.equalsIgnoreCase(temp)) {
  +                return temp;
  +            }
  +        }
  +        return null;
  +    }
  +
  +    public Object getAttributes(String name) {       
  +        throw new UnsupportedOperationException("Improper use of deprecated method 
- read javadocs");
       }
   
       public boolean updateUser(User user) {
  -     String username = user.getUserName();
  -     if (!contains(username)) {
  -         return false;
  -     }
  +    String username = user.getUserName();
  +    if (!contains(username)) {
  +        return false;
  +    }
           try {
               or.put(username, user);
           } catch (Exception e) {
               throw new RuntimeException("Exception caught while storing user: " + e 
);
           }
  -     return true;
  +        return true;
       }
   
       public synchronized void removeUser(String name) {
  @@ -194,17 +215,17 @@
       }
   
       public boolean contains(String name) {
  -     return or.containsKey(name);
  +        return or.containsKey(name);
       }
   
       public boolean containsCaseInsensitive(String name) {
  -     Iterator it = list();
  -     while (it.hasNext()) {
  -         if (name.equalsIgnoreCase((String)it.next())) {
  -             return true;
  -         }
  -     }
  -     return false;
  +        Iterator it = list();
  +        while (it.hasNext()) {
  +            if (name.equalsIgnoreCase((String)it.next())) {
  +                return true;
  +            }
  +        }
  +        return false;
       }
   
       public boolean test(String name, Object attributes) {
  @@ -216,17 +237,17 @@
       }
   
       public boolean test(String name, String password) {
  -     User user;
  -     try {
  -         if (contains(name)) {
  -             user = (User) or.get(name);
  -         } else {
  +        User user;
  +        try {
  +            if (contains(name)) {
  +                user = (User) or.get(name);
  +            } else {
                  return false;
  -         }
  +            }
           } catch (Exception e) {
               throw new RuntimeException("Exception retrieving User" + e);
           }
  -     return user.verifyPassword(password);
  +        return user.verifyPassword(password);
       }
   
       public int countUsers() {
  
  
  
  1.8       +47 -16    
jakarta-james/src/java/org/apache/james/userrepository/UsersLDAPRepository.java
  
  Index: UsersLDAPRepository.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-james/src/java/org/apache/james/userrepository/UsersLDAPRepository.java,v
  retrieving revision 1.7
  retrieving revision 1.8
  diff -u -r1.7 -r1.8
  --- UsersLDAPRepository.java  9 Aug 2002 06:09:51 -0000       1.7
  +++ UsersLDAPRepository.java  17 Aug 2002 18:33:28 -0000      1.8
  @@ -71,6 +71,40 @@
           logger = a_Logger;
       }
   
  +    /**
  +     * Pass the Context to the component.
  +     * This method is called after the setLogger()
  +     * method and before any other method.
  +     *
  +     * @param context the context
  +     * @throws ContextException if context is invalid
  +     */
  +    public void contextualize(Context context)
  +        throws ContextException {
  +        Collection serverNames
  +            = (Collection)context.get(Constants.SERVER_NAMES);
  +        usersDomain = (String)serverNames.iterator().next();
  +    }
  +
  +    /**
  +     * Pass the <code>ComponentManager</code> to the <code>composer</code>.
  +     * The instance uses the specified <code>ComponentManager</code> to 
  +     * acquire the components it needs for execution.
  +     *
  +     * @param componentManager The <code>ComponentManager</code> which this
  +     *                <code>Composable</code> uses.
  +     * @throws ComponentException if an error occurs
  +     */
  +    public void compose(ComponentManager compMgr) {
  +        this.comp = comp;
  +    }
  +
  +    /**
  +     * Pass the <code>Configuration</code> to the instance.
  +     *
  +     * @param configuration the class configurations.
  +     * @throws ConfigurationException if an error occurs
  +     */
       public void configure(Configuration conf)
           throws ConfigurationException {
   
  @@ -92,17 +126,6 @@
           passwordAttr = conf.getChild("PasswordAttribute").getValue();
       }
   
  -    public void compose(ComponentManager compMgr) {
  -        this.comp = comp;
  -    }
  -
  -    public void contextualize(Context context)
  -        throws ContextException {
  -        Collection serverNames
  -            = (Collection)context.get(Constants.SERVER_NAMES);
  -        usersDomain = (String)serverNames.iterator().next();
  -    }
  -
       public void setServerRoot() {
           StringBuffer serverRootBuffer =
               new StringBuffer(128)
  @@ -116,6 +139,13 @@
           baseNodeDN = base;
       }
   
  +    /**
  +     * Initialize the component. Initialization includes
  +     * allocating any resources required throughout the
  +     * components lifecycle.
  +     *
  +     * @throws Exception if an error occurs
  +     */
       public void initialize() throws Exception {
           //setServerRoot();
           StringBuffer urlBuffer =
  @@ -245,6 +275,9 @@
           return contains(name);
       }
   
  +    // TODO: This is in violation of the contract for the interface.
  +    //       Should only return null if the user doesn't exist.  Otherwise
  +    //       this should return a consistent string representation of the name
       public String getRealName(String name) {
           return null;
       }
  @@ -309,9 +342,6 @@
                           .append(baseNodeDN)
                           .append(e);
               logger.error(exceptionBuffer.toString());
  -            //System.out.println("Problem adding user " + userName + " to: " + 
baseNodeDN);
  -            //System.out.println(e.getMessage());
  -            //e.printStackTrace();
           }
   
           // Add attributes to user objects, if necessary
  @@ -694,8 +724,9 @@
       }
   
       /**
  -     * Disposes of all open directory contexts.
  -     * Based on signature from interface Disposable in new Avalon
  +     * Disposes of all open directory contexts
  +     *
  +     * @throws Exception if an error is encountered during shutdown
        */
       public void dispose() throws Exception {
           closeDirContext(ctx);
  
  
  
  1.1                  
jakarta-james/src/java/org/apache/james/userrepository/package.html
  
  Index: package.html
  ===================================================================
  <body>
  <p>Implementations of user repositories for use in James.</p>
  </body>
  
  
  

--
To unsubscribe, e-mail:   <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>

Reply via email to