# HG changeset patch # User Goffi # Date 1467056711 -7200 # Node ID 200cd707a46df6b9754c26ea309b61fa9960ec9c # Parent 5fbe09b9b56885dfdc9fce3d9d233970aae6546c 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 diff -r 5fbe09b9b568 -r 200cd707a46d frontends/src/primitivus/chat.py --- a/frontends/src/primitivus/chat.py Fri Jun 24 22:41:28 2016 +0200 +++ b/frontends/src/primitivus/chat.py Mon Jun 27 21:45:11 2016 +0200 @@ -30,6 +30,7 @@ from sat_frontends.primitivus.keys import action_key_map as a_key from sat_frontends.primitivus.widget import PrimitivusWidget import time +import locale from sat_frontends.tools import jid from functools import total_ordering import bisect @@ -117,8 +118,8 @@ # timestamp if self.parent.show_timestamp: # if the message was sent before today, we print the full date - time_format = "%c" if self.timestamp < self.parent.day_change else "%H:%M" - markup.append(('date', "[{}]".format(time.strftime(time_format, self.timestamp).decode('utf-8')))) + time_format = u"%c" if self.timestamp < self.parent.day_change else u"%H:%M" + markup.append(('date', u"[{}]".format(time.strftime(time_format, self.timestamp).decode(locale.getlocale()[1])))) # nickname if self.parent.show_short_nick: diff -r 5fbe09b9b568 -r 200cd707a46d frontends/src/primitivus/primitivus --- a/frontends/src/primitivus/primitivus Fri Jun 24 22:41:28 2016 +0200 +++ b/frontends/src/primitivus/primitivus Mon Jun 27 21:45:11 2016 +0200 @@ -855,7 +855,7 @@ def onJoinRoomRequest(self, menu): """User wants to join a MUC room""" - pop_up_widget = sat_widgets.InputDialog(_("Entering a MUC room"), _("Please enter MUC's JID"), default_txt=self.bridge.getDefaultMUC(), ok_cb=self.onJoinRoom) + pop_up_widget = sat_widgets.InputDialog(_("Entering a MUC room"), _("Please enter MUC's JID"), default_txt=self.bridge.mucGetDefaultService(), ok_cb=self.onJoinRoom) pop_up_widget.setCallback('cancel', lambda dummy: self.removePopUp(pop_up_widget)) self.showPopUp(pop_up_widget) diff -r 5fbe09b9b568 -r 200cd707a46d frontends/src/quick_frontend/quick_app.py --- a/frontends/src/quick_frontend/quick_app.py Fri Jun 24 22:41:28 2016 +0200 +++ b/frontends/src/quick_frontend/quick_app.py Mon Jun 27 21:45:11 2016 +0200 @@ -116,12 +116,12 @@ for sub in waiting_sub: self.host.subscribeHandler(waiting_sub[sub], sub, self.profile) - self.bridge.getRoomsJoined(self.profile, callback=self._plug_profile_gotRoomsJoined) + self.bridge.mucGetRoomsJoined(self.profile, callback=self._plug_profile_gotRoomsJoined) def _plug_profile_gotRoomsJoined(self, rooms_args): #Now we open the MUC window where we already are: for room_args in rooms_args: - self.host.roomJoinedHandler(*room_args, profile=self.profile) + self.host.mucRoomJoinedHandler(*room_args, profile=self.profile) #Presence must be requested after rooms are filled self.host.bridge.getPresenceStatuses(self.profile, callback=self._plug_profile_gotPresences) @@ -254,10 +254,10 @@ self.registerSignal("progressFinished") self.registerSignal("progressError") self.registerSignal("actionResultExt", self.actionResultHandler) - self.registerSignal("roomJoined", iface="plugin") - self.registerSignal("roomLeft", iface="plugin") - self.registerSignal("roomUserChangedNick", iface="plugin") - self.registerSignal("roomNewSubject", iface="plugin") + self.registerSignal("mucRoomJoined", iface="plugin") + self.registerSignal("mucRoomLeft", iface="plugin") + self.registerSignal("mucRoomUserChangedNick", iface="plugin") + self.registerSignal("mucRoomNewSubject", iface="plugin") self.registerSignal("chatStateReceived", iface="plugin") self.registerSignal("psEvent", iface="plugin") @@ -548,7 +548,7 @@ self.callListeners('presence', entity, show, priority, statuses, profile=profile) - def roomJoinedHandler(self, room_jid_s, occupants, user_nick, subject, profile): + def mucRoomJoinedHandler(self, room_jid_s, occupants, user_nick, subject, profile): """Called when a MUC room is joined""" log.debug(u"Room [{room_jid}] joined by {profile}, users presents:{users}".format(room_jid=room_jid_s, profile=profile, users=occupants.keys())) room_jid = jid.JID(room_jid_s) @@ -557,7 +557,7 @@ self.contact_lists[profile].setSpecial(room_jid, C.CONTACT_SPECIAL_GROUP) # chat_widget.update() - def roomLeftHandler(self, room_jid_s, profile): + def mucRoomLeftHandler(self, room_jid_s, profile): """Called when a MUC room is left""" log.debug(u"Room [%(room_jid)s] left by %(profile)s" % {'room_jid': room_jid_s, 'profile': profile}) room_jid = jid.JID(room_jid_s) @@ -566,14 +566,14 @@ self.widgets.deleteWidget(chat_widget) self.contact_lists[profile].removeContact(room_jid) - def roomUserChangedNickHandler(self, room_jid_s, old_nick, new_nick, profile): + def mucRoomUserChangedNickHandler(self, room_jid_s, old_nick, new_nick, profile): """Called when an user joined a MUC room""" room_jid = jid.JID(room_jid_s) chat_widget = self.widgets.getOrCreateWidget(quick_chat.QuickChat, room_jid, type_=C.CHAT_GROUP, profile=profile) chat_widget.changeUserNick(old_nick, new_nick) log.debug(u"user [%(old_nick)s] is now known as [%(new_nick)s] in room [%(room_jid)s]" % {'old_nick': old_nick, 'new_nick': new_nick, 'room_jid': room_jid}) - def roomNewSubjectHandler(self, room_jid_s, subject, profile): + def mucRoomNewSubjectHandler(self, room_jid_s, subject, profile): """Called when subject of MUC room change""" room_jid = jid.JID(room_jid_s) chat_widget = self.widgets.getOrCreateWidget(quick_chat.QuickChat, room_jid, type_=C.CHAT_GROUP, profile=profile) diff -r 5fbe09b9b568 -r 200cd707a46d src/core/exceptions.py --- a/src/core/exceptions.py Fri Jun 24 22:41:28 2016 +0200 +++ b/src/core/exceptions.py Mon Jun 27 21:45:11 2016 +0200 @@ -114,3 +114,8 @@ class ParsingError(Exception): pass + + +# Something which need to be done is not available yet +class NotReady(Exception): + pass diff -r 5fbe09b9b568 -r 200cd707a46d src/memory/memory.py --- a/src/memory/memory.py Fri Jun 24 22:41:28 2016 +0200 +++ b/src/memory/memory.py Mon Jun 27 21:45:11 2016 +0200 @@ -635,25 +635,25 @@ return available def _getMainResource(self, jid_s, profile_key): + client = self.host.getClient(profile_key) jid_ = jid.JID(jid_s) - return self.getMainResource(jid_, profile_key) or "" + return self.getMainResource(client, jid_) or "" - def getMainResource(self, entity_jid, profile_key): + def getMainResource(self, client, entity_jid): """Return the main resource used by an entity @param entity_jid: bare entity jid - @param profile_key: %(doc_profile_key)s @return (unicode): main resource or None """ if entity_jid.resource: raise ValueError("getMainResource must be used with a bare jid (got {})".format(entity_jid)) try: - if self.host.plugins["XEP-0045"].isRoom(entity_jid, profile_key): + if self.host.plugins["XEP-0045"].isJoinedRoom(client, entity_jid): return None # MUC rooms have no main resource except KeyError: # plugin not found pass try: - resources = self.getAllResources(entity_jid, profile_key) + resources = self.getAllResources(entity_jid, client.profile) except exceptions.UnknownEntityError: log.warning(u"Entity is not in cache, we can't find any resource") return None @@ -662,7 +662,7 @@ full_jid = copy.copy(entity_jid) full_jid.resource = resource try: - presence_data = self.getEntityDatum(full_jid, "presence", profile_key) + presence_data = self.getEntityDatum(full_jid, "presence", client.profile) except KeyError: log.debug(u"No presence information for {}".format(full_jid)) continue diff -r 5fbe09b9b568 -r 200cd707a46d src/plugins/plugin_exp_parrot.py --- a/src/plugins/plugin_exp_parrot.py Fri Jun 24 22:41:28 2016 +0200 +++ b/src/plugins/plugin_exp_parrot.py Mon Jun 27 21:45:11 2016 +0200 @@ -93,7 +93,7 @@ entity_type = "contact" if entity_type == 'chatroom': src_txt = from_jid.resource - if src_txt == self.host.plugins["XEP-0045"].getRoomNick(from_jid.userhostJID(), profile): + if src_txt == self.host.plugins["XEP-0045"].getRoomNick(client, from_jid.userhostJID()): #we won't repeat our own messages return True else: diff -r 5fbe09b9b568 -r 200cd707a46d src/plugins/plugin_misc_room_game.py --- a/src/plugins/plugin_misc_room_game.py Fri Jun 24 22:41:28 2016 +0200 +++ b/src/plugins/plugin_misc_room_game.py Mon Jun 27 21:45:11 2016 +0200 @@ -48,6 +48,8 @@ } +# FIXME: this plugin is broken, need to be fixed + class RoomGame(object): """This class is used to help launching a MUC game. @@ -123,7 +125,7 @@ host.trigger.add("MUC user joined", self.userJoinedTrigger) host.trigger.add("MUC user left", self.userLeftTrigger) - def _createOrInvite(self, room, other_players, profile): + def _createOrInvite(self, room_jid, other_players, profile): """ This is called only when someone explicitly wants to play. @@ -131,22 +133,24 @@ also its creation could be postponed until all the expected players join the room (in that case it will be created from userJoinedTrigger). @param room (wokkel.muc.Room): the room - @param other_players (list[jid.JID]): list of the other players JID (bare) + @param other_players (list[jid.JID]): list of the other players JID (bare) """ + # FIXME: broken ! + raise NotImplementedError("To be fixed") + client = self.host.getClient(profile) user_jid = self.host.getJidNStream(profile)[0] - room_jid = room.occupantJID.userhostJID() - nick = self.host.plugins["XEP-0045"].getRoomNick(room_jid, profile) + nick = self.host.plugins["XEP-0045"].getRoomNick(client, room_jid) nicks = [nick] if self._gameExists(room_jid): if not self._checkJoinAuth(room_jid, user_jid, nick): return - nicks.extend(self._invitePlayers(room, other_players, nick, profile)) + nicks.extend(self._invitePlayers(room_jid, other_players, nick, profile)) self._updatePlayers(room_jid, nicks, True, profile) else: self._initGame(room_jid, nick) - (auth, waiting, missing) = self._checkWaitAuth(room, other_players) + (auth, waiting, missing) = self._checkWaitAuth(room_jid, other_players) nicks.extend(waiting) - nicks.extend(self._invitePlayers(room, missing, nick, profile)) + nicks.extend(self._invitePlayers(room_jid, missing, nick, profile)) if auth: self.createGame(room_jid, nicks, profile) else: @@ -300,15 +304,15 @@ """ return [] - def _invitePlayers(self, room, other_players, nick, profile): + def _invitePlayers(self, room_jid, other_players, nick, profile): """Invite players to a room, associated game may exist or not. - @param room (wokkel.muc.Room): the room @param other_players (list[jid.JID]): list of the players to invite @param nick (unicode): nick of the user who send the invitation @return: list[unicode] of room nicks for invited players who are already in the room """ - room_jid = room.occupantJID.userhostJID() + raise NotImplementedError("Need to be fixed !") + # FIXME: this is broken and unsecure ! if not self._checkInviteAuth(room_jid, nick): return [] # TODO: remove invitation waiting for too long, using the time data @@ -316,7 +320,7 @@ nicks = [] for player_jid in [player.userhostJID() for player in other_players]: # TODO: find a way to make it secure - other_nick = self.host.plugins["XEP-0045"].getRoomNickOfUser(room, player_jid, secure=self.testing) + other_nick = self.host.plugins["XEP-0045"].getRoomEntityNick(room_jid, player_jid, secure=self.testing) if other_nick is None: self.host.plugins["XEP-0249"].invite(player_jid, room_jid, {"game": self.name}, profile) else: @@ -397,8 +401,9 @@ @param profile_key (unicode): %(doc_profile_key)s @return: jid.JID (unique name for a new room to be created) """ + client = self.host.getClient(profile_key) # FIXME: jid.JID must be used instead of strings - room = self.host.plugins["XEP-0045"].getUniqueName(muc_service, profile_key=profile_key) + room = self.host.plugins["XEP-0045"].getUniqueName(client, muc_service) return jid.JID("sat_%s_%s" % (self.name.lower(), room.userhost())) def _prepareRoom(self, other_players=None, room_jid_s='', profile_key=C.PROF_KEY_NONE): @@ -413,6 +418,8 @@ @param room_jid (jid.JID): JID of the room, or None to generate a unique name @param profile_key (unicode): %(doc_profile_key)s """ + # FIXME: need to be refactored + client = self.host.getClient(profile_key) log.debug(_(u'Preparing room for %s game') % self.name) profile = self.host.memory.getProfileName(profile_key) if not profile: @@ -421,21 +428,17 @@ if other_players is None: other_players = [] - def roomJoined(room): - """@param room: instance of wokkel.muc.Room""" - self._createOrInvite(room, other_players, profile) - # Create/join the given room, or a unique generated one if no room is specified. if room_jid is None: room_jid = self.getUniqueName(profile_key=profile_key) else: - if room_jid in self.host.plugins["XEP-0045"].clients[profile].joined_rooms: - roomJoined(self.host.plugins["XEP-0045"].clients[profile].joined_rooms[room_jid]) - return defer.succeed(None) + self.host.plugins["XEP-0045"].checkRoomJoined(client, room_jid) + self._createOrInvite(client, room_jid, other_players) + return defer.succeed(None) user_jid = self.host.getJidNStream(profile)[0] d = self.host.plugins["XEP-0045"].join(room_jid, user_jid.user, {}, profile) - return d.addCallback(roomJoined) + return d.addCallback(lambda dummy: self._createOrInvite(client, room_jid, other_players)) def userJoinedTrigger(self, room, user, profile): """This trigger is used to check if the new user can take part of a game, create the game if we were waiting for him or just update the players list. diff -r 5fbe09b9b568 -r 200cd707a46d src/plugins/plugin_misc_tarot.py --- a/src/plugins/plugin_misc_tarot.py Fri Jun 24 22:41:28 2016 +0200 +++ b/src/plugins/plugin_misc_tarot.py Mon Jun 27 21:45:11 2016 +0200 @@ -477,9 +477,10 @@ """ @param mess_elt: instance of twisted.words.xish.domish.Element """ + client = self.host.getClient(profile) from_jid = jid.JID(mess_elt['from']) room_jid = jid.JID(from_jid.userhost()) - nick = self.host.plugins["XEP-0045"].getRoomNick(room_jid, profile) + nick = self.host.plugins["XEP-0045"].getRoomNick(client, room_jid) game_elt = mess_elt.firstChildElement() game_data = self.games[room_jid] @@ -522,7 +523,7 @@ # TODO: use proper XEP-0004 way for answering form player = elt['player'] players_data[player]['contrat'] = unicode(elt) - contrats = [players_data[player]['contrat'] for player in game_data['players']] + contrats = [players_data[p]['contrat'] for p in game_data['players']] if contrats.count(None): # not everybody has choosed his contrat, it's next one turn player = self.__next_player(game_data) diff -r 5fbe09b9b568 -r 200cd707a46d src/plugins/plugin_misc_text_commands.py --- a/src/plugins/plugin_misc_text_commands.py Fri Jun 24 22:41:28 2016 +0200 +++ b/src/plugins/plugin_misc_text_commands.py Mon Jun 27 21:45:11 2016 +0200 @@ -305,7 +305,7 @@ if mess_data['type'] == "groupchat": room = mess_data["to"].userhostJID() try: - if self.host.plugins["XEP-0045"].isNickInRoom(room, entity, client.profile): + if self.host.plugins["XEP-0045"].isNickInRoom(client, room, entity): entity = u"%s/%s" % (room, entity) except KeyError: log.warning("plugin XEP-0045 is not present") @@ -322,7 +322,7 @@ return False if not target_jid.resource: - target_jid.resource = self.host.memory.getMainResource(target_jid, client.profile) + target_jid.resource = self.host.memory.getMainResource(client, target_jid) whois_msg = [_(u"whois for %(jid)s") % {'jid': target_jid}] diff -r 5fbe09b9b568 -r 200cd707a46d src/plugins/plugin_sec_otr.py --- a/src/plugins/plugin_sec_otr.py Fri Jun 24 22:41:28 2016 +0200 +++ b/src/plugins/plugin_sec_otr.py Mon Jun 27 21:45:11 2016 +0200 @@ -276,10 +276,11 @@ @param menu_data: %(menu_data)s @param profile: %(doc_profile)s """ + client = self.host.getClient(profile) try: to_jid = jid.JID(menu_data['jid']) if not to_jid.resource: - to_jid.resource = self.host.memory.getMainResource(to_jid, profile) # FIXME: temporary and unsecure, must be changed when frontends are refactored + to_jid.resource = self.host.memory.getMainResource(client, to_jid) # FIXME: temporary and unsecure, must be changed when frontends are refactored except KeyError: log.error(_("jid key is not present !")) return defer.fail(exceptions.DataError) @@ -294,10 +295,11 @@ @param menu_data: %(menu_data)s @param profile: %(doc_profile)s """ + client = self.host.getClient(profile) try: to_jid = jid.JID(menu_data['jid']) if not to_jid.resource: - to_jid.resource = self.host.memory.getMainResource(to_jid, profile) # FIXME: temporary and unsecure, must be changed when frontends are refactored + to_jid.resource = self.host.memory.getMainResource(client, to_jid) # FIXME: temporary and unsecure, must be changed when frontends are refactored except KeyError: log.error(_("jid key is not present !")) return defer.fail(exceptions.DataError) @@ -311,10 +313,11 @@ @param menu_data: %(menu_data)s @param profile: %(doc_profile)s """ + client = self.host.getClient(profile) try: to_jid = jid.JID(menu_data['jid']) if not to_jid.resource: - to_jid.resource = self.host.memory.getMainResource(to_jid, profile) # FIXME: temporary and unsecure, must be changed when frontends are refactored + to_jid.resource = self.host.memory.getMainResource(client, to_jid) # FIXME: temporary and unsecure, must be changed when frontends are refactored except KeyError: log.error(_("jid key is not present !")) return defer.fail(exceptions.DataError) @@ -381,10 +384,11 @@ @param menu_data: %(menu_data)s @param profile: %(doc_profile)s """ + client = self.host.getClient(profile) try: to_jid = jid.JID(menu_data['jid']) if not to_jid.resource: - to_jid.resource = self.host.memory.getMainResource(to_jid, profile) # FIXME: temporary and unsecure, must be changed when frontends are refactored + to_jid.resource = self.host.memory.getMainResource(client, to_jid) # FIXME: temporary and unsecure, must be changed when frontends are refactored except KeyError: log.error(_("jid key is not present !")) return defer.fail(exceptions.DataError) @@ -467,7 +471,7 @@ return True to_jid = copy.copy(mess_data['to']) if mess_data['type'] != 'groupchat' and not to_jid.resource: - to_jid.resource = self.host.memory.getMainResource(to_jid, profile) # FIXME: it's dirty, but frontends don't manage resources correctly now, refactoring is planed + to_jid.resource = self.host.memory.getMainResource(client, to_jid) # FIXME: it's dirty, but frontends don't manage resources correctly now, refactoring is planed otrctx = self.context_managers[profile].getContextForUser(to_jid) if mess_data['type'] != 'groupchat' and otrctx.state != potr.context.STATE_PLAINTEXT: if otrctx.state == potr.context.STATE_ENCRYPTED: @@ -496,11 +500,12 @@ return True def _presenceReceivedTrigger(self, entity, show, priority, statuses, profile): + client = self.host.getClient(profile) if show != C.PRESENCE_UNAVAILABLE: return True if not entity.resource: try: - entity.resource = self.host.memory.getMainResource(entity, profile) # FIXME: temporary and unsecure, must be changed when frontends are refactored + entity.resource = self.host.memory.getMainResource(client, entity) # FIXME: temporary and unsecure, must be changed when frontends are refactored except exceptions.UnknownEntityError: return True # entity was not connected if entity in self.context_managers[profile].contexts: diff -r 5fbe09b9b568 -r 200cd707a46d src/plugins/plugin_xep_0045.py --- 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 ## diff -r 5fbe09b9b568 -r 200cd707a46d src/plugins/plugin_xep_0054.py --- a/src/plugins/plugin_xep_0054.py Fri Jun 24 22:41:28 2016 +0200 +++ b/src/plugins/plugin_xep_0054.py Mon Jun 27 21:45:11 2016 +0200 @@ -109,16 +109,18 @@ return True - def isInRoom(self, entity_jid, profile): + def isInRoom(self, client, entity_jid): """Tell if an full jid is a member of a room @param entity_jid(jid.JID): full jid of the entity @return (bool): True if the bare jid of the entity is a room jid """ try: - return self.host.plugins['XEP-0045'].isRoom(entity_jid.userhostJID(), profile_key=profile) - except KeyError: + self.host.plugins['XEP-0045'].checkRoomJoined(client, entity_jid.userhostJID()) + except exceptions.NotFound: return False + else: + return True def _fillCachedValues(self, profile): #FIXME: this is really suboptimal, need to be reworked @@ -143,41 +145,39 @@ log.debug(u"Deleting profile cache for avatars") del self.cache[profile] - def updateCache(self, jid_, name, value, profile): + def updateCache(self, client, jid_, name, value): """update cache value save value in memory in case of change @param jid_(jid.JID): jid of the owner of the vcard @param name(str): name of the item which changed @param value(unicode): new value of the item - @param profile(unicode): profile which received the update """ if jid_.resource: - if not self.isInRoom(jid_, profile): + if not self.isInRoom(client, jid_): # VCard are retrieved with bare jid # but MUC room is a special case jid_ = jid.userhostJID() - self.host.memory.updateEntityData(jid_, name, value, profile_key=profile) + self.host.memory.updateEntityData(jid_, name, value, profile_key=client.profile) if name in CACHED_DATA: jid_s = jid_.userhost() - self.cache[profile].setdefault(jid_s, {})[name] = value - self.cache[profile].force(jid_s) + self.cache[client.profile].setdefault(jid_s, {})[name] = value + self.cache[client.profile].force(jid_s) - def getCache(self, entity_jid, name, profile): + def getCache(self, client, entity_jid, name): """return cached value for jid @param entity_jid: target contact @param name: name of the value ('nick' or 'avatar') - @param profile: %(doc_profile)s @return: wanted value or None""" if entity_jid.resource: - if not self.isInRoom(entity_jid, profile): + if not self.isInRoom(client, entity_jid): # VCard are retrieved with bare jid # but MUC room is a special case entity_jid = jid.userhostJID() try: - data = self.host.memory.getEntityData(entity_jid, [name], profile) + data = self.host.memory.getEntityData(entity_jid, [name], client.profile) except exceptions.UnknownEntityError: return None return data.get(name) @@ -217,7 +217,7 @@ return image_hash @defer.inlineCallbacks - def vCard2Dict(self, vcard, target, profile): + def vCard2Dict(self, client, vcard, target): """Convert a VCard to a dict, and save binaries""" log.debug(_("parsing vcard")) dictionary = {} @@ -227,7 +227,7 @@ dictionary['fullname'] = unicode(elem) elif elem.name == 'NICKNAME': dictionary['nick'] = unicode(elem) - self.updateCache(target, 'nick', dictionary['nick'], profile) + self.updateCache(client, target, 'nick', dictionary['nick']) elif elem.name == 'URL': dictionary['website'] = unicode(elem) elif elem.name == 'EMAIL': @@ -239,7 +239,7 @@ if not dictionary["avatar"]: # can happen in case of e.g. empty photo elem del dictionary['avatar'] else: - self.updateCache(target, 'avatar', dictionary['avatar'], profile) + self.updateCache(client, target, 'avatar', dictionary['avatar']) else: log.info(_('FIXME: [%s] VCard tag is not managed yet') % elem.name) @@ -248,56 +248,52 @@ # and we reset them for datum in CACHED_DATA.difference(dictionary.keys()): log.debug(u"reseting vcard datum [{datum}] for {entity}".format(datum=datum, entity=target.full())) - self.updateCache(target, datum, '', profile) + self.updateCache(client, target, datum, '') defer.returnValue(dictionary) - def _VCardCb(self, answer, profile): + def _VCardCb(self, answer, client): """Called after the first get IQ""" log.debug(_("VCard found")) if answer.firstChildElement().name == "vCard": - _jid, steam = self.host.getJidNStream(profile) try: from_jid = jid.JID(answer["from"]) except KeyError: - from_jid = _jid.userhostJID() - d = self.vCard2Dict(answer.firstChildElement(), from_jid, profile) - d.addCallback(lambda data: self.host.bridge.actionResult("RESULT", answer['id'], data, profile)) + from_jid = client.jid.userhostJID() + d = self.vCard2Dict(client, answer.firstChildElement(), from_jid) + d.addCallback(lambda data: self.host.bridge.actionResult("RESULT", answer['id'], data, client.profile)) else: log.error(_("FIXME: vCard not found as first child element")) - self.host.bridge.actionResult("SUPPRESS", answer['id'], {}, profile) # FIXME: maybe an error message would be better + self.host.bridge.actionResult("SUPPRESS", answer['id'], {}, client.profile) # FIXME: maybe an error message would be better - def _VCardEb(self, failure, profile): + def _VCardEb(self, failure, client): """Called when something is wrong with registration""" try: - self.host.bridge.actionResult("SUPPRESS", failure.value.stanza['id'], {}, profile) # FIXME: maybe an error message would be better + self.host.bridge.actionResult("SUPPRESS", failure.value.stanza['id'], {}, client.profile) # FIXME: maybe an error message would be better log.warning(_(u"Can't find VCard of %s") % failure.value.stanza['from']) - self.updateCache(jid.JID(failure.value.stanza['from']), "avatar", '', profile) + self.updateCache(client, jid.JID(failure.value.stanza['from']), "avatar", '') except (AttributeError, KeyError): # 'ConnectionLost' object has no attribute 'stanza' + sometimes 'from' key doesn't exist log.warning(_(u"Can't find VCard: %s") % failure.getErrorMessage()) def _getCard(self, target_s, profile_key=C.PROF_KEY_NONE): - return self.getCard(jid.JID(target_s), profile_key) + client = self.host.getClient(profile_key) + return self.getCard(client, jid.JID(target_s)) - def getCard(self, target, profile_key=C.PROF_KEY_NONE): + def getCard(self, client, target): """Ask server for VCard @param target(jid.JID): jid from which we want the VCard @result: id to retrieve the profile """ - current_jid, xmlstream = self.host.getJidNStream(profile_key) - if not xmlstream: - raise exceptions.ProfileUnknownError('Asking vcard for a non-existant or not connected profile ({})'.format(profile_key)) - profile = self.host.memory.getProfileName(profile_key) to_jid = target.userhostJID() log.debug(_(u"Asking for %s's VCard") % to_jid.userhost()) - reg_request = IQ(xmlstream, 'get') - reg_request["from"] = current_jid.full() + reg_request = client.IQ('get') + reg_request["from"] = client.jid.full() reg_request["to"] = to_jid.userhost() reg_request.addElement('vCard', NS_VCARD) - reg_request.send(to_jid.userhost()).addCallbacks(self._VCardCb, self._VCardEb, callbackArgs=[profile], errbackArgs=[profile]) + reg_request.send(to_jid.userhost()).addCallbacks(self._VCardCb, self._VCardEb, callbackArgs=[client], errbackArgs=[client]) return reg_request["id"] def getAvatarFile(self, avatar_hash): @@ -354,7 +350,7 @@ def elementBuilt(result): """Called once the image is at the right size/format, and the vcard set element is build""" set_avatar_elt, img_hash = result - self.updateCache(client.jid.userhostJID(), 'avatar', img_hash, client.profile) + self.updateCache(client, client.jid.userhostJID(), 'avatar', img_hash) return set_avatar_elt.send().addCallback(lambda ignore: client.presence.available()) # FIXME: should send the current presence, not always "available" ! d.addCallback(elementBuilt) @@ -385,7 +381,7 @@ @param presend(domish.Element): stanza """ from_jid = jid.JID(presence['from']) - if from_jid.resource and not self.plugin_parent.isInRoom(from_jid, self.parent.profile): + if from_jid.resource and not self.plugin_parent.isInRoom(self.parent, from_jid): from_jid = from_jid.userhostJID() #FIXME: wokkel's data_form should be used here try: @@ -401,12 +397,12 @@ hash_ = str(photo_elt) if not hash_: return - old_avatar = self.plugin_parent.getCache(from_jid, 'avatar', self.parent.profile) + old_avatar = self.plugin_parent.getCache(self.parent, from_jid, 'avatar') filename = self.plugin_parent._getFilename(hash_) if not old_avatar or old_avatar != hash_: if os.path.exists(filename): log.debug(u"New avatar found for [{}], it's already in cache, we use it".format(from_jid.full())) - self.plugin_parent.updateCache(from_jid, 'avatar', hash_, self.parent.profile) + self.plugin_parent.updateCache(self.parent, from_jid, 'avatar', hash_) else: log.debug(u'New avatar found for [{}], requesting vcard'.format(from_jid.full())) self.plugin_parent.getCard(from_jid, self.parent.profile) diff -r 5fbe09b9b568 -r 200cd707a46d src/plugins/plugin_xep_0085.py --- a/src/plugins/plugin_xep_0085.py Fri Jun 24 22:41:28 2016 +0200 +++ b/src/plugins/plugin_xep_0085.py Mon Jun 27 21:45:11 2016 +0200 @@ -309,18 +309,16 @@ @param profile_key (str): %(doc_profile_key)s """ # TODO: try to optimize this method which is called often - profile = self.host.memory.getProfileName(profile_key) - if profile is None: - raise exceptions.ProfileUnknownError + client = self.host.getClient(profile_key) to_jid = JID(to_jid_s) - if self._isMUC(to_jid, profile): + if self._isMUC(to_jid, client.profile): to_jid = to_jid.userhostJID() elif not to_jid.resource: - to_jid.resource = self.host.memory.getMainResource(to_jid, profile) - if not self._checkActivation(to_jid, forceEntityData=False, profile=profile): + to_jid.resource = self.host.memory.getMainResource(client, to_jid) + if not self._checkActivation(to_jid, forceEntityData=False, profile=client.profile): return try: - self.map[profile][to_jid]._onEvent("composing") + self.map[client.profile][to_jid]._onEvent("composing") except (KeyError, AttributeError): # no message has been sent/received since the notifications # have been enabled, it's better to wait for a first one diff -r 5fbe09b9b568 -r 200cd707a46d src/plugins/plugin_xep_0249.py --- a/src/plugins/plugin_xep_0249.py Fri Jun 24 22:41:28 2016 +0200 +++ b/src/plugins/plugin_xep_0249.py Mon Jun 27 21:45:11 2016 +0200 @@ -19,6 +19,7 @@ from sat.core.i18n import _ from sat.core.constants import Const as C +from sat.core import exceptions from sat.core.log import getLogger log = getLogger(__name__) from twisted.words.xish import domish @@ -136,6 +137,7 @@ @param message: message element @profile: %(doc_profile)s """ + client = self.host.getClient(profile) try: room_jid_s = message.firstChildElement()['jid'] log.info(_(u'Invitation received for room %(room)s [%(profile)s]') % {'room': room_jid_s, 'profile': profile}) @@ -144,9 +146,14 @@ return from_jid_s = message["from"] room_jid = jid.JID(room_jid_s) - if room_jid in self.host.plugins["XEP-0045"].clients[profile].joined_rooms: - log.info(_("Invitation silently discarded because user is already in the room.")) + try: + self.host.plugins["XEP-0045"].checkRoomJoined(client, room_jid) + except exceptions.NotFound: + pass + else: + log.info(_(u"Invitation silently discarded because user is already in the room.")) return + autojoin = self.host.memory.getParamA(AUTOJOIN_NAME, AUTOJOIN_KEY, profile_key=profile) def accept_cb(conf_id, accepted, data, profile):