diff src/plugins/plugin_xep_0045.py @ 1970:200cd707a46d

plugin XEP-0045, quick_frontend + primitivus (chat): cleaning of XEP-0045 (first pass): - bridge methods/signals now all start with "muc" to follow new convention - internal method use client instead of profile to follow new convention - removed excetpions from plugin XEP-0045 in favor of core.exceptions, NotReady added - cleaned/simplified several part of the code. checkClient removed as it is not needed anymore - self.clients map removed, muc data are now stored directly in client - getRoomEntityNick and getRoomNicksOfUsers are removed as they don't look sane. /!\ This break all room game plugins for the moment - use of uuid4 instead of uuid1 for getUniqueName, as host ID and current time are used for uuid1
author Goffi <goffi@goffi.org>
date Mon, 27 Jun 2016 21:45:11 +0200
parents a2bc5089c2eb
children bdc6a5b07922
line wrap: on
line diff
--- a/src/plugins/plugin_xep_0045.py	Fri Jun 24 22:41:28 2016 +0200
+++ b/src/plugins/plugin_xep_0045.py	Mon Jun 27 21:45:11 2016 +0200
@@ -63,44 +63,32 @@
 default_conf = {"default_muc": u'sat@chat.jabberfr.org'}
 
 
-class UnknownRoom(Exception):
-    pass
-
-
-class AlreadyJoinedRoom(Exception):
-    pass
-
-class NotReadyYet(Exception):
-    pass
-
-
 class XEP_0045(object):
-    # TODO: this plugin is messy, need a big cleanup/refactoring
 
     def __init__(self, host):
         log.info(_("Plugin XEP_0045 initialization"))
         self.host = host
-        self.clients = {}  # FIXME: should be moved to profile's client
         self._sessions = memory.Sessions()
         host.bridge.addMethod("mucJoin", ".plugin", in_sign='ssa{ss}s', out_sign='s', method=self._join, async=True)
-        host.bridge.addMethod("mucNick", ".plugin", in_sign='sss', out_sign='', method=self.mucNick)
-        host.bridge.addMethod("mucLeave", ".plugin", in_sign='ss', out_sign='', method=self.mucLeave, async=True)
-        host.bridge.addMethod("getRoomsJoined", ".plugin", in_sign='s', out_sign='a(sa{sa{ss}}ss)', method=self.getRoomsJoined)
-        host.bridge.addMethod("getRoomsSubjects", ".plugin", in_sign='s', out_sign='a(ss)', method=self.getRoomsSubjects)
-        host.bridge.addMethod("getUniqueRoomName", ".plugin", in_sign='ss', out_sign='s', method=self._getUniqueName)
-        host.bridge.addMethod("configureRoom", ".plugin", in_sign='ss', out_sign='s', method=self._configureRoom, async=True)
-        host.bridge.addMethod("getDefaultMUC", ".plugin", in_sign='', out_sign='s', method=self.getDefaultMUC)
-        host.bridge.addSignal("roomJoined", ".plugin", signature='sa{sa{ss}}sss')  # args: room_jid, occupants, user_nick, subject, profile
-        host.bridge.addSignal("roomLeft", ".plugin", signature='ss')  # args: room_jid, profile
-        host.bridge.addSignal("roomUserChangedNick", ".plugin", signature='ssss')  # args: room_jid, old_nick, new_nick, profile
-        host.bridge.addSignal("roomNewSubject", ".plugin", signature='sss')  # args: room_jid, subject, profile
+        host.bridge.addMethod("mucNick", ".plugin", in_sign='sss', out_sign='', method=self._nick)
+        host.bridge.addMethod("mucLeave", ".plugin", in_sign='ss', out_sign='', method=self._leave, async=True)
+        host.bridge.addMethod("mucGetRoomsJoined", ".plugin", in_sign='s', out_sign='a(sa{sa{ss}}ss)', method=self._getRoomsJoined)
+        host.bridge.addMethod("mucGetUniqueRoomName", ".plugin", in_sign='ss', out_sign='s', method=self._getUniqueName)
+        host.bridge.addMethod("mucConfigureRoom", ".plugin", in_sign='ss', out_sign='s', method=self._configureRoom, async=True)
+        host.bridge.addMethod("mucGetDefaultService", ".plugin", in_sign='', out_sign='s', method=self.getDefaultMUC)
+        host.bridge.addSignal("mucRoomJoined", ".plugin", signature='sa{sa{ss}}sss')  # args: room_jid, occupants, user_nick, subject, profile
+        host.bridge.addSignal("mucRoomLeft", ".plugin", signature='ss')  # args: room_jid, profile
+        host.bridge.addSignal("mucRoomUserChangedNick", ".plugin", signature='ssss')  # args: room_jid, old_nick, new_nick, profile
+        host.bridge.addSignal("mucRoomNewSubject", ".plugin", signature='sss')  # args: room_jid, subject, profile
         self.__submit_conf_id = host.registerCallback(self._submitConfiguration, with_data=True)
         host.importMenu((D_("MUC"), D_("configure")), self._configureRoomMenu, security_limit=0, help_string=D_("Configure Multi-User Chat room"), type_=C.MENU_ROOM)
         try:
-            self.host.plugins[C.TEXT_CMDS].registerTextCommands(self)
-            self.host.plugins[C.TEXT_CMDS].addWhoIsCb(self._whois, 100)
+            txt_cmds = self.host.plugins[C.TEXT_CMDS]
         except KeyError:
-            log.info(_("Text commands not available"))
+            log.info(_(u"Text commands not available"))
+        else:
+            txt_cmds.registerTextCommands(self)
+            txt_cmds.addWhoIsCb(self._whois, 100)
 
         host.trigger.add("presence_available", self.presenceTrigger)
         host.trigger.add("MessageReceived", self.MessageReceivedTrigger, priority=1000000)
@@ -117,8 +105,8 @@
                 return False
             from_jid = jid.JID(message_elt['from'])
             room_jid = from_jid.userhostJID()
-            if room_jid in self.clients[client.profile].joined_rooms:
-                room = self.clients[client.profile].joined_rooms[room_jid]
+            if room_jid in client._muc_client.joined_rooms:
+                room = client._muc_client.joined_rooms[room_jid]
                 if not room._room_ok:
                     log.warning(u"Received non delayed message in a room before its initialisation: {}".format(message_elt.toXml()))
                     room._cache.append(message_elt)
@@ -128,137 +116,129 @@
                 return False
         return True
 
-    def checkClient(self, profile):
-        """Check if the profile is connected and has used the MUC feature.
-
-        If profile was using MUC feature but is now disconnected, remove it from the client list.
-        @param profile: profile to check
-        @return: True if the profile is connected and has used the MUC feature, else False"""
-        if not profile or profile not in self.clients or not self.host.isConnected(profile):
-            log.error(_(u'Unknown or disconnected profile (%s)') % profile)
-            if profile in self.clients:
-                del self.clients[profile]
-            return False
-        return True
-
-    def getProfileAssertInRoom(self, room_jid, profile_key):
-        """Retrieve the profile name, assert that it's connected and participating in the given room.
+    def checkRoomJoined(self, client, room_jid):
+        """Check that given room has been joined in current session
 
         @param room_jid (JID): room JID
-        @param profile_key (str): %(doc_profile_key)
-        @return: the profile name
+        """
+        if room_jid not in client._muc_client.joined_rooms:
+            raise exceptions.NotFound(_(u"This room has not been joined"))
+
+    def isJoinedRoom(self, client, room_jid):
+        """Tell if a jid is a known and joined room
+
+        @room_jid(jid.JID): jid of the room
         """
-        profile = self.host.memory.getProfileName(profile_key)
-        if not self.checkClient(profile):
-            raise exceptions.ProfileUnknownError("Unknown or disconnected profile")
-        if room_jid not in self.clients[profile].joined_rooms:
-            raise UnknownRoom("This room has not been joined")
-        return profile
+        try:
+            self.checkRoomJoined(client, room_jid)
+        except exceptions.NotFound:
+            return False
+        else:
+            return True
 
-    def _joinCb(self, room, profile):
+    def _joinCb(self, room, client):
         """Called when the user is in the requested room"""
         if room.locked:
             # FIXME: the current behaviour is to create an instant room
             # and send the signal only when the room is unlocked
             # a proper configuration management should be done
-            print "room locked !"
-            d = self.clients[profile].configure(room.roomJID, {})
+            log.debug(_(u"room locked !"))
+            d = client._muc_client.configure(room.roomJID, {})
             d.addErrback(lambda dummy: log.error(_(u'Error while configuring the room')))
         return room
 
-    def _joinEb(self, failure, room_jid, nick, password, profile):
+    def _joinEb(self, failure, client, room_jid, nick, password):
         """Called when something is going wrong when joining the room"""
-        if hasattr(failure.value, "condition") and failure.value.condition == 'conflict':
-            # we have a nickname conflict, we try again with "_" suffixed to current nickname
-            nick += '_'
-            return self.clients[profile].join(room_jid, nick, password).addCallbacks(self._joinCb, self._joinEb, callbackKeywords={'profile': profile}, errbackArgs=[room_jid, nick, password, profile])
-        mess = D_("Error while joining the room %s" % room_jid.userhost())
         try:
-            mess += " with condition '%s'" % failure.value.condition
+            condition = failure.value.condition
         except AttributeError:
-            pass
+            msg_suffix = ''
+        else:
+            if condition == 'conflict':
+                # we have a nickname conflict, we try again with "_" suffixed to current nickname
+                nick += '_'
+                return client.join(room_jid, nick, password).addCallbacks(self._joinCb, self._joinEb, callbackKeywords={client: client}, errbackArgs=[client, room_jid, nick, password])
+            msg_suffix = ' with condition "{}"'.format(failure.value.condition)
+
+        mess = D_(u"Error while joining the room {room}{suffix}".format(
+            room = room_jid.userhost(), suffix = msg_suffix))
         log.error(mess)
-        self.host.bridge.newAlert(mess, D_("Group chat error"), "ERROR", profile)
-        raise failure
+        xmlui = xml_tools.note(mess, D_(u"Group chat error"), level=C.XMLUI_DATA_LVL_ERROR)
+        self.host.actionNew({'xmlui': xmlui.toXml()}, profile=client.profile)
 
     @staticmethod
     def _getOccupants(room):
         """Get occupants of a room in a form suitable for bridge"""
         return {u.nick: {k:unicode(getattr(u,k) or '') for k in OCCUPANT_KEYS} for u in room.roster.values()}
 
-    def isRoom(self, entity_bare, profile_key):
-        """Tell if a bare entity is a MUC room.
+    def _getRoomsJoined(self, profile_key=C.PROF_KEY_NONE):
+        client = self.host.getClient(profile_key)
+        return self.getRoomsJoined(client)
 
-        @param entity_bare (jid.JID): bare entity
-        @param profile_key (unicode): %(doc_profile_key)s
-        @return bool
-        """
-        profile = self.host.memory.getProfileName(profile_key)
-        return entity_bare in self.clients[profile].joined_rooms
-
-    def getRoomsJoined(self, profile_key=C.PROF_KEY_NONE):
-        """Return room where user is"""
-        profile = self.host.memory.getProfileName(profile_key)
+    def getRoomsJoined(self, client):
+        """Return rooms where user is"""
         result = []
-        if not self.checkClient(profile):
-            return result
-        for room in self.clients[profile].joined_rooms.values():
+        for room in client._muc_client.joined_rooms.values():
             if room._room_ok:
                 result.append((room.roomJID.userhost(), self._getOccupants(room), room.nick, room.subject))
         return result
 
-    def getRoomNick(self, room_jid, profile_key=C.PROF_KEY_NONE):
+    def getRoomNick(self, client, room_jid):
         """return nick used in room by user
 
         @param room_jid (jid.JID): JID of the room
         @profile_key: profile
-        @return: nick or empty string in case of error"""
-        profile = self.host.memory.getProfileName(profile_key)
-        if not self.checkClient(profile) or room_jid not in self.clients[profile].joined_rooms:
-            return ''
-        return self.clients[profile].joined_rooms[room_jid].nick
-
-    def getRoomNickOfUser(self, room, user_jid, secure=True):
-        """Returns the nick of the given user in the room.
-
-        @param room (wokkel.muc.Room): the room
-        @param user (jid.JID): bare JID of the user
-        @param secure (bool): set to True for a secure check
-        @return: unicode or None if the user didn't join the room.
+        @return: nick or empty string in case of error
         """
-        for user in room.roster.values():
-            if user.entity is not None:
-                if user.entity.userhostJID() == user_jid.userhostJID():
-                    return user.nick
-            elif not secure:
-                # FIXME: this is NOT ENOUGH to check an identity!!
-                # See in which conditions user.entity could be None.
-                if user.nick == user_jid.user:
-                    return user.nick
-        return None
+        try:
+            self.checkRoomJoined(client, room_jid)
+        except exceptions.NotFound:
+            return ''  # FIXME: should not return empty string but raise the error
+        return client._muc_client.joined_rooms[room_jid].nick
 
-    def getRoomNicksOfUsers(self, room, users=[], secure=True):
-        """Returns the nicks of the given users in the room.
+    # FIXME: broken, to be removed !
+    # def getRoomEntityNick(self, client, room_jid, entity_jid, =True):
+    #     """Returns the nick of the given user in the room.
 
-        @param room (wokkel.muc.Room): the room
-        @param users (list[jid.JID]): list of users
-        @param secure (True): set to True for a secure check
-        @return: a couple (x, y) with:
-            - x (list[unicode]): nicks of the users who are in the room
-            - y (list[jid.JID]): JID of the missing users.
-        """
-        nicks = []
-        missing = []
-        for user in users:
-            nick = self.getRoomNickOfUser(room, user, secure)
-            if nick is None:
-                missing.append(user)
-            else:
-                nicks.append(nick)
-        return nicks, missing
+    #     @param room (wokkel.muc.Room): the room
+    #     @param user (jid.JID): bare JID of the user
+    #     @param secure (bool): set to True for a secure check
+    #     @return: unicode or None if the user didn't join the room.
+    #     """
+    #     for user in room.roster.values():
+    #         if user.entity is not None:
+    #             if user.entity.userhostJID() == user_jid.userhostJID():
+    #                 return user.nick
+    #         elif not secure:
+    #             # FIXME: this is NOT ENOUGH to check an identity!!
+    #             # See in which conditions user.entity could be None.
+    #             if user.nick == user_jid.user:
+    #                 return user.nick
+    #     return None
+
+    # def getRoomNicksOfUsers(self, room, users=[], secure=True):
+    #     """Returns the nicks of the given users in the room.
+
+    #     @param room (wokkel.muc.Room): the room
+    #     @param users (list[jid.JID]): list of users
+    #     @param secure (True): set to True for a secure check
+    #     @return: a couple (x, y) with:
+    #         - x (list[unicode]): nicks of the users who are in the room
+    #         - y (list[jid.JID]): JID of the missing users.
+    #     """
+    #     nicks = []
+    #     missing = []
+    #     for user in users:
+    #         nick = self.getRoomNickOfUser(room, user, secure)
+    #         if nick is None:
+    #             missing.append(user)
+    #         else:
+    #             nicks.append(nick)
+    #     return nicks, missing
 
     def _configureRoom(self, room_jid_s, profile_key=C.PROF_KEY_NONE):
-        d = self.configureRoom(jid.JID(room_jid_s), profile_key)
+        client = self.host.getClient(profile_key)
+        d = self.configureRoom(client, jid.JID(room_jid_s))
         d.addCallback(lambda xmlui: xmlui.toXml())
         return d
 
@@ -268,6 +248,7 @@
         @param menu_data: %(menu_data)s
         @param profile: %(doc_profile)s
         """
+        client = self.host.getClient(profile)
         try:
             room_jid = jid.JID(menu_data['room_jid'])
         except KeyError:
@@ -276,31 +257,31 @@
 
         def xmluiReceived(xmlui):
             return {"xmlui": xmlui.toXml()}
-        return self.configureRoom(room_jid, profile).addCallback(xmluiReceived)
+        return self.configureRoom(client, room_jid).addCallback(xmluiReceived)
 
-    def configureRoom(self, room_jid, profile_key=C.PROF_KEY_NONE):
+    def configureRoom(self, client, room_jid):
         """return the room configuration form
 
         @param room: jid of the room to configure
-        @param profile_key: %(doc_profile_key)s
         @return: configuration form as XMLUI
         """
-        profile = self.getProfileAssertInRoom(room_jid, profile_key)
+        self.checkRoomJoined(client, room_jid)
 
         def config2XMLUI(result):
             if not result:
                 return ""
-            session_id, session_data = self._sessions.newSession(profile=profile)
+            session_id, session_data = self._sessions.newSession(profile=client.profile)
             session_data["room_jid"] = room_jid
             xmlui = xml_tools.dataForm2XMLUI(result, submit_id=self.__submit_conf_id)
             xmlui.session_id = session_id
             return xmlui
 
-        d = self.clients[profile].getConfiguration(room_jid)
+        d = client._muc_client.getConfiguration(room_jid)
         d.addCallback(config2XMLUI)
         return d
 
     def _submitConfiguration(self, raw_data, profile):
+        client = self.host.getClient(profile)
         try:
             session_data = self._sessions.profileGet(raw_data["session_id"], profile)
         except KeyError:
@@ -310,25 +291,17 @@
             return defer.succeed({'xmlui': _dialog.toXml()})
 
         data = xml_tools.XMLUIResult2DataFormResult(raw_data)
-        d = self.clients[profile].configure(session_data['room_jid'], data)
+        d = client._muc_client.configure(session_data['room_jid'], data)
         _dialog = xml_tools.XMLUI('popup', title=D_('Room configuration succeed'))
         _dialog.addText(D_("The new settings have been saved."))
         d.addCallback(lambda ignore: {'xmlui': _dialog.toXml()})
         del self._sessions[raw_data["session_id"]]
         return d
 
-    def isNickInRoom(self, room_jid, nick, profile):
+    def isNickInRoom(self, client, room_jid, nick):
         """Tell if a nick is currently present in a room"""
-        profile = self.getProfileAssertInRoom(room_jid, profile)
-        return self.clients[profile].joined_rooms[room_jid].inRoster(muc.User(nick))
-
-    def getRoomsSubjects(self, profile_key=C.PROF_KEY_NONE):
-        """Return received subjects of rooms"""
-        # FIXME: to be removed
-        profile = self.host.memory.getProfileName(profile_key)
-        if not self.checkClient(profile):
-            return []
-        return self.clients[profile].rec_subjects.values()
+        self.checkRoomJoined(client, room_jid)
+        return client._muc_client.joined_rooms[room_jid].inRoster(muc.User(nick))
 
     @defer.inlineCallbacks
     def getMUCService(self, jid_=None, profile=C.PROF_KEY_NONE):
@@ -349,28 +322,28 @@
         defer.returnValue(muc_service)
 
     def _getUniqueName(self, muc_service="", profile_key=C.PROF_KEY_NONE):
-        return self.getUniqueName(muc_service or None, profile_key).full()
+        client = self.host.getClient(profile_key)
+        return self.getUniqueName(client, muc_service or None).full()
 
-    def getUniqueName(self, muc_service=None, profile_key=C.PROF_KEY_NONE):
+    def getUniqueName(self, client, muc_service=None):
         """Return unique name for a room, avoiding collision
 
         @param muc_service (jid.JID) : leave empty string to use the default service
         @return: jid.JID (unique room bare JID)
         """
         # TODO: we should use #RFC-0045 10.1.4 when available here
-        client = self.host.getClient(profile_key)
-        room_name = uuid.uuid1()
+        room_name = unicode(uuid.uuid4())
         if muc_service is None:
             try:
                 muc_service = client.muc_service
             except AttributeError:
-                raise NotReadyYet("Main server MUC service has not been checked yet")
+                raise exceptions.NotReady(u"Main server MUC service has not been checked yet")
             if muc_service is None:
                 log.warning(_("No MUC service found on main server"))
                 raise exceptions.FeatureNotFound
 
         muc_service = muc_service.userhost()
-        return jid.JID("%s@%s" % (room_name, muc_service))
+        return jid.JID(u"{}@{}".format(room_name, muc_service))
 
     def getDefaultMUC(self):
         """Return the default MUC.
@@ -380,19 +353,19 @@
         return self.host.memory.getConfig(CONFIG_SECTION, 'default_muc', default_conf['default_muc'])
 
     def join(self, client, room_jid, nick, options):
-        def _errDeferred(exc_obj=Exception, txt='Error while joining room'):
+        def _errDeferred(exc_obj=Exception, txt=u'Error while joining room'):
             d = defer.Deferred()
             d.errback(exc_obj(txt))
             return d
 
-        if room_jid in self.clients[client.profile].joined_rooms:
-            log.warning(_(u'%(profile)s is already in room %(room_jid)s') % {'profile': client.profile, 'room_jid': room_jid.userhost()})
-            return _errDeferred(AlreadyJoinedRoom, D_(u"The room has already been joined"))
-        log.info(_(u"[%(profile)s] is joining room %(room)s with nick %(nick)s") % {'profile': client.profile, 'room': room_jid.userhost(), 'nick': nick})
+        if room_jid in client._muc_client.joined_rooms:
+            log.warning(_(u'{profile} is already in room {room_jid}').format(profile=client.profile, room_jid = room_jid.userhost()))
+            return defer.fail(exceptions.ConflictError(_(u"The room has already been joined")))
+        log.info(_(u"[{profile}] is joining room {room} with nick {nick}").format(profile=client.profile, room=room_jid.userhost(), nick=nick))
 
         password = options["password"] if "password" in options else None
 
-        return self.clients[client.profile].join(room_jid, nick, password).addCallbacks(self._joinCb, self._joinEb, callbackKeywords={'profile': client.profile}, errbackArgs=[room_jid, nick, password, client.profile])
+        return client._muc_client.join(room_jid, nick, password).addCallbacks(self._joinCb, self._joinEb, callbackKeywords={'client': client}, errbackArgs=[client, room_jid, nick, password])
 
     def _join(self, room_jid_s, nick, options=None, profile_key=C.PROF_KEY_NONE):
         """join method used by bridge
@@ -416,74 +389,72 @@
         d = self.join(client, room_jid, nick, options)
         return d.addCallback(lambda room: room.roomJID.userhost())
 
-    def nick(self, room_jid, nick, profile_key):
-        profile = self.getProfileAssertInRoom(room_jid, profile_key)
-        return self.clients[profile].nick(room_jid, nick)
+    def _nick(self, room_jid_s, nick, profile_key=C.PROF_KEY_NONE):
+        client = self.host.getClient(profile_key)
+        return self.nick(client, jid.JID(room_jid_s), nick)
 
-    def leave(self, room_jid, profile_key):
-        profile = self.getProfileAssertInRoom(room_jid, profile_key)
-        return self.clients[profile].leave(room_jid)
+    def nick(self, client, room_jid, nick):
+        """Change nickname in a room"""
+        self.checkRoomJoined(client, room_jid)
+        return client._muc_client.nick(room_jid, nick)
 
-    def subject(self, room_jid, subject, profile_key):
-        profile = self.getProfileAssertInRoom(room_jid, profile_key)
-        return self.clients[profile].subject(room_jid, subject)
+    def _leave(self, room_jid, profile_key):
+        client = self.host.getClient(profile_key)
+        return self.leave(client, jid.JID(room_jid))
 
-    def mucNick(self, room_jid_s, nick, profile_key=C.PROF_KEY_NONE):
-        """Change nickname in a room"""
-        return self.nick(jid.JID(room_jid_s), nick, profile_key)
+    def leave(self, client, room_jid):
+        self.checkRoomJoined(client, room_jid)
+        return client._muc_client.leave(room_jid)
 
-    def mucLeave(self, room_jid_s, profile_key=C.PROF_KEY_NONE):
-        """Leave a room"""
-        return self.leave(jid.JID(room_jid_s), profile_key)
+    def subject(self, client, room_jid, subject):
+        self.checkRoomJoined(client, room_jid)
+        return client._muc_client.subject(room_jid, subject)
 
     def getHandler(self, profile):
-        self.clients[profile] = SatMUCClient(self)
-        return self.clients[profile]
+        # create a MUC client and associate it with profile' session
+        client = self.host.getClient(profile)
+        muc_client = client._muc_client = SatMUCClient(self)
+        return muc_client
 
-    def profileDisconnected(self, profile):
-        try:
-            del self.clients[profile]
-        except KeyError:
-            pass
-
-    def kick(self, nick, room_jid, options={}, profile_key=C.PROF_KEY_NONE):
+    def kick(self, client, nick, room_jid, options=None):
         """
         Kick a participant from the room
         @param nick (str): nick of the user to kick
         @param room_jid_s (JID): jid of the room
         @param options (dict): attribute with extra info (reason, password) as in #XEP-0045
-        @param profile_key (str): %(doc_profile_key)s
         """
-        profile = self.getProfileAssertInRoom(room_jid, profile_key)
-        return self.clients[profile].kick(room_jid, nick, reason=options.get('reason', None))
+        if options is None:
+            options = {}
+        self.checkRoomJoined(client, room_jid)
+        return client._muc_client.kick(room_jid, nick, reason=options.get('reason', None))
 
-    def ban(self, entity_jid, room_jid, options={}, profile_key=C.PROF_KEY_NONE):
-        """
-        Ban an entity from the room
+    def ban(self, client, entity_jid, room_jid, options=None):
+        """Ban an entity from the room
+
         @param entity_jid (JID): bare jid of the entity to be banned
-        @param room_jid_s (JID): jid of the room
+        @param room_jid (JID): jid of the room
         @param options: attribute with extra info (reason, password) as in #XEP-0045
-        @param profile_key (str): %(doc_profile_key)s
         """
-        assert(not entity_jid.resource)
-        assert(not room_jid.resource)
-        profile = self.getProfileAssertInRoom(room_jid, profile_key)
-        return self.clients[profile].ban(room_jid, entity_jid, reason=options.get('reason', None))
+        self.checkRoomJoined(client, room_jid)
+        if options is None:
+            options = {}
+        assert not entity_jid.resource
+        assert not room_jid.resource
+        return client._muc_client.ban(room_jid, entity_jid, reason=options.get('reason', None))
 
-    def affiliate(self, entity_jid, room_jid, options=None, profile_key=C.PROF_KEY_NONE):
-        """
-        Change the affiliation of an entity
+    def affiliate(self, client, entity_jid, room_jid, options):
+        """Change the affiliation of an entity
+
         @param entity_jid (JID): bare jid of the entity
         @param room_jid_s (JID): jid of the room
         @param options: attribute with extra info (reason, nick) as in #XEP-0045
-        @param profile_key (str): %(doc_profile_key)s
         """
-        assert(not entity_jid.resource)
-        assert(not room_jid.resource)
-        assert('affiliation' in options)
-        profile = self.getProfileAssertInRoom(room_jid, profile_key)
+        self.checkRoomJoined(client, room_jid)
+        assert not entity_jid.resource
+        assert not room_jid.resource
+        assert 'affiliation' in options
         # TODO: handles reason and nick
-        return self.clients[profile].modifyAffiliationList(room_jid, [entity_jid], options['affiliation'])
+        return client._muc_client.modifyAffiliationList(room_jid, [entity_jid], options['affiliation'])
 
     # Text commands #
 
@@ -508,7 +479,7 @@
         """
         if mess_data["unparsed"].strip():
             room_jid = self.host.plugins[C.TEXT_CMDS].getRoomJID(mess_data["unparsed"].strip(), mess_data["to"].host)
-            nick = (self.getRoomNick(room_jid, client.profile) or
+            nick = (self.getRoomNick(client, room_jid) or
                     self.host.getClient(client.profile).jid.user)
             self.join(client, room_jid, nick, {})
 
@@ -546,7 +517,7 @@
         options = mess_data["unparsed"].strip().split()
         try:
             nick = options[0]
-            assert(self.isNickInRoom(mess_data["to"], nick, client.profile))
+            assert self.isNickInRoom(client, mess_data["to"], nick)
         except (IndexError, AssertionError):
             feedback = _(u"You must provide a member's nick to kick.")
             self.host.plugins[C.TEXT_CMDS].feedBack(client, feedback, mess_data)
@@ -639,7 +610,7 @@
 
         if subject:
             room = mess_data["to"]
-            self.subject(room, subject, client.profile)
+            self.subject(client, room, subject)
 
         return False
 
@@ -655,12 +626,12 @@
         """ Add MUC user information to whois """
         if mess_data['type'] != "groupchat":
             return
-        if target_jid.userhostJID() not in self.clients[client.profile].joined_rooms:
+        if target_jid.userhostJID() not in client._muc_client.joined_rooms:
             log.warning(_("This room has not been joined"))
             return
         if not target_jid.resource:
             return
-        user = self.clients[client.profile].joined_rooms[target_jid.userhostJID()].getUser(target_jid.resource)
+        user = client._muc_client.joined_rooms[target_jid.userhostJID()].getUser(target_jid.resource)
         whois_msg.append(_("Nickname: %s") % user.nick)
         if user.entity:
             whois_msg.append(_("Entity: %s") % user.entity)
@@ -675,7 +646,7 @@
 
     def presenceTrigger(self, presence_elt, client):
         # XXX: shouldn't it be done by the server ?!!
-        muc_client = self.clients[client.profile]
+        muc_client = client._muc_client
         for room_jid, room in muc_client.joined_rooms.iteritems():
             elt = copy.deepcopy(presence_elt)
             elt['to'] = room_jid.userhost() + '/' + room.nick
@@ -683,7 +654,7 @@
         return True
 
 
-class SatMUCClient (muc.MUCClient):
+class SatMUCClient(muc.MUCClient):
     implements(iwokkel.IDisco)
 
     def __init__(self, plugin_parent):
@@ -823,7 +794,7 @@
             log.info(_(u"Room ({room}) left ({profile})").format(
                 room = room_jid_s, profile = self.parent.profile))
             self.host.memory.delEntityCache(room.roomJID, profile_key=self.parent.profile)
-            self.host.bridge.roomLeft(room.roomJID.userhost(), self.parent.profile)
+            self.host.bridge.mucRoomLeft(room.roomJID.userhost(), self.parent.profile)
         else:
             log.debug(_(u"user {nick} left room {room_id}").format(nick=user.nick, room_id=room.occupantJID.userhost()))
             extra = {'info_type': ROOM_USER_LEFT,
@@ -847,7 +818,7 @@
             self.host.messageSendToBridge(mess_data, self.parent)
 
     def userChangedNick(self, room, user, new_nick):
-        self.host.bridge.roomUserChangedNick(room.roomJID.userhost(), user.nick, new_nick, self.parent.profile)
+        self.host.bridge.mucRoomUserChangedNick(room.roomJID.userhost(), user.nick, new_nick, self.parent.profile)
 
     def userUpdatedStatus(self, room, user, show, status):
         self.host.bridge.presenceUpdate(room.roomJID.userhost() + '/' + user.nick, show or '', 0, {C.PRESENCE_STATUSES_DEFAULT: status or ''}, self.parent.profile)
@@ -923,7 +894,7 @@
         return muc.MUCClientProtocol.subject(self, room, subject)
 
     def _historyCb(self, dummy, room):
-        self.host.bridge.roomJoined(
+        self.host.bridge.mucRoomJoined(
             room.roomJID.userhost(),
             XEP_0045._getOccupants(room),
             room.nick,
@@ -954,7 +925,7 @@
         else:
             # the subject has been changed
             log.debug(_(u"New subject for room ({room_id}): {subject}").format(room_id = room.roomJID.full(), subject = subject))
-            self.host.bridge.roomNewSubject(room.roomJID.userhost(), subject, self.parent.profile)
+            self.host.bridge.mucRoomNewSubject(room.roomJID.userhost(), subject, self.parent.profile)
 
     ## disco ##