-
Notifications
You must be signed in to change notification settings - Fork 886
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[extensions] Improved Support for Direct MUC Invitations (XEP-0249)
[flow: rebase of paul's initial submission which required adjustments] Co-authored-by: Florian Schmaus <[email protected]>
- Loading branch information
1 parent
726dbc0
commit 664a141
Showing
7 changed files
with
263 additions
and
45 deletions.
There are no files selected for viewing
25 changes: 25 additions & 0 deletions
25
smack-extensions/src/main/java/org/jivesoftware/smackx/muc/DirectMucInvitationListener.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
/** | ||
* | ||
* Copyright 2020 Paul Schaub. | ||
* | ||
* Licensed 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.jivesoftware.smackx.muc; | ||
|
||
import org.jivesoftware.smack.packet.Stanza; | ||
import org.jivesoftware.smackx.muc.packet.GroupChatInvitation; | ||
|
||
public interface DirectMucInvitationListener { | ||
|
||
void invitationReceived(GroupChatInvitation invitation, Stanza stanza); | ||
} |
111 changes: 111 additions & 0 deletions
111
smack-extensions/src/main/java/org/jivesoftware/smackx/muc/DirectMucInvitationManager.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
/** | ||
* | ||
* Copyright 2020 Paul Schaub. | ||
* | ||
* Licensed 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.jivesoftware.smackx.muc; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.WeakHashMap; | ||
|
||
import org.jivesoftware.smack.Manager; | ||
import org.jivesoftware.smack.SmackException; | ||
import org.jivesoftware.smack.XMPPConnection; | ||
import org.jivesoftware.smack.XMPPConnectionRegistry; | ||
import org.jivesoftware.smack.XMPPException; | ||
import org.jivesoftware.smack.filter.StanzaExtensionFilter; | ||
import org.jivesoftware.smack.packet.Message; | ||
import org.jivesoftware.smack.packet.MessageBuilder; | ||
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; | ||
import org.jivesoftware.smackx.muc.packet.GroupChatInvitation; | ||
|
||
import org.jxmpp.jid.EntityBareJid; | ||
|
||
/** | ||
* Smacks API for XEP-0249: Direct MUC Invitations. | ||
* Use this instead of {@link org.jivesoftware.smackx.muc.packet.MUCUser.Invite}. | ||
* | ||
* To invite a user to a group chat, use {@link #inviteToMuc(MultiUserChat, EntityBareJid)}. | ||
* | ||
* In order to listen for incoming invitations, register a {@link DirectMucInvitationListener} using | ||
* {@link #addInvitationListener(DirectMucInvitationListener)}. | ||
* | ||
* @see <a href="https://xmpp.org/extensions/xep-0249.html">Direct MUC Invitations</a> | ||
*/ | ||
public final class DirectMucInvitationManager extends Manager { | ||
|
||
private static final Map<XMPPConnection, DirectMucInvitationManager> INSTANCES = new WeakHashMap<>(); | ||
private final List<DirectMucInvitationListener> directMucInvitationListeners = new ArrayList<>(); | ||
private final ServiceDiscoveryManager serviceDiscoveryManager; | ||
|
||
static { | ||
XMPPConnectionRegistry.addConnectionCreationListener(DirectMucInvitationManager::getInstanceFor); | ||
} | ||
|
||
public static synchronized DirectMucInvitationManager getInstanceFor(XMPPConnection connection) { | ||
DirectMucInvitationManager manager = INSTANCES.get(connection); | ||
if (manager == null) { | ||
manager = new DirectMucInvitationManager(connection); | ||
INSTANCES.put(connection, manager); | ||
} | ||
return manager; | ||
} | ||
|
||
private DirectMucInvitationManager(XMPPConnection connection) { | ||
super(connection); | ||
serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(connection); | ||
|
||
connection().addAsyncStanzaListener(stanza -> { | ||
GroupChatInvitation invitation = stanza.getExtension(GroupChatInvitation.class); | ||
for (DirectMucInvitationListener listener : directMucInvitationListeners) { | ||
listener.invitationReceived(invitation, stanza); | ||
} | ||
}, new StanzaExtensionFilter(GroupChatInvitation.ELEMENT, GroupChatInvitation.NAMESPACE)); | ||
serviceDiscoveryManager.addFeature(GroupChatInvitation.NAMESPACE); | ||
} | ||
|
||
public void inviteToMuc(MultiUserChat muc, EntityBareJid user) | ||
throws SmackException.NotConnectedException, InterruptedException { | ||
inviteToMuc(muc, user, null, null, false, null); | ||
} | ||
|
||
public void inviteToMuc(MultiUserChat muc, EntityBareJid user, String password, String reason, boolean continueAsOneToOneChat, String thread) | ||
throws SmackException.NotConnectedException, InterruptedException { | ||
inviteToMuc(user, new GroupChatInvitation(muc.getRoom(), reason, password, continueAsOneToOneChat, thread)); | ||
} | ||
|
||
public void inviteToMuc(EntityBareJid jid, GroupChatInvitation invitation) throws SmackException.NotConnectedException, InterruptedException { | ||
Message invitationMessage = MessageBuilder.buildMessage() | ||
.to(jid) | ||
.addExtension(invitation) | ||
.build(); | ||
connection().sendStanza(invitationMessage); | ||
} | ||
|
||
public boolean userSupportsInvitations(EntityBareJid jid) | ||
throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, | ||
SmackException.NoResponseException { | ||
return serviceDiscoveryManager.supportsFeature(jid, GroupChatInvitation.NAMESPACE); | ||
} | ||
|
||
public synchronized void addInvitationListener(DirectMucInvitationListener listener) { | ||
directMucInvitationListeners.add(listener); | ||
} | ||
|
||
public synchronized void removeInvitationListener(DirectMucInvitationListener listener) { | ||
directMucInvitationListeners.remove(listener); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
/** | ||
* | ||
* Copyright 2003-2007 Jive Software. | ||
* Copyright 2003-2007 Jive Software, 2020 Paul Schaub. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
|
@@ -20,39 +20,22 @@ | |
|
||
import org.jivesoftware.smack.packet.ExtensionElement; | ||
import org.jivesoftware.smack.packet.Stanza; | ||
import org.jivesoftware.smack.util.EqualsUtil; | ||
import org.jivesoftware.smack.util.HashCode; | ||
import org.jivesoftware.smack.util.Objects; | ||
import org.jivesoftware.smack.util.XmlStringBuilder; | ||
|
||
import org.jxmpp.jid.EntityBareJid; | ||
|
||
/** | ||
* A group chat invitation stanza extension, which is used to invite other | ||
* users to a group chat room. To invite a user to a group chat room, address | ||
* a new message to the user and set the room name appropriately, as in the | ||
* following code example: | ||
* users to a group chat room. | ||
* | ||
* <pre> | ||
* Message message = new Message("[email protected]"); | ||
* message.setBody("Join me for a group chat!"); | ||
* message.addExtension(new GroupChatInvitation("[email protected]");); | ||
* con.sendStanza(message); | ||
* </pre> | ||
* | ||
* To listen for group chat invitations, use a StanzaExtensionFilter for the | ||
* <code>x</code> element name and <code>jabber:x:conference</code> namespace, as in the | ||
* following code example: | ||
* | ||
* <pre> | ||
* PacketFilter filter = new StanzaExtensionFilter("x", "jabber:x:conference"); | ||
* // Create a stanza collector or stanza listeners using the filter... | ||
* </pre> | ||
* | ||
* <b>Note</b>: this protocol is outdated now that the Multi-User Chat (MUC) XEP is available | ||
* (<a href="http://www.xmpp.org/extensions/jep-0045.html">XEP-45</a>). However, most | ||
* existing clients still use this older protocol. Once MUC support becomes more | ||
* widespread, this API may be deprecated. | ||
* This implementation now conforms to XEP-0249: Direct MUC Invitations, | ||
* while staying backwards compatible to legacy MUC invitations. | ||
* | ||
* @author Matt Tucker | ||
* @author Paul Schaub | ||
*/ | ||
public class GroupChatInvitation implements ExtensionElement { | ||
|
||
|
@@ -68,6 +51,12 @@ public class GroupChatInvitation implements ExtensionElement { | |
|
||
public static final QName QNAME = new QName(NAMESPACE, ELEMENT); | ||
|
||
public static final String ATTR_CONTINUE = "continue"; | ||
public static final String ATTR_JID = "jid"; | ||
public static final String ATTR_PASSWORD = "password"; | ||
public static final String ATTR_REASON = "reason"; | ||
public static final String ATTR_THREAD = "thread"; | ||
|
||
private final EntityBareJid roomAddress; | ||
private final String reason; | ||
private final String password; | ||
|
@@ -170,18 +159,37 @@ public String getNamespace() { | |
@Override | ||
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { | ||
XmlStringBuilder xml = new XmlStringBuilder(this); | ||
xml.attribute("jid", getRoomAddress()); | ||
xml.optAttribute("reason", getReason()); | ||
xml.optAttribute("password", getPassword()); | ||
xml.optAttribute("thread", getThread()); | ||
|
||
if (continueAsOneToOneChat()) | ||
xml.optBooleanAttribute("continue", true); | ||
xml.jidAttribute(getRoomAddress()); | ||
xml.optAttribute(ATTR_REASON, getReason()); | ||
xml.optAttribute(ATTR_PASSWORD, getPassword()); | ||
xml.optAttribute(ATTR_THREAD, getThread()); | ||
xml.optBooleanAttribute(ATTR_CONTINUE, continueAsOneToOneChat()); | ||
|
||
xml.closeEmptyElement(); | ||
return xml; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object obj) { | ||
return EqualsUtil.equals(this, obj, (equalsBuilder, other) -> equalsBuilder | ||
.append(getRoomAddress(), other.getRoomAddress()) | ||
.append(getPassword(), other.getPassword()) | ||
.append(getReason(), other.getReason()) | ||
.append(continueAsOneToOneChat(), other.continueAsOneToOneChat()) | ||
.append(getThread(), other.getThread())); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return HashCode.builder() | ||
.append(getRoomAddress()) | ||
.append(getPassword()) | ||
.append(getReason()) | ||
.append(continueAsOneToOneChat()) | ||
.append(getThread()) | ||
.build(); | ||
} | ||
|
||
/** | ||
* Get the group chat invitation from the given stanza. | ||
* @param packet TODO javadoc me please | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
69 changes: 69 additions & 0 deletions
69
...ions/src/test/java/org/jivesoftware/smackx/muc/packet/GroupChatInvitationElementTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
/** | ||
* | ||
* Copyright 2020 Paul Schaub. | ||
* | ||
* Licensed 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.jivesoftware.smackx.muc.packet; | ||
|
||
import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; | ||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
|
||
import java.io.IOException; | ||
|
||
import org.jivesoftware.smack.parsing.SmackParsingException; | ||
import org.jivesoftware.smack.test.util.TestUtils; | ||
import org.jivesoftware.smack.xml.XmlPullParserException; | ||
import org.jivesoftware.smackx.muc.provider.GroupChatInvitationProvider; | ||
|
||
import org.junit.jupiter.api.Test; | ||
import org.jxmpp.jid.EntityBareJid; | ||
import org.jxmpp.jid.impl.JidCreate; | ||
|
||
public class GroupChatInvitationElementTest { | ||
private static final GroupChatInvitationProvider TEST_PROVIDER = new GroupChatInvitationProvider(); | ||
|
||
private static final EntityBareJid mucJid = JidCreate.entityBareFromOrThrowUnchecked("[email protected]"); | ||
|
||
@Test | ||
public void serializeFullElement() throws XmlPullParserException, IOException, SmackParsingException { | ||
final String expectedXml = "" + | ||
"<x xmlns='jabber:x:conference'\n" + | ||
" continue='true'\n" + | ||
" jid='[email protected]'\n" + | ||
" password='cauldronburn'\n" + | ||
" reason='Hey Hecate, this is the place for all good witches!'\n" + | ||
" thread='e0ffe42b28561960c6b12b944a092794b9683a38'/>"; | ||
|
||
GroupChatInvitation invitation = new GroupChatInvitation(mucJid, | ||
"Hey Hecate, this is the place for all good witches!", | ||
"cauldronburn", | ||
true, | ||
"e0ffe42b28561960c6b12b944a092794b9683a38"); | ||
assertXmlSimilar(expectedXml, invitation.toXML()); | ||
|
||
GroupChatInvitation parsed = TEST_PROVIDER.parse(TestUtils.getParser(expectedXml)); | ||
assertEquals(invitation, parsed); | ||
} | ||
|
||
@Test | ||
public void serializeMinimalElementTest() throws XmlPullParserException, IOException, SmackParsingException { | ||
final String expectedXml = "<x xmlns='jabber:x:conference' jid='[email protected]'/>"; | ||
|
||
GroupChatInvitation invitation = new GroupChatInvitation(mucJid); | ||
assertXmlSimilar(expectedXml, invitation.toXML()); | ||
|
||
GroupChatInvitation parsed = TEST_PROVIDER.parse(TestUtils.getParser(expectedXml)); | ||
assertEquals(invitation, parsed); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters