From c9efa5581dcdbdfe868b1acd03ac927038b85afc Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Sun, 5 Apr 2020 12:12:00 +0200 Subject: [PATCH] Attempt implementing first IQs --- .../smackx/mix/core/MixManager.java | 11 ++- .../mix/core/element/iq/ClientJoinIQ.java | 33 ++++++++ .../core/element/iq/ClientJoinIQBuilder.java | 78 +++++++++++++++++++ ...nelOrNoPermissionToSubscribeException.java | 7 ++ .../mix/misc/element/RegisterElement.java | 38 ++++++++- .../provider/RegisterElementProvider.java | 2 +- .../mix/core/element/iq/ClientJoinIQTest.java | 25 ++++++ .../mix/misc/element/RegisterElementTest.java | 18 ++++- .../provider/RegisterElementProviderTest.java | 54 +++++++++++++ 9 files changed, 255 insertions(+), 11 deletions(-) create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/mix/core/element/iq/ClientJoinIQ.java create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/mix/core/element/iq/ClientJoinIQBuilder.java create mode 100644 smack-experimental/src/test/java/org/jivesoftware/smackx/mix/core/element/iq/ClientJoinIQTest.java create mode 100644 smack-experimental/src/test/java/org/jivesoftware/smackx/mix/misc/provider/RegisterElementProviderTest.java diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mix/core/MixManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mix/core/MixManager.java index d57f75053..d81b16408 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/mix/core/MixManager.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mix/core/MixManager.java @@ -32,12 +32,12 @@ import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; import org.jivesoftware.smackx.disco.packet.DiscoverInfo; import org.jivesoftware.smackx.disco.packet.DiscoverItems; -import org.jivesoftware.smackx.mam.MamManager; import org.jivesoftware.smackx.mam.element.MamElements; +import org.jivesoftware.smackx.mix.core.element.iq.ClientJoinIQ; +import org.jivesoftware.smackx.mix.core.element.iq.ClientJoinIQBuilder; import org.jivesoftware.smackx.mix.core.exception.NotAMixChannelOrNoPermissionToSubscribeException; import org.jivesoftware.smackx.mix.core.exception.NotAMixServiceException; import org.jivesoftware.smackx.pubsub.packet.PubSub; -import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace; import org.jxmpp.jid.DomainBareJid; import org.jxmpp.jid.EntityBareJid; @@ -172,7 +172,12 @@ public final class MixManager extends Manager { throws XMPPException.XMPPErrorException, NotAMixChannelOrNoPermissionToSubscribeException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException { MixChannel channel = discoverMixChannelInformation(channelAddress); - + ClientJoinIQ iq = new ClientJoinIQBuilder(connection()) + .setNickname(nick) + .setChannelAddress(channelAddress) + .addMixNodeSubscription(MixNodes.NODE_MESSAGES) + .addMixNodeSubscription(MixNodes.NODE_PRESENCE) + .build(); return null; } } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mix/core/element/iq/ClientJoinIQ.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mix/core/element/iq/ClientJoinIQ.java new file mode 100644 index 000000000..21e7b5329 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mix/core/element/iq/ClientJoinIQ.java @@ -0,0 +1,33 @@ +package org.jivesoftware.smackx.mix.core.element.iq; + +import java.util.ArrayList; +import java.util.List; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smackx.mix.core.element.JoinElement; +import org.jivesoftware.smackx.mix.core.element.NickElement; +import org.jivesoftware.smackx.mix.core.element.SubscribeElement; +import org.jivesoftware.smackx.mix.pam.element.ClientJoinElement; + +import org.jxmpp.jid.EntityBareJid; + +public class ClientJoinIQ extends IQ { + + private final EntityBareJid channelJid; + private final NickElement nickElement; + private final List subscriptions; + + public ClientJoinIQ(ClientJoinIQBuilder builder) { + super(builder, ClientJoinElement.V2.ELEMENT, null); + this.channelJid = builder.getChannelAddress(); + this.nickElement = builder.getNickElement(); + this.subscriptions = new ArrayList<>(builder.getSubscriptions()); + } + + @Override + protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) { + JoinElement.V1 joinElement = new JoinElement.V1(subscriptions, nickElement); + xml.append(new ClientJoinElement.V2(channelJid, joinElement)); + return xml; + } +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mix/core/element/iq/ClientJoinIQBuilder.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mix/core/element/iq/ClientJoinIQBuilder.java new file mode 100644 index 000000000..becd322db --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mix/core/element/iq/ClientJoinIQBuilder.java @@ -0,0 +1,78 @@ +package org.jivesoftware.smackx.mix.core.element.iq; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.packet.AbstractIqBuilder; +import org.jivesoftware.smack.packet.IqBuilder; +import org.jivesoftware.smackx.mix.core.element.NickElement; +import org.jivesoftware.smackx.mix.core.element.SubscribeElement; + +import org.jxmpp.jid.EntityBareJid; + +public class ClientJoinIQBuilder extends IqBuilder { + + private EntityBareJid channelAddress; + private NickElement nickElement; + private final Set subscriptions = new HashSet<>(); + + protected ClientJoinIQBuilder(AbstractIqBuilder other) { + super(other); + } + + public ClientJoinIQBuilder(XMPPConnection connection) { + super(connection); + } + + protected ClientJoinIQBuilder(String stanzaId) { + super(stanzaId); + } + + @Override + public ClientJoinIQBuilder getThis() { + return this; + } + + @Override + public ClientJoinIQ build() { + return new ClientJoinIQ(this); + } + + public ClientJoinIQBuilder setChannelAddress(EntityBareJid channelAddress) { + this.channelAddress = channelAddress; + return getThis(); + } + + public ClientJoinIQBuilder addMixNodeSubscription(String nodeName) { + return addMixNodeSubscription(new SubscribeElement(nodeName)); + } + + public ClientJoinIQBuilder addMixNodeSubscription(SubscribeElement subscription) { + return addMixNodeSubscriptions(Collections.singleton(subscription)); + } + + public ClientJoinIQBuilder addMixNodeSubscriptions(Collection subscriptions) { + this.subscriptions.addAll(subscriptions); + return getThis(); + } + + public ClientJoinIQBuilder setNickname(String nick) { + this.nickElement = nick != null ? new NickElement(nick) : null; + return getThis(); + } + + public EntityBareJid getChannelAddress() { + return channelAddress; + } + + public NickElement getNickElement() { + return nickElement; + } + + public Set getSubscriptions() { + return subscriptions; + } +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mix/core/exception/NotAMixChannelOrNoPermissionToSubscribeException.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mix/core/exception/NotAMixChannelOrNoPermissionToSubscribeException.java index 3b3947484..07856c2e4 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/mix/core/exception/NotAMixChannelOrNoPermissionToSubscribeException.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mix/core/exception/NotAMixChannelOrNoPermissionToSubscribeException.java @@ -1,5 +1,12 @@ package org.jivesoftware.smackx.mix.core.exception; +/** + * Exception that is thrown when the user tries to interact with an entity which is either not a MIX channel, + * or is one but the user doesn't have the permission to subscribe to it. + * + * @see + * XEP-0369: §6.3 about discovering channel information + */ public class NotAMixChannelOrNoPermissionToSubscribeException extends Exception { private static final long serialVersionUID = 1L; diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mix/misc/element/RegisterElement.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mix/misc/element/RegisterElement.java index dbd0028c2..38f0a87b5 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/mix/misc/element/RegisterElement.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mix/misc/element/RegisterElement.java @@ -4,18 +4,46 @@ import static org.jivesoftware.smackx.mix.misc.MixMiscConstants.NAMESPACE_MISC_0 import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.packet.XmlEnvironment; -import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.XmlStringBuilder; import org.jivesoftware.smackx.mix.core.element.NickElement; +/** + * Element for registering a nick name with the MIX channel. + * + * see + * XEP-0407: MIX Miscellaneous Capabilities - §3. Registering a Nick + */ public abstract class RegisterElement implements ExtensionElement { public static final String ELEMENT = "register"; private final NickElement nick; + /** + * Create an empty register element. + * A MIX service must assign a nick name if the request does not contain a nick child element. + */ + public RegisterElement() { + this(null); + } + + /** + * Create a register element with a desired nick name. + * + * @param nickElement nick element containing the nick name. + */ public RegisterElement(NickElement nickElement) { - this.nick = Objects.requireNonNull(nickElement, "Nick element MUST NOT be null."); + this.nick = nickElement; + } + + /** + * Return the nick child element. + * For a request, this is the requested nick name and for a response, this is the assigned nick name (might differ). + * + * @return nick element + */ + public NickElement getNick() { + return nick; } @Override @@ -27,12 +55,16 @@ public abstract class RegisterElement implements ExtensionElement { public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) { return new XmlStringBuilder(this) .rightAngleBracket() - .append(nick) + .optAppend(nick) .closeElement(this); } public static class V0 extends RegisterElement { + public V0() { + super(); + } + public V0(NickElement nickElement) { super(nickElement); } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mix/misc/provider/RegisterElementProvider.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mix/misc/provider/RegisterElementProvider.java index 196cb6fe3..8af9d249d 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/mix/misc/provider/RegisterElementProvider.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mix/misc/provider/RegisterElementProvider.java @@ -27,7 +27,7 @@ public abstract class RegisterElementProvider extends nickElement = new NickElement(parser.nextText()); } } else if (tagEvent == XmlPullParser.TagEvent.END_ELEMENT) { - if (SetNickElement.ELEMENT.equals(name)) { + if (RegisterElement.ELEMENT.equals(name)) { return new RegisterElement.V0(nickElement); } } diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mix/core/element/iq/ClientJoinIQTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mix/core/element/iq/ClientJoinIQTest.java new file mode 100644 index 000000000..41cbf1860 --- /dev/null +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mix/core/element/iq/ClientJoinIQTest.java @@ -0,0 +1,25 @@ +package org.jivesoftware.smackx.mix.core.element.iq; + +import org.jivesoftware.smack.DummyConnection; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.test.util.SmackTestSuite; +import org.jivesoftware.smackx.mix.core.MixNodes; + +import org.junit.jupiter.api.Test; +import org.jxmpp.jid.impl.JidCreate; + +public class ClientJoinIQTest extends SmackTestSuite { + + @Test + public void serializationTest() { + ClientJoinIQ iq = new ClientJoinIQBuilder(DummyConnection.newConnectedDummyConnection()) + .ofType(IQ.Type.set) + .setNickname("alice") + .setChannelAddress(JidCreate.entityBareFromOrThrowUnchecked("rabbithole@mix.wonderland.lit")) + .addMixNodeSubscription(MixNodes.NODE_MESSAGES) + .addMixNodeSubscription(MixNodes.NODE_PRESENCE) + .build(); + + System.out.println(iq.toXML()); + } +} diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mix/misc/element/RegisterElementTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mix/misc/element/RegisterElementTest.java index c404e4eab..cc7a04e2b 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/mix/misc/element/RegisterElementTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mix/misc/element/RegisterElementTest.java @@ -9,12 +9,22 @@ import org.junit.jupiter.api.Test; public class RegisterElementTest { @Test - public void v0testSerialization() { + public void testV0Serialization() { RegisterElement register = new RegisterElement.V0(new NickElement("thirdwitch")); String expectedXml = "" + - "\n" + - " thirdwitch\n" + - "\n"; + "" + + " thirdwitch" + + ""; + + assertXmlSimilar(expectedXml, register.toXML()); + } + + @Test + public void testV0NoNickSerialization() { + RegisterElement register = new RegisterElement.V0(); + String expectedXml = "" + + "" + + ""; assertXmlSimilar(expectedXml, register.toXML()); } diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mix/misc/provider/RegisterElementProviderTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mix/misc/provider/RegisterElementProviderTest.java new file mode 100644 index 000000000..66c51ec85 --- /dev/null +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mix/misc/provider/RegisterElementProviderTest.java @@ -0,0 +1,54 @@ +package org.jivesoftware.smackx.mix.misc.provider; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.io.IOException; + +import org.jivesoftware.smack.parsing.SmackParsingException; +import org.jivesoftware.smack.test.util.TestUtils; +import org.jivesoftware.smack.xml.XmlPullParser; +import org.jivesoftware.smack.xml.XmlPullParserException; +import org.jivesoftware.smackx.mix.misc.element.RegisterElement; + +import org.junit.jupiter.api.Test; + +public class RegisterElementProviderTest { + + private final RegisterElementProvider.V0 provider = new RegisterElementProvider.V0(); + + @Test + public void testV0Parsing() throws XmlPullParserException, IOException, SmackParsingException { + final String xml = "" + + "" + + " thirdwitch" + + ""; + final String nick = "thirdwitch"; + XmlPullParser parser = TestUtils.getParser(xml); + + RegisterElement parsed = provider.parse(parser); + assertEquals(nick, parsed.getNick().getValue()); + } + + @Test + public void testV0NoNickElementParsing() throws XmlPullParserException, IOException, SmackParsingException { + final String noChildElementXml = ""; + XmlPullParser noChildElementParser = TestUtils.getParser(noChildElementXml); + + RegisterElement noChildElementParsed = provider.parse(noChildElementParser); + + assertNull(noChildElementParsed.getNick()); + } + + @Test + public void testV0EmptyParsing() throws XmlPullParserException, IOException, SmackParsingException { + final String emptyElementXml = ""; + XmlPullParser emptyElementParser = TestUtils.getParser(emptyElementXml); + + RegisterElement emptyElementParsed = provider.parse(emptyElementParser); + + assertNull(emptyElementParsed.getNick()); + } + + +}