From bafef8947c4062d07f627f0fff05a20bc23b0b91 Mon Sep 17 00:00:00 2001 From: Gaston Dombiak Date: Tue, 6 Jul 2004 03:07:45 +0000 Subject: [PATCH] Moved from org.jivesoftware.smackx to org.jivesoftware.smackx.muc. git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@2334 b35dd754-fafc-0310-a699-88a17e54d16e --- .../DefaultParticipantStatusListener.java | 105 - .../smackx/DefaultUserStatusListener.java | 102 - .../smackx/InvitationListener.java | 83 - .../smackx/InvitationRejectionListener.java | 70 - .../jivesoftware/smackx/MultiUserChat.java | 2316 ----------------- .../smackx/ParticipantStatusListener.java | 186 -- .../smackx/SubjectUpdatedListener.java | 70 - .../smackx/UserStatusListener.java | 159 -- .../smackx/muc/MultiUserChatCreationTest.java | 184 ++ .../smackx/muc/MultiUserChatTest.java | 1344 ++++++++++ 10 files changed, 1528 insertions(+), 3091 deletions(-) delete mode 100644 source/org/jivesoftware/smackx/DefaultParticipantStatusListener.java delete mode 100644 source/org/jivesoftware/smackx/DefaultUserStatusListener.java delete mode 100644 source/org/jivesoftware/smackx/InvitationListener.java delete mode 100644 source/org/jivesoftware/smackx/InvitationRejectionListener.java delete mode 100644 source/org/jivesoftware/smackx/MultiUserChat.java delete mode 100644 source/org/jivesoftware/smackx/ParticipantStatusListener.java delete mode 100644 source/org/jivesoftware/smackx/SubjectUpdatedListener.java delete mode 100644 source/org/jivesoftware/smackx/UserStatusListener.java create mode 100644 test/org/jivesoftware/smackx/muc/MultiUserChatCreationTest.java create mode 100644 test/org/jivesoftware/smackx/muc/MultiUserChatTest.java diff --git a/source/org/jivesoftware/smackx/DefaultParticipantStatusListener.java b/source/org/jivesoftware/smackx/DefaultParticipantStatusListener.java deleted file mode 100644 index 5afc6ebb3..000000000 --- a/source/org/jivesoftware/smackx/DefaultParticipantStatusListener.java +++ /dev/null @@ -1,105 +0,0 @@ -/** -* $RCSfile$ -* $Revision$ -* $Date$ -* -* Copyright (C) 2002-2003 Jive Software. All rights reserved. -* ==================================================================== -* The Jive Software License (based on Apache Software License, Version 1.1) -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions -* are met: -* -* 1. Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* -* 2. Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in -* the documentation and/or other materials provided with the -* distribution. -* -* 3. The end-user documentation included with the redistribution, -* if any, must include the following acknowledgment: -* "This product includes software developed by -* Jive Software (http://www.jivesoftware.com)." -* Alternately, this acknowledgment may appear in the software itself, -* if and wherever such third-party acknowledgments normally appear. -* -* 4. The names "Smack" and "Jive Software" must not be used to -* endorse or promote products derived from this software without -* prior written permission. For written permission, please -* contact webmaster@jivesoftware.com. -* -* 5. Products derived from this software may not be called "Smack", -* nor may "Smack" appear in their name, without prior written -* permission of Jive Software. -* -* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED -* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL JIVE SOFTWARE OR -* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT -* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -* SUCH DAMAGE. -* ==================================================================== -*/ - -package org.jivesoftware.smackx; - -/** - * Default implementation of the ParticipantStatusListener interface.

- * - * This class does not provide any behavior by default. It just avoids having - * to implement all the inteface methods if the user is only interested in implementing - * some of the methods. - * - * @author Gaston Dombiak - */ -public class DefaultParticipantStatusListener implements ParticipantStatusListener { - - public void kicked(String participant) { - } - - public void voiceGranted(String participant) { - } - - public void voiceRevoked(String participant) { - } - - public void banned(String participant) { - } - - public void membershipGranted(String participant) { - } - - public void membershipRevoked(String participant) { - } - - public void moderatorGranted(String participant) { - } - - public void moderatorRevoked(String participant) { - } - - public void ownershipGranted(String participant) { - } - - public void ownershipRevoked(String participant) { - } - - public void adminGranted(String participant) { - } - - public void adminRevoked(String participant) { - } - - public void nicknameChanged(String nickname) { - } - -} diff --git a/source/org/jivesoftware/smackx/DefaultUserStatusListener.java b/source/org/jivesoftware/smackx/DefaultUserStatusListener.java deleted file mode 100644 index d5bce7bec..000000000 --- a/source/org/jivesoftware/smackx/DefaultUserStatusListener.java +++ /dev/null @@ -1,102 +0,0 @@ -/** -* $RCSfile$ -* $Revision$ -* $Date$ -* -* Copyright (C) 2002-2003 Jive Software. All rights reserved. -* ==================================================================== -* The Jive Software License (based on Apache Software License, Version 1.1) -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions -* are met: -* -* 1. Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* -* 2. Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in -* the documentation and/or other materials provided with the -* distribution. -* -* 3. The end-user documentation included with the redistribution, -* if any, must include the following acknowledgment: -* "This product includes software developed by -* Jive Software (http://www.jivesoftware.com)." -* Alternately, this acknowledgment may appear in the software itself, -* if and wherever such third-party acknowledgments normally appear. -* -* 4. The names "Smack" and "Jive Software" must not be used to -* endorse or promote products derived from this software without -* prior written permission. For written permission, please -* contact webmaster@jivesoftware.com. -* -* 5. Products derived from this software may not be called "Smack", -* nor may "Smack" appear in their name, without prior written -* permission of Jive Software. -* -* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED -* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL JIVE SOFTWARE OR -* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT -* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -* SUCH DAMAGE. -* ==================================================================== -*/ - -package org.jivesoftware.smackx; - -/** - * Default implementation of the UserStatusListener interface.

- * - * This class does not provide any behavior by default. It just avoids having - * to implement all the inteface methods if the user is only interested in implementing - * some of the methods. - * - * @author Gaston Dombiak - */ -public class DefaultUserStatusListener implements UserStatusListener { - - public void kicked(String actor, String reason) { - } - - public void voiceGranted() { - } - - public void voiceRevoked() { - } - - public void banned(String actor, String reason) { - } - - public void membershipGranted() { - } - - public void membershipRevoked() { - } - - public void moderatorGranted() { - } - - public void moderatorRevoked() { - } - - public void ownershipGranted() { - } - - public void ownershipRevoked() { - } - - public void adminGranted() { - } - - public void adminRevoked() { - } - -} diff --git a/source/org/jivesoftware/smackx/InvitationListener.java b/source/org/jivesoftware/smackx/InvitationListener.java deleted file mode 100644 index 75aa33d58..000000000 --- a/source/org/jivesoftware/smackx/InvitationListener.java +++ /dev/null @@ -1,83 +0,0 @@ -/** -* $RCSfile$ -* $Revision$ -* $Date$ -* -* Copyright (C) 2002-2003 Jive Software. All rights reserved. -* ==================================================================== -* The Jive Software License (based on Apache Software License, Version 1.1) -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions -* are met: -* -* 1. Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* -* 2. Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in -* the documentation and/or other materials provided with the -* distribution. -* -* 3. The end-user documentation included with the redistribution, -* if any, must include the following acknowledgment: -* "This product includes software developed by -* Jive Software (http://www.jivesoftware.com)." -* Alternately, this acknowledgment may appear in the software itself, -* if and wherever such third-party acknowledgments normally appear. -* -* 4. The names "Smack" and "Jive Software" must not be used to -* endorse or promote products derived from this software without -* prior written permission. For written permission, please -* contact webmaster@jivesoftware.com. -* -* 5. Products derived from this software may not be called "Smack", -* nor may "Smack" appear in their name, without prior written -* permission of Jive Software. -* -* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED -* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL JIVE SOFTWARE OR -* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT -* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -* SUCH DAMAGE. -* ==================================================================== -*/ - -package org.jivesoftware.smackx; - -import org.jivesoftware.smack.XMPPConnection; - -/** - * A listener that is fired anytime an invitation to join a MUC room is received. - * - * @author Gaston Dombiak - */ -public interface InvitationListener { - - /** - * Called when the an invitation to join a MUC room is received.

- * - * If the room is password-protected, the invitee will receive a password to use to join - * the room. If the room is members-only, the the invitee may be added to the member list. - * - * @param conn the XMPPConnection that received the invitation. - * @param room the room that invitation refers to. - * @param inviter the inviter that sent the invitation. (e.g. crone1@shakespeare.lit). - * @param reason the reason why the inviter sent the invitation. - * @param password the password to use when joining the room. - */ - public abstract void invitationReceived( - XMPPConnection conn, - String room, - String inviter, - String reason, - String password); - -} diff --git a/source/org/jivesoftware/smackx/InvitationRejectionListener.java b/source/org/jivesoftware/smackx/InvitationRejectionListener.java deleted file mode 100644 index 0835a6bbd..000000000 --- a/source/org/jivesoftware/smackx/InvitationRejectionListener.java +++ /dev/null @@ -1,70 +0,0 @@ -/** -* $RCSfile$ -* $Revision$ -* $Date$ -* -* Copyright (C) 2002-2003 Jive Software. All rights reserved. -* ==================================================================== -* The Jive Software License (based on Apache Software License, Version 1.1) -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions -* are met: -* -* 1. Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* -* 2. Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in -* the documentation and/or other materials provided with the -* distribution. -* -* 3. The end-user documentation included with the redistribution, -* if any, must include the following acknowledgment: -* "This product includes software developed by -* Jive Software (http://www.jivesoftware.com)." -* Alternately, this acknowledgment may appear in the software itself, -* if and wherever such third-party acknowledgments normally appear. -* -* 4. The names "Smack" and "Jive Software" must not be used to -* endorse or promote products derived from this software without -* prior written permission. For written permission, please -* contact webmaster@jivesoftware.com. -* -* 5. Products derived from this software may not be called "Smack", -* nor may "Smack" appear in their name, without prior written -* permission of Jive Software. -* -* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED -* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL JIVE SOFTWARE OR -* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT -* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -* SUCH DAMAGE. -* ==================================================================== -*/ - -package org.jivesoftware.smackx; - -/** - * A listener that is fired anytime an invitee declines or rejects an invitation. - * - * @author Gaston Dombiak - */ -public interface InvitationRejectionListener { - - /** - * Called when the invitee declines the invitation. - * - * @param invitee the invitee that declined the invitation. (e.g. hecate@shakespeare.lit). - * @param reason the reason why the invitee declined the invitation. - */ - public abstract void invitationDeclined(String invitee, String reason); - -} diff --git a/source/org/jivesoftware/smackx/MultiUserChat.java b/source/org/jivesoftware/smackx/MultiUserChat.java deleted file mode 100644 index 85f546da7..000000000 --- a/source/org/jivesoftware/smackx/MultiUserChat.java +++ /dev/null @@ -1,2316 +0,0 @@ -/** - * $RCSfile$ - * $Revision$ - * $Date$ - * - * Copyright (C) 2002-2003 Jive Software. All rights reserved. - * ==================================================================== - * The Jive Software License (based on Apache Software License, Version 1.1) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, - * if any, must include the following acknowledgment: - * "This product includes software developed by - * Jive Software (http://www.jivesoftware.com)." - * Alternately, this acknowledgment may appear in the software itself, - * if and wherever such third-party acknowledgments normally appear. - * - * 4. The names "Smack" and "Jive Software" must not be used to - * endorse or promote products derived from this software without - * prior written permission. For written permission, please - * contact webmaster@jivesoftware.com. - * - * 5. Products derived from this software may not be called "Smack", - * nor may "Smack" appear in their name, without prior written - * permission of Jive Software. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL JIVE SOFTWARE OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - */ -package org.jivesoftware.smackx; - -import java.lang.ref.WeakReference; -import java.lang.reflect.*; -import java.util.*; - -import org.jivesoftware.smack.*; -import org.jivesoftware.smack.filter.*; -import org.jivesoftware.smack.packet.*; -import org.jivesoftware.smackx.packet.*; - -/** - * A MultiUserChat is a conversation that takes place among many users in a virtual - * room. A room could have many occupants with different affiliation and roles. - * Possible affiliatons are "owner", "admin", "member", and "outcast". Possible roles - * are "moderator", "participant", and "visitor". Each role and affiliation guarantees - * different privileges (e.g. Send messages to all occupants, Kick participants and visitors, - * Grant voice, Edit member list, etc.). - * - * @author Gaston Dombiak - */ -public class MultiUserChat { - - private final static String discoNamespace = "http://jabber.org/protocol/muc"; - private final static String discoNode = "http://jabber.org/protocol/muc#rooms"; - - private static Map joinedRooms = new WeakHashMap(); - - private XMPPConnection connection; - private String room; - private String subject; - private String nickname = null; - private boolean joined = false; - private Map participantsMap = new HashMap(); - - private List invitationRejectionListeners = new ArrayList(); - private List subjectUpdatedListeners = new ArrayList(); - private List userStatusListeners = new ArrayList(); - private List participantStatusListeners = new ArrayList(); - - private PacketFilter presenceFilter; - private PacketListener presenceListener; - private PacketFilter subjectFilter; - private PacketListener subjectListener; - private PacketFilter messageFilter; - private PacketFilter declinesFilter; - private PacketListener declinesListener; - private PacketCollector messageCollector; - - static { - XMPPConnection.addConnectionListener(new ConnectionEstablishedListener() { - public void connectionEstablished(final XMPPConnection connection) { - // Set on every established connection that this client supports the Multi-User - // Chat protocol. This information will be used when another client tries to - // discover whether this client supports MUC or not. - ServiceDiscoveryManager.getInstanceFor(connection).addFeature(discoNamespace); - // Set the NodeInformationProvider that will provide information about the - // joined rooms whenever a disco request is received - ServiceDiscoveryManager.getInstanceFor(connection).setNodeInformationProvider( - discoNode, - new NodeInformationProvider() { - public Iterator getNodeItems() { - ArrayList answer = new ArrayList(); - Iterator rooms=MultiUserChat.getJoinedRooms(connection); - while (rooms.hasNext()) { - answer.add(new DiscoverItems.Item((String)rooms.next())); - } - return answer.iterator(); - } - }); - } - }); - } - - /** - * Creates a new multi user chat with the specified connection and room name. Note: no - * information is sent to or received from the server until you attempt to - * {@link #join(String) join} the chat room. On some server implementations, - * the room will not be created until the first person joins it.

- * - * Most XMPP servers use a sub-domain for the chat service (eg chat.example.com - * for the XMPP server example.com). You must ensure that the room address you're - * trying to connect to includes the proper chat sub-domain. - * - * @param connection the XMPP connection. - * @param room the name of the room in the form "roomName@service", where - * "service" is the hostname at which the multi-user chat - * service is running. - */ - public MultiUserChat(XMPPConnection connection, String room) { - this.connection = connection; - this.room = room; - init(); - } - - /** - * Returns true if the specified user supports the Multi-User Chat protocol. - * - * @param connection the connection to use to perform the service discovery. - * @param user the user to check. A fully qualified xmpp ID, e.g. jdoe@example.com. - * @return a boolean indicating whether the specified user supports the MUC protocol. - */ - public static boolean isServiceEnabled(XMPPConnection connection, String user) { - try { - DiscoverInfo result = - ServiceDiscoveryManager.getInstanceFor(connection).discoverInfo(user); - return result.containsFeature(discoNamespace); - } - catch (XMPPException e) { - e.printStackTrace(); - return false; - } - } - - /** - * Returns an Iterator on the rooms where the user has joined using a given connection. - * The Iterator will contain Strings where each String represents a room - * (e.g. room@muc.jabber.org). - * - * @param connection the connection used to join the rooms. - * @return an Iterator on the rooms where the user has joined using a given connection. - */ - private static Iterator getJoinedRooms(XMPPConnection connection) { - ArrayList rooms = (ArrayList)joinedRooms.get(connection); - if (rooms != null) { - return rooms.iterator(); - } - // Return an iterator on an empty collection (i.e. the user never joined a room) - return new ArrayList().iterator(); - } - - /** - * Returns an Iterator on the rooms where the requested user has joined. The Iterator will - * contain Strings where each String represents a room (e.g. room@muc.jabber.org). - * - * @param connection the connection to use to perform the service discovery. - * @param user the user to check. A fully qualified xmpp ID, e.g. jdoe@example.com. - * @return an Iterator on the rooms where the requested user has joined. - */ - public static Iterator getJoinedRooms(XMPPConnection connection, String user) { - try { - ArrayList answer = new ArrayList(); - // Send the disco packet to the user - DiscoverItems result = - ServiceDiscoveryManager.getInstanceFor(connection).discoverItems(user, discoNode); - // Collect the entityID for each returned item - for (Iterator items=result.getItems(); items.hasNext();) { - answer.add(((DiscoverItems.Item)items.next()).getEntityID()); - } - return answer.iterator(); - } - catch (XMPPException e) { - e.printStackTrace(); - // Return an iterator on an empty collection - return new ArrayList().iterator(); - } - } - - /** - * Returns the name of the room this MultiUserChat object represents. - * - * @return the multi user chat room name. - */ - public String getRoom() { - return room; - } - - /** - * Creates the room according to some default configuration, assign the requesting user - * as the room owner, and add the owner to the room but not allow anyone else to enter - * the room (effectively "locking" the room). The requesting user will join the room - * under the specified nickname as soon as the room has been created.

- * - * To create an "Instant Room", that means a room with some default configuration that is - * available for immediate access, the room's owner should send an empty form after creating - * the room. {@link #sendConfigurationForm(Form)}

- * - * To create a "Reserved Room", that means a room manually configured by the room creator - * before anyone is allowed to enter, the room's owner should complete and send a form after - * creating the room. Once the completed configutation form is sent to the server, the server - * will unlock the room. {@link #sendConfigurationForm(Form)} - * - * @param nickname the nickname to use. - * @throws XMPPException if the room couldn't be created for some reason - * (e.g. room already exists; user already joined to an existant room or - * 405 error if the user is not allowed to create the room) - */ - public synchronized void create(String nickname) throws XMPPException { - if (nickname == null || nickname.equals("")) { - throw new IllegalArgumentException("Nickname must not be null or blank."); - } - // If we've already joined the room, leave it before joining under a new - // nickname. - if (joined) { - throw new IllegalStateException("Creation failed - User already joined the room."); - } - // We create a room by sending a presence packet to room@service/nick - // and signal support for MUC. The owner will be automatically logged into the room. - Presence joinPresence = new Presence(Presence.Type.AVAILABLE); - joinPresence.setTo(room + "/" + nickname); - // Indicate the the client supports MUC - joinPresence.addExtension(new MUCInitialPresence()); - - // Wait for a presence packet back from the server. - PacketFilter responseFilter = - new AndFilter( - new FromContainsFilter(room + "/" + nickname), - new PacketTypeFilter(Presence.class)); - PacketCollector response = connection.createPacketCollector(responseFilter); - // Send create & join packet. - connection.sendPacket(joinPresence); - // Wait up to a certain number of seconds for a reply. - Presence presence = - (Presence) response.nextResult(SmackConfiguration.getPacketReplyTimeout()); - // Stop queuing results - response.cancel(); - - if (presence == null) { - throw new XMPPException("No response from server."); - } - else if (presence.getError() != null) { - throw new XMPPException(presence.getError()); - } - // Whether the room existed before or was created, the user has joined the room - this.nickname = nickname; - joined = true; - userHasJoined(); - - // Look for confirmation of room creation from the server - MUCUser mucUser = getMUCUserExtension(presence); - if (mucUser != null && mucUser.getStatus() != null) { - if ("201".equals(mucUser.getStatus().getCode())) { - // Room was created and the user has joined the room - return; - } - } - // We need to leave the room since it seems that the room already existed - leave(); - throw new XMPPException("Creation failed - Missing acknowledge of room creation."); - } - - /** - * Joins the chat room using the specified nickname. If already joined - * using another nickname, this method will first leave the room and then - * re-join using the new nickname. The default timeout of Smack for a reply - * from the group chat server that the join succeeded will be used. After - * joining the room, the room will decide the amount of history to send. - * - * @param nickname the nickname to use. - * @throws XMPPException if an error occurs joining the room. In particular, a - * 401 error can occur if no password was provided and one is required; or a - * 403 error can occur if the user is banned; or a - * 404 error can occur if the room does not exist or is locked; or a - * 407 error can occur if user is not on the member list; or a - * 409 error can occur if someone is already in the group chat with the same nickname. - */ - public void join(String nickname) throws XMPPException { - join(nickname, SmackConfiguration.getPacketReplyTimeout(), null, -1, -1, -1, null); - } - - /** - * Joins the chat room using the specified nickname and password. If already joined - * using another nickname, this method will first leave the room and then - * re-join using the new nickname. The default timeout of Smack for a reply - * from the group chat server that the join succeeded will be used. After - * joining the room, the room will decide the amount of history to send.

- * - * A password is required when joining password protected rooms. If the room does - * not require a password there is no need to provide one. - * - * @param nickname the nickname to use. - * @param password the password to use. - * @throws XMPPException if an error occurs joining the room. In particular, a - * 401 error can occur if no password was provided and one is required; or a - * 403 error can occur if the user is banned; or a - * 404 error can occur if the room does not exist or is locked; or a - * 407 error can occur if user is not on the member list; or a - * 409 error can occur if someone is already in the group chat with the same nickname. - */ - public void join(String nickname, String password) throws XMPPException { - join(nickname, SmackConfiguration.getPacketReplyTimeout(), password, -1, -1, -1, null); - } - - /** - * Joins the chat room using the specified nickname and password. If already joined - * using another nickname, this method will first leave the room and then - * re-join using the new nickname.

- * - * This method provides different parameters to control the amount of history to receive when - * joining the room. If you don't complete any of these parameters the room will decide the - * amount of history to return. If you decide to control the amount of history to receive, you - * can use some or all of the following parameters: - *

- * - * A password is required when joining password protected rooms. If the room does - * not require a password there is no need to provide one.

- * - * If the room does not already exist when the user seeks to enter it, the server will - * decide to create a new room or not. - * - * @param nickname the nickname to use. - * @param timeout the amount of time to wait for a reply from the MUC service(in milleseconds). - * @param password the password to use. - * @param maxchars the total number of characters to receive in the history. - * @param maxstanzas the total number of messages to receive in the history. - * @param seconds the number of seconds to use to filter the messages received during - * that time. - * @param since the since date to use to filter the messages received during that time. - * @throws XMPPException if an error occurs joining the room. In particular, a - * 401 error can occur if no password was provided and one is required; or a - * 403 error can occur if the user is banned; or a - * 404 error can occur if the room does not exist or is locked; or a - * 407 error can occur if user is not on the member list; or a - * 409 error can occur if someone is already in the group chat with the same nickname. - */ - public synchronized void join( - String nickname, - long timeout, - String password, - int maxchars, - int maxstanzas, - int seconds, - Date since) - throws XMPPException { - // TODO Review protocol (too many params). Use setters or history class? - if (nickname == null || nickname.equals("")) { - throw new IllegalArgumentException("Nickname must not be null or blank."); - } - // If we've already joined the room, leave it before joining under a new - // nickname. - if (joined) { - leave(); - } - // We join a room by sending a presence packet where the "to" - // field is in the form "roomName@service/nickname" - Presence joinPresence = new Presence(Presence.Type.AVAILABLE); - joinPresence.setTo(room + "/" + nickname); - - // Indicate the the client supports MUC - MUCInitialPresence mucInitialPresence = new MUCInitialPresence(); - if (password != null) { - mucInitialPresence.setPassword(password); - } - if (maxchars > -1 || maxstanzas > -1 || seconds > -1 || since != null) { - MUCInitialPresence.History history = new MUCInitialPresence.History(); - if (maxchars > -1) { - history.setMaxChars(maxchars); - } - if (maxstanzas > -1) { - history.setMaxStanzas(maxstanzas); - } - if (seconds > -1) { - history.setSeconds(seconds); - } - if (since != null) { - history.setSince(since); - } - mucInitialPresence.setHistory(history); - } - joinPresence.addExtension(mucInitialPresence); - - // Wait for a presence packet back from the server. - PacketFilter responseFilter = - new AndFilter( - new FromContainsFilter(room + "/" + nickname), - new PacketTypeFilter(Presence.class)); - PacketCollector response = connection.createPacketCollector(responseFilter); - // Send join packet. - connection.sendPacket(joinPresence); - // Wait up to a certain number of seconds for a reply. - Presence presence = (Presence) response.nextResult(timeout); - // Stop queuing results - response.cancel(); - - if (presence == null) { - throw new XMPPException("No response from server."); - } - else if (presence.getError() != null) { - throw new XMPPException(presence.getError()); - } - this.nickname = nickname; - joined = true; - userHasJoined(); - } - - /** - * Returns true if currently in the multi user chat (after calling the {@link - * #join(String)} method). - * - * @return true if currently in the multi user chat room. - */ - public boolean isJoined() { - return joined; - } - - /** - * Leave the chat room. - */ - public synchronized void leave() { - // If not joined already, do nothing. - if (!joined) { - return; - } - // We leave a room by sending a presence packet where the "to" - // field is in the form "roomName@service/nickname" - Presence leavePresence = new Presence(Presence.Type.UNAVAILABLE); - leavePresence.setTo(room + "/" + nickname); - connection.sendPacket(leavePresence); - // Reset participant information. - participantsMap = new HashMap(); - nickname = null; - joined = false; - userHasLeft(); - } - - /** - * Returns the room's configuration form that the room's owner can use or null if - * no configuration is possible. The configuration form allows to set the room's language, - * enable logging, specify room's type, etc.. - * - * @return the Form that contains the fields to complete together with the instrucions or - * null if no configuration is possible. - * @throws XMPPException if an error occurs asking the configuration form for the room. - */ - public Form getConfigurationForm() throws XMPPException { - MUCOwner iq = new MUCOwner(); - iq.setTo(room); - iq.setType(IQ.Type.GET); - - // Filter packets looking for an answer from the server. - PacketFilter responseFilter = new PacketIDFilter(iq.getPacketID()); - PacketCollector response = connection.createPacketCollector(responseFilter); - // Request the configuration form to the server. - connection.sendPacket(iq); - // Wait up to a certain number of seconds for a reply. - IQ answer = (IQ) response.nextResult(SmackConfiguration.getPacketReplyTimeout()); - // Stop queuing results - response.cancel(); - - if (answer == null) { - throw new XMPPException("No response from server."); - } - else if (answer.getError() != null) { - throw new XMPPException(answer.getError()); - } - return Form.getFormFrom(answer); - } - - /** - * Sends the completed configuration form to the server. The room will be configured - * with the new settings defined in the form. If the form is empty then the server - * will create an instant room (will use default configuration). - * - * @param form the form with the new settings. - * @throws XMPPException if an error occurs setting the new rooms' configuration. - */ - public void sendConfigurationForm(Form form) throws XMPPException { - MUCOwner iq = new MUCOwner(); - iq.setTo(room); - iq.setType(IQ.Type.SET); - iq.addExtension(form.getDataFormToSend()); - - // Filter packets looking for an answer from the server. - PacketFilter responseFilter = new PacketIDFilter(iq.getPacketID()); - PacketCollector response = connection.createPacketCollector(responseFilter); - // Send the completed configuration form to the server. - connection.sendPacket(iq); - // Wait up to a certain number of seconds for a reply. - IQ answer = (IQ) response.nextResult(SmackConfiguration.getPacketReplyTimeout()); - // Stop queuing results - response.cancel(); - - if (answer == null) { - throw new XMPPException("No response from server."); - } - else if (answer.getError() != null) { - throw new XMPPException(answer.getError()); - } - } - - /** - * Returns the room's registration form that an unaffiliated user, can use to become a member - * of the room or null if no registration is possible. Some rooms may restrict the - * privilege to register members and allow only room admins to add new members.

- * - * If the user requesting registration requirements is not allowed to register with the room - * (e.g. because that privilege has been restricted), the room will return a "Not Allowed" - * error to the user (error code 405). - * - * @return the registration Form that contains the fields to complete together with the - * instrucions or null if no registration is possible. - * @throws XMPPException if an error occurs asking the registration form for the room or a - * 405 error if the user is not allowed to register with the room. - */ - public Form getRegistrationForm() throws XMPPException { - Registration reg = new Registration(); - reg.setType(IQ.Type.GET); - reg.setTo(room); - - PacketFilter filter = - new AndFilter(new PacketIDFilter(reg.getPacketID()), new PacketTypeFilter(IQ.class)); - PacketCollector collector = connection.createPacketCollector(filter); - connection.sendPacket(reg); - IQ result = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout()); - collector.cancel(); - if (result == null) { - throw new XMPPException("No response from server."); - } - else if (result.getType() == IQ.Type.ERROR) { - throw new XMPPException(result.getError()); - } - return Form.getFormFrom(result); - } - - /** - * Sends the completed registration form to the server. After the user successfully submits - * the form, the room may queue the request for review by the room admins or may immediately - * add the user to the member list by changing the user's affiliation from "none" to "member.

- * - * If the desired room nickname is already reserved for that room, the room will return a - * "Conflict" error to the user (error code 409). If the room does not support registration, - * it will return a "Service Unavailable" error to the user (error code 503). - * - * @param form the completed registration form. - * @throws XMPPException if an error occurs submitting the registration form. In particular, a - * 409 error can occur if the desired room nickname is already reserved for that room; - * or a 503 error can occur if the room does not support registration. - */ - public void sendRegistrationForm(Form form) throws XMPPException { - Registration reg = new Registration(); - reg.setType(IQ.Type.SET); - reg.setTo(room); - reg.addExtension(form.getDataFormToSend()); - - PacketFilter filter = - new AndFilter(new PacketIDFilter(reg.getPacketID()), new PacketTypeFilter(IQ.class)); - PacketCollector collector = connection.createPacketCollector(filter); - connection.sendPacket(reg); - IQ result = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout()); - collector.cancel(); - if (result == null) { - throw new XMPPException("No response from server."); - } - else if (result.getType() == IQ.Type.ERROR) { - throw new XMPPException(result.getError()); - } - } - - /** - * Sends a request to the server to destroy the room. The sender of the request - * should be the room's owner. If the sender of the destroy request is not the room's owner - * then the server will answer a "Forbidden" error (403). - * - * @param reason the reason for the room destruction. - * @param alternateJID the JID of an alternate location. - * @throws XMPPException if an error occurs while trying to destroy the room. - * An error can occur which will be wrapped by an XMPPException -- - * XMPP error code 403. The error code can be used to present more - * appropiate error messages to end-users. - */ - public void destroy(String reason, String alternateJID) throws XMPPException { - MUCOwner iq = new MUCOwner(); - iq.setTo(room); - iq.setType(IQ.Type.SET); - - // Create the reason for the room destruction - MUCOwner.Destroy destroy = new MUCOwner.Destroy(); - destroy.setReason(reason); - destroy.setJid(alternateJID); - iq.setDestroy(destroy); - - // Wait for a presence packet back from the server. - PacketFilter responseFilter = new PacketIDFilter(iq.getPacketID()); - PacketCollector response = connection.createPacketCollector(responseFilter); - // Send the room destruction request. - connection.sendPacket(iq); - // Wait up to a certain number of seconds for a reply. - IQ answer = (IQ) response.nextResult(SmackConfiguration.getPacketReplyTimeout()); - // Stop queuing results - response.cancel(); - - if (answer == null) { - throw new XMPPException("No response from server."); - } - else if (answer.getError() != null) { - throw new XMPPException(answer.getError()); - } - // Reset participant information. - participantsMap = new HashMap(); - nickname = null; - joined = false; - userHasLeft(); - } - - /** - * Invites another user to the room in which one is an occupant. The invitation - * will be sent to the room which in turn will forward the invitation to the invitee.

- * - * If the room is password-protected, the invitee will receive a password to use to join - * the room. If the room is members-only, the the invitee may be added to the member list. - * - * @param participant the user to invite to the room.(e.g. hecate@shakespeare.lit) - * @param reason the reason why the user is being invited. - */ - public void invite(String participant, String reason) { - // TODO listen for 404 error code when inviter supplies a non-existent JID - Message message = new Message(room); - - // Create the MUCUser packet that will include the invitation - MUCUser mucUser = new MUCUser(); - MUCUser.Invite invite = new MUCUser.Invite(); - invite.setTo(participant); - invite.setReason(reason); - mucUser.setInvite(invite); - // Add the MUCUser packet that includes the invitation to the message - message.addExtension(mucUser); - - connection.sendPacket(message); - - } - - /** - * Informs the sender of an invitation that the invitee declines the invitation. The rejection - * will be sent to the room which in turn will forward the rejection to the inviter. - * - * @param conn the connection to use for sending the rejection. - * @param room the room that sent the original invitation. - * @param inviter the inviter of the declined invitation. - * @param reason the reason why the invitee is declining the invitation. - */ - public static void decline(XMPPConnection conn, String room, String inviter, String reason) { - Message message = new Message(room); - - // Create the MUCUser packet that will include the rejection - MUCUser mucUser = new MUCUser(); - MUCUser.Decline decline = new MUCUser.Decline(); - decline.setTo(inviter); - decline.setReason(reason); - mucUser.setDecline(decline); - // Add the MUCUser packet that includes the rejection - message.addExtension(mucUser); - - conn.sendPacket(message); - } - - /** - * Adds a listener to invitation notifications. The listener will be fired anytime - * an invitation is received. - * - * @param conn the connection where the listener will be applied. - * @param listener an invitation listener. - */ - public static void addInvitationListener(XMPPConnection conn, InvitationListener listener) { - InvitationsMonitor.getInvitationsMonitor(conn).addInvitationListener(listener); - } - - /** - * Removes a listener to invitation notifications. The listener will be fired anytime - * an invitation is received. - * - * @param conn the connection where the listener was applied. - * @param listener an invitation listener. - */ - public static void removeInvitationListener(XMPPConnection conn, InvitationListener listener) { - InvitationsMonitor.getInvitationsMonitor(conn).removeInvitationListener(listener); - } - - /** - * Adds a listener to invitation rejections notifications. The listener will be fired anytime - * an invitation is declined. - * - * @param listener an invitation rejection listener. - */ - public void addInvitationRejectionListener(InvitationRejectionListener listener) { - synchronized (invitationRejectionListeners) { - if (!invitationRejectionListeners.contains(listener)) { - invitationRejectionListeners.add(listener); - } - } - } - - /** - * Removes a listener from invitation rejections notifications. The listener will be fired - * anytime an invitation is declined. - * - * @param listener an invitation rejection listener. - */ - public void removeInvitationRejectionListener(InvitationRejectionListener listener) { - synchronized (invitationRejectionListeners) { - invitationRejectionListeners.remove(listener); - } - } - - /** - * Fires invitation rejection listeners. - */ - private void fireInvitationRejectionListeners(String invitee, String reason) { - InvitationRejectionListener[] listeners = null; - synchronized (invitationRejectionListeners) { - listeners = new InvitationRejectionListener[invitationRejectionListeners.size()]; - invitationRejectionListeners.toArray(listeners); - } - for (int i = 0; i < listeners.length; i++) { - listeners[i].invitationDeclined(invitee, reason); - } - } - - /** - * Adds a listener to subject change notifications. The listener will be fired anytime - * the room's subject changes. - * - * @param listener a subject updated listener. - */ - public void addSubjectUpdatedListener(SubjectUpdatedListener listener) { - synchronized (subjectUpdatedListeners) { - if (!subjectUpdatedListeners.contains(listener)) { - subjectUpdatedListeners.add(listener); - } - } - } - - /** - * Removes a listener from subject change notifications. The listener will be fired - * anytime the room's subject changes. - * - * @param listener a subject updated listener. - */ - public void removeSubjectUpdatedListener(SubjectUpdatedListener listener) { - synchronized (subjectUpdatedListeners) { - subjectUpdatedListeners.remove(listener); - } - } - - /** - * Fires subject updated listeners. - */ - private void fireSubjectUpdatedListeners(String subject, String from) { - SubjectUpdatedListener[] listeners = null; - synchronized (subjectUpdatedListeners) { - listeners = new SubjectUpdatedListener[subjectUpdatedListeners.size()]; - subjectUpdatedListeners.toArray(listeners); - } - for (int i = 0; i < listeners.length; i++) { - listeners[i].subjectUpdated(subject, from); - } - } - - /** - * Returns the last known room's subject or null if the user hasn't joined the room - * or the room does not have a subject yet. In case the room has a subject, as soon as the - * user joins the room a message with the current room's subject will be received.

- * - * To be notified every time the room's subject change you should add a listener - * to this room. {@link #addSubjectUpdatedListener(SubjectUpdatedListener)}

- * - * To change the room's subject use {@link #changeSubject(String)}. - * - * @return the room's subject or null if the user hasn't joined the room or the - * room does not have a subject yet. - */ - public String getSubject() { - return subject; - } - - /** - * Returns the reserved room nickname for the user in the room. A user may have a reserved - * nickname, for example through explicit room registration or database integration. In such - * cases it may be desirable for the user to discover the reserved nickname before attempting - * to enter the room. - * - * @return the reserved room nickname or null if none. - */ - public String getReservedNickname() { - try { - DiscoverInfo result = - ServiceDiscoveryManager.getInstanceFor(connection).discoverInfo( - room, - "x-roomuser-item"); - // Look for an Identity that holds the reserved nickname and return its name - for (Iterator identities = result.getIdentities(); identities.hasNext();) { - DiscoverInfo.Identity identity = (DiscoverInfo.Identity) identities.next(); - return identity.getName(); - } - // If no Identity was found then the user does not have a reserved room nickname - return null; - } - catch (XMPPException e) { - e.printStackTrace(); - return null; - } - } - - /** - * Returns the nickname that was used to join the room, or null if not - * currently joined. - * - * @return the nickname currently being used. - */ - public String getNickname() { - return nickname; - } - - /** - * Changes the participant's nickname to a new nickname within the room. Each room participant - * will receive two presence packets. One of type "unavailable" for the old nickname and one - * indicating availability for the new nickname. The unavailable presence will contain the new - * nickname and an appropriate status code (namely 303) as extended presence information. The - * status code 303 indicates that the participant is changing his/her nickname. - * - * @param nickname the new nickname within the room. - * @throws XMPPException if the new nickname is already in use by another occupant. - */ - public void changeNickname(String nickname) throws XMPPException { - if (nickname == null || nickname.equals("")) { - throw new IllegalArgumentException("Nickname must not be null or blank."); - } - // Check that we already have joined the room before attempting to change the - // nickname. - if (!joined) { - throw new IllegalStateException("Must be logged into the room to change nickname."); - } - // We change the nickname by sending a presence packet where the "to" - // field is in the form "roomName@service/nickname" - // We don't have to signal the MUC support again - Presence joinPresence = new Presence(Presence.Type.AVAILABLE); - joinPresence.setTo(room + "/" + nickname); - - // Wait for a presence packet back from the server. - PacketFilter responseFilter = - new AndFilter( - new FromContainsFilter(room + "/" + nickname), - new PacketTypeFilter(Presence.class)); - PacketCollector response = connection.createPacketCollector(responseFilter); - // Send join packet. - connection.sendPacket(joinPresence); - // Wait up to a certain number of seconds for a reply. - Presence presence = - (Presence) response.nextResult(SmackConfiguration.getPacketReplyTimeout()); - // Stop queuing results - response.cancel(); - - if (presence == null) { - throw new XMPPException("No response from server."); - } - else if (presence.getError() != null) { - throw new XMPPException(presence.getError()); - } - this.nickname = nickname; - } - - /** - * Changes the participant's availability status within the room. The presence type - * will remain available but with a new status that describes the presence update and - * a new presence mode (e.g. Extended away). - * - * @param status a text message describing the presence update. - * @param mode the mode type for the presence update. - */ - public void changeAvailabilityStatus(String status, Presence.Mode mode) { - if (nickname == null || nickname.equals("")) { - throw new IllegalArgumentException("Nickname must not be null or blank."); - } - // Check that we already have joined the room before attempting to change the - // availability status. - if (!joined) { - throw new IllegalStateException( - "Must be logged into the room to change the " + "availability status."); - } - // We change the availability status by sending a presence packet to the room with the - // new presence status and mode - Presence joinPresence = new Presence(Presence.Type.AVAILABLE); - joinPresence.setStatus(status); - joinPresence.setMode(mode); - joinPresence.setTo(room + "/" + nickname); - - // Send join packet. - connection.sendPacket(joinPresence); - } - - /** - * Kicks a visitor or participant from the room. The kicked participant will receive a presence - * of type "unavailable" including a status code 307 and optionally along with the reason - * (if provided) and the bare JID of the user who initiated the kick. After the participant - * was kicked from the room, the rest of the participants will receive a presence of type - * "unavailable". The presence will include a status code 307 which means that the participant - * was kicked from the room. - * - * @param nickname the nickname of the participant or visitor to kick from the room - * (e.g. "john"). - * @param reason the reason why the participant or visitor is being kicked from the room. - * @throws XMPPException if an error occurs kicking the participant. In particular, a - * 405 error can occur if a moderator or a user with an affiliation of "owner" or "admin" - * was intended to be kicked (i.e. Not Allowed error); or a - * 403 error can occur if the participant that intended to kick another participant does - * not have kicking privileges (i.e. Forbidden error); or a - * 400 error can occur if the provided nickname is not present in the room. - */ - public void kickParticipant(String nickname, String reason) throws XMPPException { - MUCAdmin iq = new MUCAdmin(); - iq.setTo(room); - iq.setType(IQ.Type.SET); - // Set the role to "none". This will request the room to kick the participant or visitor - MUCAdmin.Item item = new MUCAdmin.Item(null, "none"); - item.setNick(nickname); - item.setReason(reason); - iq.addItem(item); - - // Wait for a response packet back from the server. - PacketFilter responseFilter = new PacketIDFilter(iq.getPacketID()); - PacketCollector response = connection.createPacketCollector(responseFilter); - // Send the kick request to the server. - connection.sendPacket(iq); - // Wait up to a certain number of seconds for a reply. - IQ answer = (IQ) response.nextResult(SmackConfiguration.getPacketReplyTimeout()); - // Stop queuing results - response.cancel(); - - if (answer == null) { - throw new XMPPException("No response from server."); - } - else if (answer.getError() != null) { - throw new XMPPException(answer.getError()); - } - } - - /** - * Grants voice to a visitor in the room. In a moderated room, a moderator may want to manage - * who does and does not have "voice" in the room. To have voice means that a room participant - * is able to send messages to the room occupants. - * - * @param nickname the nickname of the visitor to grant voice in the room (e.g. "john"). - * @throws XMPPException if an error occurs granting voice to a visitor. In particular, a - * 403 error can occur if the participant that intended to grant voice is not - * a moderator in this room (i.e. Forbidden error); or a - * 400 error can occur if the provided nickname is not present in the room. - */ - public void grantVoice(String nickname) throws XMPPException { - MUCAdmin iq = new MUCAdmin(); - iq.setTo(room); - iq.setType(IQ.Type.SET); - // Set the role to "participant". This will grant voice to the visitor - MUCAdmin.Item item = new MUCAdmin.Item(null, "participant"); - item.setNick(nickname); - iq.addItem(item); - - // Wait for a response packet back from the server. - PacketFilter responseFilter = new PacketIDFilter(iq.getPacketID()); - PacketCollector response = connection.createPacketCollector(responseFilter); - // Send the grant voice request to the server. - connection.sendPacket(iq); - // Wait up to a certain number of seconds for a reply. - IQ answer = (IQ) response.nextResult(SmackConfiguration.getPacketReplyTimeout()); - // Stop queuing results - response.cancel(); - - if (answer == null) { - throw new XMPPException("No response from server."); - } - else if (answer.getError() != null) { - throw new XMPPException(answer.getError()); - } - } - - /** - * Revokes voice from a participant in the room. In a moderated room, a moderator may want to - * revoke a participant's privileges to speak. To have voice means that a room participant - * is able to send messages to the room occupants. - * - * @param nickname the nickname of the participant to revoke voice (e.g. "john"). - * @throws XMPPException if an error occurs revoking voice from a participant. In particular, a - * 405 error can occur if a moderator or a user with an affiliation of "owner" or "admin" - * was tried to revoke his voice (i.e. Not Allowed error); or a - * 400 error can occur if the provided nickname is not present in the room. - */ - public void revokeVoice(String nickname) throws XMPPException { - MUCAdmin iq = new MUCAdmin(); - iq.setTo(room); - iq.setType(IQ.Type.SET); - // Set the role to "visitor". This will revoke voice from the participant - MUCAdmin.Item item = new MUCAdmin.Item(null, "visitor"); - item.setNick(nickname); - iq.addItem(item); - - // Wait for a response packet back from the server. - PacketFilter responseFilter = new PacketIDFilter(iq.getPacketID()); - PacketCollector response = connection.createPacketCollector(responseFilter); - // Send the revoke voice request to the server. - connection.sendPacket(iq); - // Wait up to a certain number of seconds for a reply. - IQ answer = (IQ) response.nextResult(SmackConfiguration.getPacketReplyTimeout()); - // Stop queuing results - response.cancel(); - - if (answer == null) { - throw new XMPPException("No response from server."); - } - else if (answer.getError() != null) { - throw new XMPPException(answer.getError()); - } - } - - /** - * Bans a user from the room. An admin or owner of the room can ban users from a room. This - * means that the banned user will no longer be able to join the room unless the ban has been - * removed. If the banned user was present in the room then he/she will be removed from the - * room and notified that he/she was banned along with the reason (if provided) and the bare - * XMPP user ID of the user who initiated the ban. - * - * @param jid the bare XMPP user ID of the user to ban (e.g. "user@host.org"). - * @param reason the reason why the user was banned. - * @throws XMPPException if an error occurs banning a user. In particular, a - * 405 error can occur if a moderator or a user with an affiliation of "owner" or "admin" - * was tried to be banned (i.e. Not Allowed error). - */ - public void banUser(String jid, String reason) throws XMPPException { - MUCAdmin iq = new MUCAdmin(); - iq.setTo(room); - iq.setType(IQ.Type.SET); - // Set the affiliation to "outcast". This will ban the user - MUCAdmin.Item item = new MUCAdmin.Item("outcast", null); - item.setJid(jid); - item.setReason(reason); - iq.addItem(item); - - // Wait for a response packet back from the server. - PacketFilter responseFilter = new PacketIDFilter(iq.getPacketID()); - PacketCollector response = connection.createPacketCollector(responseFilter); - // Send the ban user request to the server. - connection.sendPacket(iq); - // Wait up to a certain number of seconds for a reply. - IQ answer = (IQ) response.nextResult(SmackConfiguration.getPacketReplyTimeout()); - // Stop queuing results - response.cancel(); - - if (answer == null) { - throw new XMPPException("No response from server."); - } - else if (answer.getError() != null) { - throw new XMPPException(answer.getError()); - } - } - - /** - * Grants membership to a user. Only administrators are able to grant membership. A user - * that becomes a room member will be able to enter a room of type Members-Only (i.e. a room - * that a user cannot enter without being on the member list). - * - * @param jid the XMPP user ID of the user to grant membership (e.g. "user@host.org"). - * @throws XMPPException if an error occurs granting membership to a user. - */ - public void grantMembership(String jid) throws XMPPException { - MUCAdmin iq = new MUCAdmin(); - iq.setTo(room); - iq.setType(IQ.Type.SET); - // Set the affiliation to "member". This will grant membership to the user - MUCAdmin.Item item = new MUCAdmin.Item("member", null); - item.setJid(jid); - iq.addItem(item); - - // Wait for a response packet back from the server. - PacketFilter responseFilter = new PacketIDFilter(iq.getPacketID()); - PacketCollector response = connection.createPacketCollector(responseFilter); - // Send the grant membership request to the server. - connection.sendPacket(iq); - // Wait up to a certain number of seconds for a reply. - IQ answer = (IQ) response.nextResult(SmackConfiguration.getPacketReplyTimeout()); - // Stop queuing results - response.cancel(); - - if (answer == null) { - throw new XMPPException("No response from server."); - } - else if (answer.getError() != null) { - throw new XMPPException(answer.getError()); - } - } - - /** - * Revokes a user's membership. Only administrators are able to revoke membership. A user - * that becomes a room member will be able to enter a room of type Members-Only (i.e. a room - * that a user cannot enter without being on the member list). If the user is in the room and - * the room is of type members-only then the user will be removed from the room. - * - * @param jid the bare XMPP user ID of the user to grant membership (e.g. "user@host.org"). - * @throws XMPPException if an error occurs revoking membership to a user. - */ - public void revokeMembership(String jid) throws XMPPException { - MUCAdmin iq = new MUCAdmin(); - iq.setTo(room); - iq.setType(IQ.Type.SET); - // Set the affiliation to "none". This will revoke a user's membership - MUCAdmin.Item item = new MUCAdmin.Item("none", null); - item.setJid(jid); - iq.addItem(item); - - // Wait for a response packet back from the server. - PacketFilter responseFilter = new PacketIDFilter(iq.getPacketID()); - PacketCollector response = connection.createPacketCollector(responseFilter); - // Send the revoke membership request to the server. - connection.sendPacket(iq); - // Wait up to a certain number of seconds for a reply. - IQ answer = (IQ) response.nextResult(SmackConfiguration.getPacketReplyTimeout()); - // Stop queuing results - response.cancel(); - - if (answer == null) { - throw new XMPPException("No response from server."); - } - else if (answer.getError() != null) { - throw new XMPPException(answer.getError()); - } - } - - /** - * Grants moderator privileges to a participant or visitor. Room administrators may grant - * moderator privileges. A moderator is allowed to kick users, grant and revoke voice, invite - * other users, modify room's subject plus all the partcipants privileges. - * - * @param nickname the nickname of the occupant to grant moderator privileges. - * @throws XMPPException if an error occurs granting moderator privileges to a user. - */ - public void grantModerator(String nickname) throws XMPPException { - MUCAdmin iq = new MUCAdmin(); - iq.setTo(room); - iq.setType(IQ.Type.SET); - // Set the role to "moderator". This will grant moderator privileges - MUCAdmin.Item item = new MUCAdmin.Item(null, "moderator"); - item.setNick(nickname); - iq.addItem(item); - - // Wait for a response packet back from the server. - PacketFilter responseFilter = new PacketIDFilter(iq.getPacketID()); - PacketCollector response = connection.createPacketCollector(responseFilter); - // Send the grant moderator privileges request to the server. - connection.sendPacket(iq); - // Wait up to a certain number of seconds for a reply. - IQ answer = (IQ) response.nextResult(SmackConfiguration.getPacketReplyTimeout()); - // Stop queuing results - response.cancel(); - - if (answer == null) { - throw new XMPPException("No response from server."); - } - else if (answer.getError() != null) { - throw new XMPPException(answer.getError()); - } - } - - /** - * Revokes moderator privileges from another user. The occupant that loses moderator - * privileges will become a participant. Room administrators may revoke moderator privileges - * only to occupants whose affiliation is member or none. This means that an administrator is - * not allowed to revoke moderator privileges from other room administrators or owners. - * - * @param nickname the nickname of the occupant to revoke moderator privileges. - * @throws XMPPException if an error occurs revoking moderator privileges from a user. - */ - public void revokeModerator(String nickname) throws XMPPException { - MUCAdmin iq = new MUCAdmin(); - iq.setTo(room); - iq.setType(IQ.Type.SET); - // Set the role to "participant". This will revoke moderator privileges - MUCAdmin.Item item = new MUCAdmin.Item(null, "participant"); - item.setNick(nickname); - iq.addItem(item); - - // Wait for a response packet back from the server. - PacketFilter responseFilter = new PacketIDFilter(iq.getPacketID()); - PacketCollector response = connection.createPacketCollector(responseFilter); - // Send the revoke moderator privileges request to the server. - connection.sendPacket(iq); - // Wait up to a certain number of seconds for a reply. - IQ answer = (IQ) response.nextResult(SmackConfiguration.getPacketReplyTimeout()); - // Stop queuing results - response.cancel(); - - if (answer == null) { - throw new XMPPException("No response from server."); - } - else if (answer.getError() != null) { - throw new XMPPException(answer.getError()); - } - } - - /** - * Grants ownership privileges to another user. Room owners may grant ownership privileges. - * Some room implementations will not allow to grant ownership privileges to other users. - * An owner is allowed to change defining room features as well as perform all administrative - * functions. - * - * @param jid the bare XMPP user ID of the user to grant ownership (e.g. "user@host.org"). - * @throws XMPPException if an error occurs granting ownership privileges to a user. - */ - public void grantOwnership(String jid) throws XMPPException { - MUCAdmin iq = new MUCAdmin(); - iq.setTo(room); - iq.setType(IQ.Type.SET); - // Set the affiliation to "owner". This will grant a user's ownership - MUCAdmin.Item item = new MUCAdmin.Item("owner", null); - item.setJid(jid); - iq.addItem(item); - - // Wait for a response packet back from the server. - PacketFilter responseFilter = new PacketIDFilter(iq.getPacketID()); - PacketCollector response = connection.createPacketCollector(responseFilter); - // Send the grant ownership request to the server. - connection.sendPacket(iq); - // Wait up to a certain number of seconds for a reply. - IQ answer = (IQ) response.nextResult(SmackConfiguration.getPacketReplyTimeout()); - // Stop queuing results - response.cancel(); - - if (answer == null) { - throw new XMPPException("No response from server."); - } - else if (answer.getError() != null) { - throw new XMPPException(answer.getError()); - } - } - - /** - * Revokes ownership privileges from another user. The occupant that loses ownership - * privileges will become an administrator. Room owners may revoke ownership privileges. - * Some room implementations will not allow to grant ownership privileges to other users. - * - * @param jid the bare XMPP user ID of the user to grant ownership (e.g. "user@host.org"). - * @throws XMPPException if an error occurs granting ownership privileges to a user. - */ - public void revokeOwnership(String jid) throws XMPPException { - MUCAdmin iq = new MUCAdmin(); - iq.setTo(room); - iq.setType(IQ.Type.SET); - // Set the affiliation to "admin". This will revoke a user's ownership - MUCAdmin.Item item = new MUCAdmin.Item("admin", null); - item.setJid(jid); - iq.addItem(item); - - // Wait for a response packet back from the server. - PacketFilter responseFilter = new PacketIDFilter(iq.getPacketID()); - PacketCollector response = connection.createPacketCollector(responseFilter); - // Send the revoke ownership request to the server. - connection.sendPacket(iq); - // Wait up to a certain number of seconds for a reply. - IQ answer = (IQ) response.nextResult(SmackConfiguration.getPacketReplyTimeout()); - // Stop queuing results - response.cancel(); - - if (answer == null) { - throw new XMPPException("No response from server."); - } - else if (answer.getError() != null) { - throw new XMPPException(answer.getError()); - } - } - - /** - * Grants administrator privileges to another user. Room owners may grant administrator - * privileges to a member or unaffiliated user. An administrator is allowed to perform - * administrative functions such as banning users and edit moderator list. - * - * @param jid the bare XMPP user ID of the user to grant administrator privileges - * (e.g. "user@host.org"). - * @throws XMPPException if an error occurs granting administrator privileges to a user. - */ - public void grantAdmin(String jid) throws XMPPException { - MUCAdmin iq = new MUCAdmin(); - iq.setTo(room); - iq.setType(IQ.Type.SET); - // Set the affiliation to "admin". This will grant administrator privileges - MUCAdmin.Item item = new MUCAdmin.Item("admin", null); - item.setJid(jid); - iq.addItem(item); - - // Wait for a response packet back from the server. - PacketFilter responseFilter = new PacketIDFilter(iq.getPacketID()); - PacketCollector response = connection.createPacketCollector(responseFilter); - // Send the grant administrator privileges request to the server. - connection.sendPacket(iq); - // Wait up to a certain number of seconds for a reply. - IQ answer = (IQ) response.nextResult(SmackConfiguration.getPacketReplyTimeout()); - // Stop queuing results - response.cancel(); - - if (answer == null) { - throw new XMPPException("No response from server."); - } - else if (answer.getError() != null) { - throw new XMPPException(answer.getError()); - } - } - - /** - * Revokes administrator privileges from a user. The occupant that loses administrator - * privileges will become a member. Room owners may revoke administrator privileges from - * a member or unaffiliated user. - * - * @param jid the bare XMPP user ID of the user to revoke administrator privileges - * (e.g. "user@host.org"). - * @throws XMPPException if an error occurs revoking administrator privileges from a user. - */ - public void revokeAdmin(String jid) throws XMPPException { - MUCAdmin iq = new MUCAdmin(); - iq.setTo(room); - iq.setType(IQ.Type.SET); - // Set the affiliation to "member". This will revoke a user's ownership - MUCAdmin.Item item = new MUCAdmin.Item("member", null); - item.setJid(jid); - iq.addItem(item); - - // Wait for a response packet back from the server. - PacketFilter responseFilter = new PacketIDFilter(iq.getPacketID()); - PacketCollector response = connection.createPacketCollector(responseFilter); - // Send the revoke ownership request to the server. - connection.sendPacket(iq); - // Wait up to a certain number of seconds for a reply. - IQ answer = (IQ) response.nextResult(SmackConfiguration.getPacketReplyTimeout()); - // Stop queuing results - response.cancel(); - - if (answer == null) { - throw new XMPPException("No response from server."); - } - else if (answer.getError() != null) { - throw new XMPPException(answer.getError()); - } - } - - /** - * Returns the number of participants in the group chat.

- * - * Note: this value will only be accurate after joining the group chat, and - * may fluctuate over time. If you query this value directly after joining the - * group chat it may not be accurate, as it takes a certain amount of time for - * the server to send all presence packets to this client. - * - * @return the number of participants in the group chat. - */ - public int getParticipantCount() { - synchronized (participantsMap) { - return participantsMap.size(); - } - } - - /** - * Returns an Iterator (of Strings) for the list of fully qualified participants - * in the group chat. For example, "conference@chat.jivesoftware.com/SomeUser". - * Typically, a client would only display the nickname of the participant. To - * get the nickname from the fully qualified name, use the - * {@link org.jivesoftware.smack.util.StringUtils#parseResource(String)} method. - * Note: this value will only be accurate after joining the group chat, and may - * fluctuate over time. - * - * @return an Iterator for the participants in the group chat. - */ - public Iterator getParticipants() { - synchronized (participantsMap) { - return Collections.unmodifiableList(new ArrayList(participantsMap.keySet())).iterator(); - } - } - - /** - * Returns the presence info for a particular participant, or null if the participant - * is not in the room.

- * - * @param participant the room occupant to search for his presence. The format of participant must - * be: roomName@service/nickname (e.g. darkcave@macbeth.shakespeare.lit/thirdwitch). - * @return the participant's current presence, or null if the user is unavailable - * or if no presence information is available. - */ - public Presence getParticipantPresence(String participant) { - return (Presence) participantsMap.get(participant); - } - - /** - * Returns the participant's full JID when joining a Non-Anonymous room or null - * if the room is of type anonymous. If the room is of type semi-anonymous only the - * moderators will have access to the participants full JID. - * - * @param participant the room occupant to search for his JID. The format of participant must - * be: roomName@service/nickname (e.g. darkcave@macbeth.shakespeare.lit/thirdwitch). - * @return the participant's full JID when joining a Non-Anonymous room otherwise returns - * null. - */ - public String getParticipantJID(String participant) { - // Get the participant's presence - Presence presence = getParticipantPresence(participant); - // Get the MUC User extension - MUCUser mucUser = getMUCUserExtension(presence); - if (mucUser != null) { - return mucUser.getItem().getJid(); - } - return null; - } - - /** - * Adds a packet listener that will be notified of any new Presence packets - * sent to the group chat. Using a listener is a suitable way to know when the list - * of participants should be re-loaded due to any changes. - * - * @param listener a packet listener that will be notified of any presence packets - * sent to the group chat. - */ - public void addParticipantListener(PacketListener listener) { - connection.addPacketListener(listener, presenceFilter); - } - - /** - * Remoces a packet listener that was being notified of any new Presence packets - * sent to the group chat. - * - * @param listener a packet listener that was being notified of any presence packets - * sent to the group chat. - */ - public void removeParticipantListener(PacketListener listener) { - connection.removePacketListener(listener); - } - - /** - * Sends a message to the chat room. - * - * @param text the text of the message to send. - * @throws XMPPException if sending the message fails. - */ - public void sendMessage(String text) throws XMPPException { - Message message = new Message(room, Message.Type.GROUP_CHAT); - message.setBody(text); - connection.sendPacket(message); - } - - /** - * Returns a new Chat for sending private messages to a given room participant. - * The Chat's participant address is the room's JID (i.e. roomName@service/nick). The server - * service will change the 'from' address to the sender's room JID and delivering the message - * to the intended recipient's full JID. - * - * @param participant occupant unique room JID (e.g. 'darkcave@macbeth.shakespeare.lit/Paul'). - * @return new Chat for sending private messages to a given room participant. - */ - public Chat createPrivateChat(String participant) { - return new Chat(connection, participant); - } - - /** - * Creates a new Message to send to the chat room. - * - * @return a new Message addressed to the chat room. - */ - public Message createMessage() { - return new Message(room, Message.Type.GROUP_CHAT); - } - - /** - * Sends a Message to the chat room. - * - * @param message the message. - * @throws XMPPException if sending the message fails. - */ - public void sendMessage(Message message) throws XMPPException { - connection.sendPacket(message); - } - - /** - * Polls for and returns the next message, or null if there isn't - * a message immediately available. This method provides significantly different - * functionalty than the {@link #nextMessage()} method since it's non-blocking. - * In other words, the method call will always return immediately, whereas the - * nextMessage method will return only when a message is available (or after - * a specific timeout). - * - * @return the next message if one is immediately available and - * null otherwise. - */ - public Message pollMessage() { - return (Message) messageCollector.pollResult(); - } - - /** - * Returns the next available message in the chat. The method call will block - * (not return) until a message is available. - * - * @return the next message. - */ - public Message nextMessage() { - return (Message) messageCollector.nextResult(); - } - - /** - * Returns the next available message in the chat. The method call will block - * (not return) until a packet is available or the timeout has elapased. - * If the timeout elapses without a result, null will be returned. - * - * @param timeout the maximum amount of time to wait for the next message. - * @return the next message, or null if the timeout elapses without a - * message becoming available. - */ - public Message nextMessage(long timeout) { - return (Message) messageCollector.nextResult(timeout); - } - - /** - * Adds a packet listener that will be notified of any new messages in the - * group chat. Only "group chat" messages addressed to this group chat will - * be delivered to the listener. If you wish to listen for other packets - * that may be associated with this group chat, you should register a - * PacketListener directly with the XMPPConnection with the appropriate - * PacketListener. - * - * @param listener a packet listener. - */ - public void addMessageListener(PacketListener listener) { - connection.addPacketListener(listener, messageFilter); - } - - /** - * Removes a packet listener that was being notified of any new messages in the - * multi user chat. Only "group chat" messages addressed to this multi user chat were - * being delivered to the listener. - * - * @param listener a packet listener. - */ - public void removeMessageListener(PacketListener listener) { - connection.removePacketListener(listener); - } - - /** - * Changes the subject within the room. As a default, only users with a role of "moderator" - * are allowed to change the subject in a room. Although some rooms may be configured to - * allow a mere participant or even a visitor to change the subject. - * - * @param subject the new room's subject to set. - * @throws XMPPException if someone without appropriate privileges attempts to change the - * room subject will throw an error with code 403 (i.e. Forbidden) - */ - public void changeSubject(final String subject) throws XMPPException { - Message message = new Message(room, Message.Type.GROUP_CHAT); - message.setSubject(subject); - // Wait for an error or confirmation message back from the server. - PacketFilter responseFilter = - new AndFilter( - new FromContainsFilter(room), - new PacketTypeFilter(Message.class)); - responseFilter = new AndFilter(responseFilter, new PacketFilter() { - public boolean accept(Packet packet) { - Message msg = (Message) packet; - return subject.equals(msg.getSubject()); - } - }); - PacketCollector response = connection.createPacketCollector(responseFilter); - // Send change subject packet. - connection.sendPacket(message); - // Wait up to a certain number of seconds for a reply. - Message answer = - (Message) response.nextResult(SmackConfiguration.getPacketReplyTimeout()); - // Stop queuing results - response.cancel(); - - if (answer == null) { - throw new XMPPException("No response from server."); - } - else if (answer.getError() != null) { - throw new XMPPException(answer.getError()); - } - } - - /** - * Notification message that the user has joined the room. - */ - private synchronized void userHasJoined() { - // Update the list of joined rooms through this connection - ArrayList rooms = (ArrayList)joinedRooms.get(connection); - if (rooms == null) { - rooms = new ArrayList(); - joinedRooms.put(connection, rooms); - } - rooms.add(room); - } - - /** - * Notification message that the user has left the room. - */ - private synchronized void userHasLeft() { - // Update the list of joined rooms through this connection - ArrayList rooms = (ArrayList)joinedRooms.get(connection); - if (rooms == null) { - return; - } - rooms.remove(room); - } - - /** - * Returns the MUCUser packet extension included in the packet or null if none. - * - * @param packet the packet that may include the MUCUser extension. - * @return the MUCUser found in the packet. - */ - private MUCUser getMUCUserExtension(Packet packet) { - if (packet != null) { - // Get the MUC User extension - return (MUCUser) packet.getExtension("x", "http://jabber.org/protocol/muc#user"); - } - return null; - } - - /** - * Adds a listener that will be notified of changes in your status in the room - * such as the user being kicked, banned, or granted admin permissions. - * - * @param listener a user status listener. - */ - public void addUserStatusListener(UserStatusListener listener) { - synchronized (userStatusListeners) { - if (!userStatusListeners.contains(listener)) { - userStatusListeners.add(listener); - } - } - } - - /** - * Removes a listener that was being notified of changes in your status in the room - * such as the user being kicked, banned, or granted admin permissions. - * - * @param listener a user status listener. - */ - public void removeUserStatusListener(UserStatusListener listener) { - synchronized (userStatusListeners) { - userStatusListeners.remove(listener); - } - } - - private void fireUserStatusListeners(String methodName, Object[] params) { - UserStatusListener[] listeners = null; - synchronized (userStatusListeners) { - listeners = new UserStatusListener[userStatusListeners.size()]; - userStatusListeners.toArray(listeners); - } - // Get the classes of the method parameters - Class[] paramClasses = new Class[params.length]; - for (int i = 0; i < params.length; i++) { - paramClasses[i] = params[i].getClass(); - } - try { - // Get the method to execute based on the requested methodName and parameters classes - Method method = UserStatusListener.class.getDeclaredMethod(methodName, paramClasses); - for (int i = 0; i < listeners.length; i++) { - method.invoke(listeners[i], params); - } - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - } - - /** - * Adds a listener that will be notified of changes in participants status in the room - * such as the user being kicked, banned, or granted admin permissions. - * - * @param listener a participant status listener. - */ - public void addParticipantStatusListener(ParticipantStatusListener listener) { - synchronized (participantStatusListeners) { - if (!participantStatusListeners.contains(listener)) { - participantStatusListeners.add(listener); - } - } - } - - /** - * Removes a listener that was being notified of changes in participants status in the room - * such as the user being kicked, banned, or granted admin permissions. - * - * @param listener a participant status listener. - */ - public void removeParticipantStatusListener(ParticipantStatusListener listener) { - synchronized (participantStatusListeners) { - participantStatusListeners.remove(listener); - } - } - - private void fireParticipantStatusListeners(String methodName, String param) { - ParticipantStatusListener[] listeners = null; - synchronized (participantStatusListeners) { - listeners = new ParticipantStatusListener[participantStatusListeners.size()]; - participantStatusListeners.toArray(listeners); - } - try { - // Get the method to execute based on the requested methodName and parameter - Method method = - ParticipantStatusListener.class.getDeclaredMethod( - methodName, - new Class[] { String.class }); - for (int i = 0; i < listeners.length; i++) { - method.invoke(listeners[i], new Object[] {param}); - } - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - } - - private void init() { - // Create a collector for incoming messages. - messageFilter = - new AndFilter( - new FromContainsFilter(room), - new MessageTypeFilter(Message.Type.GROUP_CHAT)); - messageFilter = new AndFilter(messageFilter, new PacketFilter() { - public boolean accept(Packet packet) { - Message msg = (Message) packet; - return msg.getBody() != null; - } - }); - messageCollector = connection.createPacketCollector(messageFilter); - - // Create a listener for subject updates. - subjectFilter = - new AndFilter( - new FromContainsFilter(room), - new MessageTypeFilter(Message.Type.GROUP_CHAT)); - subjectFilter = new AndFilter(subjectFilter, new PacketFilter() { - public boolean accept(Packet packet) { - Message msg = (Message) packet; - return msg.getSubject() != null; - } - }); - subjectListener = new PacketListener() { - public void processPacket(Packet packet) { - Message msg = (Message) packet; - // Update the room subject - subject = msg.getSubject(); - // Fire event for subject updated listeners - fireSubjectUpdatedListeners( - msg.getSubject(), - msg.getFrom()); - - } - }; - connection.addPacketListener(subjectListener, subjectFilter); - - // Create a listener for all presence updates. - presenceFilter = - new AndFilter(new FromContainsFilter(room), new PacketTypeFilter(Presence.class)); - presenceListener = new PacketListener() { - public void processPacket(Packet packet) { - Presence presence = (Presence) packet; - String from = presence.getFrom(); - String myRoomJID = room + "/" + nickname; - boolean isUserStatusModification = presence.getFrom().equals(myRoomJID); - if (presence.getType() == Presence.Type.AVAILABLE) { - Presence oldPresence; - synchronized (participantsMap) { - oldPresence = (Presence)participantsMap.get(from); - participantsMap.put(from, presence); - } - if (oldPresence != null) { - // Get the previous participant's affiliation & role - MUCUser mucExtension = getMUCUserExtension(oldPresence); - String oldAffiliation = mucExtension.getItem().getAffiliation(); - String oldRole = mucExtension.getItem().getRole(); - // Get the new participant's affiliation & role - mucExtension = getMUCUserExtension(presence); - String newAffiliation = mucExtension.getItem().getAffiliation(); - String newRole = mucExtension.getItem().getRole(); - // Fire role modification events - checkRoleModifications(oldRole, newRole, isUserStatusModification, from); - // Fire affiliation modification events - checkAffiliationModifications( - oldAffiliation, - newAffiliation, - isUserStatusModification, - from); - } - } - else if (presence.getType() == Presence.Type.UNAVAILABLE) { - synchronized (participantsMap) { - participantsMap.remove(from); - } - MUCUser mucUser = getMUCUserExtension(presence); - if (mucUser != null && mucUser.getStatus() != null) { - // Fire events according to the received presence code - checkPresenceCode( - mucUser.getStatus().getCode(), - presence.getFrom().equals(myRoomJID), - mucUser, - from); - } - } - } - }; - connection.addPacketListener(presenceListener, presenceFilter); - - // Listens for all messages that include a MUCUser extension and fire the invitation - // rejection listeners if the message includes an invitation rejection. - declinesFilter = new PacketExtensionFilter("x", "http://jabber.org/protocol/muc#user"); - declinesListener = new PacketListener() { - public void processPacket(Packet packet) { - // Get the MUC User extension - MUCUser mucUser = getMUCUserExtension(packet); - // Check if the MUCUser informs that the invitee has declined the invitation - if (mucUser.getDecline() != null) { - // Fire event for invitation rejection listeners - fireInvitationRejectionListeners( - mucUser.getDecline().getFrom(), - mucUser.getDecline().getReason()); - } - }; - }; - connection.addPacketListener(declinesListener, declinesFilter); - } - - /** - * Fires notification events if the role of a room occupant has changed. If the occupant that - * changed his role is your occupant then the UserStatusListeners added to this - * MultiUserChat will be fired. On the other hand, if the occupant that changed - * his role is not yours then the ParticipantStatusListeners added to this - * MultiUserChat will be fired. The following table shows the events that will - * be fired depending on the previous and new role of the occupant. - * - *

-     * 
-     * 
-     * 
-     * 
-     * 
-     * 
-     *
-     * 
-     * 
-     * 
-     * 
-     * 
-     * 
-     * 
-     *
-     * 
-     * 
-     * 
-     * 
OldNewEvents
NoneVisitor--
VisitorParticipantvoiceGranted
ParticipantModeratormoderatorGranted
NoneParticipantvoiceGranted
NoneModeratorvoiceGranted + moderatorGranted
VisitorModeratorvoiceGranted + moderatorGranted
ModeratorParticipantmoderatorRevoked
ParticipantVisitorvoiceRevoked
VisitorNonekicked
ModeratorVisitorvoiceRevoked + moderatorRevoked
ModeratorNonekicked
ParticipantNonekicked
- *
- * - * @param oldRole the previous role of the user in the room before receiving the new presence - * @param newRole the new role of the user in the room after receiving the new presence - * @param isUserModification whether the received presence is about your user in the room or not - * @param from the participant whose role in the room has changed - * (e.g. room@conference.jabber.org/nick). - */ - private void checkRoleModifications( - String oldRole, - String newRole, - boolean isUserModification, - String from) { - // Voice was granted to a visitor - if (("visitor".equals(oldRole) || "none".equals(oldRole)) - && "participant".equals(newRole)) { - if (isUserModification) { - fireUserStatusListeners("voiceGranted", new Object[] {}); - } - else { - fireParticipantStatusListeners("voiceGranted", from); - } - } - // The participant's voice was revoked from the room - else if ( - "participant".equals(oldRole) - && ("visitor".equals(newRole) || "none".equals(newRole))) { - if (isUserModification) { - fireUserStatusListeners("voiceRevoked", new Object[] {}); - } - else { - fireParticipantStatusListeners("voiceRevoked", from); - } - } - // Moderator privileges were granted to a participant - if (!"moderator".equals(oldRole) && "moderator".equals(newRole)) { - if ("visitor".equals(oldRole) || "none".equals(oldRole)) { - if (isUserModification) { - fireUserStatusListeners("voiceGranted", new Object[] {}); - } - else { - fireParticipantStatusListeners("voiceGranted", from); - } - } - if (isUserModification) { - fireUserStatusListeners("moderatorGranted", new Object[] {}); - } - else { - fireParticipantStatusListeners("moderatorGranted", from); - } - } - // Moderator privileges were revoked from a participant - else if ("moderator".equals(oldRole) && !"moderator".equals(newRole)) { - if ("visitor".equals(newRole) || "none".equals(newRole)) { - if (isUserModification) { - fireUserStatusListeners("voiceRevoked", new Object[] {}); - } - else { - fireParticipantStatusListeners("voiceRevoked", from); - } - } - if (isUserModification) { - fireUserStatusListeners("moderatorRevoked", new Object[] {}); - } - else { - fireParticipantStatusListeners("moderatorRevoked", from); - } - } - } - - /** - * Fires notification events if the affiliation of a room occupant has changed. If the - * occupant that changed his affiliation is your occupant then the - * UserStatusListeners added to this MultiUserChat will be fired. - * On the other hand, if the occupant that changed his affiliation is not yours then the - * ParticipantStatusListeners added to this MultiUserChat will be - * fired. The following table shows the events that will be fired depending on the previous - * and new affiliation of the occupant. - * - *
-     * 
-     * 
-     * 
-     * 
-     * 
-     * 
-     * 
-     * 
-     * 
-     * 
-     *
-     * 
-     * 
-     * 
-     * 
-     * 
-     * 
-     * 
-     * 
-     * 
OldNewEvents
NoneMembermembershipGranted
MemberAdminmembershipRevoked + adminGranted
AdminOwneradminRevoked + ownershipGranted
NoneAdminadminGranted
NoneOwnerownershipGranted
MemberOwnermembershipRevoked + ownershipGranted
OwnerAdminownershipRevoked + adminGranted
AdminMemberadminRevoked + membershipGranted
MemberNonemembershipRevoked
OwnerMemberownershipRevoked + membershipGranted
OwnerNoneownershipRevoked
AdminNoneadminRevoked
AnyoneOutcastbanned
- *
- * - * @param oldAffiliation the previous affiliation of the user in the room before receiving the - * new presence - * @param newAffiliation the new affiliation of the user in the room after receiving the new - * presence - * @param isUserModification whether the received presence is about your user in the room or not - * @param from the participant whose role in the room has changed - * (e.g. room@conference.jabber.org/nick). - */ - private void checkAffiliationModifications( - String oldAffiliation, - String newAffiliation, - boolean isUserModification, - String from) { - // First check for revoked affiliation and then for granted affiliations. The idea is to - // first fire the "revoke" events and then fire the "grant" events. - - // The user's ownership to the room was revoked - if ("owner".equals(oldAffiliation) && !"owner".equals(newAffiliation)) { - if (isUserModification) { - fireUserStatusListeners("ownershipRevoked", new Object[] {}); - } - else { - fireParticipantStatusListeners("ownershipRevoked", from); - } - } - // The user's administrative privileges to the room were revoked - else if ("admin".equals(oldAffiliation) && !"admin".equals(newAffiliation)) { - if (isUserModification) { - fireUserStatusListeners("adminRevoked", new Object[] {}); - } - else { - fireParticipantStatusListeners("adminRevoked", from); - } - } - // The user's membership to the room was revoked - else if ("member".equals(oldAffiliation) && !"member".equals(newAffiliation)) { - if (isUserModification) { - fireUserStatusListeners("membershipRevoked", new Object[] {}); - } - else { - fireParticipantStatusListeners("membershipRevoked", from); - } - } - - // The user was granted ownership to the room - if (!"owner".equals(oldAffiliation) && "owner".equals(newAffiliation)) { - if (isUserModification) { - fireUserStatusListeners("ownershipGranted", new Object[] {}); - } - else { - fireParticipantStatusListeners("ownershipGranted", from); - } - } - // The user was granted administrative privileges to the room - else if (!"admin".equals(oldAffiliation) && "admin".equals(newAffiliation)) { - if (isUserModification) { - fireUserStatusListeners("adminGranted", new Object[] {}); - } - else { - fireParticipantStatusListeners("adminGranted", from); - } - } - // The user was granted membership to the room - else if (!"member".equals(oldAffiliation) && "member".equals(newAffiliation)) { - if (isUserModification) { - fireUserStatusListeners("membershipGranted", new Object[] {}); - } - else { - fireParticipantStatusListeners("membershipGranted", from); - } - } - } - - /** - * Fires events according to the received presence code. - * - * @param code - * @param isUserModification - * @param mucUser - * @param from - */ - private void checkPresenceCode( - String code, - boolean isUserModification, - MUCUser mucUser, - String from) { - // Check if a participant was kicked from the room - if ("307".equals(code)) { - // Check if this participant was kicked - if (isUserModification) { - joined = false; - - fireUserStatusListeners( - "kicked", - new Object[] { mucUser.getItem().getActor(), mucUser.getItem().getReason()}); - - // Reset participant information. - participantsMap = new HashMap(); - nickname = null; - userHasLeft(); - } - else { - fireParticipantStatusListeners("kicked", from); - } - } - // A user was banned from the room - else if ("301".equals(code)) { - // Check if this participant was banned - if (isUserModification) { - joined = false; - - fireUserStatusListeners( - "banned", - new Object[] { mucUser.getItem().getActor(), mucUser.getItem().getReason()}); - - // Reset participant information. - participantsMap = new HashMap(); - nickname = null; - userHasLeft(); - } - else { - // TODO Check if we have to send the JID of the banned user - fireParticipantStatusListeners("banned", from); - } - } - // A user's membership was revoked from the room - else if ("321".equals(code)) { - // Check if this participant's membership was revoked - if (isUserModification) { - joined = false; - - fireUserStatusListeners("membershipRevoked", new Object[] {}); - - // Reset participant information. - participantsMap = new HashMap(); - nickname = null; - userHasLeft(); - } - } - // A participant has changed his nickname in the room - else if ("303".equals(code)) { - fireParticipantStatusListeners("nicknameChanged", mucUser.getItem().getNick()); - } - } - - public void finalize() { - if (connection != null) { - messageCollector.cancel(); - connection.removePacketListener(subjectListener); - connection.removePacketListener(presenceListener); - connection.removePacketListener(declinesListener); - } - } - - /** - * An InvitationsMonitor monitors a given connection to detect room invitations. Every - * time the InvitationsMonitor detects a new invitation it will fire the invitation listeners. - * - * @author Gaston Dombiak - */ - private static class InvitationsMonitor implements ConnectionListener { - // We use a WeakHashMap so that the GC can collect the monitor when the - // connection is no longer referenced by any object. - private static Map monitors = new WeakHashMap(); - - private List invitationsListeners = new ArrayList(); - private XMPPConnection connection; - private PacketFilter invitationFilter; - private PacketListener invitationPacketListener; - - /** - * Returns a new or existing InvitationsMonitor for a given connection. - * - * @param connection the connection to monitor for room invitations. - * @return a new or existing InvitationsMonitor for a given connection. - */ - public static InvitationsMonitor getInvitationsMonitor(XMPPConnection conn) { - synchronized (monitors) { - if (!monitors.containsKey(conn)) { - // We need to use a WeakReference because the monitor references the - // connection and this could prevent the GC from collecting the monitor - // when no other object references the monitor - monitors.put(conn, new WeakReference(new InvitationsMonitor(conn))); - } - // Return the InvitationsMonitor that monitors the connection - return (InvitationsMonitor) ((WeakReference) monitors.get(conn)).get(); - } - } - - /** - * Creates a new InvitationsMonitor that will monitor invitations received - * on a given connection. - * - * @param connection the connection to monitor for possible room invitations - */ - private InvitationsMonitor(XMPPConnection connection) { - this.connection = connection; - } - - /** - * Adds a listener to invitation notifications. The listener will be fired anytime - * an invitation is received.

- * - * If this is the first monitor's listener then the monitor will be initialized in - * order to start listening to room invitations. - * - * @param listener an invitation listener. - */ - public void addInvitationListener(InvitationListener listener) { - synchronized (invitationsListeners) { - // If this is the first monitor's listener then initialize the listeners - // on the connection to detect room invitations - if (invitationsListeners.size() == 0) { - init(); - } - if (!invitationsListeners.contains(listener)) { - invitationsListeners.add(listener); - } - } - } - - /** - * Removes a listener to invitation notifications. The listener will be fired anytime - * an invitation is received.

- * - * If there are no more listeners to notifiy for room invitations then the monitor will - * be stopped. As soon as a new listener is added to the monitor, the monitor will resume - * monitoring the connection for new room invitations. - * - * @param listener an invitation listener. - */ - public void removeInvitationListener(InvitationListener listener) { - synchronized (invitationsListeners) { - if (invitationsListeners.contains(listener)) { - invitationsListeners.remove(listener); - } - // If there are no more listeners to notifiy for room invitations - // then proceed to cancel/release this monitor - if (invitationsListeners.size() == 0) { - cancel(); - } - } - } - - /** - * Fires invitation listeners. - */ - private void fireInvitationListeners( - String room, - String inviter, - String reason, - String password) { - InvitationListener[] listeners = null; - synchronized (invitationsListeners) { - listeners = new InvitationListener[invitationsListeners.size()]; - invitationsListeners.toArray(listeners); - } - for (int i = 0; i < listeners.length; i++) { - listeners[i].invitationReceived(connection, room, inviter, reason, password); - } - } - - public void connectionClosed() { - cancel(); - } - - public void connectionClosedOnError(Exception e) { - cancel(); - } - - /** - * Initializes the listeners to detect received room invitations and to detect when the - * connection gets closed. As soon as a room invitation is received the invitations - * listeners will be fired. When the connection gets closed the monitor will remove - * his listeners on the connection. - */ - private void init() { - // Listens for all messages that include a MUCUser extension and fire the invitation - // listeners if the message includes an invitation. - invitationFilter = - new PacketExtensionFilter("x", "http://jabber.org/protocol/muc#user"); - invitationPacketListener = new PacketListener() { - public void processPacket(Packet packet) { - // Get the MUCUser extension - MUCUser mucUser = - (MUCUser) packet.getExtension("x", "http://jabber.org/protocol/muc#user"); - // Check if the MUCUser extension includes an invitation - if (mucUser.getInvite() != null) { - // Fire event for invitation listeners - fireInvitationListeners( - packet.getFrom(), - mucUser.getInvite().getFrom(), - mucUser.getInvite().getReason(), - mucUser.getPassword()); - } - }; - }; - connection.addPacketListener(invitationPacketListener, invitationFilter); - // Add a listener to detect when the connection gets closed in order to - // cancel/release this monitor - connection.addConnectionListener(this); - } - - /** - * Cancels all the listeners that this InvitationsMonitor has added to the connection. - */ - private void cancel() { - connection.removePacketListener(invitationPacketListener); - connection.removeConnectionListener(this); - } - - } -} diff --git a/source/org/jivesoftware/smackx/ParticipantStatusListener.java b/source/org/jivesoftware/smackx/ParticipantStatusListener.java deleted file mode 100644 index ff339d3c9..000000000 --- a/source/org/jivesoftware/smackx/ParticipantStatusListener.java +++ /dev/null @@ -1,186 +0,0 @@ -/** -* $RCSfile$ -* $Revision$ -* $Date$ -* -* Copyright (C) 2002-2003 Jive Software. All rights reserved. -* ==================================================================== -* The Jive Software License (based on Apache Software License, Version 1.1) -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions -* are met: -* -* 1. Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* -* 2. Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in -* the documentation and/or other materials provided with the -* distribution. -* -* 3. The end-user documentation included with the redistribution, -* if any, must include the following acknowledgment: -* "This product includes software developed by -* Jive Software (http://www.jivesoftware.com)." -* Alternately, this acknowledgment may appear in the software itself, -* if and wherever such third-party acknowledgments normally appear. -* -* 4. The names "Smack" and "Jive Software" must not be used to -* endorse or promote products derived from this software without -* prior written permission. For written permission, please -* contact webmaster@jivesoftware.com. -* -* 5. Products derived from this software may not be called "Smack", -* nor may "Smack" appear in their name, without prior written -* permission of Jive Software. -* -* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED -* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL JIVE SOFTWARE OR -* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT -* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -* SUCH DAMAGE. -* ==================================================================== -*/ - -package org.jivesoftware.smackx; - -/** - * A listener that is fired anytime a participant's status in a room is changed, such as the - * user being kicked, banned, or granted admin permissions. - * - * @author Gaston Dombiak - */ -public interface ParticipantStatusListener { - - /** - * Called when a room participant has been kicked from the room. This means that the kicked - * participant is no longer participating in the room. - * - * @param participant the participant that was kicked from the room - * (e.g. room@conference.jabber.org/nick). - */ - public abstract void kicked(String participant); - - /** - * Called when a moderator grants voice to a visitor. This means that the visitor - * can now participate in the moderated room sending messages to all occupants. - * - * @param participant the participant that was granted voice in the room - * (e.g. room@conference.jabber.org/nick). - */ - public abstract void voiceGranted(String participant); - - /** - * Called when a moderator revokes voice from a participant. This means that the participant - * in the room was able to speak and now is a visitor that can't send messages to the room - * occupants. - * - * @param participant the participant that was revoked voice from the room - * (e.g. room@conference.jabber.org/nick). - */ - public abstract void voiceRevoked(String participant); - - /** - * Called when an administrator or owner banned a participant from the room. This means that - * banned participant will no longer be able to join the room unless the ban has been removed. - * - * @param participant the participant that was banned from the room - * (e.g. room@conference.jabber.org/nick). - */ - public abstract void banned(String participant); - - /** - * Called when an administrator grants a user membership to the room. This means that the user - * will be able to join the members-only room. - * - * @param participant the participant that was granted membership in the room - * (e.g. room@conference.jabber.org/nick). - */ - public abstract void membershipGranted(String participant); - - /** - * Called when an administrator revokes a user membership to the room. This means that the - * user will not be able to join the members-only room. - * - * @param participant the participant that was revoked membership from the room - * (e.g. room@conference.jabber.org/nick). - */ - public abstract void membershipRevoked(String participant); - - /** - * Called when an administrator grants moderator privileges to a user. This means that the user - * will be able to kick users, grant and revoke voice, invite other users, modify room's - * subject plus all the partcipants privileges. - * - * @param participant the participant that was granted moderator privileges in the room - * (e.g. room@conference.jabber.org/nick). - */ - public abstract void moderatorGranted(String participant); - - /** - * Called when an administrator revokes moderator privileges from a user. This means that the - * user will no longer be able to kick users, grant and revoke voice, invite other users, - * modify room's subject plus all the partcipants privileges. - * - * @param participant the participant that was revoked moderator privileges in the room - * (e.g. room@conference.jabber.org/nick). - */ - public abstract void moderatorRevoked(String participant); - - /** - * Called when an owner grants a user ownership on the room. This means that the user - * will be able to change defining room features as well as perform all administrative - * functions. - * - * @param participant the participant that was granted ownership on the room - * (e.g. room@conference.jabber.org/nick). - */ - public abstract void ownershipGranted(String participant); - - /** - * Called when an owner revokes a user ownership on the room. This means that the user - * will no longer be able to change defining room features as well as perform all - * administrative functions. - * - * @param participant the participant that was revoked ownership on the room - * (e.g. room@conference.jabber.org/nick). - */ - public abstract void ownershipRevoked(String participant); - - /** - * Called when an owner grants administrator privileges to a user. This means that the user - * will be able to perform administrative functions such as banning users and edit moderator - * list. - * - * @param participant the participant that was granted administrator privileges - * (e.g. room@conference.jabber.org/nick). - */ - public abstract void adminGranted(String participant); - - /** - * Called when an owner revokes administrator privileges from a user. This means that the user - * will no longer be able to perform administrative functions such as banning users and edit - * moderator list. - * - * @param participant the participant that was revoked administrator privileges - * (e.g. room@conference.jabber.org/nick). - */ - public abstract void adminRevoked(String participant); - - /** - * Called when a participant changed his/her nickname in the room. The new participant's - * nickname will be informed with the next available presence. - * - * @param nickname the old nickname that the participant decided to change. - */ - public abstract void nicknameChanged(String nickname); - -} diff --git a/source/org/jivesoftware/smackx/SubjectUpdatedListener.java b/source/org/jivesoftware/smackx/SubjectUpdatedListener.java deleted file mode 100644 index 62f6dc227..000000000 --- a/source/org/jivesoftware/smackx/SubjectUpdatedListener.java +++ /dev/null @@ -1,70 +0,0 @@ -/** -* $RCSfile$ -* $Revision$ -* $Date$ -* -* Copyright (C) 2002-2003 Jive Software. All rights reserved. -* ==================================================================== -* The Jive Software License (based on Apache Software License, Version 1.1) -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions -* are met: -* -* 1. Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* -* 2. Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in -* the documentation and/or other materials provided with the -* distribution. -* -* 3. The end-user documentation included with the redistribution, -* if any, must include the following acknowledgment: -* "This product includes software developed by -* Jive Software (http://www.jivesoftware.com)." -* Alternately, this acknowledgment may appear in the software itself, -* if and wherever such third-party acknowledgments normally appear. -* -* 4. The names "Smack" and "Jive Software" must not be used to -* endorse or promote products derived from this software without -* prior written permission. For written permission, please -* contact webmaster@jivesoftware.com. -* -* 5. Products derived from this software may not be called "Smack", -* nor may "Smack" appear in their name, without prior written -* permission of Jive Software. -* -* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED -* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL JIVE SOFTWARE OR -* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT -* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -* SUCH DAMAGE. -* ==================================================================== -*/ - -package org.jivesoftware.smackx; - -/** - * A listener that is fired anytime a MUC room changes its subject. - * - * @author Gaston Dombiak - */ -public interface SubjectUpdatedListener { - - /** - * Called when a MUC room has changed its subject. - * - * @param subject the new room's subject. - * @param from the user that changed the room's subject (e.g. room@conference.jabber.org/nick). - */ - public abstract void subjectUpdated(String subject, String from); - -} diff --git a/source/org/jivesoftware/smackx/UserStatusListener.java b/source/org/jivesoftware/smackx/UserStatusListener.java deleted file mode 100644 index 004780e14..000000000 --- a/source/org/jivesoftware/smackx/UserStatusListener.java +++ /dev/null @@ -1,159 +0,0 @@ -/** -* $RCSfile$ -* $Revision$ -* $Date$ -* -* Copyright (C) 2002-2003 Jive Software. All rights reserved. -* ==================================================================== -* The Jive Software License (based on Apache Software License, Version 1.1) -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions -* are met: -* -* 1. Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* -* 2. Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in -* the documentation and/or other materials provided with the -* distribution. -* -* 3. The end-user documentation included with the redistribution, -* if any, must include the following acknowledgment: -* "This product includes software developed by -* Jive Software (http://www.jivesoftware.com)." -* Alternately, this acknowledgment may appear in the software itself, -* if and wherever such third-party acknowledgments normally appear. -* -* 4. The names "Smack" and "Jive Software" must not be used to -* endorse or promote products derived from this software without -* prior written permission. For written permission, please -* contact webmaster@jivesoftware.com. -* -* 5. Products derived from this software may not be called "Smack", -* nor may "Smack" appear in their name, without prior written -* permission of Jive Software. -* -* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED -* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL JIVE SOFTWARE OR -* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT -* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -* SUCH DAMAGE. -* ==================================================================== -*/ - -package org.jivesoftware.smackx; - -/** - * A listener that is fired anytime your participant's status in a room is changed, such as the - * user being kicked, banned, or granted admin permissions. - * - * @author Gaston Dombiak - */ -public interface UserStatusListener { - - /** - * Called when a moderator kicked your user from the room. This means that you are no longer - * participanting in the room. - * - * @param actor the moderator that kicked your user from the room (e.g. user@host.org). - * @param reason the reason provided by the actor to kick you from the room. - */ - public abstract void kicked(String actor, String reason); - - /** - * Called when a moderator grants voice to your user. This means that you were a visitor in - * the moderated room before and now you can participate in the room by sending messages to - * all occupants. - * - */ - public abstract void voiceGranted(); - - /** - * Called when a moderator revokes voice from your user. This means that you were a - * participant in the room able to speak and now you are a visitor that can't send - * messages to the room occupants. - * - */ - public abstract void voiceRevoked(); - - /** - * Called when an administrator or owner banned your user from the room. This means that you - * will no longer be able to join the room unless the ban has been removed. - * - * @param actor the administrator that banned your user (e.g. user@host.org). - * @param reason the reason provided by the administrator to banned you. - */ - public abstract void banned(String actor, String reason); - - /** - * Called when an administrator grants your user membership to the room. This means that you - * will be able to join the members-only room. - * - */ - public abstract void membershipGranted(); - - /** - * Called when an administrator revokes your user membership to the room. This means that you - * will not be able to join the members-only room. - * - */ - public abstract void membershipRevoked(); - - /** - * Called when an administrator grants moderator privileges to your user. This means that you - * will be able to kick users, grant and revoke voice, invite other users, modify room's - * subject plus all the partcipants privileges. - * - */ - public abstract void moderatorGranted(); - - /** - * Called when an administrator revokes moderator privileges from your user. This means that - * you will no longer be able to kick users, grant and revoke voice, invite other users, - * modify room's subject plus all the partcipants privileges. - * - */ - public abstract void moderatorRevoked(); - - /** - * Called when an owner grants to your user ownership on the room. This means that you - * will be able to change defining room features as well as perform all administrative - * functions. - * - */ - public abstract void ownershipGranted(); - - /** - * Called when an owner revokes from your user ownership on the room. This means that you - * will no longer be able to change defining room features as well as perform all - * administrative functions. - * - */ - public abstract void ownershipRevoked(); - - /** - * Called when an owner grants administrator privileges to your user. This means that you - * will be able to perform administrative functions such as banning users and edit moderator - * list. - * - */ - public abstract void adminGranted(); - - /** - * Called when an owner revokes administrator privileges from your user. This means that you - * will no longer be able to perform administrative functions such as banning users and edit - * moderator list. - * - */ - public abstract void adminRevoked(); - -} diff --git a/test/org/jivesoftware/smackx/muc/MultiUserChatCreationTest.java b/test/org/jivesoftware/smackx/muc/MultiUserChatCreationTest.java new file mode 100644 index 000000000..1ca314fb8 --- /dev/null +++ b/test/org/jivesoftware/smackx/muc/MultiUserChatCreationTest.java @@ -0,0 +1,184 @@ +/** + * $RCSfile$ + * $Revision$ + * $Date$ + * + * Copyright (C) 2002-2003 Jive Software. All rights reserved. + * ==================================================================== + * The Jive Software License (based on Apache Software License, Version 1.1) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, + * if any, must include the following acknowledgment: + * "This product includes software developed by + * Jive Software (http://www.jivesoftware.com)." + * Alternately, this acknowledgment may appear in the software itself, + * if and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Smack" and "Jive Software" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please + * contact webmaster@jivesoftware.com. + * + * 5. Products derived from this software may not be called "Smack", + * nor may "Smack" appear in their name, without prior written + * permission of Jive Software. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JIVE SOFTWARE OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + */ +package org.jivesoftware.smackx.muc; + +import java.util.*; + +import org.jivesoftware.smack.*; +import org.jivesoftware.smackx.*; + +import junit.framework.TestCase; + +/** + * Tests creating new MUC rooms. + * + * @author Gaston Dombiak + */ +public class MultiUserChatCreationTest extends TestCase { + + private String host = "gatoux"; + private String room = "fruta124@conference." + host; + + private XMPPConnection conn1 = null; + private XMPPConnection conn2 = null; + + private String user1 = null; + private String user2 = null; + + /** + * Constructor for MultiUserChatCreationTest. + * @param arg0 + */ + public MultiUserChatCreationTest(String arg0) { + super(arg0); + } + + /** + * Tests creating a new "Reserved Room". + */ + public void testCreateReservedRoom() { + MultiUserChat muc = new MultiUserChat(conn1, room); + + try { + // Create the room + muc.create("testbot1"); + + // Get the the room's configuration form + Form form = muc.getConfigurationForm(); + assertNotNull("No room configuration form", form); + // Create a new form to submit based on the original form + Form submitForm = form.createAnswerForm(); + // Add default answers to the form to submit + for (Iterator fields = form.getFields(); fields.hasNext();) { + FormField field = (FormField) fields.next(); + if (!FormField.TYPE_HIDDEN.equals(field.getType()) + && field.getVariable() != null) { + // Add the field values to a List + List values = new ArrayList(); + for (Iterator it = field.getValues(); it.hasNext();) { + values.add((String) it.next()); + } + // Sets the new answer to form to submit + submitForm.setAnswer(field.getVariable(), values); + } + } + // Update the new room's configuration + muc.sendConfigurationForm(submitForm); + + // Destroy the new room + muc.destroy("The room has almost no activity...", null); + + } + catch (XMPPException e) { + fail(e.getMessage()); + } + } + + /** + * Tests creating a new "Instant Room". + */ + public void testCreateInstantRoom() { + MultiUserChat muc = new MultiUserChat(conn1, room); + + try { + // Create the room + muc.create("testbot"); + + // Send an empty room configuration form which indicates that we want + // an instant room + muc.sendConfigurationForm(new Form(Form.TYPE_SUBMIT)); + + // Destroy the new room + muc.destroy("The room has almost no activity...", null); + } + catch (XMPPException e) { + fail(e.getMessage()); + } + } + + protected void setUp() throws Exception { + super.setUp(); + try { + // Connect to the server + conn1 = new XMPPConnection(host); + conn2 = new XMPPConnection(host); + + // Create the test accounts + if (!conn1.getAccountManager().supportsAccountCreation()) + fail("Server does not support account creation"); + conn1.getAccountManager().createAccount("gato3", "gato3"); + conn2.getAccountManager().createAccount("gato4", "gato4"); + + // Login with the test accounts + conn1.login("gato3", "gato3"); + conn2.login("gato4", "gato4"); + + user1 = "gato3@" + conn1.getHost(); + user2 = "gato4@" + conn2.getHost(); + + } + catch (Exception e) { + fail(e.getMessage()); + } + } + + protected void tearDown() throws Exception { + super.tearDown(); + // Delete the created accounts for the test + conn1.getAccountManager().deleteAccount(); + conn2.getAccountManager().deleteAccount(); + + // Close all the connections + conn1.close(); + conn2.close(); + } +} diff --git a/test/org/jivesoftware/smackx/muc/MultiUserChatTest.java b/test/org/jivesoftware/smackx/muc/MultiUserChatTest.java new file mode 100644 index 000000000..304838d19 --- /dev/null +++ b/test/org/jivesoftware/smackx/muc/MultiUserChatTest.java @@ -0,0 +1,1344 @@ +/** +* $RCSfile$ +* $Revision$ +* $Date$ +* +* Copyright (C) 2002-2003 Jive Software. All rights reserved. +* ==================================================================== +* The Jive Software License (based on Apache Software License, Version 1.1) +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in +* the documentation and/or other materials provided with the +* distribution. +* +* 3. The end-user documentation included with the redistribution, +* if any, must include the following acknowledgment: +* "This product includes software developed by +* Jive Software (http://www.jivesoftware.com)." +* Alternately, this acknowledgment may appear in the software itself, +* if and wherever such third-party acknowledgments normally appear. +* +* 4. The names "Smack" and "Jive Software" must not be used to +* endorse or promote products derived from this software without +* prior written permission. For written permission, please +* contact webmaster@jivesoftware.com. +* +* 5. Products derived from this software may not be called "Smack", +* nor may "Smack" appear in their name, without prior written +* permission of Jive Software. +* +* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL JIVE SOFTWARE OR +* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +* SUCH DAMAGE. +* ==================================================================== +*/ + +package org.jivesoftware.smackx.muc; + +import java.util.Iterator; + +import org.jivesoftware.smack.*; +import org.jivesoftware.smack.filter.*; +import org.jivesoftware.smack.packet.*; +import org.jivesoftware.smackx.Form; + +import junit.framework.TestCase; + +/** + * Tests the new MUC functionalities. + * + * @author Gaston Dombiak + */ +public class MultiUserChatTest extends TestCase { + + private String host = "gatoux"; + private String room = "fruta124@conference." + host; + + private XMPPConnection conn1 = null; + private XMPPConnection conn2 = null; + private XMPPConnection conn3 = null; + + private String user1 = null; + private String user2 = null; + private String user3 = null; + + private MultiUserChat muc; + + /** + * Constructor for MultiUserChatTest. + * @param arg0 + */ + public MultiUserChatTest(String arg0) { + super(arg0); + } + + /*public void testDiscussionHistory() { + try { + // User2 joins the room + MultiUserChat muc2 = new MultiUserChat(conn2, room); + muc2.join("testbot2"); + // User2 sends some messages to the room + muc2.sendMessage("Message 1"); + muc2.sendMessage("Message 2"); + muc2.sendMessage("Message 3"); + + // User3 joins the room requesting to receive the last 2 messages. + MultiUserChat muc3 = new MultiUserChat(conn2, room); + DiscussionHistory history = new DiscussionHistory(); + history.setMaxStanzas(2); + muc3.join("testbot3", null, history, SmackConfiguration.getPacketReplyTimeout()); + + Message msg; + msg = muc3.nextMessage(1000); + assertNotNull("First message is null", msg); + assertEquals("Body of first message is incorrect", "Message 2", msg.getBody()); + msg = muc3.nextMessage(1000); + assertNotNull("Second message is null", msg); + assertEquals("Body of second message is incorrect", "Message 3", msg.getBody()); + msg = muc3.nextMessage(1000); + assertNull("Third message is not null", msg); + + // User2 leaves the room + muc2.leave(); + // User3 leaves the room + muc3.leave(); + + } + catch (Exception e) { + fail(e.getMessage()); + e.printStackTrace(); + } + }*/ + + public void testParticipantPresence() { + try { + // User2 joins the new room + MultiUserChat muc2 = new MultiUserChat(conn2, room); + muc2.join("testbot2"); + Thread.sleep(300); + + // User1 checks the presence of user2 in the room + Presence presence = muc.getParticipantPresence(room + "/testbot2"); + assertNotNull("Presence of user2 in room is missing", presence); + assertEquals( + "Presence mode of user2 is wrong", + Presence.Mode.AVAILABLE, + presence.getMode()); + + // User2 changes his availability to AWAY + muc2.changeAvailabilityStatus("Gone to have lunch", Presence.Mode.AWAY); + Thread.sleep(200); + // User1 checks the presence of user2 in the room + presence = muc.getParticipantPresence(room + "/testbot2"); + assertNotNull("Presence of user2 in room is missing", presence); + assertEquals("Presence mode of user2 is wrong", Presence.Mode.AWAY, presence.getMode()); + assertEquals( + "Presence status of user2 is wrong", + "Gone to have lunch", + presence.getStatus()); + + // User2 changes his nickname + muc2.changeNickname("testbotII"); + Thread.sleep(200); + // User1 checks the presence of user2 in the room + presence = muc.getParticipantPresence(room + "/testbot2"); + assertNull("Presence of participant testbot2 still exists", presence); + presence = muc.getParticipantPresence(room + "/testbotII"); + assertNotNull("Presence of participant testbotII does not exist", presence); + assertEquals( + "Presence of participant testbotII has a wrong from", + room + "/testbotII", + presence.getFrom()); + + // User2 leaves the room + muc2.leave(); + Thread.sleep(200); + // User1 checks the presence of user2 in the room + presence = muc.getParticipantPresence(room + "/testbotII"); + assertNull("Presence of participant testbotII still exists", presence); + + } + catch (Exception e) { + fail(e.getMessage()); + e.printStackTrace(); + } + } + + public void testInvitation() { + final String[] answer = new String[2]; + try { + // User2 joins the new room + MultiUserChat muc2 = new MultiUserChat(conn2, room); + muc2.join("testbot2"); + + // User3 is listening to MUC invitations + MultiUserChat.addInvitationListener(conn3, new InvitationListener() { + public void invitationReceived( + XMPPConnection conn, + String room, + String inviter, + String reason, + String password) { + // Indicate that the invitation was received + answer[0] = reason; + // Reject the invitation + MultiUserChat.decline(conn, room, inviter, "I'm busy right now"); + } + }); + + // User2 is listening to invitation rejections + muc2.addInvitationRejectionListener(new InvitationRejectionListener() { + public void invitationDeclined(String invitee, String reason) { + // Indicate that the rejection was received + answer[1] = reason; + } + }); + + // User2 invites user3 to join to the room + muc2.invite(user3, "Meet me in this excellent room"); + Thread.sleep(350); + + assertEquals( + "Invitation was not received", + "Meet me in this excellent room", + answer[0]); + // TODO This line was commented because jabberd2 is not accepting rejections + // from users that aren't participants of the room. Comment out this line when + // running the test against a server that correctly implements JEP-45 + //assertEquals("Rejection was not received", "I'm busy right now", answer[1]); + + // User2 leaves the room + muc2.leave(); + } + catch (Exception e) { + fail(e.getMessage()); + e.printStackTrace(); + } + } + + public void testDiscoverJoinedRooms() { + try { + // Check that user1 has joined only to one room + Iterator joinedRooms = MultiUserChat.getJoinedRooms(conn2, user1); + assertTrue("Joined rooms shouldn't be empty", joinedRooms.hasNext()); + assertEquals( + "Joined room is incorrect", + joinedRooms.next(), + room); + assertFalse("User has joined more than one room", joinedRooms.hasNext()); + + // Leave the new room + muc.leave(); + + // Check that user1 is not currently join any room + joinedRooms = MultiUserChat.getJoinedRooms(conn2, user1); + assertFalse("Joined rooms should be empty", joinedRooms.hasNext()); + + muc.join("testbot"); + } + catch (XMPPException e) { + fail(e.getMessage()); + e.printStackTrace(); + } + } + + public void testDiscoverMUCSupport() { + // Discover user1 support of MUC + boolean supports = MultiUserChat.isServiceEnabled(conn2, user1); + assertTrue("Couldn't detect that user1 supports MUC", supports); + } + + public void testPrivateChat() { + try { + // User2 joins the new room + MultiUserChat muc2 = new MultiUserChat(conn2, room); + muc2.join("testbot2"); + + conn1.addPacketListener(new PacketListener() { + public void processPacket(Packet packet) { + Message message = (Message) packet; + Chat chat2 = new Chat(conn1, message.getFrom(), message.getThread()); + assertEquals( + "Sender of chat is incorrect", + room + "/testbot2", + message.getFrom()); + try { + chat2.sendMessage("ACK"); + } + catch (XMPPException e) { + fail(e.getMessage()); + } + } + }, + new AndFilter( + new MessageTypeFilter(Message.Type.CHAT), + new PacketTypeFilter(Message.class)) + ); + + // Start a private chat with another participant + Chat chat = muc2.createPrivateChat(room + "/testbot"); + chat.sendMessage("Hello there"); + + Message response = chat.nextMessage(2000); + assertEquals("Sender of response is incorrect",room + "/testbot", response.getFrom()); + assertEquals("Body of response is incorrect","ACK", response.getBody()); + + // User2 leaves the room + muc2.leave(); + } + catch (Exception e) { + fail(e.getMessage()); + e.printStackTrace(); + } + } + + // TODO This test is commented because jabberd2 doesn't support discovering reserved nicknames + /*public void testReservedNickname() { + // User2 joins the new room + MultiUserChat muc2 = new MultiUserChat(conn2, room); + + String reservedNickname = muc2.getReservedNickname(); + assertNull("Reserved nickname is not null", reservedNickname); + }*/ + + public void testChangeSubject() { + final String[] answer = new String[2]; + try { + // User1 sets an initial subject + muc.changeSubject("Initial Subject"); + + // User2 joins the new room + MultiUserChat muc2 = new MultiUserChat(conn2, room); + muc2.join("testbot2"); + + // User3 joins the new room + MultiUserChat muc3 = new MultiUserChat(conn3, room); + muc3.join("testbot3"); + + // User3 wants to be notified every time the room's subject is changed. + muc3.addSubjectUpdatedListener(new SubjectUpdatedListener() { + public void subjectUpdated(String subject, String from) { + answer[0] = subject; + answer[1] = from; + } + }); + + // Check that a 403 error is received when a not allowed user tries to change the + // subject in a room + try { + muc2.changeSubject("New Subject2"); + fail("User2 was allowed to change the room's subject"); + } + catch (XMPPException e) { + XMPPError xmppError = e.getXMPPError(); + assertNotNull( + "No XMPPError was received when changing the room's subject", + xmppError); + assertEquals( + "Different error code was received while changing the room's subject", + 403, + xmppError.getCode()); + } + + // Check that every MUC updates its subject when an allowed user changes the subject + // in a room + muc.changeSubject("New Subject1"); + Thread.sleep(300); + // Check that User2's MUC has updated its subject + assertEquals( + "User2 didn't receive the subject notification", + "New Subject1", + muc2.getSubject()); + // Check that SubjectUpdatedListener is working OK + assertEquals( + "User3 didn't receive the subject notification", + "New Subject1", + answer[0]); + assertEquals( + "User3 didn't receive the correct user that changed the subject", + room + "/testbot", + answer[1]); + + // User2 leaves the room + muc2.leave(); + // User3 leaves the room + muc3.leave(); + } + catch (Exception e) { + fail(e.getMessage()); + e.printStackTrace(); + } + } + + public void testKickParticipant() { + final String[] answer = new String[3]; + try { + // User2 joins the new room + MultiUserChat muc2 = new MultiUserChat(conn2, room); + muc2.join("testbot2"); + // User2 will lister for his own "kicking" + muc2.addUserStatusListener(new DefaultUserStatusListener() { + public void kicked(String actor, String reason) { + super.kicked(actor, reason); + answer[0] = actor; + answer[1] = reason; + } + }); + + // User3 joins the new room + MultiUserChat muc3 = new MultiUserChat(conn3, room); + muc3.join("testbot3"); + // User3 will lister for user2's "kicking" + muc3.addParticipantStatusListener(new DefaultParticipantStatusListener() { + public void kicked(String participant) { + super.kicked(participant); + answer[2] = participant; + } + }); + + try { + // Check whether a simple participant can kick a room owner or not + muc2.kickParticipant("testbot", "Because I'm bad"); + fail("User2 was able to kick a room owner"); + } + catch (XMPPException e) { + XMPPError xmppError = e.getXMPPError(); + assertNotNull( + "No XMPPError was received when kicking a room owner", + xmppError); + assertEquals( + "A simple participant was able to kick another participant from the room", + 403, + xmppError.getCode()); + } + + // Check that the room's owner can kick a simple participant + muc.kickParticipant("testbot2", "Because I'm the owner"); + Thread.sleep(300); + + assertNull("User2 wasn't kicked from the room", muc.getParticipantPresence(room + "/testbot2")); + + assertFalse("User2 thinks that he's still in the room", muc2.isJoined()); + + // Check that UserStatusListener is working OK + assertEquals( + "User2 didn't receive the correct initiator of the kick", + "gato3@" + conn1.getHost(), + answer[0]); + assertEquals( + "User2 didn't receive the correct reason for the kick", + "Because I'm the owner", + answer[1]); + + // Check that ParticipantStatusListener is working OK + assertEquals( + "User3 didn't receive the correct kicked participant", + room + "/testbot2", + answer[2]); + + // User2 leaves the room + muc2.leave(); + // User3 leaves the room + muc3.leave(); + } + catch (Exception e) { + fail(e.getMessage()); + e.printStackTrace(); + } + } + + public void testBanUser() { + final String[] answer = new String[3]; + try { + // User2 joins the new room + MultiUserChat muc2 = new MultiUserChat(conn2, room); + muc2.join("testbot2"); + // User2 will lister for his own "banning" + muc2.addUserStatusListener(new DefaultUserStatusListener() { + public void banned(String actor, String reason) { + super.banned(actor, reason); + answer[0] = actor; + answer[1] = reason; + } + }); + + // User3 joins the new room + MultiUserChat muc3 = new MultiUserChat(conn3, room); + muc3.join("testbot3"); + // User3 will lister for user2's "banning" + muc3.addParticipantStatusListener(new DefaultParticipantStatusListener() { + public void banned(String participant) { + super.banned(participant); + answer[2] = participant; + } + }); + + try { + // Check whether a simple participant can ban a room owner or not + muc2.banUser("gato3@" + conn2.getHost(), "Because I'm bad"); + fail("User2 was able to ban a room owner"); + } + catch (XMPPException e) { + XMPPError xmppError = e.getXMPPError(); + assertNotNull( + "No XMPPError was received when banning a room owner", + xmppError); + assertEquals( + "A simple participant was able to ban another participant from the room", + 403, + xmppError.getCode()); + } + + // Check that the room's owner can ban a simple participant + muc.banUser("gato4@" + conn2.getHost(), "Because I'm the owner"); + Thread.sleep(300); + + assertNull("User2 wasn't banned from the room", muc.getParticipantPresence(room + "/testbot2")); + + assertFalse("User2 thinks that he's still in the room", muc2.isJoined()); + + // Check that UserStatusListener is working OK + assertEquals( + "User2 didn't receive the correct initiator of the ban", + "gato3@" + conn1.getHost(), + answer[0]); + assertEquals( + "User2 didn't receive the correct reason for the banning", + "Because I'm the owner", + answer[1]); + + // Check that ParticipantStatusListener is working OK + assertEquals( + "User3 didn't receive the correct banned JID", + room + "/testbot2", + answer[2]); + + // User2 leaves the room + muc2.leave(); + // User3 leaves the room + muc3.leave(); + } + catch (Exception e) { + fail(e.getMessage()); + e.printStackTrace(); + } + } + + public void testVoice() { + final String[] answer = new String[4]; + try { + + makeRoomModerated(); + + // User2 joins the new room (as a visitor) + MultiUserChat muc2 = new MultiUserChat(conn2, room); + muc2.join("testbot2"); + // User2 will listen for his own "voice" + muc2.addUserStatusListener(new DefaultUserStatusListener() { + public void voiceGranted() { + super.voiceGranted(); + answer[0] = "canSpeak"; + } + public void voiceRevoked() { + super.voiceRevoked(); + answer[1] = "cannot speak"; + } + }); + + // User3 joins the new room (as a visitor) + MultiUserChat muc3 = new MultiUserChat(conn3, room); + muc3.join("testbot3"); + // User3 will lister for user2's "voice" + muc3.addParticipantStatusListener(new DefaultParticipantStatusListener() { + public void voiceGranted(String participant) { + super.voiceGranted(participant); + answer[2] = participant; + } + + public void voiceRevoked(String participant) { + super.voiceRevoked(participant); + answer[3] = participant; + } + }); + + try { + // Check whether a visitor can grant voice to another visitor + muc2.grantVoice("testbot3"); + fail("User2 was able to grant voice"); + } + catch (XMPPException e) { + XMPPError xmppError = e.getXMPPError(); + assertNotNull( + "No XMPPError was received granting voice", + xmppError); + assertEquals( + "A visitor was able to grant voice to another visitor", + 403, + xmppError.getCode()); + } + + // Check that the room's owner can grant voice to a participant + muc.grantVoice("testbot2"); + Thread.sleep(300); + + // Check that UserStatusListener is working OK + assertEquals( + "User2 didn't receive the grant voice notification", + "canSpeak", + answer[0]); + // Check that ParticipantStatusListener is working OK + assertEquals( + "User3 didn't receive user2's grant voice notification", + room + "/testbot2", + answer[2]); + + // Check that the room's owner can revoke voice from a participant + muc.revokeVoice("testbot2"); + Thread.sleep(300); + + assertEquals( + "User2 didn't receive the revoke voice notification", + "cannot speak", + answer[1]); + assertEquals( + "User3 didn't receive user2's revoke voice notification", + room + "/testbot2", + answer[3]); + + // User2 leaves the room + muc2.leave(); + // User3 leaves the room + muc3.leave(); + } + catch (Exception e) { + fail(e.getMessage()); + e.printStackTrace(); + } + } + + public void testModerator() { + final String[] answer = new String[8]; + try { + + makeRoomModerated(); + + // User2 joins the new room (as a visitor) + MultiUserChat muc2 = new MultiUserChat(conn2, room); + muc2.join("testbot2"); + // User2 will listen for moderator privileges + muc2.addUserStatusListener(new DefaultUserStatusListener() { + public void voiceGranted() { + super.voiceGranted(); + answer[0] = "canSpeak"; + } + public void voiceRevoked() { + super.voiceRevoked(); + answer[1] = "cannot speak"; + } + public void moderatorGranted() { + super.moderatorGranted(); + answer[4] = "I'm a moderator"; + } + public void moderatorRevoked() { + super.moderatorRevoked(); + answer[5] = "I'm not a moderator"; + } + }); + + // User3 joins the new room (as a visitor) + MultiUserChat muc3 = new MultiUserChat(conn3, room); + muc3.join("testbot3"); + // User3 will lister for user2's moderator privileges + muc3.addParticipantStatusListener(new DefaultParticipantStatusListener() { + public void voiceGranted(String participant) { + super.voiceGranted(participant); + answer[2] = participant; + } + public void voiceRevoked(String participant) { + super.voiceRevoked(participant); + answer[3] = participant; + } + public void moderatorGranted(String participant) { + super.moderatorGranted(participant); + answer[6] = participant; + } + public void moderatorRevoked(String participant) { + super.moderatorRevoked(participant); + answer[7] = participant; + } + }); + + try { + // Check whether a visitor can grant moderator privileges to another visitor + muc2.grantModerator("testbot3"); + fail("User2 was able to grant moderator privileges"); + } + catch (XMPPException e) { + XMPPError xmppError = e.getXMPPError(); + assertNotNull( + "No XMPPError was received granting moderator privileges", + xmppError); + assertEquals( + "A visitor was able to grant moderator privileges to another visitor", + 403, + xmppError.getCode()); + } + + // Check that the room's owner can grant moderator privileges to a visitor + muc.grantModerator("testbot2"); + Thread.sleep(300); + + // Check that UserStatusListener is working OK + assertEquals( + "User2 didn't receive the grant voice notification", + "canSpeak", + answer[0]); + assertEquals( + "User2 didn't receive the grant moderator privileges notification", + "I'm a moderator", + answer[4]); + // Check that ParticipantStatusListener is working OK + assertEquals( + "User3 didn't receive user2's grant voice notification", + room + "/testbot2", + answer[2]); + assertEquals( + "User3 didn't receive user2's grant moderator privileges notification", + room + "/testbot2", + answer[6]); + + // Check that the room's owner can revoke moderator privileges from a moderator + muc.revokeModerator("testbot2"); + Thread.sleep(300); + + assertNull( + "User2 received a false revoke voice notification", + answer[1]); + assertNull( + "User3 received a false user2's voice privileges notification", + answer[3]); + assertEquals( + "User2 didn't receive the revoke moderator privileges notification", + "I'm not a moderator", + answer[5]); + assertEquals( + "User3 didn't receive user2's revoke moderator privileges notification", + room + "/testbot2", + answer[7]); + + + // Check that the room's owner can grant moderator privileges to a participant + clearAnswer(answer); + muc.grantModerator("testbot2"); + Thread.sleep(300); + + // Check that UserStatusListener is working OK + assertNull( + "User2 received a false grant voice notification", + answer[0]); + assertEquals( + "User2 didn't receive the grant moderator privileges notification", + "I'm a moderator", + answer[4]); + // Check that ParticipantStatusListener is working OK + assertNull( + "User3 received a false user2's grant voice notification", + answer[2]); + assertEquals( + "User3 didn't receive user2's grant moderator privileges notification", + room + "/testbot2", + answer[6]); + + // Check that the room's owner can revoke voice from a moderator + clearAnswer(answer); + muc.revokeVoice("testbot2"); + Thread.sleep(300); + + assertEquals( + "User2 didn't receive the revoke voice notification", + "cannot speak", + answer[1]); + assertEquals( + "User3 didn't receive user2's revoke voice notification", + room + "/testbot2", + answer[3]); + + // User2 leaves the room + muc2.leave(); + // User3 leaves the room + muc3.leave(); + } + catch (Exception e) { + fail(e.getMessage()); + e.printStackTrace(); + } + } + + public void testMembership() { + final String[] answer = new String[4]; + try { + + makeRoomModerated(); + + // User2 joins the new room (as a visitor) + MultiUserChat muc2 = new MultiUserChat(conn2, room); + muc2.join("testbot2"); + // User2 will listen for membership privileges + muc2.addUserStatusListener(new DefaultUserStatusListener() { + public void membershipGranted() { + super.membershipGranted(); + answer[0] = "I'm a member"; + } + public void membershipRevoked() { + super.membershipRevoked(); + answer[1] = "I'm not a member"; + } + }); + + // User3 joins the new room (as a visitor) + MultiUserChat muc3 = new MultiUserChat(conn3, room); + muc3.join("testbot3"); + // User3 will lister for user2's membership privileges + muc3.addParticipantStatusListener(new DefaultParticipantStatusListener() { + public void membershipGranted(String participant) { + super.membershipGranted(participant); + answer[2] = participant; + } + public void membershipRevoked(String participant) { + super.membershipRevoked(participant); + answer[3] = participant; + } + }); + + try { + // Check whether a visitor can grant membership privileges to another visitor + muc2.grantMembership("gato5@" + conn2.getHost()); + fail("User2 was able to grant membership privileges"); + } + catch (XMPPException e) { + XMPPError xmppError = e.getXMPPError(); + assertNotNull( + "No XMPPError was received granting membership privileges", + xmppError); + assertEquals( + "A visitor was able to grant membership privileges to another visitor", + 403, + xmppError.getCode()); + } + + // Check that the room's owner can grant membership privileges to a visitor + muc.grantMembership("gato4@" + conn2.getHost()); + Thread.sleep(300); + + // Check that UserStatusListener is working OK + assertEquals( + "User2 didn't receive the grant membership notification", + "I'm a member", + answer[0]); + // Check that ParticipantStatusListener is working OK + assertEquals( + "User3 didn't receive user2's grant membership notification", + room + "/testbot2", + answer[2]); + + // Check that the room's owner can revoke membership privileges from a member + // and make the occupant a visitor + muc.revokeMembership("gato4@" + conn2.getHost()); + Thread.sleep(300); + + // Check that UserStatusListener is working OK + assertEquals( + "User2 didn't receive the revoke membership notification", + "I'm not a member", + answer[1]); + // Check that ParticipantStatusListener is working OK + assertEquals( + "User3 didn't receive user2's revoke membership notification", + room + "/testbot2", + answer[3]); + + // User2 leaves the room + muc2.leave(); + // User3 leaves the room + muc3.leave(); + } + catch (Exception e) { + fail(e.getMessage()); + e.printStackTrace(); + } + } + + public void testAdmin() { + final String[] answer = new String[8]; + try { + + makeRoomModerated(); + + // User2 joins the new room (as a visitor) + MultiUserChat muc2 = new MultiUserChat(conn2, room); + muc2.join("testbot2"); + // User2 will listen for admin privileges + muc2.addUserStatusListener(new DefaultUserStatusListener() { + public void membershipGranted() { + super.membershipGranted(); + answer[0] = "I'm a member"; + } + public void membershipRevoked() { + super.membershipRevoked(); + answer[1] = "I'm not a member"; + } + public void adminGranted() { + super.adminGranted(); + answer[2] = "I'm an admin"; + } + public void adminRevoked() { + super.adminRevoked(); + answer[3] = "I'm not an admin"; + } + }); + + // User3 joins the new room (as a visitor) + MultiUserChat muc3 = new MultiUserChat(conn3, room); + muc3.join("testbot3"); + // User3 will lister for user2's admin privileges + muc3.addParticipantStatusListener(new DefaultParticipantStatusListener() { + public void membershipGranted(String participant) { + super.membershipGranted(participant); + answer[4] = participant; + } + public void membershipRevoked(String participant) { + super.membershipRevoked(participant); + answer[5] = participant; + } + public void adminGranted(String participant) { + super.adminGranted(participant); + answer[6] = participant; + } + public void adminRevoked(String participant) { + super.adminRevoked(participant); + answer[7] = participant; + } + }); + + try { + // Check whether a visitor can grant admin privileges to another visitor + muc2.grantAdmin("gato5@" + conn2.getHost()); + fail("User2 was able to grant admin privileges"); + } + catch (XMPPException e) { + XMPPError xmppError = e.getXMPPError(); + assertNotNull( + "No XMPPError was received granting admin privileges", + xmppError); + assertEquals( + "A visitor was able to grant admin privileges to another visitor", + 403, + xmppError.getCode()); + } + + // Check that the room's owner can grant admin privileges to a visitor + muc.grantAdmin("gato4@" + conn2.getHost()); + Thread.sleep(300); + + // Check that UserStatusListener is working OK + assertEquals( + "User2 didn't receive the grant admin notification", + "I'm an admin", + answer[2]); + // Check that ParticipantStatusListener is working OK + assertEquals( + "User3 didn't receive user2's grant admin notification", + room + "/testbot2", + answer[6]); + + // Check that the room's owner can revoke admin privileges from an admin + // and make the occupant a visitor + muc.revokeMembership("gato4@" + conn2.getHost()); + Thread.sleep(300); + + // Check that UserStatusListener is working OK + assertEquals( + "User2 didn't receive the revoke admin notification", + "I'm not an admin", + answer[3]); + // Check that ParticipantStatusListener is working OK + assertEquals( + "User3 didn't receive user2's revoke admin notification", + room + "/testbot2", + answer[7]); + + // Check that the room's owner can grant admin privileges to a member + clearAnswer(answer); + muc.grantMembership("gato4@" + conn2.getHost()); + Thread.sleep(300); + muc.grantAdmin("gato4@" + conn2.getHost()); + Thread.sleep(300); + + // Check that UserStatusListener is working OK + assertEquals( + "User2 didn't receive the revoke membership notification", + "I'm not a member", + answer[1]); + assertEquals( + "User2 didn't receive the grant admin notification", + "I'm an admin", + answer[2]); + // Check that ParticipantStatusListener is working OK + assertEquals( + "User3 didn't receive user2's revoke membership notification", + room + "/testbot2", + answer[5]); + assertEquals( + "User3 didn't receive user2's grant admin notification", + room + "/testbot2", + answer[6]); + + // Check that the room's owner can revoke admin privileges from an admin + // and make the occupant a member + clearAnswer(answer); + muc.revokeAdmin("gato4@" + conn2.getHost()); + Thread.sleep(300); + + // Check that UserStatusListener is working OK + assertEquals( + "User2 didn't receive the revoke admin notification", + "I'm not an admin", + answer[3]); + assertEquals( + "User2 didn't receive the grant membership notification", + "I'm a member", + answer[0]); + // Check that ParticipantStatusListener is working OK + assertEquals( + "User3 didn't receive user2's revoke admin notification", + room + "/testbot2", + answer[7]); + assertEquals( + "User3 didn't receive user2's grant membership notification", + room + "/testbot2", + answer[4]); + + // User2 leaves the room + muc2.leave(); + // User3 leaves the room + muc3.leave(); + } + catch (Exception e) { + fail(e.getMessage()); + e.printStackTrace(); + } + } + + public void testOwnership() { + final String[] answer = new String[12]; + try { + + makeRoomModerated(); + + // User2 joins the new room (as a visitor) + MultiUserChat muc2 = new MultiUserChat(conn2, room); + muc2.join("testbot2"); + // User2 will listen for ownership privileges + muc2.addUserStatusListener(new DefaultUserStatusListener() { + public void membershipGranted() { + super.membershipGranted(); + answer[0] = "I'm a member"; + } + public void membershipRevoked() { + super.membershipRevoked(); + answer[1] = "I'm not a member"; + } + public void adminGranted() { + super.adminGranted(); + answer[2] = "I'm an admin"; + } + public void adminRevoked() { + super.adminRevoked(); + answer[3] = "I'm not an admin"; + } + public void ownershipGranted() { + super.ownershipGranted(); + answer[4] = "I'm an owner"; + } + public void ownershipRevoked() { + super.ownershipRevoked(); + answer[5] = "I'm not an owner"; + } + }); + + // User3 joins the new room (as a visitor) + MultiUserChat muc3 = new MultiUserChat(conn3, room); + muc3.join("testbot3"); + // User3 will lister for user2's ownership privileges + muc3.addParticipantStatusListener(new DefaultParticipantStatusListener() { + public void membershipGranted(String participant) { + super.membershipGranted(participant); + answer[6] = participant; + } + public void membershipRevoked(String participant) { + super.membershipRevoked(participant); + answer[7] = participant; + } + public void adminGranted(String participant) { + super.adminGranted(participant); + answer[8] = participant; + } + public void adminRevoked(String participant) { + super.adminRevoked(participant); + answer[9] = participant; + } + public void ownershipGranted(String participant) { + super.ownershipGranted(participant); + answer[10] = participant; + } + public void ownershipRevoked(String participant) { + super.ownershipRevoked(participant); + answer[11] = participant; + } + }); + + try { + // Check whether a visitor can grant ownership privileges to another visitor + muc2.grantOwnership("gato5@" + conn2.getHost()); + fail("User2 was able to grant ownership privileges"); + } + catch (XMPPException e) { + XMPPError xmppError = e.getXMPPError(); + assertNotNull( + "No XMPPError was received granting ownership privileges", + xmppError); + assertEquals( + "A visitor was able to grant ownership privileges to another visitor", + 403, + xmppError.getCode()); + } + + // Check that the room's owner can grant ownership privileges to a visitor + muc.grantOwnership("gato4@" + conn2.getHost()); + Thread.sleep(300); + + // Check that UserStatusListener is working OK + assertEquals( + "User2 didn't receive the grant ownership notification", + "I'm an owner", + answer[4]); + // Check that ParticipantStatusListener is working OK + assertEquals( + "User3 didn't receive user2's grant ownership notification", + room + "/testbot2", + answer[10]); + + // Check that the room's owner can revoke ownership privileges from an owner + // and make the occupant a visitor + muc.revokeMembership("gato4@" + conn2.getHost()); + Thread.sleep(300); + + // Check that UserStatusListener is working OK + assertEquals( + "User2 didn't receive the revoke ownership notification", + "I'm not an owner", + answer[5]); + // Check that ParticipantStatusListener is working OK + assertEquals( + "User3 didn't receive user2's revoke ownership notification", + room + "/testbot2", + answer[11]); + + // Check that the room's owner can grant ownership privileges to a member + clearAnswer(answer); + muc.grantMembership("gato4@" + conn2.getHost()); + Thread.sleep(300); + muc.grantOwnership("gato4@" + conn2.getHost()); + Thread.sleep(300); + + // Check that UserStatusListener is working OK + assertEquals( + "User2 didn't receive the revoke membership notification", + "I'm not a member", + answer[1]); + assertEquals( + "User2 didn't receive the grant ownership notification", + "I'm an owner", + answer[4]); + // Check that ParticipantStatusListener is working OK + assertEquals( + "User3 didn't receive user2's revoke membership notification", + room + "/testbot2", + answer[7]); + assertEquals( + "User3 didn't receive user2's grant ownership notification", + room + "/testbot2", + answer[10]); + + // Check that the room's owner can revoke ownership privileges from an owner + // and make the occupant a member + clearAnswer(answer); + muc.revokeAdmin("gato4@" + conn2.getHost()); + Thread.sleep(300); + + // Check that UserStatusListener is working OK + assertEquals( + "User2 didn't receive the revoke ownership notification", + "I'm not an owner", + answer[5]); + assertEquals( + "User2 didn't receive the grant membership notification", + "I'm a member", + answer[0]); + // Check that ParticipantStatusListener is working OK + assertEquals( + "User3 didn't receive user2's revoke ownership notification", + room + "/testbot2", + answer[11]); + assertEquals( + "User3 didn't receive user2's grant membership notification", + room + "/testbot2", + answer[6]); + + // Check that the room's owner can grant ownership privileges to an admin + clearAnswer(answer); + muc.grantAdmin("gato4@" + conn2.getHost()); + Thread.sleep(300); + muc.grantOwnership("gato4@" + conn2.getHost()); + Thread.sleep(300); + + // Check that UserStatusListener is working OK + assertEquals( + "User2 didn't receive the revoke admin notification", + "I'm not an admin", + answer[3]); + assertEquals( + "User2 didn't receive the grant ownership notification", + "I'm an owner", + answer[4]); + // Check that ParticipantStatusListener is working OK + assertEquals( + "User3 didn't receive user2's revoke admin notification", + room + "/testbot2", + answer[9]); + assertEquals( + "User3 didn't receive user2's grant ownership notification", + room + "/testbot2", + answer[10]); + + // Check that the room's owner can revoke ownership privileges from an owner + // and make the occupant an admin + clearAnswer(answer); + muc.revokeOwnership("gato4@" + conn2.getHost()); + Thread.sleep(300); + + // Check that UserStatusListener is working OK + assertEquals( + "User2 didn't receive the revoke ownership notification", + "I'm not an owner", + answer[5]); + assertEquals( + "User2 didn't receive the grant admin notification", + "I'm an admin", + answer[2]); + // Check that ParticipantStatusListener is working OK + assertEquals( + "User3 didn't receive user2's revoke ownership notification", + room + "/testbot2", + answer[11]); + assertEquals( + "User3 didn't receive user2's grant admin notification", + room + "/testbot2", + answer[8]); + + // User2 leaves the room + muc2.leave(); + // User3 leaves the room + muc3.leave(); + } + catch (Exception e) { + fail(e.getMessage()); + e.printStackTrace(); + } + } + + private void makeRoomModerated() throws XMPPException { + // User1 (which is the room owner) converts the instant room into a moderated room + Form form = muc.getConfigurationForm(); + Form answerForm = form.createAnswerForm(); + answerForm.setAnswer("muc#owner_moderatedroom", "1"); + muc.sendConfigurationForm(answerForm); + } + + private void clearAnswer(String[] answer) { + for (int i=0; i < answer.length; i++) { + answer[i] = null; + } + } + + protected void setUp() throws Exception { + super.setUp(); + try { + // Connect to the server + conn1 = new XMPPConnection(host); + conn2 = new XMPPConnection(host); + conn3 = new XMPPConnection(host); + + // Create the test accounts + if (!conn1.getAccountManager().supportsAccountCreation()) + fail("Server does not support account creation"); + conn1.getAccountManager().createAccount("gato3", "gato3"); + conn2.getAccountManager().createAccount("gato4", "gato4"); + conn3.getAccountManager().createAccount("gato5", "gato5"); + + // Login with the test accounts + conn1.login("gato3", "gato3"); + conn2.login("gato4", "gato4"); + conn3.login("gato5", "gato5"); + + user1 = "gato3@" + conn1.getHost() + "/Smack"; + user2 = "gato4@" + conn2.getHost() + "/Smack"; + user3 = "gato5@" + conn2.getHost() + "/Smack"; + + // User1 creates the room + muc = new MultiUserChat(conn1, room); + muc.create("testbot"); + + // User1 sends an empty room configuration form which indicates that we want + // an instant room + muc.sendConfigurationForm(new Form(Form.TYPE_SUBMIT)); + + } + catch (Exception e) { + fail(e.getMessage()); + } + } + + protected void tearDown() throws Exception { + super.tearDown(); + // Destroy the new room + muc.destroy("The room has almost no activity...", null); + + // Delete the created accounts for the test + conn1.getAccountManager().deleteAccount(); + conn2.getAccountManager().deleteAccount(); + conn3.getAccountManager().deleteAccount(); + + // Close all the connections + conn1.close(); + conn2.close(); + conn3.close(); + } +}