comparison src/plugins/plugin_xep_0045.py @ 944:e1842ebcb2f3

core, plugin XEP-0115: discovery refactoring: - hashing algorithm of XEP-0115 has been including in core - our own hash is still calculated by XEP-0115 and can be regenerated with XEP_0115.recalculateHash - old discovery methods have been removed. Now the following methods are used: - hasFeature: tell if a feature is available for an entity - getDiscoInfos: self explaining - getDiscoItems: self explaining - findServiceEntities: return all available items of an entity which given (category, type) - findFeaturesSet: search for a set of features in entity + entity's items all these methods are asynchronous, and manage cache automatically - XEP-0115 manage in a better way hashes, and now use a trigger for presence instead of monkey patch - new FeatureNotFound exception, when we want to do something which is not available - refactored client initialisation sequence, removed client.initialized Deferred - added constant APP_URL - test_plugin_xep_0033.py has been temporarly deactivated, the time to adapt it - lot of cleaning
author Goffi <goffi@goffi.org>
date Fri, 28 Mar 2014 18:07:22 +0100
parents 73873e9b56f7
children 5e8e8a034411
comparison
equal deleted inserted replaced
943:71926ec2114d 944:e1842ebcb2f3
29 import uuid 29 import uuid
30 30
31 from wokkel import muc 31 from wokkel import muc
32 from sat.tools import xml_tools 32 from sat.tools import xml_tools
33 33
34
35 try:
36 from twisted.words.protocols.xmlstream import XMPPHandler
37 except ImportError:
38 from wokkel.subprotocols import XMPPHandler
39 34
40 PLUGIN_INFO = { 35 PLUGIN_INFO = {
41 "name": "XEP 0045 Plugin", 36 "name": "XEP 0045 Plugin",
42 "import_name": "XEP-0045", 37 "import_name": "XEP-0045",
43 "type": "XEP", 38 "type": "XEP",
51 46
52 47
53 class UnknownRoom(Exception): 48 class UnknownRoom(Exception):
54 pass 49 pass
55 50
51 class NotReadyYet(Exception):
52 pass
56 53
57 class XEP_0045(object): 54 class XEP_0045(object):
58 # TODO: this plugin is messy, need a big cleanup/refactoring 55 # TODO: this plugin is messy, need a big cleanup/refactoring
59 56
60 def __init__(self, host): 57 def __init__(self, host):
65 host.bridge.addMethod("joinMUC", ".plugin", in_sign='ssa{ss}s', out_sign='s', method=self._join) 62 host.bridge.addMethod("joinMUC", ".plugin", in_sign='ssa{ss}s', out_sign='s', method=self._join)
66 host.bridge.addMethod("mucNick", ".plugin", in_sign='sss', out_sign='', method=self.mucNick) 63 host.bridge.addMethod("mucNick", ".plugin", in_sign='sss', out_sign='', method=self.mucNick)
67 host.bridge.addMethod("mucLeave", ".plugin", in_sign='ss', out_sign='', method=self.mucLeave, async=True) 64 host.bridge.addMethod("mucLeave", ".plugin", in_sign='ss', out_sign='', method=self.mucLeave, async=True)
68 host.bridge.addMethod("getRoomsJoined", ".plugin", in_sign='s', out_sign='a(sass)', method=self.getRoomsJoined) 65 host.bridge.addMethod("getRoomsJoined", ".plugin", in_sign='s', out_sign='a(sass)', method=self.getRoomsJoined)
69 host.bridge.addMethod("getRoomsSubjects", ".plugin", in_sign='s', out_sign='a(ss)', method=self.getRoomsSubjects) 66 host.bridge.addMethod("getRoomsSubjects", ".plugin", in_sign='s', out_sign='a(ss)', method=self.getRoomsSubjects)
70 host.bridge.addMethod("getUniqueRoomName", ".plugin", in_sign='ss', out_sign='s', method=self.getUniqueName) 67 host.bridge.addMethod("getUniqueRoomName", ".plugin", in_sign='ss', out_sign='s', method=self._getUniqueName)
71 host.bridge.addMethod("configureRoom", ".plugin", in_sign='ss', out_sign='s', method=self._configureRoom, async=True) 68 host.bridge.addMethod("configureRoom", ".plugin", in_sign='ss', out_sign='s', method=self._configureRoom, async=True)
72 host.bridge.addSignal("roomJoined", ".plugin", signature='sasss') # args: room_jid, room_nicks, user_nick, profile 69 host.bridge.addSignal("roomJoined", ".plugin", signature='sasss') # args: room_jid, room_nicks, user_nick, profile
73 host.bridge.addSignal("roomLeft", ".plugin", signature='ss') # args: room_jid, profile 70 host.bridge.addSignal("roomLeft", ".plugin", signature='ss') # args: room_jid, profile
74 host.bridge.addSignal("roomUserJoined", ".plugin", signature='ssa{ss}s') # args: room_jid, user_nick, user_data, profile 71 host.bridge.addSignal("roomUserJoined", ".plugin", signature='ssa{ss}s') # args: room_jid, user_nick, user_data, profile
75 host.bridge.addSignal("roomUserLeft", ".plugin", signature='ssa{ss}s') # args: room_jid, user_nick, user_data, profile 72 host.bridge.addSignal("roomUserLeft", ".plugin", signature='ssa{ss}s') # args: room_jid, user_nick, user_data, profile
80 self.host.plugins[C.TEXT_CMDS].registerTextCommands(self) 77 self.host.plugins[C.TEXT_CMDS].registerTextCommands(self)
81 self.host.plugins[C.TEXT_CMDS].addWhoIsCb(self._whois, 100) 78 self.host.plugins[C.TEXT_CMDS].addWhoIsCb(self._whois, 100)
82 except KeyError: 79 except KeyError:
83 info(_("Text commands not available")) 80 info(_("Text commands not available"))
84 81
82 def profileConnected(self, profile):
83 def assign_service(service):
84 client = self.host.getClient(profile)
85 client.muc_service = service
86 self.getMUCService(profile_key=profile).addCallback(assign_service)
87
85 def __check_profile(self, profile): 88 def __check_profile(self, profile):
86 """check if profile is used and connected 89 """check if profile is used and connected
90
87 if profile known but disconnected, remove it from known profiles 91 if profile known but disconnected, remove it from known profiles
88 @param profile: profile to check 92 @param profile: profile to check
89 @return: True if the profile is known and connected, else False""" 93 @return: True if the profile is known and connected, else False"""
90 if not profile or profile not in self.clients or not self.host.isConnected(profile): 94 if not profile or profile not in self.clients or not self.host.isConnected(profile):
91 error(_('Unknown or disconnected profile (%s)') % profile) 95 error(_('Unknown or disconnected profile (%s)') % profile)
134 result.append((room.roomJID.userhost(), [user.nick for user in room.roster.values()], room.nick)) 138 result.append((room.roomJID.userhost(), [user.nick for user in room.roster.values()], room.nick))
135 return result 139 return result
136 140
137 def getRoomNick(self, room_jid_s, profile_key=C.PROF_KEY_NONE): 141 def getRoomNick(self, room_jid_s, profile_key=C.PROF_KEY_NONE):
138 """return nick used in room by user 142 """return nick used in room by user
143
139 @param room_jid_s: unicode room id 144 @param room_jid_s: unicode room id
140 @profile_key: profile 145 @profile_key: profile
141 @return: nick or empty string in case of error""" 146 @return: nick or empty string in case of error"""
142 profile = self.host.memory.getProfileName(profile_key) 147 profile = self.host.memory.getProfileName(profile_key)
143 if not self.__check_profile(profile) or room_jid_s not in self.clients[profile].joined_rooms: 148 if not self.__check_profile(profile) or room_jid_s not in self.clients[profile].joined_rooms:
144 return '' 149 return ''
145 return self.clients[profile].joined_rooms[room_jid_s].nick 150 return self.clients[profile].joined_rooms[room_jid_s].nick
146 151
147 def getRoomNickOfUser(self, room, user_jid, secure=True): 152 def getRoomNickOfUser(self, room, user_jid, secure=True):
148 """Returns the nick of the given user in the room. 153 """Returns the nick of the given user in the room.
154
149 @room: instance of wokkel.muc.Room 155 @room: instance of wokkel.muc.Room
150 @user: JID or unicode (JID userhost). 156 @user: JID or unicode (JID userhost).
151 @param secure: set to True for a secure check 157 @param secure: set to True for a secure check
152 @return: the nick or None if the user didn't join the room. 158 @return: the nick or None if the user didn't join the room.
153 """ 159 """
164 return user.nick 170 return user.nick
165 return None 171 return None
166 172
167 def getRoomNicksOfUsers(self, room, users=[], secure=True): 173 def getRoomNicksOfUsers(self, room, users=[], secure=True):
168 """Returns the nicks of the given users in the room. 174 """Returns the nicks of the given users in the room.
175
169 @room: instance of wokkel.muc.Room 176 @room: instance of wokkel.muc.Room
170 @users: list of JID or unicode (JID userhost). 177 @users: list of JID or unicode (JID userhost).
171 @param secure: set to True for a secure check 178 @param secure: set to True for a secure check
172 @return: (x, y) with x a list containing the nicks of 179 @return: (x, y) with x a list containing the nicks of
173 the users who are in the room, and y the missing users. 180 the users who are in the room, and y the missing users.
187 d.addCallback(lambda xmlui: xmlui.toXml()) 194 d.addCallback(lambda xmlui: xmlui.toXml())
188 return d 195 return d
189 196
190 def configureRoom(self, room_jid, profile_key=C.PROF_KEY_NONE): 197 def configureRoom(self, room_jid, profile_key=C.PROF_KEY_NONE):
191 """ return the room configuration form 198 """ return the room configuration form
199
192 @param room: jid of the room to configure 200 @param room: jid of the room to configure
193 @param profile_key: %(doc_profile_key)s 201 @param profile_key: %(doc_profile_key)s
194 @return: configuration form as XMLUI 202 @return: configuration form as XMLUI
195
196 """ 203 """
197 profile = self.host.memory.getProfileName(profile_key) 204 profile = self.host.memory.getProfileName(profile_key)
198 if not self.__check_profile(profile): 205 if not self.__check_profile(profile):
199 raise exceptions.ProfileUnknownError("Unknown or disconnected profile") 206 raise exceptions.ProfileUnknownError("Unknown or disconnected profile")
200 if room_jid.userhost() not in self.clients[profile].joined_rooms: 207 if room_jid.userhost() not in self.clients[profile].joined_rooms:
241 profile = self.host.memory.getProfileName(profile_key) 248 profile = self.host.memory.getProfileName(profile_key)
242 if not self.__check_profile(profile): 249 if not self.__check_profile(profile):
243 return [] 250 return []
244 return self.clients[profile].rec_subjects.values() 251 return self.clients[profile].rec_subjects.values()
245 252
246 def getMUCService(self, profile): 253 @defer.inlineCallbacks
247 """Return the MUC service or None""" 254 def getMUCService(self, jid_=None, profile_key=C.PROF_KEY_NONE):
255 """Return first found MUC service of an entity
256
257 @param jid_: entity which may have a MUC service, or None for our own server
258 @param profile_key: %(doc_profile_key)s
259 """
248 muc_service = None 260 muc_service = None
249 for service in self.host.memory.getServerServiceEntities("conference", "text", profile=profile): 261 services = yield self.host.findServiceEntities("conference", "text", jid_, profile_key=profile_key)
262 for service in services:
250 if not ".irc." in service.userhost(): 263 if not ".irc." in service.userhost():
251 #FIXME: 264 # FIXME:
252 #This awfull ugly hack is here to avoid an issue with openfire: the irc gateway 265 # This ugly hack is here to avoid an issue with openfire: the IRC gateway
253 #use "conference/text" identity (instead of "conference/irc"), there is certainly a better way 266 # use "conference/text" identity (instead of "conference/irc")
254 #to manage this, but this hack fill do it for test purpose
255 muc_service = service 267 muc_service = service
256 break 268 break
257 return muc_service 269 defer.returnValue(muc_service)
258 270
259 def getUniqueName(self, muc_service="", profile_key=C.PROF_KEY_NONE): 271 def _getUniqueName(self, muc_service="", profile_key=C.PROF_KEY_NONE):
260 """Return unique name for room, avoiding collision 272 return self.getUniqueName(muc_service or None, profile_key).full()
273
274 def getUniqueName(self, muc_service=None, profile_key=C.PROF_KEY_NONE):
275 """Return unique name for a room, avoiding collision
276
261 @param muc_service: leave empty string to use the default service 277 @param muc_service: leave empty string to use the default service
262 @return: unique room userhost, or '' if an error occured. 278 @return: unique room userhost, or '' if an error occured.
263 """ 279 """
264 #TODO: we should use #RFC-0045 10.1.4 when available here 280 #TODO: we should use #RFC-0045 10.1.4 when available here
265 profile = self.host.memory.getProfileName(profile_key) 281 client = self.host.getClient(profile_key)
266 if not profile:
267 error(_("Unknown profile"))
268 return ""
269 room_name = uuid.uuid1() 282 room_name = uuid.uuid1()
270 print "\n\n===> room_name:", room_name 283 if muc_service is None:
271 if muc_service == "": 284 try:
272 muc_service = self.getMUCService(profile) 285 muc_service = client.muc_service
273 if not muc_service: 286 except AttributeError:
274 error(_("Can't find a MUC service")) 287 raise NotReadyYet("Main server MUC service has not been checked yet")
275 return "" 288 if muc_service is None:
276 muc_service = muc_service.userhost() 289 warning(_("No MUC service found on main server"))
277 return "%s@%s" % (room_name, muc_service) 290 raise exceptions.FeatureNotFound
291
292 muc_service = muc_service.userhost()
293 return jid.JID("%s@%s" % (room_name, muc_service))
278 294
279 def join(self, room_jid, nick, options, profile_key=C.PROF_KEY_NONE): 295 def join(self, room_jid, nick, options, profile_key=C.PROF_KEY_NONE):
280 def _errDeferred(exc_obj=Exception, txt='Error while joining room'): 296 def _errDeferred(exc_obj=Exception, txt='Error while joining room'):
281 d = defer.Deferred() 297 d = defer.Deferred()
282 d.errback(exc_obj(txt)) 298 d.errback(exc_obj(txt))
313 except: 329 except:
314 mess = _("Invalid room jid: %s") % room_jid_s 330 mess = _("Invalid room jid: %s") % room_jid_s
315 warning(mess) 331 warning(mess)
316 self.host.bridge.newAlert(mess, _("Group chat error"), "ERROR", profile) 332 self.host.bridge.newAlert(mess, _("Group chat error"), "ERROR", profile)
317 return 333 return
318 d = self.join(room_jid, nick, options, profile) 334 self.join(room_jid, nick, options, profile)
319 # TODO: error management + signal in bridge 335 # TODO: error management + signal in bridge
320 return room_jid_s 336 return room_jid_s
321 337
322 def nick(self, room_jid, nick, profile_key): 338 def nick(self, room_jid, nick, profile_key):
323 profile = self.host.memory.getProfileName(profile_key) 339 profile = self.host.memory.getProfileName(profile_key)