summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTarun Gupta <tarun1995gupta@gmail.com>2015-07-14 15:15:09 (GMT)
committerNick Hudson <nick.hudson@isode.com>2015-07-22 16:34:21 (GMT)
commit9ead0fdcca595df9dc3f4143122776b398dbe405 (patch)
treebf82bac84b07431ea61a5da5865bdc07a3afb53c
parent2341df7c72714df988b39cb2019562987ea28cbb (diff)
downloadstroke-9ead0fdcca595df9dc3f4143122776b398dbe405.zip
stroke-9ead0fdcca595df9dc3f4143122776b398dbe405.tar.bz2
Completes MUC in accordance with Swiften.
Updates MUC to be an abstract class. Updates MUCBookmark, MUCManager, DirectedPresenceSender. Adds MUCImpl. License: This patch is BSD-licensed, see Documentation/Licenses/BSD-simplified.txt for details. Test-Information: Test updated for MUC, in accordance with Swiften, which passes. Added MockMUC too. Change-Id: If5aa672e8adc093162d3c9890c8437d3edadea68
-rw-r--r--src/com/isode/stroke/muc/MUC.java682
-rw-r--r--src/com/isode/stroke/muc/MUCBookmark.java4
-rw-r--r--src/com/isode/stroke/muc/MUCImpl.java736
-rw-r--r--src/com/isode/stroke/muc/MUCManager.java2
-rw-r--r--src/com/isode/stroke/presence/DirectedPresenceSender.java4
-rw-r--r--test/com/isode/stroke/muc/MUCTest.java137
-rw-r--r--test/com/isode/stroke/muc/MockMUC.java146
7 files changed, 1080 insertions, 631 deletions
diff --git a/src/com/isode/stroke/muc/MUC.java b/src/com/isode/stroke/muc/MUC.java
index ed3c34a..9806e34 100644
--- a/src/com/isode/stroke/muc/MUC.java
+++ b/src/com/isode/stroke/muc/MUC.java
@@ -1,7 +1,14 @@
/*
- * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * Copyright (c) 2010-2014 Isode Limited.
* All rights reserved.
+ * See the COPYING file for more information.
*/
+/*
+ * Copyright (c) 2015 Tarun Gupta.
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
package com.isode.stroke.muc;
import java.util.Collections;
@@ -37,614 +44,65 @@ import com.isode.stroke.signals.SignalConnection;
import com.isode.stroke.signals.Slot1;
import com.isode.stroke.signals.Slot2;
-/**
- * Class representing multi user chat room
- *
- */
-public class MUC {
-
- public enum JoinResult { JoinFailed, JoinSucceeded };
- public enum LeavingType { Disconnect, LeaveBan, LeaveDestroy, LeaveKick, LeaveNotMember, LeavePart };
-
- public Signal3<ErrorPayload,JID, MUCOccupant.Affiliation> onAffiliationChangeFailed =
- new Signal3<ErrorPayload,JID, MUCOccupant.Affiliation>();
- public Signal1<ErrorPayload> onAffiliationListFailed = new Signal1<ErrorPayload>();
- public Signal2<MUCOccupant.Affiliation, Vector<JID>> onAffiliationListReceived =
- new Signal2<MUCOccupant.Affiliation, Vector<JID>>();
- public Signal1<ErrorPayload> onConfigurationFailed = new Signal1<ErrorPayload>();
- public Signal1<Form> onConfigurationFormReceived = new Signal1<Form>();
- public Signal1<String> onJoinComplete = new Signal1<String>();
- public Signal1<ErrorPayload> onJoinFailed = new Signal1<ErrorPayload>();
- public Signal3<String, MUCOccupant.Affiliation, MUCOccupant.Affiliation> onOccupantAffiliationChanged =
- new Signal3<String, MUCOccupant.Affiliation, MUCOccupant.Affiliation>();
- public Signal1<MUCOccupant> onOccupantJoined = new Signal1<MUCOccupant>();
-
- public Signal3<MUCOccupant, LeavingType, String> onOccupantLeft =
- new Signal3<MUCOccupant, LeavingType, String>();
- public Signal1<Presence> onOccupantPresenceChange = new Signal1<Presence>();
- public Signal3<String, MUCOccupant, MUCOccupant.Role> onOccupantRoleChanged =
- new Signal3<String, MUCOccupant, MUCOccupant.Role>();
-
- public Signal3<ErrorPayload,JID,MUCOccupant.Role> onRoleChangeFailed =
- new Signal3<ErrorPayload,JID,MUCOccupant.Role>();
-
- public final Signal onUnlocked = new Signal();
-
- private boolean createAsReservedIfNew;
- private IQRouter iqRouter_;
- private boolean joinComplete_;
- private Date joinSince_;
- private boolean joinSucceeded_;
- private MUCRegistry mucRegistry;
- private Map<String, MUCOccupant> occupants = new HashMap<String, MUCOccupant>();
- private JID ownMUCJID;
- private String password;
- private DirectedPresenceSender presenceSender;
- private StanzaChannel stanzaChannel;
- private boolean unlocking;
- private SignalConnection signalPresRcvd;
-
- /**
- * Create a MUC Session
- * @param stanzaChannel stanza channel, not null
- * @param iqRouter IQ stanza router, not null
- * @param presenceSender Presence Sender, not null
- * @param muc JID of the chat room, not null
- * @param mucRegistry MUC registry, not null
- *
- * @see #disconnect()
- */
- public MUC(StanzaChannel stanzaChannel, IQRouter iqRouter,
- DirectedPresenceSender presenceSender, final JID muc,
- MUCRegistry mucRegistry) {
- ownMUCJID = muc;
- this.stanzaChannel = stanzaChannel;
- this.iqRouter_ = iqRouter;
- this.presenceSender = presenceSender;
- this.mucRegistry = mucRegistry;
- this.createAsReservedIfNew = false;
- this.unlocking = false;
- signalPresRcvd = this.stanzaChannel.onPresenceReceived.connect(
- new Slot1<Presence>() {
- @Override
- public void call(Presence p1) {
- handleIncomingPresence(p1);
- }
- });
- }
-
- /**
- * Cancel the command for configuring room
- */
- public void cancelConfigureRoom() {
- MUCOwnerPayload mucPayload = new MUCOwnerPayload();
- mucPayload.setPayload(new Form(Form.Type.CANCEL_TYPE));
- GenericRequest<MUCOwnerPayload> request = new GenericRequest<MUCOwnerPayload>(
- IQ.Type.Set, getJID(), mucPayload, iqRouter_);
- request.send();
- }
-
- /**
- * Change the affiliation of the given Jabber ID.
- * It must be called with the real JID, not the room JID.
- * @param jid real jabber ID, not null
- * @param affiliation new affiliation, not null
- */
- public void changeAffiliation(final JID jid, final MUCOccupant.Affiliation affiliation) {
- final MUCAdminPayload mucPayload = new MUCAdminPayload();
- MUCItem item = new MUCItem();
- item.affiliation = affiliation;
- item.realJID = jid.toBare();
- mucPayload.addItem(item);
- GenericRequest<MUCAdminPayload> request = new GenericRequest<MUCAdminPayload>(
- IQ.Type.Set, getJID(), mucPayload, iqRouter_);
- request.onResponse.connect(new Slot2<MUCAdminPayload, ErrorPayload>() {
- @Override
- public void call(MUCAdminPayload p1, ErrorPayload p2) {
- handleAffiliationChangeResponse(mucPayload,p2,jid,affiliation);
- }
- });
- request.send();
- }
-
- /**
- * Change the role of the specified occupant. It must be
- * called with the room JID, not the real JID.
- * @param jid Jabber ID of the occupant in the chat room, not null
- * @param role new role, not null
- */
- public void changeOccupantRole(final JID jid, final MUCOccupant.Role role) {
- final MUCAdminPayload mucPayload = new MUCAdminPayload();
- MUCItem item = new MUCItem();
- item.role = role;
- item.nick = jid.getResource();
- mucPayload.addItem(item);
- GenericRequest<MUCAdminPayload> request = new GenericRequest<MUCAdminPayload>(
- IQ.Type.Set, getJID(), mucPayload, iqRouter_);
- request.onResponse.connect(new Slot2<MUCAdminPayload, ErrorPayload>() {
- @Override
- public void call(MUCAdminPayload p1, ErrorPayload p2) {
- handleOccupantRoleChangeResponse(mucPayload,p2,jid,role);
- }
- });
- request.send();
- }
-
- /**
- * Change the subject of the chat room
- * @param subject new subject, not null
- */
- public void changeSubject(String subject) {
- Message message = new Message();
- message.setSubject(subject);
- message.setType(Message.Type.Groupchat);
- message.setTo(ownMUCJID.toBare());
- stanzaChannel.sendMessage(message);
- }
-
- /**
- * Configure a chat room room
- * @param form form to be used for configuration, not null
- */
- public void configureRoom(Form form) {
- MUCOwnerPayload mucPayload = new MUCOwnerPayload();
- mucPayload.setPayload(form);
- GenericRequest<MUCOwnerPayload> request = new GenericRequest<MUCOwnerPayload>(
- IQ.Type.Set, getJID(), mucPayload, iqRouter_);
- if (unlocking) {
- request.onResponse.connect(new Slot2<MUCOwnerPayload, ErrorPayload>() {
- @Override
- public void call(MUCOwnerPayload p1, ErrorPayload p2) {
- handleCreationConfigResponse(p1, p2);
- }
- });
- }else {
- request.onResponse.connect(new Slot2<MUCOwnerPayload, ErrorPayload>() {
- @Override
- public void call(MUCOwnerPayload p1, ErrorPayload p2) {
- handleConfigurationResultReceived(p1, p2);
- }
- });
- }
- request.send();
- }
-
- /**
- * Destroy the chat room
- */
- public void destroyRoom() {
- MUCOwnerPayload mucPayload = new MUCOwnerPayload();
- MUCDestroyPayload mucDestroyPayload = new MUCDestroyPayload();
- mucPayload.setPayload(mucDestroyPayload);
- GenericRequest<MUCOwnerPayload> request = new GenericRequest<MUCOwnerPayload>(
- IQ.Type.Set, getJID(), mucPayload, iqRouter_);
- request.onResponse.connect(new Slot2<MUCOwnerPayload, ErrorPayload>() {
- @Override
- public void call(MUCOwnerPayload p1, ErrorPayload p2) {
- handleConfigurationResultReceived(p1,p2);
- }
- });
- request.send();
- }
-
- /**
- * Returns the (bare) JID of the MUC.
- * @return bare JID(i.e. without resource)
- */
- public JID getJID() {
- return ownMUCJID.toBare();
- }
-
- /**
- * Get the MUC occupant with the given nick name
- * @param nick nick name, not null
- * @return MUC occupant if it exists or null if not
- */
- public MUCOccupant getOccupant(String nick) {
- return occupants.get(nick);
- }
-
- /**
- * Determine if the room contains occupant with given nick
- * @param nick given nick
- * @return true if the occupant exists, false otherwise
- */
- public boolean hasOccupant(String nick) {
- return occupants.containsKey(nick);
- }
-
- public Map<String, MUCOccupant> getOccupants() {
- return Collections.unmodifiableMap(occupants);
- }
-
- /**
- * Invite the person with give JID to the chat room
- * @param person jabber ID o the person to invite,not nul
- */
- public void invitePerson(JID person) {
- invitePerson(person, "", false, false);
- }
-
- /**
- * Send an invite for the person to join the MUC
- * @param person jabber ID of the person to invite, not null
- * @param reason join reason, not null
- * @param isImpromptu
- */
- public void invitePerson(JID person, String reason, boolean isImpromptu) {
- invitePerson(person, reason, isImpromptu, false);
- }
-
-
- /**
- * Send an invite for the person to join the MUC
- * @param person jabber ID of the person to invite, not null
- * @param reason join reason, not null
- * @param isImpromptu
- * @param isReuseChat
- */
- public void invitePerson(JID person, String reason, boolean isImpromptu, boolean isReuseChat) {
- Message message = new Message();
- message.setTo(person);
- message.setType(Message.Type.Normal);
- MUCInvitationPayload invite = new MUCInvitationPayload();
- invite.setReason(reason);
- invite.setIsImpromptu(isImpromptu);
- invite.setIsContinuation(isReuseChat);
- invite.setJID(ownMUCJID.toBare());
- message.addPayload(invite);
- stanzaChannel.sendMessage(message);
- }
-
- /**
- * Join the MUC with default context.
- * @param nick nick name of the user, not null
- */
- public void joinAs(String nick) {
- joinSince_ = null;
- internalJoin(nick);
- }
-
- /**
- * Join the MUC with context since date.
- * @param nick nick name, not null
- * @param since date since the nick joined, not null
- */
- public void joinWithContextSince(String nick, Date since) {
- joinSince_ = since;
- internalJoin(nick);
- }
-
- /**
- * Kick the given occupant out of the chat room
- * @param jid jabber ID of the user to kick, not null
- */
- public void kickOccupant(JID jid) {
- changeOccupantRole(jid, MUCOccupant.Role.NoRole);
- }
-
- /**
- * Leave the chat room
- */
- public void part() {
- presenceSender.removeDirectedPresenceReceiver(ownMUCJID,
- DirectedPresenceSender.SendPresence.AndSendPresence);
- mucRegistry.removeMUC(getJID());
- }
-
- /**
- * Send a request to get a list of users for the given affiliation
- * @param affiliation affiliation, not null
- */
- public void requestAffiliationList(final MUCOccupant.Affiliation affiliation) {
- MUCAdminPayload mucPayload = new MUCAdminPayload();
- MUCItem item = new MUCItem();
- item.affiliation = affiliation;
- mucPayload.addItem(item);
- GenericRequest<MUCAdminPayload> request = new GenericRequest<MUCAdminPayload>(
- IQ.Type.Get, getJID(), mucPayload, iqRouter_);
- request.onResponse.connect(new Slot2<MUCAdminPayload, ErrorPayload>() {
- @Override
- public void call(MUCAdminPayload p1, ErrorPayload p2) {
- handleAffiliationListResponse(p1,p2,affiliation);
- }
- });
- request.send();
- }
-
- /**
- * Send a request for getting form for configuring a room
- */
- public void requestConfigurationForm() {
- MUCOwnerPayload mucPayload = new MUCOwnerPayload();
- GenericRequest<MUCOwnerPayload> request = new GenericRequest<MUCOwnerPayload>(
- IQ.Type.Get, getJID(), mucPayload, iqRouter_);
- request.onResponse.connect(new Slot2<MUCOwnerPayload, ErrorPayload>() {
- @Override
- public void call(MUCOwnerPayload p1, ErrorPayload p2) {
- handleConfigurationFormReceived(p1,p2);
- }
- });
- request.send();
- }
-
- /**
- * Set the reserved status of room to true.
- * By default a new room with the default configuration is created.
- * The effect of calling this function is to leave the room in reserved state
- * but not configured so that it can be configured later.
- */
- public void setCreateAsReservedIfNew() {
- createAsReservedIfNew = true;
- }
-
- /**
- * Set the password used for entering the room.
- * @param newPassword password, can be null
- */
- public void setPassword(String newPassword) {
- password = newPassword;
- }
-
- /**
- * Get the nick name of the MUC room
- * @return nick name, can be null
- */
- private String getOwnNick() {
- return ownMUCJID.getResource();
- }
-
- private void handleAffiliationChangeResponse(MUCAdminPayload ref,
- ErrorPayload error, JID jid, MUCOccupant.Affiliation affiliation) {
- if (error != null) {
- onAffiliationChangeFailed.emit(error, jid, affiliation);
- }
- }
-
- private void handleAffiliationListResponse(MUCAdminPayload payload,
- ErrorPayload error, MUCOccupant.Affiliation affiliation) {
- if (error != null) {
- onAffiliationListFailed.emit(error);
- } else {
- Vector<JID> jids = new Vector<JID>();
- for (MUCItem item : payload.getItems()) {
- if (item.realJID != null) {
- jids.add(item.realJID);
- }
- }
- onAffiliationListReceived.emit(affiliation, jids);
- }
- }
-
- private void handleConfigurationFormReceived(MUCOwnerPayload payload,
- ErrorPayload error) {
- Form form = null;
- if (payload != null) {
- form = payload.getForm();
- }
- if (error != null || form == null) {
- onConfigurationFailed.emit(error);
- } else {
- onConfigurationFormReceived.emit(form);
- }
- }
-
- private void handleConfigurationResultReceived(
- MUCOwnerPayload payload, ErrorPayload error) {
- if (error != null) {
- onConfigurationFailed.emit(error);
- }
- }
-
- private void handleCreationConfigResponse(MUCOwnerPayload ref , ErrorPayload error) {
- unlocking = false;
- if (error != null) {
- presenceSender.removeDirectedPresenceReceiver(ownMUCJID,
- DirectedPresenceSender.SendPresence.AndSendPresence);
- onJoinFailed.emit(error);
- } else {
- onJoinComplete.emit(getOwnNick()); /* Previously, this wasn't needed here,
- as the presence duplication bug caused an emit elsewhere. */
- }
- }
-
- private void handleIncomingPresence(Presence presence) {
- if (!isFromMUC(presence.getFrom())) {
- return;
- }
-
- MUCUserPayload mucPayload = null;
-
- MUCUserPayload dummyUserPayload = new MUCUserPayload();
- for (MUCUserPayload payload : presence.getPayloads(dummyUserPayload)) {
- if (!payload.getItems().isEmpty() || !payload.getStatusCodes().isEmpty()) {
- mucPayload = payload;
- }
- }
-
- // On the first incoming presence, check if our join has succeeded
- // (i.e. we start getting non-error presence from the MUC) or not
- if (!joinSucceeded_) {
- if(presence.getType() == Presence.Type.Error) {
- onJoinFailed.emit(presence.getPayload(new ErrorPayload()));
- return;
- } else {
- joinSucceeded_ = true;
- presenceSender.addDirectedPresenceReceiver(ownMUCJID,
- DirectedPresenceSender.SendPresence.AndSendPresence);
- }
- }
-
- String nick = presence.getFrom().getResource();
- if (nick == null || nick.isEmpty()) {
- return;
- }
- MUCOccupant.Role role = MUCOccupant.Role.NoRole;
- MUCOccupant.Affiliation affiliation= MUCOccupant.Affiliation.NoAffiliation;
- JID realJID = null;
- if (mucPayload != null && mucPayload.getItems().size() > 0) {
- role = mucPayload.getItems().get(0).role != null
- ? mucPayload.getItems().get(0).role : MUCOccupant.Role.NoRole;
- affiliation = mucPayload.getItems().get(0).affiliation != null
- ? mucPayload.getItems().get(0).affiliation : MUCOccupant.Affiliation.NoAffiliation;
- realJID = mucPayload.getItems().get(0).realJID;
- }
-
- //100 is non-anonymous
- //TODO: 100 may also be specified in a <message/>
- //170 is room logging to http
- //TODO: Nick changes
-
- if (presence.getType() == Presence.Type.Unavailable) {
- LeavingType type = LeavingType.LeavePart;
- if (mucPayload != null) {
- if (mucPayload.getPayload() instanceof MUCDestroyPayload) {
- type = LeavingType.LeaveDestroy;
- } else for (MUCUserPayload.StatusCode status : mucPayload.getStatusCodes()) {
- if (status.code == 307) {
- type = LeavingType.LeaveKick;
- } else if (status.code == 301) {
- type = LeavingType.LeaveBan;
- } else if (status.code == 321) {
- type = LeavingType.LeaveNotMember;
- }
- }
- }
-
- if (presence.getFrom().equals(ownMUCJID)) {
- handleUserLeft(type);
- return;
- }else {
- if (occupants.containsKey(nick)) {
- //TODO: part type
- onOccupantLeft.emit(occupants.get(nick), type, "");
- occupants.remove(nick);
- }
- }
- } else if (presence.getType() == Presence.Type.Available) {
- MUCOccupant occupant = new MUCOccupant(nick, role, affiliation);
- boolean isJoin = true;
- if (realJID != null) {
- occupant.setRealJID(realJID);
- }
- if (occupants.containsKey(nick)) {
- isJoin = false;
- MUCOccupant oldOccupant = occupants.get(nick);
- if (oldOccupant.getRole() != role) {
- onOccupantRoleChanged.emit(nick, occupant, oldOccupant.getRole());
- }
- if (oldOccupant.getAffiliation() != affiliation) {
- onOccupantAffiliationChanged.emit(nick, affiliation, oldOccupant.getAffiliation());
- }
- occupants.remove(nick);
- }
- occupants.put(nick, occupant);
-
- if (isJoin) {
- onOccupantJoined.emit(occupant);
- }
- onOccupantPresenceChange.emit(presence);
- }
-
- if (mucPayload != null && !joinComplete_) {
- for (MUCUserPayload.StatusCode status : mucPayload.getStatusCodes()) {
- if(status.code == 110) {
- /* Simply knowing this is your presence is enough, 210 doesn't seem to be necessary. */
- joinComplete_ = true;
- if (!ownMUCJID.equals(presence.getFrom())) {
- presenceSender.removeDirectedPresenceReceiver(ownMUCJID, DirectedPresenceSender.SendPresence.DontSendPresence);
- ownMUCJID = presence.getFrom();
- presenceSender.addDirectedPresenceReceiver(ownMUCJID, DirectedPresenceSender.SendPresence.AndSendPresence);
- }
- onJoinComplete.emit(getOwnNick());
- }
- if (status.code == 201) {
- /* Room is created and locked */
- /* Currently deal with this by making an instant room */
- if (!ownMUCJID.equals(presence.getFrom())) {
- presenceSender.removeDirectedPresenceReceiver(ownMUCJID, DirectedPresenceSender.SendPresence.DontSendPresence);
- ownMUCJID = presence.getFrom();
- presenceSender.addDirectedPresenceReceiver(ownMUCJID, DirectedPresenceSender.SendPresence.AndSendPresence);
- }
- if (createAsReservedIfNew) {
- unlocking = true;
- requestConfigurationForm();
- } else {
- MUCOwnerPayload mucOwnerPayload = new MUCOwnerPayload();
- presenceSender.addDirectedPresenceReceiver(ownMUCJID, DirectedPresenceSender.SendPresence.DontSendPresence);
- mucOwnerPayload.setPayload(new Form(Form.Type.SUBMIT_TYPE));
- GenericRequest<MUCOwnerPayload> request = new GenericRequest<MUCOwnerPayload>(IQ.Type.Set,
- getJID(), mucOwnerPayload, iqRouter_);
- request.onResponse.connect(new Slot2<MUCOwnerPayload, ErrorPayload>() {
- @Override
- public void call(MUCOwnerPayload p1,ErrorPayload p2) {
- handleCreationConfigResponse(p1,p2);
-
- }
- });
- request.send();
- }
- }
- }
- }
- }
-
- private void handleOccupantRoleChangeResponse(MUCAdminPayload ref , ErrorPayload error, JID jid, MUCOccupant.Role role) {
- if (error != null) {
- onRoleChangeFailed.emit(error, jid, role);
- }
- }
- private void internalJoin(String nick) {
- //TODO: history request
- joinComplete_ = false;
- joinSucceeded_ = false;
-
- mucRegistry.addMUC(getJID());
-
- ownMUCJID = new JID(ownMUCJID.getNode(), ownMUCJID.getDomain(), nick);
-
- Presence joinPresence = new Presence(presenceSender.getLastSentUndirectedPresence());
- if(joinPresence.getType() != Presence.Type.Available) {
- throw new RuntimeException("From[" + joinPresence.getFrom() + "] and" +
- " To[" + joinPresence.getTo() + "] is not available");
- }
- joinPresence.setTo(ownMUCJID);
- MUCPayload mucPayload = new MUCPayload();
- if (joinSince_ != null) {
- mucPayload.setSince(joinSince_);
- }
- if (password != null) {
- mucPayload.setPassword(password);
- }
- joinPresence.addPayload(mucPayload);
- presenceSender.sendPresence(joinPresence);
- }
-
-
- private boolean isFromMUC(final JID j) {
- return ownMUCJID.compare(j, CompareType.WithoutResource) == 0;
- }
-
- public void handleUserLeft(LeavingType type) {
- String resource = ownMUCJID.getResource();
- if (occupants.containsKey(resource)) {
- MUCOccupant me = occupants.get(resource);
- occupants.remove(resource);
- onOccupantLeft.emit(me, type, "");
- }
- occupants.clear();
- joinComplete_ = false;
- joinSucceeded_ = false;
- presenceSender.removeDirectedPresenceReceiver(ownMUCJID,
- DirectedPresenceSender.SendPresence.DontSendPresence);
- }
-
- /**
- * Disconnect signals for this MUC.
- * This method should be called when the MUC object is no longer in use
- * so as to enable the garbage collector to remove this object from used space.
- */
- public void disconnect() {
- signalPresRcvd.onDestroyed.emit();
- }
-}
+public abstract class MUC {
+
+ public enum JoinResult { JoinSucceeded, JoinFailed };
+ public enum LeavingType { LeavePart, LeaveKick, LeaveBan, LeaveDestroy, LeaveNotMember, Disconnect };
+
+ /**
+ * Returns the (bare) JID of the MUC.
+ */
+ public abstract JID getJID();
+
+ /**
+ * Returns if the room is unlocked and other people can join the room.
+ * @return True if joinable by others; false otherwise.
+ */
+ public abstract boolean isUnlocked();
+
+ public abstract void joinAs(final String nick);
+ public abstract void joinWithContextSince(final String nick, final Date since);
+ /*public abstract void queryRoomInfo(); */
+ /*public abstract void queryRoomItems(); */
+ /*public abstract String getCurrentNick(); */
+ public abstract Map<String, MUCOccupant> getOccupants();
+ public abstract void changeNickname(final String newNickname);
+ public abstract void part();
+ /*public abstract void handleIncomingMessage(Message::ref message); */
+ /** Expose public so it can be called when e.g. user goes offline */
+ public abstract void handleUserLeft(LeavingType l);
+ /** Get occupant information*/
+ public abstract MUCOccupant getOccupant(final String nick);
+ public abstract boolean hasOccupant(final String nick);
+ public abstract void kickOccupant(final JID jid);
+ public abstract void changeOccupantRole(final JID jid, MUCOccupant.Role role);
+ public abstract void requestAffiliationList(MUCOccupant.Affiliation aff);
+ public abstract void changeAffiliation(final JID jid, MUCOccupant.Affiliation affiliation);
+ public abstract void changeSubject(final String subject);
+ public abstract void requestConfigurationForm();
+ public abstract void configureRoom(Form f);
+ public abstract void cancelConfigureRoom();
+ public abstract void destroyRoom();
+ /** Send an invite for the person to join the MUC */
+ public abstract void invitePerson(final JID person, final String reason, boolean isImpromptu, boolean isReuseChat);
+ public abstract void setCreateAsReservedIfNew();
+ public abstract void setPassword(final String password);
+
+ public final Signal1<String> onJoinComplete = new Signal1<String>();
+ public final Signal1<ErrorPayload> onJoinFailed = new Signal1<ErrorPayload>();
+ public final Signal3<ErrorPayload, JID, MUCOccupant.Role> onRoleChangeFailed = new Signal3<ErrorPayload, JID, MUCOccupant.Role>();
+ public final Signal3<ErrorPayload, JID, MUCOccupant.Affiliation> onAffiliationChangeFailed = new Signal3<ErrorPayload, JID, MUCOccupant.Affiliation>();
+ public final Signal1<ErrorPayload> onConfigurationFailed = new Signal1<ErrorPayload>();
+ public final Signal1<ErrorPayload> onAffiliationListFailed = new Signal1<ErrorPayload>();
+ public final Signal1<Presence> onOccupantPresenceChange = new Signal1<Presence>();
+ public final Signal3<String, MUCOccupant, MUCOccupant.Role> onOccupantRoleChanged = new Signal3<String, MUCOccupant, MUCOccupant.Role>();
+ public final Signal3<String, MUCOccupant.Affiliation /*new*/, MUCOccupant.Affiliation /*old*/> onOccupantAffiliationChanged = new Signal3<String, MUCOccupant.Affiliation, MUCOccupant.Affiliation>();
+ public final Signal1<MUCOccupant> onOccupantJoined = new Signal1<MUCOccupant>();
+ public final Signal2<String, String> onOccupantNicknameChanged = new Signal2<String, String>();
+ public final Signal3<MUCOccupant, LeavingType, String> onOccupantLeft = new Signal3<MUCOccupant, LeavingType, String>();
+ public final Signal1<Form> onConfigurationFormReceived = new Signal1<Form>();
+ public final Signal2<MUCOccupant.Affiliation, Vector<JID> > onAffiliationListReceived = new Signal2<MUCOccupant.Affiliation, Vector<JID> >();
+ public final Signal onUnlocked = new Signal();
+ /* public final Signal1<MUCInfo> onInfoResult; */
+ /* public final Signal1<blah> onItemsResult; */
+} \ No newline at end of file
diff --git a/src/com/isode/stroke/muc/MUCBookmark.java b/src/com/isode/stroke/muc/MUCBookmark.java
index 42b9dff..e4a306c 100644
--- a/src/com/isode/stroke/muc/MUCBookmark.java
+++ b/src/com/isode/stroke/muc/MUCBookmark.java
@@ -16,7 +16,7 @@ import com.isode.stroke.jid.JID;
*/
public class MUCBookmark {
private JID room_;
- private String name_;
+ private String name_ = "";
private String nick_;
private String password_;
private boolean autojoin_;
@@ -136,7 +136,7 @@ public class MUCBookmark {
if(!checkEqualsWhenNull(rhs.room_,room_)) return false;
if(!checkEqualsWhenNull(rhs.name_,name_)) return false;
if(!checkEqualsWhenNull(rhs.nick_,nick_)) return false;
- if(!checkEqualsWhenNull(rhs.password_,password_)) return false;
+ //if(!checkEqualsWhenNull(rhs.password_,password_)) return false;
if(!rhs.autojoin_ != autojoin_) return false;
return true;
}
diff --git a/src/com/isode/stroke/muc/MUCImpl.java b/src/com/isode/stroke/muc/MUCImpl.java
new file mode 100644
index 0000000..88d98a1
--- /dev/null
+++ b/src/com/isode/stroke/muc/MUCImpl.java
@@ -0,0 +1,736 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+/*
+ * Copyright (c) 2015 Tarun Gupta.
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+package com.isode.stroke.muc;
+
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Vector;
+
+import com.isode.stroke.client.StanzaChannel;
+import com.isode.stroke.elements.ErrorPayload;
+import com.isode.stroke.elements.CapsInfo;
+import com.isode.stroke.elements.Form;
+import com.isode.stroke.elements.IQ;
+import com.isode.stroke.elements.MUCAdminPayload;
+import com.isode.stroke.elements.MUCDestroyPayload;
+import com.isode.stroke.elements.MUCInvitationPayload;
+import com.isode.stroke.elements.MUCItem;
+import com.isode.stroke.elements.MUCOccupant;
+import com.isode.stroke.elements.MUCOwnerPayload;
+import com.isode.stroke.elements.MUCPayload;
+import com.isode.stroke.elements.MUCUserPayload;
+import com.isode.stroke.elements.Message;
+import com.isode.stroke.elements.Presence;
+import com.isode.stroke.jid.JID;
+import com.isode.stroke.jid.JID.CompareType;
+import com.isode.stroke.presence.DirectedPresenceSender;
+import com.isode.stroke.queries.GenericRequest;
+import com.isode.stroke.queries.IQRouter;
+import com.isode.stroke.signals.Signal;
+import com.isode.stroke.signals.Signal1;
+import com.isode.stroke.signals.Signal2;
+import com.isode.stroke.signals.Signal3;
+import com.isode.stroke.signals.SignalConnection;
+import com.isode.stroke.signals.Slot1;
+import com.isode.stroke.signals.Slot2;
+
+/**
+ * Class representing multi user chat room
+ *
+ */
+public class MUCImpl extends MUC {
+
+ private boolean createAsReservedIfNew;
+ private IQRouter iqRouter_;
+ private boolean joinComplete_;
+ private Date joinSince_;
+ private boolean joinSucceeded_;
+ private MUCRegistry mucRegistry;
+ private Map<String, MUCOccupant> occupants = new HashMap<String, MUCOccupant>();
+ private JID ownMUCJID = new JID();
+ private String password;
+ private DirectedPresenceSender presenceSender;
+ private StanzaChannel stanzaChannel;
+ private boolean unlocking;
+ private boolean isUnlocked_;
+ private SignalConnection scopedConnection_;
+ private Presence joinRequestPresence_ = new Presence();
+
+ /**
+ * Create a MUC Session
+ * @param stanzaChannel stanza channel, not null
+ * @param iqRouter IQ stanza router, not null
+ * @param presenceSender Presence Sender, not null
+ * @param muc JID of the chat room, not null
+ * @param mucRegistry MUC registry, not null
+ *
+ * @see #disconnect()
+ */
+ public MUCImpl(StanzaChannel stanzaChannel, IQRouter iqRouter,
+ DirectedPresenceSender presenceSender, final JID muc,
+ MUCRegistry mucRegistry) {
+ ownMUCJID = muc;
+ this.stanzaChannel = stanzaChannel;
+ this.iqRouter_ = iqRouter;
+ this.presenceSender = presenceSender;
+ this.mucRegistry = mucRegistry;
+ this.createAsReservedIfNew = false;
+ this.unlocking = false;
+ this.isUnlocked_ = false;
+ scopedConnection_ = this.stanzaChannel.onPresenceReceived.connect(
+ new Slot1<Presence>() {
+ @Override
+ public void call(Presence p1) {
+ handleIncomingPresence(p1);
+ }
+ });
+ }
+
+ /**
+ * Cancel the command for configuring room
+ */
+ public void cancelConfigureRoom() {
+ MUCOwnerPayload mucPayload = new MUCOwnerPayload();
+ mucPayload.setPayload(new Form(Form.Type.CANCEL_TYPE));
+ GenericRequest<MUCOwnerPayload> request = new GenericRequest<MUCOwnerPayload>(
+ IQ.Type.Set, getJID(), mucPayload, iqRouter_);
+ request.send();
+ }
+
+ /**
+ * Change the affiliation of the given Jabber ID.
+ * It must be called with the real JID, not the room JID.
+ * @param jid real jabber ID, not null
+ * @param affiliation new affiliation, not null
+ */
+ public void changeAffiliation(final JID jid, final MUCOccupant.Affiliation affiliation) {
+ final MUCAdminPayload mucPayload = new MUCAdminPayload();
+ MUCItem item = new MUCItem();
+ item.affiliation = affiliation;
+ item.realJID = jid.toBare();
+ mucPayload.addItem(item);
+ GenericRequest<MUCAdminPayload> request = new GenericRequest<MUCAdminPayload>(
+ IQ.Type.Set, getJID(), mucPayload, iqRouter_);
+ request.onResponse.connect(new Slot2<MUCAdminPayload, ErrorPayload>() {
+ @Override
+ public void call(MUCAdminPayload p1, ErrorPayload p2) {
+ handleAffiliationChangeResponse(p1,p2,jid,affiliation);
+ }
+ });
+ request.send();
+ }
+
+ /**
+ * Change the role of the specified occupant. It must be
+ * called with the room JID, not the real JID.
+ * @param jid Jabber ID of the occupant in the chat room, not null
+ * @param role new role, not null
+ */
+ public void changeOccupantRole(final JID jid, final MUCOccupant.Role role) {
+ final MUCAdminPayload mucPayload = new MUCAdminPayload();
+ MUCItem item = new MUCItem();
+ item.role = role;
+ item.nick = jid.getResource();
+ mucPayload.addItem(item);
+ GenericRequest<MUCAdminPayload> request = new GenericRequest<MUCAdminPayload>(
+ IQ.Type.Set, getJID(), mucPayload, iqRouter_);
+ request.onResponse.connect(new Slot2<MUCAdminPayload, ErrorPayload>() {
+ @Override
+ public void call(MUCAdminPayload p1, ErrorPayload p2) {
+ handleOccupantRoleChangeResponse(p1,p2,jid,role);
+ }
+ });
+ request.send();
+ }
+
+ /**
+ * Change the subject of the chat room
+ * @param subject new subject, not null
+ */
+ public void changeSubject(String subject) {
+ Message message = new Message();
+ message.setSubject(subject);
+ message.setType(Message.Type.Groupchat);
+ message.setTo(ownMUCJID.toBare());
+ stanzaChannel.sendMessage(message);
+ }
+
+ /**
+ * Configure a chat room room
+ * @param form form to be used for configuration, not null
+ */
+ public void configureRoom(Form form) {
+ MUCOwnerPayload mucPayload = new MUCOwnerPayload();
+ mucPayload.setPayload(form);
+ GenericRequest<MUCOwnerPayload> request = new GenericRequest<MUCOwnerPayload>(
+ IQ.Type.Set, getJID(), mucPayload, iqRouter_);
+ if (unlocking) {
+ request.onResponse.connect(new Slot2<MUCOwnerPayload, ErrorPayload>() {
+ @Override
+ public void call(MUCOwnerPayload p1, ErrorPayload p2) {
+ handleCreationConfigResponse(p1, p2);
+ }
+ });
+ }else {
+ request.onResponse.connect(new Slot2<MUCOwnerPayload, ErrorPayload>() {
+ @Override
+ public void call(MUCOwnerPayload p1, ErrorPayload p2) {
+ handleConfigurationResultReceived(p1, p2);
+ }
+ });
+ }
+ request.send();
+ }
+
+ /**
+ * Destroy the chat room
+ */
+ public void destroyRoom() {
+ MUCOwnerPayload mucPayload = new MUCOwnerPayload();
+ MUCDestroyPayload mucDestroyPayload = new MUCDestroyPayload();
+ mucPayload.setPayload(mucDestroyPayload);
+ GenericRequest<MUCOwnerPayload> request = new GenericRequest<MUCOwnerPayload>(
+ IQ.Type.Set, getJID(), mucPayload, iqRouter_);
+ request.onResponse.connect(new Slot2<MUCOwnerPayload, ErrorPayload>() {
+ @Override
+ public void call(MUCOwnerPayload p1, ErrorPayload p2) {
+ handleConfigurationResultReceived(p1,p2);
+ }
+ });
+ request.send();
+ }
+
+ /**
+ * Returns the (bare) JID of the MUC.
+ * @return bare JID(i.e. without resource)
+ */
+ public JID getJID() {
+ return ownMUCJID.toBare();
+ }
+
+ /**
+ * Returns if the room is unlocked and other people can join the room.
+ * @return True if joinable by others; false otherwise.
+ */
+ public boolean isUnlocked() {
+ return isUnlocked_;
+ }
+
+ /**
+ * Get the MUC occupant with the given nick name
+ * @param nick nick name, not null
+ * @return MUC occupant if it exists or null if not
+ */
+ public MUCOccupant getOccupant(String nick) {
+ return occupants.get(nick);
+ }
+
+ /**
+ * Determine if the room contains occupant with given nick
+ * @param nick given nick
+ * @return true if the occupant exists, false otherwise
+ */
+ public boolean hasOccupant(String nick) {
+ return occupants.containsKey(nick);
+ }
+
+ public Map<String, MUCOccupant> getOccupants() {
+ return occupants;
+ }
+
+ /**
+ * Invite the person with give JID to the chat room
+ * @param person jabber ID o the person to invite,not nul
+ */
+ public void invitePerson(JID person) {
+ invitePerson(person, "", false, false);
+ }
+
+ /**
+ * Send an invite for the person to join the MUC
+ * @param person jabber ID of the person to invite, not null
+ * @param reason join reason, not null
+ * @param isImpromptu
+ */
+ public void invitePerson(JID person, String reason, boolean isImpromptu) {
+ invitePerson(person, reason, isImpromptu, false);
+ }
+
+
+ /**
+ * Send an invite for the person to join the MUC
+ * @param person jabber ID of the person to invite, not null
+ * @param reason join reason, not null
+ * @param isImpromptu
+ * @param isReuseChat
+ */
+ public void invitePerson(JID person, String reason, boolean isImpromptu, boolean isReuseChat) {
+ Message message = new Message();
+ message.setTo(person);
+ message.setType(Message.Type.Normal);
+ MUCInvitationPayload invite = new MUCInvitationPayload();
+ invite.setReason(reason);
+ invite.setIsImpromptu(isImpromptu);
+ invite.setIsContinuation(isReuseChat);
+ invite.setJID(ownMUCJID.toBare());
+ message.addPayload(invite);
+ stanzaChannel.sendMessage(message);
+ }
+
+ /**
+ * Join the MUC with default context.
+ * @param nick nick name of the user, not null
+ */
+ public void joinAs(String nick) {
+ joinSince_ = null;
+ internalJoin(nick);
+ }
+
+ /**
+ * Join the MUC with context since date.
+ * @param nick nick name, not null
+ * @param since date since the nick joined, not null
+ */
+ public void joinWithContextSince(String nick, Date since) {
+ joinSince_ = since;
+ internalJoin(nick);
+ }
+
+ /**
+ * Kick the given occupant out of the chat room
+ * @param jid jabber ID of the user to kick, not null
+ */
+ public void kickOccupant(JID jid) {
+ changeOccupantRole(jid, MUCOccupant.Role.NoRole);
+ }
+
+ public void changeNickname(final String newNickname) {
+ Presence changeNicknamePresence = new Presence();
+ changeNicknamePresence.setTo(new JID(ownMUCJID.toBare().toString() + "/" + newNickname));
+ presenceSender.sendPresence(changeNicknamePresence);
+ }
+
+ /**
+ * Leave the chat room
+ */
+ public void part() {
+ presenceSender.removeDirectedPresenceReceiver(ownMUCJID,
+ DirectedPresenceSender.SendPresence.AndSendPresence);
+ mucRegistry.removeMUC(getJID());
+ }
+
+ /**
+ * Send a request to get a list of users for the given affiliation
+ * @param affiliation affiliation, not null
+ */
+ public void requestAffiliationList(final MUCOccupant.Affiliation affiliation) {
+ MUCAdminPayload mucPayload = new MUCAdminPayload();
+ MUCItem item = new MUCItem();
+ item.affiliation = affiliation;
+ mucPayload.addItem(item);
+ GenericRequest<MUCAdminPayload> request = new GenericRequest<MUCAdminPayload>(
+ IQ.Type.Get, getJID(), mucPayload, iqRouter_);
+ request.onResponse.connect(new Slot2<MUCAdminPayload, ErrorPayload>() {
+ @Override
+ public void call(MUCAdminPayload p1, ErrorPayload p2) {
+ handleAffiliationListResponse(p1,p2,affiliation);
+ }
+ });
+ request.send();
+ }
+
+ /**
+ * Send a request for getting form for configuring a room
+ */
+ public void requestConfigurationForm() {
+ MUCOwnerPayload mucPayload = new MUCOwnerPayload();
+ GenericRequest<MUCOwnerPayload> request = new GenericRequest<MUCOwnerPayload>(
+ IQ.Type.Get, getJID(), mucPayload, iqRouter_);
+ request.onResponse.connect(new Slot2<MUCOwnerPayload, ErrorPayload>() {
+ @Override
+ public void call(MUCOwnerPayload p1, ErrorPayload p2) {
+ handleConfigurationFormReceived(p1,p2);
+ }
+ });
+ request.send();
+ }
+
+ /**
+ * Set the reserved status of room to true.
+ * By default a new room with the default configuration is created.
+ * The effect of calling this function is to leave the room in reserved state
+ * but not configured so that it can be configured later.
+ */
+ public void setCreateAsReservedIfNew() {
+ createAsReservedIfNew = true;
+ }
+
+ /**
+ * Set the password used for entering the room.
+ * @param newPassword password, can be null
+ */
+ public void setPassword(String newPassword) {
+ password = newPassword;
+ }
+
+ /**
+ * Get the nick name of the MUC room
+ * @return nick name, can be null
+ */
+ private String getOwnNick() {
+ return ownMUCJID.getResource();
+ }
+
+ private void handleAffiliationChangeResponse(MUCAdminPayload ref,
+ ErrorPayload error, JID jid, MUCOccupant.Affiliation affiliation) {
+ if (error != null) {
+ onAffiliationChangeFailed.emit(error, jid, affiliation);
+ }
+ }
+
+ private void handleAffiliationListResponse(MUCAdminPayload payload,
+ ErrorPayload error, MUCOccupant.Affiliation affiliation) {
+ if (error != null) {
+ onAffiliationListFailed.emit(error);
+ } else {
+ Vector<JID> jids = new Vector<JID>();
+ for (MUCItem item : payload.getItems()) {
+ if (item.realJID != null) {
+ jids.add(item.realJID);
+ }
+ }
+ onAffiliationListReceived.emit(affiliation, jids);
+ }
+ }
+
+ private void handleConfigurationFormReceived(MUCOwnerPayload payload,
+ ErrorPayload error) {
+ Form form = null;
+ if (payload != null) {
+ form = payload.getForm();
+ }
+ if (error != null || form == null) {
+ onConfigurationFailed.emit(error);
+ } else {
+ onConfigurationFormReceived.emit(form);
+ }
+ }
+
+ private void handleConfigurationResultReceived(
+ MUCOwnerPayload payload, ErrorPayload error) {
+ if (error != null) {
+ onConfigurationFailed.emit(error);
+ }
+ }
+
+ private void handleCreationConfigResponse(MUCOwnerPayload ref , ErrorPayload error) {
+ unlocking = false;
+ if (error != null) {
+ presenceSender.removeDirectedPresenceReceiver(ownMUCJID,
+ DirectedPresenceSender.SendPresence.AndSendPresence);
+ onJoinFailed.emit(error);
+ } else {
+ onJoinComplete.emit(getOwnNick()); /* Previously, this wasn't needed here,
+ as the presence duplication bug caused an emit elsewhere. */
+ isUnlocked_ = true;
+ onUnlocked.emit();
+ }
+ }
+
+ private void handleIncomingPresence(Presence presence) {
+ if (!isFromMUC(presence.getFrom())) {
+ return;
+ }
+
+ MUCUserPayload mucPayload = null;
+
+ MUCUserPayload dummyUserPayload = new MUCUserPayload();
+ for (MUCUserPayload payload : presence.getPayloads(dummyUserPayload)) {
+ if (!payload.getItems().isEmpty() || !payload.getStatusCodes().isEmpty()) {
+ mucPayload = payload;
+ }
+ }
+
+ // On the first incoming presence, check if our join has succeeded
+ // (i.e. we start getting non-error presence from the MUC) or not
+ if (!joinSucceeded_) {
+ if(presence.getType().equals(Presence.Type.Error)) {
+ String reason = "";
+ onJoinFailed.emit(presence.getPayload(new ErrorPayload()));
+ return;
+ }
+ else {
+ joinSucceeded_ = true;
+ presenceSender.addDirectedPresenceReceiver(ownMUCJID,
+ DirectedPresenceSender.SendPresence.DontSendPresence);
+ if ((presenceSender.getLastSentUndirectedPresence() != null) && !(isEqualExceptID(presenceSender.getLastSentUndirectedPresence(), joinRequestPresence_))) {
+ // our presence changed between join request and join complete, send current presence to MUC
+ Presence latestPresence = new Presence(presenceSender.getLastSentUndirectedPresence());
+ latestPresence.setTo(ownMUCJID);
+ presenceSender.sendPresence(latestPresence);
+ }
+ }
+ }
+
+ String nick = presence.getFrom().getResource();
+ if (nick == null || nick.isEmpty()) {
+ return;
+ }
+ MUCOccupant.Role role = MUCOccupant.Role.NoRole;
+ MUCOccupant.Affiliation affiliation= MUCOccupant.Affiliation.NoAffiliation;
+ JID realJID = null;
+ if (mucPayload != null && mucPayload.getItems().size() > 0) {
+ role = mucPayload.getItems().get(0).role != null
+ ? mucPayload.getItems().get(0).role : MUCOccupant.Role.NoRole;
+ affiliation = mucPayload.getItems().get(0).affiliation != null
+ ? mucPayload.getItems().get(0).affiliation : MUCOccupant.Affiliation.NoAffiliation;
+ realJID = mucPayload.getItems().get(0).realJID;
+ }
+
+ //100 is non-anonymous
+ //TODO: 100 may also be specified in a <message/>
+ //170 is room logging to http
+ //TODO: Nick changes
+
+ if (presence.getType().equals(Presence.Type.Unavailable)) {
+ LeavingType type = LeavingType.LeavePart;
+ String newNickname = null;
+ if (mucPayload != null) {
+ if (mucPayload.getPayload() instanceof MUCDestroyPayload) {
+ type = LeavingType.LeaveDestroy;
+ } else for (MUCUserPayload.StatusCode status : mucPayload.getStatusCodes()) {
+ if (status.code == 307) {
+ type = LeavingType.LeaveKick;
+ } else if (status.code == 301) {
+ type = LeavingType.LeaveBan;
+ } else if (status.code == 321) {
+ type = LeavingType.LeaveNotMember;
+ } else if (status.code == 303) {
+ if (mucPayload.getItems().size() == 1) {
+ newNickname = mucPayload.getItems().get(0).nick;
+ }
+ }
+ }
+ }
+
+ if (newNickname != null) {
+ if (occupants.containsKey(nick)) {
+ MUCOccupant occupant = occupants.get(nick);
+ occupants.remove(nick);
+ occupant.setNick(newNickname);
+ occupants.put(newNickname, occupant);
+ onOccupantNicknameChanged.emit(nick, newNickname);
+ }
+ }
+ else {
+ if (presence.getFrom().equals(ownMUCJID)) {
+ handleUserLeft(type);
+ return;
+ } else {
+ if (occupants.containsKey(nick)) {
+ //TODO: part type
+ MUCOccupant occupant = occupants.get(nick);
+ occupants.remove(nick);
+ onOccupantLeft.emit(occupant, type, "");
+ }
+ }
+ }
+ }
+ else if (presence.getType().equals(Presence.Type.Available)) {
+ MUCOccupant occupant = new MUCOccupant(nick, role, affiliation);
+ boolean isJoin = true;
+ if (realJID != null) {
+ occupant.setRealJID(realJID);
+ }
+ if (occupants.containsKey(nick)) {
+ isJoin = false;
+ MUCOccupant oldOccupant = occupants.get(nick);
+ if (!oldOccupant.getRole().equals(role)) {
+ onOccupantRoleChanged.emit(nick, occupant, oldOccupant.getRole());
+ }
+ if (!oldOccupant.getAffiliation().equals(affiliation)) {
+ onOccupantAffiliationChanged.emit(nick, affiliation, oldOccupant.getAffiliation());
+ }
+ occupants.remove(nick);
+ }
+ occupants.put(nick, occupant);
+
+ if (isJoin) {
+ onOccupantJoined.emit(occupant);
+ }
+ onOccupantPresenceChange.emit(presence);
+ }
+
+ if (mucPayload != null && !joinComplete_) {
+ boolean isLocked = false;
+ for (MUCUserPayload.StatusCode status : mucPayload.getStatusCodes()) {
+ if(status.code == 110) {
+ /* Simply knowing this is your presence is enough, 210 doesn't seem to be necessary. */
+ joinComplete_ = true;
+ if (!ownMUCJID.equals(presence.getFrom())) {
+ presenceSender.removeDirectedPresenceReceiver(ownMUCJID, DirectedPresenceSender.SendPresence.DontSendPresence);
+ ownMUCJID = presence.getFrom();
+ presenceSender.addDirectedPresenceReceiver(ownMUCJID, DirectedPresenceSender.SendPresence.AndSendPresence);
+ }
+ }
+ if (status.code == 201) {
+ isLocked = true;
+ /* Room is created and locked */
+ /* Currently deal with this by making an instant room */
+ if (!ownMUCJID.equals(presence.getFrom())) {
+ presenceSender.removeDirectedPresenceReceiver(ownMUCJID, DirectedPresenceSender.SendPresence.DontSendPresence);
+ ownMUCJID = presence.getFrom();
+ presenceSender.addDirectedPresenceReceiver(ownMUCJID, DirectedPresenceSender.SendPresence.AndSendPresence);
+ }
+ if (createAsReservedIfNew) {
+ unlocking = true;
+ requestConfigurationForm();
+ } else {
+ // Accept default room configuration and create an instant room http://xmpp.org/extensions/xep-0045.html#createroom-instant
+ MUCOwnerPayload mucOwnerPayload = new MUCOwnerPayload();
+ presenceSender.addDirectedPresenceReceiver(ownMUCJID, DirectedPresenceSender.SendPresence.DontSendPresence);
+ mucOwnerPayload.setPayload(new Form(Form.Type.SUBMIT_TYPE));
+ GenericRequest<MUCOwnerPayload> request = new GenericRequest<MUCOwnerPayload>(IQ.Type.Set,
+ getJID(), mucOwnerPayload, iqRouter_);
+ request.onResponse.connect(new Slot2<MUCOwnerPayload, ErrorPayload>() {
+ @Override
+ public void call(MUCOwnerPayload p1,ErrorPayload p2) {
+ handleCreationConfigResponse(p1,p2);
+
+ }
+ });
+ request.send();
+ }
+ }
+ }
+ if (joinComplete_ && !isLocked) {
+ onJoinComplete.emit(getOwnNick());
+ }
+ if (!isLocked && !isUnlocked_ && (presence.getFrom().equals(ownMUCJID))) {
+ isUnlocked_ = true;
+ onUnlocked.emit();
+ }
+ }
+ }
+
+ private void handleOccupantRoleChangeResponse(MUCAdminPayload ref , ErrorPayload error, JID jid, MUCOccupant.Role role) {
+ if (error != null) {
+ onRoleChangeFailed.emit(error, jid, role);
+ }
+ }
+
+ /**
+ * This function compares two Presence elements for equality based on to, from, status, show and entity capability information.
+ * @return True if equal; else otherwise.
+ */
+ private static boolean isEqualExceptID(final Presence lhs, final Presence rhs) {
+ boolean isEqual = false;
+ if(lhs == null || rhs == null) {
+ isEqual = (lhs == null && rhs == null);
+ }
+ else if (lhs.getFrom().equals(rhs.getFrom()) && lhs.getTo().equals(rhs.getTo()) && lhs.getStatus().equals(rhs.getStatus()) && lhs.getShow().equals(rhs.getShow())) {
+ CapsInfo lhsCaps = lhs.getPayload(new CapsInfo());
+ CapsInfo rhsCaps = rhs.getPayload(new CapsInfo());
+
+ if (lhsCaps != null && rhsCaps != null) {
+ isEqual = (lhsCaps.equals(rhsCaps));
+ }
+ else {
+ isEqual = (lhsCaps == null && rhsCaps == null);
+ }
+ }
+ return isEqual;
+ }
+
+ private void internalJoin(String nick) {
+ //TODO: history request
+ joinComplete_ = false;
+ joinSucceeded_ = false;
+
+ mucRegistry.addMUC(getJID());
+
+ ownMUCJID = new JID(ownMUCJID.getNode(), ownMUCJID.getDomain(), nick);
+ Presence joinPresence = (presenceSender.getLastSentUndirectedPresence() != null ? new Presence(presenceSender.getLastSentUndirectedPresence()) : new Presence());
+ if(joinPresence.getType() != Presence.Type.Available) {
+ throw new RuntimeException("From[" + joinPresence.getFrom() + "] and" +
+ " To[" + joinPresence.getTo() + "] is not available");
+ }
+ joinPresence.setTo(ownMUCJID);
+ MUCPayload mucPayload = new MUCPayload();
+ if (joinSince_ != null) {
+ mucPayload.setSince(joinSince_);
+ }
+ if (password != null) {
+ mucPayload.setPassword(password);
+ }
+ joinPresence.addPayload(mucPayload);
+ joinRequestPresence_ = joinPresence;
+ presenceSender.sendPresence(joinPresence);
+ }
+
+
+ private boolean isFromMUC(final JID j) {
+ return ownMUCJID.compare(j, CompareType.WithoutResource) == 0;
+ }
+
+ public void handleUserLeft(LeavingType type) {
+ String resource = ownMUCJID.getResource();
+ if (occupants.containsKey(resource)) {
+ MUCOccupant me = occupants.get(resource);
+ occupants.remove(resource);
+ onOccupantLeft.emit(me, type, "");
+ }
+ occupants.clear();
+ joinComplete_ = false;
+ joinSucceeded_ = false;
+ isUnlocked_ = false;
+ presenceSender.removeDirectedPresenceReceiver(ownMUCJID,
+ DirectedPresenceSender.SendPresence.DontSendPresence);
+ }
+
+ /**
+ * Disconnect signals for this MUC.
+ * This method should be called when the MUC object is no longer in use
+ * so as to enable the garbage collector to remove this object from used space.
+ */
+ public void disconnect() {
+ if (scopedConnection_ != null) {
+ scopedConnection_.onDestroyed.emit();
+ scopedConnection_ = null;
+ }
+ }
+
+ protected void finalize() throws Throwable {
+ try {
+ disconnect();
+ }
+ finally {
+ super.finalize();
+ }
+ }
+
+ //TODO: Invites(direct/mediated)
+
+ //TODO: requesting membership
+
+ //TODO: get member list
+
+ //TODO: request voice
+
+ //TODO: moderator use cases
+
+ //TODO: Admin use cases
+
+ //TODO: Owner use cases
+
+}
diff --git a/src/com/isode/stroke/muc/MUCManager.java b/src/com/isode/stroke/muc/MUCManager.java
index 6a7757a..5d5ccba 100644
--- a/src/com/isode/stroke/muc/MUCManager.java
+++ b/src/com/isode/stroke/muc/MUCManager.java
@@ -44,6 +44,6 @@ public class MUCManager {
* @return MUC room, not null
*/
public MUC createMUC(JID jid) {
- return new MUC(stanzaChannel_, iqRouter_, presenceSender_, jid, mucRegistry_);
+ return new MUCImpl(stanzaChannel_, iqRouter_, presenceSender_, jid, mucRegistry_);
}
}
diff --git a/src/com/isode/stroke/presence/DirectedPresenceSender.java b/src/com/isode/stroke/presence/DirectedPresenceSender.java
index a08a2b8..5af85ef 100644
--- a/src/com/isode/stroke/presence/DirectedPresenceSender.java
+++ b/src/com/isode/stroke/presence/DirectedPresenceSender.java
@@ -78,7 +78,7 @@ public class DirectedPresenceSender implements PresenceSender {
return;
}
sender_.sendPresence(presence);
- if (presence.getTo() == null || !presence.getTo().isValid()) {
+ if (!presence.getTo().isValid()) {
Presence presenceCopy = new Presence(presence);
for(JID jid : directedPresenceReceivers_) {
presenceCopy.setTo(jid);
@@ -96,7 +96,7 @@ public class DirectedPresenceSender implements PresenceSender {
*/
public Presence getLastSentUndirectedPresence(){
if(lastSentUndirectedPresence_ == null) {
- return new Presence();
+ return null;
}else {
return new Presence(lastSentUndirectedPresence_);
}
diff --git a/test/com/isode/stroke/muc/MUCTest.java b/test/com/isode/stroke/muc/MUCTest.java
index 4b845c0..3de840c 100644
--- a/test/com/isode/stroke/muc/MUCTest.java
+++ b/test/com/isode/stroke/muc/MUCTest.java
@@ -26,10 +26,14 @@ import com.isode.stroke.elements.MUCOccupant;
import com.isode.stroke.elements.MUCOwnerPayload;
import com.isode.stroke.elements.MUCUserPayload;
import com.isode.stroke.elements.Presence;
+import com.isode.stroke.elements.CapsInfo;
+import com.isode.stroke.elements.VCard;
import com.isode.stroke.jid.JID;
import com.isode.stroke.presence.DirectedPresenceSender;
import com.isode.stroke.presence.StanzaChannelPresenceSender;
import com.isode.stroke.queries.IQRouter;
+import com.isode.stroke.base.ByteArray;
+import com.isode.stroke.signals.Slot2;
/**
* Unit tests for MUC
@@ -38,14 +42,15 @@ import com.isode.stroke.queries.IQRouter;
public class MUCTest {
private DummyStanzaChannel channel;
private IQRouter router;
- MUCRegistry mucRegistry;
- StanzaChannelPresenceSender stanzaChannelPresenceSender;
- DirectedPresenceSender presenceSender;
+ private MUCRegistry mucRegistry;
+ private StanzaChannelPresenceSender stanzaChannelPresenceSender;
+ private DirectedPresenceSender presenceSender;
private static class JoinResult {
- String nick;
+ String nick = "";
ErrorPayload error;
};
- private Vector<JoinResult> joinResults;
+ private Vector<JoinResult> joinResults = new Vector<JoinResult>();
+ private int nickChanges;
@Before
public void setUp() {
@@ -54,6 +59,7 @@ public class MUCTest {
mucRegistry = new MUCRegistry();
stanzaChannelPresenceSender = new StanzaChannelPresenceSender(channel);
presenceSender = new DirectedPresenceSender(stanzaChannelPresenceSender);
+ nickChanges = 0;
}
@Test
@@ -92,6 +98,20 @@ public class MUCTest {
}
@Test
+ public void testJoin_NoPresenceChangeDuringJoinDoesNotResendAfterJoinSuccess() {
+ MUC testling = createMUC(new JID("foo@bar.com"));
+ testling.joinAs("Alice");
+
+ receivePresence(new JID("foo@bar.com/Rabbit"), "Here");
+
+ assertEquals(1, channel.sentStanzas.size());
+ Presence p = channel.getStanzaAtIndex(new Presence(), 0);
+ assertTrue(p != null);
+ assertEquals(new JID("foo@bar.com/Alice"), p.getTo());
+ assertEquals("", p.getStatus());
+ }
+
+ @Test
public void testCreateInstant() {
MUC testling = createMUC(new JID("rabbithole@wonderland.lit"));
testling.joinAs("Alice");
@@ -120,14 +140,13 @@ public class MUCTest {
Presence initialPresence = new Presence();
initialPresence.setStatus("");
- //TODO: after vcard is ported this can be uncommented
- /*VCard vcard = new VCard();
- vcard.setPhoto(createByteArray("15c30080ae98ec48be94bf0e191d43edd06e500a"));
+ VCard vcard = new VCard();
+ vcard.setPhoto(new ByteArray("15c30080ae98ec48be94bf0e191d43edd06e500a"));
initialPresence.addPayload(vcard);
- CapsInfo caps = boost::make_shared<CapsInfo>();
+ CapsInfo caps = new CapsInfo();
caps.setNode("http://swift.im");
caps.setVersion("p2UP0DrcVgKM6jJqYN/B92DKK0o=");
- initialPresence.addPayload(caps);*/
+ initialPresence.addPayload(caps);
channel.sendPresence(initialPresence);
@@ -136,8 +155,8 @@ public class MUCTest {
Presence serverRespondsLocked = new Presence();
serverRespondsLocked.setFrom(new JID("test@rooms.swift.im/Test"));
serverRespondsLocked.setTo(new JID("test@swift.im/6913d576d55f0b67"));
- //serverRespondsLocked.addPayload(vcard);
- //serverRespondsLocked.addPayload(caps);
+ serverRespondsLocked.addPayload(vcard);
+ serverRespondsLocked.addPayload(caps);
serverRespondsLocked.setStatus("");
MUCUserPayload mucPayload = new MUCUserPayload();
MUCItem myItem = new MUCItem();
@@ -153,11 +172,94 @@ public class MUCTest {
assertTrue(iq.getPayload(new MUCOwnerPayload()) != null);
assertTrue(iq.getPayload(new MUCOwnerPayload()).getForm() != null);
assertEquals(Form.Type.SUBMIT_TYPE, iq.getPayload(new MUCOwnerPayload()).getForm().getType());
-}
+ }
+
+ @Test
+ public void testNicknameChange() {
+ MUC testling = createMUC(new JID("foo@bar.com"));
+ // Join as Rabbit
+ testling.joinAs("Rabbit");
+
+ // Rabbit joins
+ Presence rabbitJoins = new Presence();
+ rabbitJoins.setTo(new JID("test@swift.im/6913d576d55f0b67"));
+ rabbitJoins.setFrom(new JID(testling.getJID().toString() + "/Rabbit"));
+ channel.onPresenceReceived.emit(rabbitJoins);
+ assertEquals(true, testling.hasOccupant("Rabbit"));
+ // Alice joins
+ Presence aliceJoins = new Presence();
+ aliceJoins.setTo(new JID("test@swift.im/6913d576d55f0b67"));
+ aliceJoins.setFrom(new JID(testling.getJID().toString() + "/Alice"));
+ channel.onPresenceReceived.emit(aliceJoins);
+ assertEquals(true, testling.hasOccupant("Alice"));
+
+ // Change nick to Dodo
+ testling.changeNickname("Dodo");
+ Presence stanza = channel.getStanzaAtIndex(new Presence(), 1);
+ assertNotNull(stanza);
+ assertEquals("Dodo", stanza.getTo().getResource());
+
+ // Alice changes nick to Alice2
+ stanza = new Presence();
+ stanza.setFrom(new JID("foo@bar.com/Alice"));
+ stanza.setTo(router.getJID());
+ stanza.setType(Presence.Type.Unavailable);
+ MUCUserPayload mucPayload = new MUCUserPayload();
+ MUCItem myItem = new MUCItem();
+ myItem.affiliation = MUCOccupant.Affiliation.Member;
+ myItem.nick = "Alice2";
+ myItem.role = MUCOccupant.Role.Participant;
+ mucPayload.addItem(myItem);
+ mucPayload.addStatusCode(new MUCUserPayload.StatusCode(303));
+ stanza.addPayload(mucPayload);
+ channel.onPresenceReceived.emit(stanza);
+ assertEquals(1, nickChanges);
+ assertEquals(false, testling.hasOccupant("Alice"));
+ assertEquals(true, testling.hasOccupant("Alice2"));
+
+ // We (Rabbit) change nick to Robot
+ stanza = new Presence();
+ stanza.setFrom(new JID("foo@bar.com/Rabbit"));
+ stanza.setTo(router.getJID());
+ stanza.setType(Presence.Type.Unavailable);
+ mucPayload = new MUCUserPayload();
+ myItem.affiliation = MUCOccupant.Affiliation.Member;
+ myItem.nick = "Robot";
+ myItem.role = MUCOccupant.Role.Participant;
+ mucPayload.addItem(myItem);
+ mucPayload.addStatusCode(new MUCUserPayload.StatusCode(303));
+ stanza.addPayload(mucPayload);
+ channel.onPresenceReceived.emit(stanza);
+ assertEquals(2, nickChanges);
+ assertEquals(false, testling.hasOccupant("Rabbit"));
+ assertEquals(true, testling.hasOccupant("Robot"));
+ }
+
+ /*void testJoin_Success() {
+ MUC::ref testling = createMUC(JID("foo@bar.com"));
+ testling.onJoinFinished.connect(boost::bind(&MUCTest::handleJoinFinished, this, _1, _2));
+ testling.joinAs("Alice");
+ receivePresence(JID("foo@bar.com/Rabbit"), "Here");
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(joinResults.size()));
+ CPPUNIT_ASSERT_EQUAL(std::string("Alice"), joinResults[0].nick);
+ CPPUNIT_ASSERT(joinResults[0].error);
+ }
+
+ void testJoin_Fail() {
+ //CPPUNIT_ASSERT(!mucRegistry.isMUC(JID("foo@bar.com")));
+ }*/
private MUC createMUC(JID jid) {
- return new MUC(channel, router, presenceSender, jid, mucRegistry);
+ MUC muc = new MUCImpl(channel, router, presenceSender, jid, mucRegistry);
+ muc.onOccupantNicknameChanged.connect(new Slot2<String, String>() {
+ @Override
+ public void call(String s1, String s2) {
+ handleOccupantNicknameChanged(s1, s2);
+ }
+ });
+ return muc;
}
private void handleJoinFinished(String nick, ErrorPayload error) {
@@ -170,6 +272,13 @@ public class MUCTest {
private void receivePresence(JID jid, String status) {
Presence p = new Presence(status);
p.setFrom(jid);
+ //MUCUserPayload mucUserPayload = new MUCUserPayload();
+ //mucUserPayload.addItem(item);
+ //p.addPayload(mucUserPayload);
channel.onPresenceReceived.emit(p);
}
+
+ private void handleOccupantNicknameChanged(final String s1, final String s2) {
+ nickChanges++;
+ }
}
diff --git a/test/com/isode/stroke/muc/MockMUC.java b/test/com/isode/stroke/muc/MockMUC.java
new file mode 100644
index 0000000..1b695d2
--- /dev/null
+++ b/test/com/isode/stroke/muc/MockMUC.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2013 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+/*
+ * Copyright (c) 2015 Tarun Gupta.
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+package com.isode.stroke.muc;
+
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Vector;
+
+import com.isode.stroke.client.StanzaChannel;
+import com.isode.stroke.elements.ErrorPayload;
+import com.isode.stroke.elements.CapsInfo;
+import com.isode.stroke.elements.Form;
+import com.isode.stroke.elements.IQ;
+import com.isode.stroke.elements.MUCAdminPayload;
+import com.isode.stroke.elements.MUCDestroyPayload;
+import com.isode.stroke.elements.MUCInvitationPayload;
+import com.isode.stroke.elements.MUCItem;
+import com.isode.stroke.elements.MUCOccupant;
+import com.isode.stroke.elements.MUCOwnerPayload;
+import com.isode.stroke.elements.MUCPayload;
+import com.isode.stroke.elements.MUCUserPayload;
+import com.isode.stroke.elements.Message;
+import com.isode.stroke.elements.Presence;
+import com.isode.stroke.jid.JID;
+import com.isode.stroke.jid.JID.CompareType;
+import com.isode.stroke.presence.DirectedPresenceSender;
+import com.isode.stroke.queries.GenericRequest;
+import com.isode.stroke.queries.IQRouter;
+import com.isode.stroke.signals.Signal;
+import com.isode.stroke.signals.Signal1;
+import com.isode.stroke.signals.Signal2;
+import com.isode.stroke.signals.Signal3;
+import com.isode.stroke.signals.SignalConnection;
+import com.isode.stroke.signals.Slot1;
+import com.isode.stroke.signals.Slot2;
+
+public class MockMUC extends MUC {
+
+ private JID ownMUCJID = new JID();
+ private Map<String, MUCOccupant> occupants_ = new HashMap<String, MUCOccupant>();
+
+ public MockMUC(JID muc) {
+ ownMUCJID = muc;
+ }
+
+ /**
+ * Cause a user to appear to have entered the room. For testing only.
+ */
+ public void insertOccupant(final MUCOccupant occupant) {
+ occupants_.put(occupant.getNick(), occupant);
+ onOccupantJoined.emit(occupant);
+ }
+
+ /**
+ * Returns the (bare) JID of the MUC.
+ */
+ public JID getJID() {
+ return ownMUCJID.toBare();
+ }
+
+ /**
+ * Returns if the room is unlocked and other people can join the room.
+ * @return True if joinable by others; false otherwise.
+ */
+ public boolean isUnlocked() {
+ return true;
+ }
+
+ public void joinAs(final String nick) {}
+ public void joinWithContextSince(final String nick, final Date since) {}
+ /*public void queryRoomInfo(); */
+ /*public void queryRoomItems(); */
+ /*public String getCurrentNick(); */
+ public Map<String, MUCOccupant> getOccupants() {
+ return occupants_;
+ }
+
+ public void changeNickname(final String newNickname) {}
+ public void part() {}
+ /*public void handleIncomingMessage(Message::ref message); */
+ /** Expose public so it can be called when e.g. user goes offline */
+ public void handleUserLeft(LeavingType l) {}
+
+ /**
+ * Get occupant information.
+ */
+ public MUCOccupant getOccupant(final String nick) {
+ return occupants_.get(nick);
+ }
+ public boolean hasOccupant(final String nick){
+ return occupants_.containsKey(nick);
+ }
+
+ public void kickOccupant(final JID jid) {}
+
+ public void changeOccupantRole(final JID jid, MUCOccupant.Role newRole) {
+ String resource = jid.getResource();
+ if(occupants_.containsKey(resource)) {
+ MUCOccupant old = occupants_.get(resource);
+ occupants_.remove(resource);
+ occupants_.put(resource, new MUCOccupant(old.getNick(), newRole, old.getAffiliation()));
+ onOccupantRoleChanged.emit(resource, occupants_.get(resource), old.getRole());
+ }
+ }
+
+ public void requestAffiliationList(MUCOccupant.Affiliation aff) {}
+
+ public void changeAffiliation(final JID jid, MUCOccupant.Affiliation newAffilation) {
+ String resource = jid.getResource();
+ if(occupants_.containsKey(resource)) {
+ MUCOccupant old = occupants_.get(resource);
+ occupants_.remove(resource);
+ occupants_.put(resource, new MUCOccupant(old.getNick(), old.getRole(), newAffilation));
+ onOccupantAffiliationChanged.emit(resource, newAffilation, old.getAffiliation());
+ }
+ }
+
+ public void changeSubject(final String subject) {}
+ public void requestConfigurationForm() {}
+ public void configureRoom(Form f) {}
+ public void cancelConfigureRoom() {}
+ public void destroyRoom() {}
+ /** Send an invite for the person to join the MUC */
+ public void invitePerson(final JID person, final String reason, boolean isImpromptu, boolean isReuseChat) {}
+ public void setCreateAsReservedIfNew() {}
+ public void setPassword(final String password) {}
+
+ protected boolean isFromMUC(final JID j) {
+ return (ownMUCJID.compare(j, JID.CompareType.WithoutResource) == 0);
+ }
+
+ protected String getOwnNick() {
+ return ownMUCJID.getResource();
+ }
+} \ No newline at end of file