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 2011/01/16 22:24:45 UTC

svn commit: r1059682 - in /mina/vysper/trunk/server/core/src: main/java/org/apache/vysper/xmpp/modules/extension/xep007_inbandreg/ main/java/org/apache/vysper/xmpp/protocol/ main/java/org/apache/vysper/xmpp/protocol/worker/ main/java/org/apache/vysper/...

Author: ngn
Date: Sun Jan 16 21:24:44 2011
New Revision: 1059682

URL: http://svn.apache.org/viewvc?rev=1059682&view=rev
Log:
Add support for in-band registration (XEP-0077). Supports adding account and changing password (VYSPER-264)

Added:
    mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/modules/extension/xep007_inbandreg/
    mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/modules/extension/xep007_inbandreg/InBandRegistrationHandler.java
    mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/modules/extension/xep007_inbandreg/InBandRegistrationModule.java
    mina/vysper/trunk/server/core/src/test/java/org/apache/vysper/xmpp/modules/extension/xep007_inbandreg/
    mina/vysper/trunk/server/core/src/test/java/org/apache/vysper/xmpp/modules/extension/xep007_inbandreg/InBandRegistrationHandlerTestCase.java
Modified:
    mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/protocol/NamespaceURIs.java
    mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/protocol/ProtocolWorker.java
    mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/protocol/worker/EncryptedProtocolWorker.java
    mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/protocol/worker/StartedProtocolWorker.java
    mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/server/ServerMain.java
    mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/server/response/ServerErrorResponses.java

Added: mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/modules/extension/xep007_inbandreg/InBandRegistrationHandler.java
URL: http://svn.apache.org/viewvc/mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/modules/extension/xep007_inbandreg/InBandRegistrationHandler.java?rev=1059682&view=auto
==============================================================================
--- mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/modules/extension/xep007_inbandreg/InBandRegistrationHandler.java (added)
+++ mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/modules/extension/xep007_inbandreg/InBandRegistrationHandler.java Sun Jan 16 21:24:44 2011
@@ -0,0 +1,177 @@
+/*
+ *  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.vysper.xmpp.modules.extension.xep007_inbandreg;
+
+import org.apache.vysper.compliance.SpecCompliant;
+import org.apache.vysper.xml.fragment.XMLElement;
+import org.apache.vysper.xml.fragment.XMLSemanticError;
+import org.apache.vysper.xmpp.addressing.Entity;
+import org.apache.vysper.xmpp.addressing.EntityFormatException;
+import org.apache.vysper.xmpp.addressing.EntityImpl;
+import org.apache.vysper.xmpp.authorization.AccountCreationException;
+import org.apache.vysper.xmpp.authorization.AccountManagement;
+import org.apache.vysper.xmpp.modules.core.base.handler.DefaultIQHandler;
+import org.apache.vysper.xmpp.protocol.NamespaceURIs;
+import org.apache.vysper.xmpp.server.ServerRuntimeContext;
+import org.apache.vysper.xmpp.server.SessionContext;
+import org.apache.vysper.xmpp.server.SessionState;
+import org.apache.vysper.xmpp.server.response.ServerErrorResponses;
+import org.apache.vysper.xmpp.stanza.IQStanza;
+import org.apache.vysper.xmpp.stanza.IQStanzaType;
+import org.apache.vysper.xmpp.stanza.Stanza;
+import org.apache.vysper.xmpp.stanza.StanzaBuilder;
+import org.apache.vysper.xmpp.stanza.StanzaErrorCondition;
+import org.apache.vysper.xmpp.stanza.StanzaErrorType;
+
+/**
+ * Implementation of <a href="http://xmpp.org/extensions/xep-0077.html">XEP-0077 In-Band Registration</a>.
+ *  
+ * @author The Apache MINA Project (dev@mina.apache.org)
+ */
+@SpecCompliant(spec = "xep-0077", status = SpecCompliant.ComplianceStatus.IN_PROGRESS, coverage = SpecCompliant.ComplianceCoverage.PARTIAL)
+public class InBandRegistrationHandler extends DefaultIQHandler {
+
+    public InBandRegistrationHandler() {
+    }
+
+    @Override
+    protected boolean verifyNamespace(Stanza stanza) {
+        return verifyInnerNamespace(stanza, NamespaceURIs.JABBER_IQ_REGISTER);
+    }
+
+    @Override
+    public boolean isSessionRequired() {
+        return false;
+    }
+
+    @Override
+    protected Stanza handleGet(IQStanza stanza, ServerRuntimeContext serverRuntimeContext, SessionContext sessionContext) {
+        //        <iq type='result' id='reg1'>
+        //        <query xmlns='jabber:iq:register'>
+        //          <instructions>
+        //            Choose a username and password for use with this service.
+        //            Please also provide your email address.
+        //          </instructions>
+        //          <username/>
+        //          <password/>
+        //          <email/>
+        //        </query>
+        //      </iq>
+
+        if(sessionContext.getState().equals(SessionState.STARTED)
+                || sessionContext.getState().equals(SessionState.ENCRYPTED) 
+                || sessionContext.getState().equals(SessionState.AUTHENTICATED)) {
+            StanzaBuilder stanzaBuilder = StanzaBuilder.createIQStanza(stanza.getTo(), stanza.getFrom(),
+                    IQStanzaType.RESULT, stanza.getID());
+            stanzaBuilder.startInnerElement("query", NamespaceURIs.JABBER_IQ_REGISTER)
+                .startInnerElement("instructions", NamespaceURIs.JABBER_IQ_REGISTER)
+                .addText("Choose a username and password for use with this service.")
+                .endInnerElement();
+                if(sessionContext.getState().equals(SessionState.AUTHENTICATED)) {
+                    stanzaBuilder.startInnerElement("registered", NamespaceURIs.JABBER_IQ_REGISTER).endInnerElement()
+                        .startInnerElement("username", NamespaceURIs.JABBER_IQ_REGISTER)
+                        .addText(sessionContext.getInitiatingEntity().getNode())
+                        .endInnerElement();
+                } else {
+                    stanzaBuilder.startInnerElement("username", NamespaceURIs.JABBER_IQ_REGISTER).endInnerElement()
+                        .startInnerElement("password", NamespaceURIs.JABBER_IQ_REGISTER).endInnerElement();
+                }
+                return stanzaBuilder.build();
+        } else {
+            return ServerErrorResponses.getStanzaError(StanzaErrorCondition.SERVICE_UNAVAILABLE, stanza, StanzaErrorType.CANCEL, null, null, null);
+        }
+    }
+    
+    @Override
+    protected Stanza handleSet(IQStanza stanza, ServerRuntimeContext serverRuntimeContext, SessionContext sessionContext) {
+        //        <iq type='set' id='reg2'>
+        //        <query xmlns='jabber:iq:register'>
+        //          <username>bill</username>
+        //          <password>Calliope</password>
+        //          <email>bard@shakespeare.lit</email>
+        //        </query>
+        //      </iq>
+        
+        if(sessionContext.getState().equals(SessionState.STARTED)
+                || sessionContext.getState().equals(SessionState.ENCRYPTED)
+                || sessionContext.getState().equals(SessionState.AUTHENTICATED)) {
+
+            try {
+                XMLElement query = stanza.getSingleInnerElementsNamed("query", NamespaceURIs.JABBER_IQ_REGISTER);
+                XMLElement usernameElm = query.getSingleInnerElementsNamed("username", NamespaceURIs.JABBER_IQ_REGISTER);
+                if(usernameElm == null || usernameElm.getInnerText() == null) throw new XMLSemanticError("Invalid or missing username");
+                String username = usernameElm.getInnerText().getText();
+                
+                XMLElement passwordElm = query.getSingleInnerElementsNamed("password", NamespaceURIs.JABBER_IQ_REGISTER);
+                if(passwordElm == null ||  passwordElm.getInnerText() == null) throw new XMLSemanticError("Invalid or missing password");
+                String password = passwordElm.getInnerText().getText();
+                if(password.trim().length() == 0) throw new XMLSemanticError("Invalid password");
+    
+                AccountManagement accountManagement = (AccountManagement) serverRuntimeContext.getStorageProvider(AccountManagement.class);
+                Entity user;
+                if(username.contains("@")) {
+                    user = EntityImpl.parse(username);
+                    if(!serverRuntimeContext.getServerEnitity().getDomain().equals(user.getDomain())) {
+                        throw new XMLSemanticError("Username must be in the same domain as the server");
+                    }
+                } else {
+                    user = EntityImpl.parse(username + "@" + serverRuntimeContext.getServerEnitity());
+                }
+                
+                if(sessionContext.getState().equals(SessionState.AUTHENTICATED)) {
+                    if(accountManagement.verifyAccountExists(user)) {
+                        // account exists
+                        accountManagement.changePassword(user, password);
+                    } else {
+                        throw new AccountCreationException("Account does not exist");
+                    }
+                } else {
+                    if(accountManagement.verifyAccountExists(user)) {
+                        // account exists
+                        throw new AccountCreationException("Account already exists");
+                    } else {
+                        accountManagement.addUser(user, password);
+                    }
+                }
+                return StanzaBuilder.createDirectReply(stanza, true, IQStanzaType.RESULT).build();
+                
+            } catch (XMLSemanticError e) {
+                //        <iq type='error' id='reg2'>
+                //            <query xmlns='jabber:iq:register'>
+                //              <username>bill</username>
+                //              <password>Calliope</password>
+                //            </query>
+                //            <error code='406' type='modify'>
+                //              <not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
+                //            </error>
+                //          </iq>
+                return ServerErrorResponses.getStanzaError(StanzaErrorCondition.NOT_ACCEPTABLE, stanza, StanzaErrorType.MODIFY, 406, null, null, null);
+            } catch (EntityFormatException e) {
+                return ServerErrorResponses.getStanzaError(StanzaErrorCondition.NOT_ACCEPTABLE, stanza, StanzaErrorType.MODIFY, 406, null, null, null);
+            } catch (AccountCreationException e) {
+                return ServerErrorResponses.getStanzaError(StanzaErrorCondition.CONFLICT, stanza, StanzaErrorType.CANCEL, 409, e.getMessage(), null, null);
+            }
+        } else {
+            return ServerErrorResponses.getStanzaError(StanzaErrorCondition.SERVICE_UNAVAILABLE, stanza, StanzaErrorType.CANCEL, null, null, null);
+        }
+    }
+  
+    
+}

Added: mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/modules/extension/xep007_inbandreg/InBandRegistrationModule.java
URL: http://svn.apache.org/viewvc/mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/modules/extension/xep007_inbandreg/InBandRegistrationModule.java?rev=1059682&view=auto
==============================================================================
--- mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/modules/extension/xep007_inbandreg/InBandRegistrationModule.java (added)
+++ mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/modules/extension/xep007_inbandreg/InBandRegistrationModule.java Sun Jan 16 21:24:44 2011
@@ -0,0 +1,51 @@
+/*
+ *  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.vysper.xmpp.modules.extension.xep007_inbandreg;
+
+import java.util.List;
+
+import org.apache.vysper.xmpp.modules.DefaultModule;
+import org.apache.vysper.xmpp.protocol.DefaultHandlerDictionary;
+import org.apache.vysper.xmpp.protocol.HandlerDictionary;
+
+/**
+ * A module for <a href="http://xmpp.org/extensions/xep-0077.html">XEP-0077 In-Band Registration</a>.
+ *
+ * @author The Apache MINA Project (dev@mina.apache.org)
+ */
+public class InBandRegistrationModule extends DefaultModule {
+
+    private InBandRegistrationHandler handler = new InBandRegistrationHandler();
+    
+    @Override
+    public String getName() {
+        return "XEP-0077 In-Band Registration";
+    }
+
+    @Override
+    public String getVersion() {
+        return "2.3";
+    }
+
+    @Override
+    protected void addHandlerDictionaries(List<HandlerDictionary> dictionary) {
+        dictionary.add(new DefaultHandlerDictionary(handler));
+    }
+}

Modified: mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/protocol/NamespaceURIs.java
URL: http://svn.apache.org/viewvc/mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/protocol/NamespaceURIs.java?rev=1059682&r1=1059681&r2=1059682&view=diff
==============================================================================
--- mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/protocol/NamespaceURIs.java (original)
+++ mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/protocol/NamespaceURIs.java Sun Jan 16 21:24:44 2011
@@ -48,6 +48,8 @@ public class NamespaceURIs {
 
     public static final String JABBER_SERVER_DIALBACK = "jabber:server:dialback";
 
+    public static final String JABBER_IQ_REGISTER = "jabber:iq:register";
+
     public static final String JABBER_IQ_ROSTER = "jabber:iq:roster";
 
     // compatibility namespaces

Modified: mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/protocol/ProtocolWorker.java
URL: http://svn.apache.org/viewvc/mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/protocol/ProtocolWorker.java?rev=1059682&r1=1059681&r2=1059682&view=diff
==============================================================================
--- mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/protocol/ProtocolWorker.java (original)
+++ mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/protocol/ProtocolWorker.java Sun Jan 16 21:24:44 2011
@@ -24,6 +24,7 @@ import java.util.List;
 import java.util.Map;
 
 import org.apache.vysper.xmpp.addressing.Entity;
+import org.apache.vysper.xmpp.modules.extension.xep007_inbandreg.InBandRegistrationHandler;
 import org.apache.vysper.xmpp.protocol.exception.TLSException;
 import org.apache.vysper.xmpp.protocol.worker.AuthenticatedProtocolWorker;
 import org.apache.vysper.xmpp.protocol.worker.EncryptedProtocolWorker;
@@ -105,7 +106,8 @@ public class ProtocolWorker implements S
         // check as of RFC3920/4.3:
         if (sessionStateHolder.getState() != SessionState.AUTHENTICATED) {
             // is not authenticated...
-            if (XMPPCoreStanza.getWrapper(stanza) != null) {
+            if (XMPPCoreStanza.getWrapper(stanza) != null
+                    && !(stanzaHandler instanceof InBandRegistrationHandler)) {
                 // ... and is a IQ/PRESENCE/MESSAGE stanza!
                 responseWriter.handleNotAuthorized(sessionContext, stanza);
                 return;

Modified: mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/protocol/worker/EncryptedProtocolWorker.java
URL: http://svn.apache.org/viewvc/mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/protocol/worker/EncryptedProtocolWorker.java?rev=1059682&r1=1059681&r2=1059682&view=diff
==============================================================================
--- mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/protocol/worker/EncryptedProtocolWorker.java (original)
+++ mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/protocol/worker/EncryptedProtocolWorker.java Sun Jan 16 21:24:44 2011
@@ -22,6 +22,7 @@ package org.apache.vysper.xmpp.protocol.
 import org.apache.vysper.xmpp.modules.core.base.handler.StreamStartHandler;
 import org.apache.vysper.xmpp.modules.core.base.handler.XMLPrologHandler;
 import org.apache.vysper.xmpp.modules.core.sasl.handler.AbstractSASLHandler;
+import org.apache.vysper.xmpp.modules.extension.xep007_inbandreg.InBandRegistrationHandler;
 import org.apache.vysper.xmpp.modules.extension.xep0220_server_dailback.DbResultHandler;
 import org.apache.vysper.xmpp.modules.extension.xep0220_server_dailback.DbVerifyHandler;
 import org.apache.vysper.xmpp.protocol.ResponseWriter;
@@ -45,17 +46,20 @@ public class EncryptedProtocolWorker ext
     @Override
     protected boolean checkState(SessionContext sessionContext, SessionStateHolder sessionStateHolder, Stanza stanza,
             StanzaHandler stanzaHandler) {
-        
-        if (stanzaHandler instanceof StreamStartHandler)
+
+        if (stanzaHandler instanceof StreamStartHandler) {
             return true;
-        if (stanzaHandler instanceof AbstractSASLHandler)
+        } else if (stanzaHandler instanceof AbstractSASLHandler) {
             return true;
-        if (stanzaHandler instanceof XMLPrologHandler)
+        } else if (stanzaHandler instanceof XMLPrologHandler) {
             return true; // PSI client sends that. 
-        if (sessionContext.isServerToServer() && stanzaHandler instanceof DbResultHandler)
+        } else if (stanzaHandler instanceof InBandRegistrationHandler) {
+            return true;
+        } else if (sessionContext.isServerToServer() && stanzaHandler instanceof DbResultHandler) {
             return true;
-        if (sessionContext.isServerToServer() && stanzaHandler instanceof DbVerifyHandler)
+        } else if (sessionContext.isServerToServer() && stanzaHandler instanceof DbVerifyHandler) {
             return true;
+        }
         ResponseWriter.writeUnsupportedStanzaError(sessionContext);
         return false;
     }

Modified: mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/protocol/worker/StartedProtocolWorker.java
URL: http://svn.apache.org/viewvc/mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/protocol/worker/StartedProtocolWorker.java?rev=1059682&r1=1059681&r2=1059682&view=diff
==============================================================================
--- mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/protocol/worker/StartedProtocolWorker.java (original)
+++ mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/protocol/worker/StartedProtocolWorker.java Sun Jan 16 21:24:44 2011
@@ -20,6 +20,7 @@
 package org.apache.vysper.xmpp.protocol.worker;
 
 import org.apache.vysper.xmpp.modules.core.starttls.handler.StartTLSHandler;
+import org.apache.vysper.xmpp.modules.extension.xep007_inbandreg.InBandRegistrationHandler;
 import org.apache.vysper.xmpp.modules.extension.xep0220_server_dailback.DbResultHandler;
 import org.apache.vysper.xmpp.modules.extension.xep0220_server_dailback.DbVerifyHandler;
 import org.apache.vysper.xmpp.protocol.ResponseWriter;
@@ -44,12 +45,15 @@ public class StartedProtocolWorker exten
     protected boolean checkState(SessionContext sessionContext, SessionStateHolder sessionStateHolder, Stanza stanza,
             StanzaHandler stanzaHandler) {
 
-        if (stanzaHandler instanceof StartTLSHandler)
+        if (stanzaHandler instanceof StartTLSHandler) {
             return true;
-        if (sessionContext.isServerToServer() && stanzaHandler instanceof DbVerifyHandler)
+        } else if (stanzaHandler instanceof InBandRegistrationHandler) {
             return true;
-        if (sessionContext.isServerToServer() && stanzaHandler instanceof DbResultHandler)
+        } else if (sessionContext.isServerToServer() && stanzaHandler instanceof DbVerifyHandler) {
             return true;
+        } else if (sessionContext.isServerToServer() && stanzaHandler instanceof DbResultHandler) {
+            return true;
+        }
         ResponseWriter.writeUnsupportedStanzaError(sessionContext);
         return false;
     }

Modified: mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/server/ServerMain.java
URL: http://svn.apache.org/viewvc/mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/server/ServerMain.java?rev=1059682&r1=1059681&r2=1059682&view=diff
==============================================================================
--- mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/server/ServerMain.java (original)
+++ mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/server/ServerMain.java Sun Jan 16 21:24:44 2011
@@ -19,12 +19,16 @@
  */
 package org.apache.vysper.xmpp.server;
 
-import org.apache.commons.lang.RandomStringUtils;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
 import org.apache.vysper.mina.TCPEndpoint;
 import org.apache.vysper.storage.StorageProviderRegistry;
 import org.apache.vysper.storage.inmemory.MemoryStorageProviderRegistry;
 import org.apache.vysper.xmpp.addressing.Entity;
-import org.apache.vysper.xmpp.addressing.EntityFormatException;
 import org.apache.vysper.xmpp.addressing.EntityImpl;
 import org.apache.vysper.xmpp.authorization.AccountCreationException;
 import org.apache.vysper.xmpp.authorization.AccountManagement;
@@ -32,17 +36,12 @@ import org.apache.vysper.xmpp.modules.Mo
 import org.apache.vysper.xmpp.modules.extension.xep0049_privatedata.PrivateDataModule;
 import org.apache.vysper.xmpp.modules.extension.xep0050_adhoc_commands.AdhocCommandsModule;
 import org.apache.vysper.xmpp.modules.extension.xep0054_vcardtemp.VcardTempModule;
+import org.apache.vysper.xmpp.modules.extension.xep007_inbandreg.InBandRegistrationModule;
 import org.apache.vysper.xmpp.modules.extension.xep0092_software_version.SoftwareVersionModule;
 import org.apache.vysper.xmpp.modules.extension.xep0119_xmppping.XmppPingModule;
 import org.apache.vysper.xmpp.modules.extension.xep0133_service_administration.ServiceAdministrationModule;
 import org.apache.vysper.xmpp.modules.extension.xep0202_entity_time.EntityTimeModule;
 
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
 /**
  * starts the server as a standalone application
  *
@@ -102,6 +101,7 @@ public class ServerMain {
         server.addModule(new VcardTempModule());
         server.addModule(new XmppPingModule());
         server.addModule(new PrivateDataModule());
+        server.addModule(new InBandRegistrationModule());
         server.addModule(new AdhocCommandsModule());
         final ServiceAdministrationModule serviceAdministrationModule = new ServiceAdministrationModule();
         // unless admin user account with a secure password is added, this will be not become effective

Modified: mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/server/response/ServerErrorResponses.java
URL: http://svn.apache.org/viewvc/mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/server/response/ServerErrorResponses.java?rev=1059682&r1=1059681&r2=1059682&view=diff
==============================================================================
--- mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/server/response/ServerErrorResponses.java (original)
+++ mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/server/response/ServerErrorResponses.java Sun Jan 16 21:24:44 2011
@@ -93,13 +93,28 @@ public class ServerErrorResponses {
 
         StanzaBuilder responseBuilder = StanzaBuilder.createDirectReply(stanza, true, "error");
 
-        fillErrorStanza(stanza, type, errorCondition, errorText, errorLang, errorConditionElement, responseBuilder);
+        fillErrorStanza(stanza, type, errorCondition, -1, errorText, errorLang, errorConditionElement, responseBuilder);
 
         return responseBuilder.build();
     }
 
+    public static Stanza getStanzaError(StanzaErrorCondition errorCondition, XMPPCoreStanza stanza, StanzaErrorType type, int code,
+            String errorText, String errorLang, XMLElement errorConditionElement) {
+        
+        if (stanza != null && "error".equals(stanza.getType())) {
+            return ServerErrorResponses.getStreamError(StreamErrorCondition.UNSUPPORTED_STANZA_TYPE,
+                    errorLang, "cannot respond to IQ stanza of type error with the same", null);
+        }
+        
+        StanzaBuilder responseBuilder = StanzaBuilder.createDirectReply(stanza, true, "error");
+        
+        fillErrorStanza(stanza, type, errorCondition, code, errorText, errorLang, errorConditionElement, responseBuilder);
+        
+        return responseBuilder.build();
+    }
+
     private static void fillErrorStanza(XMPPCoreStanza stanza, StanzaErrorType type, StanzaErrorCondition errorCondition,
-            String errorText, String errorLang, XMLElement errorConditionElement, StanzaBuilder responseBuilder) {
+            int code, String errorText, String errorLang, XMLElement errorConditionElement, StanzaBuilder responseBuilder) {
         // inline incoming stanza as of RFC 3920 9.3.1
         for (XMLElement innerElement : stanza.getInnerElements()) {
             responseBuilder.addPreparedElement(innerElement);
@@ -107,7 +122,9 @@ public class ServerErrorResponses {
 
         // error element
         responseBuilder.startInnerElement("error", NamespaceURIs.JABBER_CLIENT).addAttribute("type", type.value());
-
+        if(code != -1) responseBuilder.addAttribute("code", Integer.toString(code));
+        
+        
         // insert defined error condition relating to the stanza error type
         responseBuilder.startInnerElement(errorCondition.value(), NamespaceURIs.URN_IETF_PARAMS_XML_NS_XMPP_STANZAS);
         responseBuilder.endInnerElement();

Added: mina/vysper/trunk/server/core/src/test/java/org/apache/vysper/xmpp/modules/extension/xep007_inbandreg/InBandRegistrationHandlerTestCase.java
URL: http://svn.apache.org/viewvc/mina/vysper/trunk/server/core/src/test/java/org/apache/vysper/xmpp/modules/extension/xep007_inbandreg/InBandRegistrationHandlerTestCase.java?rev=1059682&view=auto
==============================================================================
--- mina/vysper/trunk/server/core/src/test/java/org/apache/vysper/xmpp/modules/extension/xep007_inbandreg/InBandRegistrationHandlerTestCase.java (added)
+++ mina/vysper/trunk/server/core/src/test/java/org/apache/vysper/xmpp/modules/extension/xep007_inbandreg/InBandRegistrationHandlerTestCase.java Sun Jan 16 21:24:44 2011
@@ -0,0 +1,305 @@
+/*
+ *  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.vysper.xmpp.modules.extension.xep007_inbandreg;
+
+import org.apache.vysper.xml.fragment.XMLElement;
+import org.apache.vysper.xml.fragment.XMLSemanticError;
+import org.apache.vysper.xmpp.addressing.Entity;
+import org.apache.vysper.xmpp.addressing.EntityImpl;
+import org.apache.vysper.xmpp.authorization.AccountCreationException;
+import org.apache.vysper.xmpp.authorization.AccountManagement;
+import org.apache.vysper.xmpp.protocol.NamespaceURIs;
+import org.apache.vysper.xmpp.protocol.SessionStateHolder;
+import org.apache.vysper.xmpp.server.ServerRuntimeContext;
+import org.apache.vysper.xmpp.server.SessionContext;
+import org.apache.vysper.xmpp.server.SessionState;
+import org.apache.vysper.xmpp.stanza.IQStanzaType;
+import org.apache.vysper.xmpp.stanza.Stanza;
+import org.apache.vysper.xmpp.stanza.StanzaBuilder;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+/**
+ */
+public class InBandRegistrationHandlerTestCase {
+
+    private static final String IQ_ID = "id1";
+
+    private AccountManagement accountManagement = Mockito.mock(AccountManagement.class);
+    private SessionContext sessionContext = Mockito.mock(SessionContext.class);
+    private ServerRuntimeContext serverRuntimeContext = Mockito.mock(ServerRuntimeContext.class);
+    private SessionStateHolder sessionStateHolder = Mockito.mock(SessionStateHolder.class);
+
+    private static final Entity FROM = EntityImpl.parseUnchecked("from@vysper.org");
+    private static final Entity SERVER = EntityImpl.parseUnchecked("vysper.org");
+    private static final Entity EXISTING = EntityImpl.parseUnchecked("existing@vysper.org");
+
+    protected InBandRegistrationHandler handler = new InBandRegistrationHandler();
+
+    @Before
+    public void before() {
+        Mockito.when(serverRuntimeContext.getStorageProvider(AccountManagement.class)).thenReturn(accountManagement);
+        Mockito.when(serverRuntimeContext.getServerEnitity()).thenReturn(SERVER);
+        Mockito.when(accountManagement.verifyAccountExists(EXISTING)).thenReturn(true);
+        Mockito.when(sessionContext.getState()).thenReturn(SessionState.STARTED);
+    }
+    
+    
+    @Test
+    public void testGetUnauthenticated() throws XMLSemanticError {
+        //        <iq type='get' id='reg1'>
+        //            <query xmlns='jabber:iq:register'/>
+        //        </iq>
+        Stanza get = StanzaBuilder.createIQStanza(FROM, SERVER, IQStanzaType.GET, IQ_ID)
+            .startInnerElement("query", NamespaceURIs.JABBER_IQ_REGISTER).build();
+        
+        Stanza response = handler.execute(get, serverRuntimeContext, true, sessionContext, sessionStateHolder).getResponseStanza();
+
+        //        <iq type='result' id='reg1'>
+        //            <query xmlns='jabber:iq:register'>
+        //              <instructions>
+        //                Choose a username and password for use with this service.
+        //                Please also provide your email address.
+        //              </instructions>
+        //              <username/>
+        //              <password/>
+        //              <email/>
+        //            </query>
+        //          </iq>
+        Assert.assertNotNull(response);
+        Assert.assertEquals("iq", response.getName());
+        Assert.assertEquals(NamespaceURIs.JABBER_CLIENT, response.getNamespaceURI());
+        Assert.assertEquals(IQ_ID, response.getAttributeValue("id"));
+        Assert.assertEquals(SERVER.getFullQualifiedName(), response.getAttributeValue("from"));
+        Assert.assertEquals(FROM.getFullQualifiedName(), response.getAttributeValue("to"));
+        
+        XMLElement query = response.getSingleInnerElementsNamed("query", NamespaceURIs.JABBER_IQ_REGISTER);
+        Assert.assertNotNull(query.getSingleInnerElementsNamed("instructions", NamespaceURIs.JABBER_IQ_REGISTER));
+        Assert.assertNotNull(query.getSingleInnerElementsNamed("username", NamespaceURIs.JABBER_IQ_REGISTER));
+        Assert.assertNotNull(query.getSingleInnerElementsNamed("password", NamespaceURIs.JABBER_IQ_REGISTER));
+    }
+    
+    @Test
+    public void testGetAuthenticated() throws XMLSemanticError {
+        //        <iq type='get' id='reg1'>
+        //            <query xmlns='jabber:iq:register'/>
+        //        </iq>
+        Stanza get = StanzaBuilder.createIQStanza(EXISTING, SERVER, IQStanzaType.GET, IQ_ID)
+            .startInnerElement("query", NamespaceURIs.JABBER_IQ_REGISTER).build();
+        
+        Mockito.when(sessionContext.getState()).thenReturn(SessionState.AUTHENTICATED);
+        Mockito.when(sessionContext.getInitiatingEntity()).thenReturn(EXISTING);
+
+        Stanza response = handler.execute(get, serverRuntimeContext, true, sessionContext, sessionStateHolder).getResponseStanza();
+
+        //        <iq type='result' id='reg1'>
+        //            <query xmlns='jabber:iq:register'>
+        //              <registered/>
+        //              <username>juliet</username>
+        //              <password>R0m30</password>
+        //              <email>juliet@capulet.com</email>
+        //            </query>
+        //          </iq>
+        Assert.assertNotNull(response);
+        Assert.assertEquals("iq", response.getName());
+        Assert.assertEquals(NamespaceURIs.JABBER_CLIENT, response.getNamespaceURI());
+        Assert.assertEquals(IQ_ID, response.getAttributeValue("id"));
+        Assert.assertEquals("result", response.getAttributeValue("type"));
+        Assert.assertEquals(SERVER.getFullQualifiedName(), response.getAttributeValue("from"));
+        Assert.assertEquals(EXISTING.getFullQualifiedName(), response.getAttributeValue("to"));
+        
+        XMLElement query = response.getSingleInnerElementsNamed("query", NamespaceURIs.JABBER_IQ_REGISTER);
+        Assert.assertNotNull(query.getSingleInnerElementsNamed("registered", NamespaceURIs.JABBER_IQ_REGISTER));
+        assertInnerText(EXISTING.getNode(), "username", query); 
+    }
+
+    @Test
+    public void testSet() throws XMLSemanticError, AccountCreationException {
+        //        <iq type='set' id='reg2'>
+        //            <query xmlns='jabber:iq:register'>
+        //              <username>bill</username>
+        //              <password>Calliope</password>
+        //              <email>bard@shakespeare.lit</email>
+        //            </query>
+        //          </iq>        
+      Stanza set = StanzaBuilder.createIQStanza(FROM, SERVER, IQStanzaType.SET, IQ_ID)
+            .startInnerElement("query", NamespaceURIs.JABBER_IQ_REGISTER)
+            .startInnerElement("username", NamespaceURIs.JABBER_IQ_REGISTER).addText(FROM.getNode()).endInnerElement()
+            .startInnerElement("password", NamespaceURIs.JABBER_IQ_REGISTER).addText("password").endInnerElement()
+            .endInnerElement()
+            .build();
+        
+        Stanza response = handler.execute(set, serverRuntimeContext, true, sessionContext, sessionStateHolder).getResponseStanza();
+
+        //        <iq type='result' id='reg2'/>
+        Assert.assertNotNull(response);
+        Assert.assertEquals("iq", response.getName());
+        Assert.assertEquals(NamespaceURIs.JABBER_CLIENT, response.getNamespaceURI());
+        Assert.assertEquals(IQ_ID, response.getAttributeValue("id"));
+        Assert.assertEquals("result", response.getAttributeValue("type"));
+        Assert.assertEquals(SERVER.getFullQualifiedName(), response.getAttributeValue("from"));
+        Assert.assertEquals(FROM.getFullQualifiedName(), response.getAttributeValue("to"));
+        Assert.assertEquals(0, response.getInnerElements().size());
+        
+        Mockito.verify(accountManagement).addUser(FROM, "password");
+    }
+
+    @Test
+    public void testSetExisting() throws XMLSemanticError, AccountCreationException {
+        //        <iq type='set' id='reg2'>
+        //            <query xmlns='jabber:iq:register'>
+        //              <username>bill</username>
+        //              <password>Calliope</password>
+        //              <email>bard@shakespeare.lit</email>
+        //            </query>
+        //          </iq>        
+      Stanza set = StanzaBuilder.createIQStanza(EXISTING, SERVER, IQStanzaType.SET, IQ_ID)
+            .startInnerElement("query", NamespaceURIs.JABBER_IQ_REGISTER)
+            .startInnerElement("username", NamespaceURIs.JABBER_IQ_REGISTER).addText(EXISTING.getNode()).endInnerElement()
+            .startInnerElement("password", NamespaceURIs.JABBER_IQ_REGISTER).addText("password").endInnerElement()
+            .endInnerElement()
+            .build();
+        
+        Stanza response = handler.execute(set, serverRuntimeContext, true, sessionContext, sessionStateHolder).getResponseStanza();
+
+        //        <iq type='error' id='reg2'>
+        //            <query xmlns='jabber:iq:register'>
+        //              <username>bill</username>
+        //              <password>m1cro$oft</password>
+        //              <email>billg@bigcompany.com</email>
+        //            </query>
+        //            <error code='409' type='cancel'>
+        //              <conflict xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
+        //            </error>
+        //          </iq>
+        Assert.assertNotNull(response);
+        Assert.assertEquals("iq", response.getName());
+        Assert.assertEquals(NamespaceURIs.JABBER_CLIENT, response.getNamespaceURI());
+        Assert.assertEquals(IQ_ID, response.getAttributeValue("id"));
+        Assert.assertEquals("error", response.getAttributeValue("type"));
+        Assert.assertEquals(SERVER.getFullQualifiedName(), response.getAttributeValue("from"));
+        Assert.assertEquals(EXISTING.getFullQualifiedName(), response.getAttributeValue("to"));
+     
+        XMLElement query = response.getSingleInnerElementsNamed("query", NamespaceURIs.JABBER_IQ_REGISTER);
+        assertInnerText(EXISTING.getNode(), "username", query); 
+        assertInnerText("password", "password", query); 
+
+        XMLElement error = response.getSingleInnerElementsNamed("error", NamespaceURIs.JABBER_CLIENT);
+        Assert.assertEquals("409", error.getAttributeValue("code"));
+        Assert.assertEquals("cancel", error.getAttributeValue("type"));
+        Assert.assertNotNull(error.getSingleInnerElementsNamed("conflict", NamespaceURIs.URN_IETF_PARAMS_XML_NS_XMPP_STANZAS));
+        
+        Mockito.verify(accountManagement, Mockito.never()).addUser(EXISTING, "password");
+    }
+
+    @Test
+    public void testSetMissingPassword() throws XMLSemanticError, AccountCreationException {
+        //        <iq type='set' id='reg2'>
+        //            <query xmlns='jabber:iq:register'>
+        //              <username>bill</username>
+        //              <password>Calliope</password>
+        //              <email>bard@shakespeare.lit</email>
+        //            </query>
+        //          </iq>        
+      Stanza set = StanzaBuilder.createIQStanza(FROM, SERVER, IQStanzaType.SET, IQ_ID)
+            .startInnerElement("query", NamespaceURIs.JABBER_IQ_REGISTER)
+            .startInnerElement("username", NamespaceURIs.JABBER_IQ_REGISTER).addText(FROM.getNode()).endInnerElement()
+            .endInnerElement()
+            .build();
+        
+        Stanza response = handler.execute(set, serverRuntimeContext, true, sessionContext, sessionStateHolder).getResponseStanza();
+
+        //        <iq type='error' id='reg2'>
+        //            <query xmlns='jabber:iq:register'>
+        //              <username>bill</username>
+        //              <password>Calliope</password>
+        //            </query>
+        //            <error code='406' type='modify'>
+        //              <not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
+        //            </error>
+        //          </iq>
+        Assert.assertNotNull(response);
+        Assert.assertEquals("iq", response.getName());
+        Assert.assertEquals(NamespaceURIs.JABBER_CLIENT, response.getNamespaceURI());
+        Assert.assertEquals(IQ_ID, response.getAttributeValue("id"));
+        Assert.assertEquals("error", response.getAttributeValue("type"));
+        Assert.assertEquals(SERVER.getFullQualifiedName(), response.getAttributeValue("from"));
+        Assert.assertEquals(FROM.getFullQualifiedName(), response.getAttributeValue("to"));
+     
+        XMLElement query = response.getSingleInnerElementsNamed("query", NamespaceURIs.JABBER_IQ_REGISTER);
+        assertInnerText(FROM.getNode(), "username", query); 
+
+        XMLElement error = response.getSingleInnerElementsNamed("error", NamespaceURIs.JABBER_CLIENT);
+        Assert.assertEquals("406", error.getAttributeValue("code"));
+        Assert.assertEquals("modify", error.getAttributeValue("type"));
+        Assert.assertNotNull(error.getSingleInnerElementsNamed("not-acceptable", NamespaceURIs.URN_IETF_PARAMS_XML_NS_XMPP_STANZAS));
+        
+        Mockito.verify(accountManagement, Mockito.never()).addUser(EXISTING, "password");
+    }
+
+    @Test
+    public void testSetChangePassword() throws XMLSemanticError, AccountCreationException {
+        //        <iq type='set' id='reg2'>
+        //            <query xmlns='jabber:iq:register'>
+        //              <username>bill</username>
+        //              <password>Calliope</password>
+        //              <email>bard@shakespeare.lit</email>
+        //            </query>
+        //          </iq>        
+        Stanza set = StanzaBuilder.createIQStanza(EXISTING, SERVER, IQStanzaType.SET, IQ_ID)
+            .startInnerElement("query", NamespaceURIs.JABBER_IQ_REGISTER)
+            .startInnerElement("username", NamespaceURIs.JABBER_IQ_REGISTER).addText(EXISTING.getNode()).endInnerElement()
+            .startInnerElement("password", NamespaceURIs.JABBER_IQ_REGISTER).addText("password").endInnerElement()
+            .endInnerElement()
+            .build();
+        
+        Mockito.when(sessionContext.getState()).thenReturn(SessionState.AUTHENTICATED);
+        Mockito.when(sessionContext.getInitiatingEntity()).thenReturn(EXISTING);
+
+        Stanza response = handler.execute(set, serverRuntimeContext, true, sessionContext, sessionStateHolder)
+                .getResponseStanza();
+
+        //        <iq type='result' id='reg2'/>
+        Assert.assertNotNull(response);
+        Assert.assertEquals("iq", response.getName());
+        Assert.assertEquals(NamespaceURIs.JABBER_CLIENT, response.getNamespaceURI());
+        Assert.assertEquals(IQ_ID, response.getAttributeValue("id"));
+        Assert.assertEquals("result", response.getAttributeValue("type"));
+        Assert.assertEquals(SERVER.getFullQualifiedName(), response.getAttributeValue("from"));
+        Assert.assertEquals(EXISTING.getFullQualifiedName(), response.getAttributeValue("to"));
+        Assert.assertEquals(0, response.getInnerElements().size());
+        
+        Mockito.verify(accountManagement).changePassword(EXISTING, "password");
+    }
+
+    
+    private void assertInnerText(String expected, String name, XMLElement parent) {
+        try {
+            Assert.assertEquals(expected, 
+                    parent.getSingleInnerElementsNamed(name, NamespaceURIs.JABBER_IQ_REGISTER).getInnerText().getText());
+        } catch (XMLSemanticError e) {
+            Assert.fail("Incorrect number of elements: " + e.getMessage());
+        }
+        
+    }
+
+}