comparison sat/plugins/plugin_xep_0045.py @ 4037:524856bd7b19

massive refactoring to switch from camelCase to snake_case: historically, Libervia (SàT before) was using camelCase as allowed by PEP8 when using a pre-PEP8 code, to use the same coding style as in Twisted. However, snake_case is more readable and it's better to follow PEP8 best practices, so it has been decided to move on full snake_case. Because Libervia has a huge codebase, this ended with a ugly mix of camelCase and snake_case. To fix that, this patch does a big refactoring by renaming every function and method (including bridge) that are not coming from Twisted or Wokkel, to use fully snake_case. This is a massive change, and may result in some bugs.
author Goffi <goffi@goffi.org>
date Sat, 08 Apr 2023 13:54:42 +0200
parents 32d714a8ea51
children c23cad65ae99
comparison
equal deleted inserted replaced
4036:c4464d7ae97b 4037:524856bd7b19
85 85
86 def __init__(self, host): 86 def __init__(self, host):
87 log.info(_("Plugin XEP_0045 initialization")) 87 log.info(_("Plugin XEP_0045 initialization"))
88 self.host = host 88 self.host = host
89 self._sessions = memory.Sessions() 89 self._sessions = memory.Sessions()
90 # return same arguments as mucRoomJoined + a boolean set to True is the room was 90 # return same arguments as muc_room_joined + a boolean set to True is the room was
91 # already joined (first argument) 91 # already joined (first argument)
92 host.bridge.addMethod( 92 host.bridge.add_method(
93 "mucJoin", ".plugin", in_sign='ssa{ss}s', out_sign='(bsa{sa{ss}}ssass)', 93 "muc_join", ".plugin", in_sign='ssa{ss}s', out_sign='(bsa{sa{ss}}ssass)',
94 method=self._join, async_=True) 94 method=self._join, async_=True)
95 host.bridge.addMethod( 95 host.bridge.add_method(
96 "mucNick", ".plugin", in_sign='sss', out_sign='', method=self._nick) 96 "muc_nick", ".plugin", in_sign='sss', out_sign='', method=self._nick)
97 host.bridge.addMethod( 97 host.bridge.add_method(
98 "mucNickGet", ".plugin", in_sign='ss', out_sign='s', method=self._getRoomNick) 98 "muc_nick_get", ".plugin", in_sign='ss', out_sign='s', method=self._get_room_nick)
99 host.bridge.addMethod( 99 host.bridge.add_method(
100 "mucLeave", ".plugin", in_sign='ss', out_sign='', method=self._leave, 100 "muc_leave", ".plugin", in_sign='ss', out_sign='', method=self._leave,
101 async_=True) 101 async_=True)
102 host.bridge.addMethod( 102 host.bridge.add_method(
103 "mucOccupantsGet", ".plugin", in_sign='ss', out_sign='a{sa{ss}}', 103 "muc_occupants_get", ".plugin", in_sign='ss', out_sign='a{sa{ss}}',
104 method=self._getRoomOccupants) 104 method=self._get_room_occupants)
105 host.bridge.addMethod( 105 host.bridge.add_method(
106 "mucSubject", ".plugin", in_sign='sss', out_sign='', method=self._subject) 106 "muc_subject", ".plugin", in_sign='sss', out_sign='', method=self._subject)
107 host.bridge.addMethod( 107 host.bridge.add_method(
108 "mucGetRoomsJoined", ".plugin", in_sign='s', out_sign='a(sa{sa{ss}}ssas)', 108 "muc_get_rooms_joined", ".plugin", in_sign='s', out_sign='a(sa{sa{ss}}ssas)',
109 method=self._getRoomsJoined) 109 method=self._get_rooms_joined)
110 host.bridge.addMethod( 110 host.bridge.add_method(
111 "mucGetUniqueRoomName", ".plugin", in_sign='ss', out_sign='s', 111 "muc_get_unique_room_name", ".plugin", in_sign='ss', out_sign='s',
112 method=self._getUniqueName) 112 method=self._get_unique_name)
113 host.bridge.addMethod( 113 host.bridge.add_method(
114 "mucConfigureRoom", ".plugin", in_sign='ss', out_sign='s', 114 "muc_configure_room", ".plugin", in_sign='ss', out_sign='s',
115 method=self._configureRoom, async_=True) 115 method=self._configure_room, async_=True)
116 host.bridge.addMethod( 116 host.bridge.add_method(
117 "mucGetDefaultService", ".plugin", in_sign='', out_sign='s', 117 "muc_get_default_service", ".plugin", in_sign='', out_sign='s',
118 method=self.getDefaultMUC) 118 method=self.get_default_muc)
119 host.bridge.addMethod( 119 host.bridge.add_method(
120 "mucGetService", ".plugin", in_sign='ss', out_sign='s', 120 "muc_get_service", ".plugin", in_sign='ss', out_sign='s',
121 method=self._getMUCService, async_=True) 121 method=self._get_muc_service, async_=True)
122 # called when a room will be joined but must be locked until join is received 122 # called when a room will be joined but must be locked until join is received
123 # (room is prepared, history is getting retrieved) 123 # (room is prepared, history is getting retrieved)
124 # args: room_jid, profile 124 # args: room_jid, profile
125 host.bridge.addSignal( 125 host.bridge.add_signal(
126 "mucRoomPrepareJoin", ".plugin", signature='ss') 126 "muc_room_prepare_join", ".plugin", signature='ss')
127 # args: room_jid, occupants, user_nick, subject, profile 127 # args: room_jid, occupants, user_nick, subject, profile
128 host.bridge.addSignal( 128 host.bridge.add_signal(
129 "mucRoomJoined", ".plugin", signature='sa{sa{ss}}ssass') 129 "muc_room_joined", ".plugin", signature='sa{sa{ss}}ssass')
130 # args: room_jid, profile 130 # args: room_jid, profile
131 host.bridge.addSignal( 131 host.bridge.add_signal(
132 "mucRoomLeft", ".plugin", signature='ss') 132 "muc_room_left", ".plugin", signature='ss')
133 # args: room_jid, old_nick, new_nick, profile 133 # args: room_jid, old_nick, new_nick, profile
134 host.bridge.addSignal( 134 host.bridge.add_signal(
135 "mucRoomUserChangedNick", ".plugin", signature='ssss') 135 "muc_room_user_changed_nick", ".plugin", signature='ssss')
136 # args: room_jid, subject, profile 136 # args: room_jid, subject, profile
137 host.bridge.addSignal( 137 host.bridge.add_signal(
138 "mucRoomNewSubject", ".plugin", signature='sss') 138 "muc_room_new_subject", ".plugin", signature='sss')
139 self.__submit_conf_id = host.registerCallback( 139 self.__submit_conf_id = host.register_callback(
140 self._submitConfiguration, with_data=True) 140 self._submit_configuration, with_data=True)
141 self._room_join_id = host.registerCallback(self._UIRoomJoinCb, with_data=True) 141 self._room_join_id = host.register_callback(self._ui_room_join_cb, with_data=True)
142 host.importMenu( 142 host.import_menu(
143 (D_("MUC"), D_("configure")), self._configureRoomMenu, security_limit=0, 143 (D_("MUC"), D_("configure")), self._configure_room_menu, security_limit=0,
144 help_string=D_("Configure Multi-User Chat room"), type_=C.MENU_ROOM) 144 help_string=D_("Configure Multi-User Chat room"), type_=C.MENU_ROOM)
145 try: 145 try:
146 self.text_cmds = self.host.plugins[C.TEXT_CMDS] 146 self.text_cmds = self.host.plugins[C.TEXT_CMDS]
147 except KeyError: 147 except KeyError:
148 log.info(_("Text commands not available")) 148 log.info(_("Text commands not available"))
149 else: 149 else:
150 self.text_cmds.registerTextCommands(self) 150 self.text_cmds.register_text_commands(self)
151 self.text_cmds.addWhoIsCb(self._whois, 100) 151 self.text_cmds.add_who_is_cb(self._whois, 100)
152 152
153 self._mam = self.host.plugins.get("XEP-0313") 153 self._mam = self.host.plugins.get("XEP-0313")
154 self._si = self.host.plugins["XEP-0359"] 154 self._si = self.host.plugins["XEP-0359"]
155 155
156 host.trigger.add("presence_available", self.presenceTrigger) 156 host.trigger.add("presence_available", self.presence_trigger)
157 host.trigger.add("presence_received", self.presenceReceivedTrigger) 157 host.trigger.add("presence_received", self.presence_received_trigger)
158 host.trigger.add("messageReceived", self.messageReceivedTrigger, priority=1000000) 158 host.trigger.add("messageReceived", self.message_received_trigger, priority=1000000)
159 host.trigger.add("message_parse", self._message_parseTrigger) 159 host.trigger.add("message_parse", self._message_parse_trigger)
160 160
161 async def profileConnected(self, client): 161 async def profile_connected(self, client):
162 client.muc_service = await self.get_MUC_service(client) 162 client.muc_service = await self.get_muc_service(client)
163 163
164 def _message_parseTrigger(self, client, message_elt, data): 164 def _message_parse_trigger(self, client, message_elt, data):
165 """Add stanza-id from the room if present""" 165 """Add stanza-id from the room if present"""
166 if message_elt.getAttribute("type") != C.MESS_TYPE_GROUPCHAT: 166 if message_elt.getAttribute("type") != C.MESS_TYPE_GROUPCHAT:
167 return True 167 return True
168 168
169 # stanza_id will not be filled by parseMessage because the emitter 169 # stanza_id will not be filled by parse_message because the emitter
170 # is the room and not our server, so we have to parse it here 170 # is the room and not our server, so we have to parse it here
171 room_jid = data["from"].userhostJID() 171 room_jid = data["from"].userhostJID()
172 stanza_id = self._si.getStanzaId(message_elt, room_jid) 172 stanza_id = self._si.get_stanza_id(message_elt, room_jid)
173 if stanza_id: 173 if stanza_id:
174 data["extra"]["stanza_id"] = stanza_id 174 data["extra"]["stanza_id"] = stanza_id
175 175
176 def messageReceivedTrigger(self, client, message_elt, post_treat): 176 def message_received_trigger(self, client, message_elt, post_treat):
177 if message_elt.getAttribute("type") == C.MESS_TYPE_GROUPCHAT: 177 if message_elt.getAttribute("type") == C.MESS_TYPE_GROUPCHAT:
178 if message_elt.subject or message_elt.delay: 178 if message_elt.subject or message_elt.delay:
179 return False 179 return False
180 from_jid = jid.JID(message_elt['from']) 180 from_jid = jid.JID(message_elt['from'])
181 room_jid = from_jid.userhostJID() 181 room_jid = from_jid.userhostJID()
198 log.warning("Received groupchat message for a room which has not been " 198 log.warning("Received groupchat message for a room which has not been "
199 "joined, ignoring it: {}".format(message_elt.toXml())) 199 "joined, ignoring it: {}".format(message_elt.toXml()))
200 return False 200 return False
201 return True 201 return True
202 202
203 def getRoom(self, client: SatXMPPEntity, room_jid: jid.JID) -> muc.Room: 203 def get_room(self, client: SatXMPPEntity, room_jid: jid.JID) -> muc.Room:
204 """Retrieve Room instance from its jid 204 """Retrieve Room instance from its jid
205 205
206 @param room_jid: jid of the room 206 @param room_jid: jid of the room
207 @raise exceptions.NotFound: the room has not been joined 207 @raise exceptions.NotFound: the room has not been joined
208 """ 208 """
209 try: 209 try:
210 return client._muc_client.joined_rooms[room_jid] 210 return client._muc_client.joined_rooms[room_jid]
211 except KeyError: 211 except KeyError:
212 raise exceptions.NotFound(_("This room has not been joined")) 212 raise exceptions.NotFound(_("This room has not been joined"))
213 213
214 def checkRoomJoined(self, client, room_jid): 214 def check_room_joined(self, client, room_jid):
215 """Check that given room has been joined in current session 215 """Check that given room has been joined in current session
216 216
217 @param room_jid (JID): room JID 217 @param room_jid (JID): room JID
218 """ 218 """
219 if room_jid not in client._muc_client.joined_rooms: 219 if room_jid not in client._muc_client.joined_rooms:
220 raise exceptions.NotFound(_("This room has not been joined")) 220 raise exceptions.NotFound(_("This room has not been joined"))
221 221
222 def isJoinedRoom(self, client: SatXMPPEntity, room_jid: jid.JID) -> bool: 222 def is_joined_room(self, client: SatXMPPEntity, room_jid: jid.JID) -> bool:
223 """Tell if a jid is a known and joined room 223 """Tell if a jid is a known and joined room
224 224
225 @room_jid: jid of the room 225 @room_jid: jid of the room
226 """ 226 """
227 try: 227 try:
228 self.checkRoomJoined(client, room_jid) 228 self.check_room_joined(client, room_jid)
229 except exceptions.NotFound: 229 except exceptions.NotFound:
230 return False 230 return False
231 else: 231 else:
232 return True 232 return True
233 233
234 def isRoom(self, client, entity_jid): 234 def is_room(self, client, entity_jid):
235 """Tell if a jid is a joined MUC 235 """Tell if a jid is a joined MUC
236 236
237 similar to isJoinedRoom but returns a boolean 237 similar to is_joined_room but returns a boolean
238 @param entity_jid(jid.JID): full or bare jid of the entity check 238 @param entity_jid(jid.JID): full or bare jid of the entity check
239 @return (bool): True if the bare jid of the entity is a room jid 239 @return (bool): True if the bare jid of the entity is a room jid
240 """ 240 """
241 try: 241 try:
242 self.checkRoomJoined(client, entity_jid.userhostJID()) 242 self.check_room_joined(client, entity_jid.userhostJID())
243 except exceptions.NotFound: 243 except exceptions.NotFound:
244 return False 244 return False
245 else: 245 else:
246 return True 246 return True
247 247
248 def getBareOrFull(self, client, peer_jid): 248 def get_bare_or_full(self, client, peer_jid):
249 """use full jid if peer_jid is an occupant of a room, bare jid else 249 """use full jid if peer_jid is an occupant of a room, bare jid else
250 250
251 @param peer_jid(jid.JID): entity to test 251 @param peer_jid(jid.JID): entity to test
252 @return (jid.JID): bare or full jid 252 @return (jid.JID): bare or full jid
253 """ 253 """
254 if peer_jid.resource: 254 if peer_jid.resource:
255 if not self.isRoom(client, peer_jid): 255 if not self.is_room(client, peer_jid):
256 return peer_jid.userhostJID() 256 return peer_jid.userhostJID()
257 return peer_jid 257 return peer_jid
258 258
259 def _getRoomJoinedArgs(self, room, profile): 259 def _get_room_joined_args(self, room, profile):
260 return [ 260 return [
261 room.roomJID.userhost(), 261 room.roomJID.userhost(),
262 XEP_0045._getOccupants(room), 262 XEP_0045._get_occupants(room),
263 room.nick, 263 room.nick,
264 room.subject, 264 room.subject,
265 [s.name for s in room.statuses], 265 [s.name for s in room.statuses],
266 profile 266 profile
267 ] 267 ]
268 268
269 def _UIRoomJoinCb(self, data, profile): 269 def _ui_room_join_cb(self, data, profile):
270 room_jid = jid.JID(data['index']) 270 room_jid = jid.JID(data['index'])
271 client = self.host.getClient(profile) 271 client = self.host.get_client(profile)
272 self.join(client, room_jid) 272 self.join(client, room_jid)
273 return {} 273 return {}
274 274
275 def _passwordUICb(self, data, client, room_jid, nick): 275 def _password_ui_cb(self, data, client, room_jid, nick):
276 """Called when the user has given room password (or cancelled)""" 276 """Called when the user has given room password (or cancelled)"""
277 if C.bool(data.get(C.XMLUI_DATA_CANCELLED, "false")): 277 if C.bool(data.get(C.XMLUI_DATA_CANCELLED, "false")):
278 log.info("room join for {} is cancelled".format(room_jid.userhost())) 278 log.info("room join for {} is cancelled".format(room_jid.userhost()))
279 raise failure.Failure(exceptions.CancelError(D_("Room joining cancelled by user"))) 279 raise failure.Failure(exceptions.CancelError(D_("Room joining cancelled by user")))
280 password = data[xml_tools.formEscape('password')] 280 password = data[xml_tools.form_escape('password')]
281 return client._muc_client.join(room_jid, nick, password).addCallbacks(self._joinCb, self._joinEb, (client, room_jid, nick), errbackArgs=(client, room_jid, nick, password)) 281 return client._muc_client.join(room_jid, nick, password).addCallbacks(self._join_cb, self._join_eb, (client, room_jid, nick), errbackArgs=(client, room_jid, nick, password))
282 282
283 def _showListUI(self, items, client, service): 283 def _show_list_ui(self, items, client, service):
284 xmlui = xml_tools.XMLUI(title=D_('Rooms in {}'.format(service.full()))) 284 xmlui = xml_tools.XMLUI(title=D_('Rooms in {}'.format(service.full())))
285 adv_list = xmlui.changeContainer('advanced_list', columns=1, selectable='single', callback_id=self._room_join_id) 285 adv_list = xmlui.change_container('advanced_list', columns=1, selectable='single', callback_id=self._room_join_id)
286 items = sorted(items, key=lambda i: i.name.lower()) 286 items = sorted(items, key=lambda i: i.name.lower())
287 for item in items: 287 for item in items:
288 adv_list.setRowIndex(item.entity.full()) 288 adv_list.set_row_index(item.entity.full())
289 xmlui.addText(item.name) 289 xmlui.addText(item.name)
290 adv_list.end() 290 adv_list.end()
291 self.host.actionNew({'xmlui': xmlui.toXml()}, profile=client.profile) 291 self.host.action_new({'xmlui': xmlui.toXml()}, profile=client.profile)
292 292
293 def _joinCb(self, room, client, room_jid, nick): 293 def _join_cb(self, room, client, room_jid, nick):
294 """Called when the user is in the requested room""" 294 """Called when the user is in the requested room"""
295 if room.locked: 295 if room.locked:
296 # FIXME: the current behaviour is to create an instant room 296 # FIXME: the current behaviour is to create an instant room
297 # and send the signal only when the room is unlocked 297 # and send the signal only when the room is unlocked
298 # a proper configuration management should be done 298 # a proper configuration management should be done
299 log.debug(_("room locked !")) 299 log.debug(_("room locked !"))
300 d = client._muc_client.configure(room.roomJID, {}) 300 d = client._muc_client.configure(room.roomJID, {})
301 d.addErrback(self.host.logErrback, 301 d.addErrback(self.host.log_errback,
302 msg=_('Error while configuring the room: {failure_}')) 302 msg=_('Error while configuring the room: {failure_}'))
303 return room.fully_joined 303 return room.fully_joined
304 304
305 def _joinEb(self, failure_, client, room_jid, nick, password): 305 def _join_eb(self, failure_, client, room_jid, nick, password):
306 """Called when something is going wrong when joining the room""" 306 """Called when something is going wrong when joining the room"""
307 try: 307 try:
308 condition = failure_.value.condition 308 condition = failure_.value.condition
309 except AttributeError: 309 except AttributeError:
310 msg_suffix = f': {failure_}' 310 msg_suffix = f': {failure_}'
311 else: 311 else:
312 if condition == 'conflict': 312 if condition == 'conflict':
313 # we have a nickname conflict, we try again with "_" suffixed to current nickname 313 # we have a nickname conflict, we try again with "_" suffixed to current nickname
314 nick += '_' 314 nick += '_'
315 return client._muc_client.join(room_jid, nick, password).addCallbacks(self._joinCb, self._joinEb, (client, room_jid, nick), errbackArgs=(client, room_jid, nick, password)) 315 return client._muc_client.join(room_jid, nick, password).addCallbacks(self._join_cb, self._join_eb, (client, room_jid, nick), errbackArgs=(client, room_jid, nick, password))
316 elif condition == 'not-allowed': 316 elif condition == 'not-allowed':
317 # room is restricted, we need a password 317 # room is restricted, we need a password
318 password_ui = xml_tools.XMLUI("form", title=D_('Room {} is restricted').format(room_jid.userhost()), submit_id='') 318 password_ui = xml_tools.XMLUI("form", title=D_('Room {} is restricted').format(room_jid.userhost()), submit_id='')
319 password_ui.addText(D_("This room is restricted, please enter the password")) 319 password_ui.addText(D_("This room is restricted, please enter the password"))
320 password_ui.addPassword('password') 320 password_ui.addPassword('password')
321 d = xml_tools.deferXMLUI(self.host, password_ui, profile=client.profile) 321 d = xml_tools.defer_xmlui(self.host, password_ui, profile=client.profile)
322 d.addCallback(self._passwordUICb, client, room_jid, nick) 322 d.addCallback(self._password_ui_cb, client, room_jid, nick)
323 return d 323 return d
324 324
325 msg_suffix = ' with condition "{}"'.format(failure_.value.condition) 325 msg_suffix = ' with condition "{}"'.format(failure_.value.condition)
326 326
327 mess = D_("Error while joining the room {room}{suffix}".format( 327 mess = D_("Error while joining the room {room}{suffix}".format(
328 room = room_jid.userhost(), suffix = msg_suffix)) 328 room = room_jid.userhost(), suffix = msg_suffix))
329 log.warning(mess) 329 log.warning(mess)
330 xmlui = xml_tools.note(mess, D_("Group chat error"), level=C.XMLUI_DATA_LVL_ERROR) 330 xmlui = xml_tools.note(mess, D_("Group chat error"), level=C.XMLUI_DATA_LVL_ERROR)
331 self.host.actionNew({'xmlui': xmlui.toXml()}, profile=client.profile) 331 self.host.action_new({'xmlui': xmlui.toXml()}, profile=client.profile)
332 332
333 @staticmethod 333 @staticmethod
334 def _getOccupants(room): 334 def _get_occupants(room):
335 """Get occupants of a room in a form suitable for bridge""" 335 """Get occupants of a room in a form suitable for bridge"""
336 return {u.nick: {k:str(getattr(u,k) or '') for k in OCCUPANT_KEYS} for u in list(room.roster.values())} 336 return {u.nick: {k:str(getattr(u,k) or '') for k in OCCUPANT_KEYS} for u in list(room.roster.values())}
337 337
338 def _getRoomOccupants(self, room_jid_s, profile_key): 338 def _get_room_occupants(self, room_jid_s, profile_key):
339 client = self.host.getClient(profile_key) 339 client = self.host.get_client(profile_key)
340 room_jid = jid.JID(room_jid_s) 340 room_jid = jid.JID(room_jid_s)
341 return self.getRoomOccupants(client, room_jid) 341 return self.get_room_occupants(client, room_jid)
342 342
343 def getRoomOccupants(self, client, room_jid): 343 def get_room_occupants(self, client, room_jid):
344 room = self.getRoom(client, room_jid) 344 room = self.get_room(client, room_jid)
345 return self._getOccupants(room) 345 return self._get_occupants(room)
346 346
347 def _getRoomsJoined(self, profile_key=C.PROF_KEY_NONE): 347 def _get_rooms_joined(self, profile_key=C.PROF_KEY_NONE):
348 client = self.host.getClient(profile_key) 348 client = self.host.get_client(profile_key)
349 return self.getRoomsJoined(client) 349 return self.get_rooms_joined(client)
350 350
351 def getRoomsJoined(self, client): 351 def get_rooms_joined(self, client):
352 """Return rooms where user is""" 352 """Return rooms where user is"""
353 result = [] 353 result = []
354 for room in list(client._muc_client.joined_rooms.values()): 354 for room in list(client._muc_client.joined_rooms.values()):
355 if room.state == ROOM_STATE_LIVE: 355 if room.state == ROOM_STATE_LIVE:
356 result.append( 356 result.append(
357 (room.roomJID.userhost(), 357 (room.roomJID.userhost(),
358 self._getOccupants(room), 358 self._get_occupants(room),
359 room.nick, 359 room.nick,
360 room.subject, 360 room.subject,
361 [s.name for s in room.statuses], 361 [s.name for s in room.statuses],
362 ) 362 )
363 ) 363 )
364 return result 364 return result
365 365
366 def _getRoomNick(self, room_jid_s, profile_key=C.PROF_KEY_NONE): 366 def _get_room_nick(self, room_jid_s, profile_key=C.PROF_KEY_NONE):
367 client = self.host.getClient(profile_key) 367 client = self.host.get_client(profile_key)
368 return self.getRoomNick(client, jid.JID(room_jid_s)) 368 return self.get_room_nick(client, jid.JID(room_jid_s))
369 369
370 def getRoomNick(self, client, room_jid): 370 def get_room_nick(self, client, room_jid):
371 """return nick used in room by user 371 """return nick used in room by user
372 372
373 @param room_jid (jid.JID): JID of the room 373 @param room_jid (jid.JID): JID of the room
374 @profile_key: profile 374 @profile_key: profile
375 @return: nick or empty string in case of error 375 @return: nick or empty string in case of error
376 @raise exceptions.Notfound: use has not joined the room 376 @raise exceptions.Notfound: use has not joined the room
377 """ 377 """
378 self.checkRoomJoined(client, room_jid) 378 self.check_room_joined(client, room_jid)
379 return client._muc_client.joined_rooms[room_jid].nick 379 return client._muc_client.joined_rooms[room_jid].nick
380 380
381 def _configureRoom(self, room_jid_s, profile_key=C.PROF_KEY_NONE): 381 def _configure_room(self, room_jid_s, profile_key=C.PROF_KEY_NONE):
382 client = self.host.getClient(profile_key) 382 client = self.host.get_client(profile_key)
383 d = self.configureRoom(client, jid.JID(room_jid_s)) 383 d = self.configure_room(client, jid.JID(room_jid_s))
384 d.addCallback(lambda xmlui: xmlui.toXml()) 384 d.addCallback(lambda xmlui: xmlui.toXml())
385 return d 385 return d
386 386
387 def _configureRoomMenu(self, menu_data, profile): 387 def _configure_room_menu(self, menu_data, profile):
388 """Return room configuration form 388 """Return room configuration form
389 389
390 @param menu_data: %(menu_data)s 390 @param menu_data: %(menu_data)s
391 @param profile: %(doc_profile)s 391 @param profile: %(doc_profile)s
392 """ 392 """
393 client = self.host.getClient(profile) 393 client = self.host.get_client(profile)
394 try: 394 try:
395 room_jid = jid.JID(menu_data['room_jid']) 395 room_jid = jid.JID(menu_data['room_jid'])
396 except KeyError: 396 except KeyError:
397 log.error(_("room_jid key is not present !")) 397 log.error(_("room_jid key is not present !"))
398 return defer.fail(exceptions.DataError) 398 return defer.fail(exceptions.DataError)
399 399
400 def xmluiReceived(xmlui): 400 def xmlui_received(xmlui):
401 if not xmlui: 401 if not xmlui:
402 msg = D_("No configuration available for this room") 402 msg = D_("No configuration available for this room")
403 return {"xmlui": xml_tools.note(msg).toXml()} 403 return {"xmlui": xml_tools.note(msg).toXml()}
404 return {"xmlui": xmlui.toXml()} 404 return {"xmlui": xmlui.toXml()}
405 return self.configureRoom(client, room_jid).addCallback(xmluiReceived) 405 return self.configure_room(client, room_jid).addCallback(xmlui_received)
406 406
407 def configureRoom(self, client, room_jid): 407 def configure_room(self, client, room_jid):
408 """return the room configuration form 408 """return the room configuration form
409 409
410 @param room: jid of the room to configure 410 @param room: jid of the room to configure
411 @return: configuration form as XMLUI 411 @return: configuration form as XMLUI
412 """ 412 """
413 self.checkRoomJoined(client, room_jid) 413 self.check_room_joined(client, room_jid)
414 414
415 def config2XMLUI(result): 415 def config_2_xmlui(result):
416 if not result: 416 if not result:
417 return "" 417 return ""
418 session_id, session_data = self._sessions.newSession(profile=client.profile) 418 session_id, session_data = self._sessions.new_session(profile=client.profile)
419 session_data["room_jid"] = room_jid 419 session_data["room_jid"] = room_jid
420 xmlui = xml_tools.dataForm2XMLUI(result, submit_id=self.__submit_conf_id) 420 xmlui = xml_tools.data_form_2_xmlui(result, submit_id=self.__submit_conf_id)
421 xmlui.session_id = session_id 421 xmlui.session_id = session_id
422 return xmlui 422 return xmlui
423 423
424 d = client._muc_client.getConfiguration(room_jid) 424 d = client._muc_client.getConfiguration(room_jid)
425 d.addCallback(config2XMLUI) 425 d.addCallback(config_2_xmlui)
426 return d 426 return d
427 427
428 def _submitConfiguration(self, raw_data, profile): 428 def _submit_configuration(self, raw_data, profile):
429 cancelled = C.bool(raw_data.get("cancelled", C.BOOL_FALSE)) 429 cancelled = C.bool(raw_data.get("cancelled", C.BOOL_FALSE))
430 if cancelled: 430 if cancelled:
431 return defer.succeed({}) 431 return defer.succeed({})
432 client = self.host.getClient(profile) 432 client = self.host.get_client(profile)
433 try: 433 try:
434 session_data = self._sessions.profileGet(raw_data["session_id"], profile) 434 session_data = self._sessions.profile_get(raw_data["session_id"], profile)
435 except KeyError: 435 except KeyError:
436 log.warning(D_("Session ID doesn't exist, session has probably expired.")) 436 log.warning(D_("Session ID doesn't exist, session has probably expired."))
437 _dialog = xml_tools.XMLUI('popup', title=D_('Room configuration failed')) 437 _dialog = xml_tools.XMLUI('popup', title=D_('Room configuration failed'))
438 _dialog.addText(D_("Session ID doesn't exist, session has probably expired.")) 438 _dialog.addText(D_("Session ID doesn't exist, session has probably expired."))
439 return defer.succeed({'xmlui': _dialog.toXml()}) 439 return defer.succeed({'xmlui': _dialog.toXml()})
440 440
441 data = xml_tools.XMLUIResult2DataFormResult(raw_data) 441 data = xml_tools.xmlui_result_2_data_form_result(raw_data)
442 d = client._muc_client.configure(session_data['room_jid'], data) 442 d = client._muc_client.configure(session_data['room_jid'], data)
443 _dialog = xml_tools.XMLUI('popup', title=D_('Room configuration succeed')) 443 _dialog = xml_tools.XMLUI('popup', title=D_('Room configuration succeed'))
444 _dialog.addText(D_("The new settings have been saved.")) 444 _dialog.addText(D_("The new settings have been saved."))
445 d.addCallback(lambda ignore: {'xmlui': _dialog.toXml()}) 445 d.addCallback(lambda ignore: {'xmlui': _dialog.toXml()})
446 del self._sessions[raw_data["session_id"]] 446 del self._sessions[raw_data["session_id"]]
447 return d 447 return d
448 448
449 def isNickInRoom(self, client, room_jid, nick): 449 def is_nick_in_room(self, client, room_jid, nick):
450 """Tell if a nick is currently present in a room""" 450 """Tell if a nick is currently present in a room"""
451 self.checkRoomJoined(client, room_jid) 451 self.check_room_joined(client, room_jid)
452 return client._muc_client.joined_rooms[room_jid].inRoster(muc.User(nick)) 452 return client._muc_client.joined_rooms[room_jid].inRoster(muc.User(nick))
453 453
454 def _getMUCService(self, jid_=None, profile=C.PROF_KEY_NONE): 454 def _get_muc_service(self, jid_=None, profile=C.PROF_KEY_NONE):
455 client = self.host.getClient(profile) 455 client = self.host.get_client(profile)
456 d = defer.ensureDeferred(self.get_MUC_service(client, jid_ or None)) 456 d = defer.ensureDeferred(self.get_muc_service(client, jid_ or None))
457 d.addCallback(lambda service_jid: service_jid.full() if service_jid is not None else '') 457 d.addCallback(lambda service_jid: service_jid.full() if service_jid is not None else '')
458 return d 458 return d
459 459
460 async def get_MUC_service( 460 async def get_muc_service(
461 self, 461 self,
462 client: SatXMPPEntity, 462 client: SatXMPPEntity,
463 jid_: Optional[jid.JID] = None) -> Optional[jid.JID]: 463 jid_: Optional[jid.JID] = None) -> Optional[jid.JID]:
464 """Return first found MUC service of an entity 464 """Return first found MUC service of an entity
465 465
472 except AttributeError: 472 except AttributeError:
473 pass 473 pass
474 else: 474 else:
475 # we have a cached value, we return it 475 # we have a cached value, we return it
476 return muc_service 476 return muc_service
477 services = await self.host.findServiceEntities(client, "conference", "text", jid_) 477 services = await self.host.find_service_entities(client, "conference", "text", jid_)
478 for service in services: 478 for service in services:
479 if ".irc." not in service.userhost(): 479 if ".irc." not in service.userhost():
480 # FIXME: 480 # FIXME:
481 # This ugly hack is here to avoid an issue with openfire: the IRC gateway 481 # This ugly hack is here to avoid an issue with openfire: the IRC gateway
482 # use "conference/text" identity (instead of "conference/irc") 482 # use "conference/text" identity (instead of "conference/irc")
484 break 484 break
485 else: 485 else:
486 muc_service = None 486 muc_service = None
487 return muc_service 487 return muc_service
488 488
489 def _getUniqueName(self, muc_service="", profile_key=C.PROF_KEY_NONE): 489 def _get_unique_name(self, muc_service="", profile_key=C.PROF_KEY_NONE):
490 client = self.host.getClient(profile_key) 490 client = self.host.get_client(profile_key)
491 return self.getUniqueName(client, muc_service or None).full() 491 return self.get_unique_name(client, muc_service or None).full()
492 492
493 def getUniqueName(self, client, muc_service=None): 493 def get_unique_name(self, client, muc_service=None):
494 """Return unique name for a room, avoiding collision 494 """Return unique name for a room, avoiding collision
495 495
496 @param muc_service (jid.JID) : leave empty string to use the default service 496 @param muc_service (jid.JID) : leave empty string to use the default service
497 @return: jid.JID (unique room bare JID) 497 @return: jid.JID (unique room bare JID)
498 """ 498 """
508 raise exceptions.FeatureNotFound 508 raise exceptions.FeatureNotFound
509 509
510 muc_service = muc_service.userhost() 510 muc_service = muc_service.userhost()
511 return jid.JID("{}@{}".format(room_name, muc_service)) 511 return jid.JID("{}@{}".format(room_name, muc_service))
512 512
513 def getDefaultMUC(self): 513 def get_default_muc(self):
514 """Return the default MUC. 514 """Return the default MUC.
515 515
516 @return: unicode 516 @return: unicode
517 """ 517 """
518 return self.host.memory.getConfig(CONFIG_SECTION, 'default_muc', default_conf['default_muc']) 518 return self.host.memory.config_get(CONFIG_SECTION, 'default_muc', default_conf['default_muc'])
519 519
520 def _join_eb(self, failure_, client): 520 def _join_eb(self, failure_, client):
521 failure_.trap(AlreadyJoined) 521 failure_.trap(AlreadyJoined)
522 room = failure_.value.room 522 room = failure_.value.room
523 return [True] + self._getRoomJoinedArgs(room, client.profile) 523 return [True] + self._get_room_joined_args(room, client.profile)
524 524
525 def _join(self, room_jid_s, nick, options, profile_key=C.PROF_KEY_NONE): 525 def _join(self, room_jid_s, nick, options, profile_key=C.PROF_KEY_NONE):
526 """join method used by bridge 526 """join method used by bridge
527 527
528 @return (tuple): already_joined boolean + room joined arguments (see [_getRoomJoinedArgs]) 528 @return (tuple): already_joined boolean + room joined arguments (see [_get_room_joined_args])
529 """ 529 """
530 client = self.host.getClient(profile_key) 530 client = self.host.get_client(profile_key)
531 if room_jid_s: 531 if room_jid_s:
532 muc_service = client.muc_service 532 muc_service = client.muc_service
533 try: 533 try:
534 room_jid = jid.JID(room_jid_s) 534 room_jid = jid.JID(room_jid_s)
535 except (RuntimeError, jid.InvalidFormat, AttributeError): 535 except (RuntimeError, jid.InvalidFormat, AttributeError):
537 room_id=room_jid_s, 537 room_id=room_jid_s,
538 muc_service=str(muc_service)))) 538 muc_service=str(muc_service))))
539 if not room_jid.user: 539 if not room_jid.user:
540 room_jid.user, room_jid.host = room_jid.host, muc_service 540 room_jid.user, room_jid.host = room_jid.host, muc_service
541 else: 541 else:
542 room_jid = self.getUniqueName(profile_key=client.profile) 542 room_jid = self.get_unique_name(profile_key=client.profile)
543 # TODO: error management + signal in bridge 543 # TODO: error management + signal in bridge
544 d = self.join(client, room_jid, nick, options or None) 544 d = self.join(client, room_jid, nick, options or None)
545 d.addCallback(lambda room: [False] + self._getRoomJoinedArgs(room, client.profile)) 545 d.addCallback(lambda room: [False] + self._get_room_joined_args(room, client.profile))
546 d.addErrback(self._join_eb, client) 546 d.addErrback(self._join_eb, client)
547 return d 547 return d
548 548
549 async def join( 549 async def join(
550 self, 550 self,
562 log.info(_('{profile} is already in room {room_jid}').format( 562 log.info(_('{profile} is already in room {room_jid}').format(
563 profile=client.profile, room_jid = room_jid.userhost())) 563 profile=client.profile, room_jid = room_jid.userhost()))
564 raise AlreadyJoined(room) 564 raise AlreadyJoined(room)
565 log.info(_("[{profile}] is joining room {room} with nick {nick}").format( 565 log.info(_("[{profile}] is joining room {room} with nick {nick}").format(
566 profile=client.profile, room=room_jid.userhost(), nick=nick)) 566 profile=client.profile, room=room_jid.userhost(), nick=nick))
567 self.host.bridge.mucRoomPrepareJoin(room_jid.userhost(), client.profile) 567 self.host.bridge.muc_room_prepare_join(room_jid.userhost(), client.profile)
568 568
569 password = options.get("password") 569 password = options.get("password")
570 570
571 try: 571 try:
572 room = await client._muc_client.join(room_jid, nick, password) 572 room = await client._muc_client.join(room_jid, nick, password)
573 except Exception as e: 573 except Exception as e:
574 room = await utils.asDeferred( 574 room = await utils.as_deferred(
575 self._joinEb(failure.Failure(e), client, room_jid, nick, password) 575 self._join_eb(failure.Failure(e), client, room_jid, nick, password)
576 ) 576 )
577 else: 577 else:
578 await defer.ensureDeferred( 578 await defer.ensureDeferred(
579 self._joinCb(room, client, room_jid, nick) 579 self._join_cb(room, client, room_jid, nick)
580 ) 580 )
581 return room 581 return room
582 582
583 def popRooms(self, client): 583 def pop_rooms(self, client):
584 """Remove rooms and return data needed to re-join them 584 """Remove rooms and return data needed to re-join them
585 585
586 This methods is to be called before a hot reconnection 586 This methods is to be called before a hot reconnection
587 @return (list[(jid.JID, unicode)]): arguments needed to re-join the rooms 587 @return (list[(jid.JID, unicode)]): arguments needed to re-join the rooms
588 This list can be used directly (unpacked) with self.join 588 This list can be used directly (unpacked) with self.join
592 client._muc_client._removeRoom(room.roomJID) 592 client._muc_client._removeRoom(room.roomJID)
593 args_list.append((client, room.roomJID, room.nick)) 593 args_list.append((client, room.roomJID, room.nick))
594 return args_list 594 return args_list
595 595
596 def _nick(self, room_jid_s, nick, profile_key=C.PROF_KEY_NONE): 596 def _nick(self, room_jid_s, nick, profile_key=C.PROF_KEY_NONE):
597 client = self.host.getClient(profile_key) 597 client = self.host.get_client(profile_key)
598 return self.nick(client, jid.JID(room_jid_s), nick) 598 return self.nick(client, jid.JID(room_jid_s), nick)
599 599
600 def nick(self, client, room_jid, nick): 600 def nick(self, client, room_jid, nick):
601 """Change nickname in a room""" 601 """Change nickname in a room"""
602 self.checkRoomJoined(client, room_jid) 602 self.check_room_joined(client, room_jid)
603 return client._muc_client.nick(room_jid, nick) 603 return client._muc_client.nick(room_jid, nick)
604 604
605 def _leave(self, room_jid, profile_key): 605 def _leave(self, room_jid, profile_key):
606 client = self.host.getClient(profile_key) 606 client = self.host.get_client(profile_key)
607 return self.leave(client, jid.JID(room_jid)) 607 return self.leave(client, jid.JID(room_jid))
608 608
609 def leave(self, client, room_jid): 609 def leave(self, client, room_jid):
610 self.checkRoomJoined(client, room_jid) 610 self.check_room_joined(client, room_jid)
611 return client._muc_client.leave(room_jid) 611 return client._muc_client.leave(room_jid)
612 612
613 def _subject(self, room_jid_s, new_subject, profile_key): 613 def _subject(self, room_jid_s, new_subject, profile_key):
614 client = self.host.getClient(profile_key) 614 client = self.host.get_client(profile_key)
615 return self.subject(client, jid.JID(room_jid_s), new_subject) 615 return self.subject(client, jid.JID(room_jid_s), new_subject)
616 616
617 def subject(self, client, room_jid, subject): 617 def subject(self, client, room_jid, subject):
618 self.checkRoomJoined(client, room_jid) 618 self.check_room_joined(client, room_jid)
619 return client._muc_client.subject(room_jid, subject) 619 return client._muc_client.subject(room_jid, subject)
620 620
621 def getHandler(self, client): 621 def get_handler(self, client):
622 # create a MUC client and associate it with profile' session 622 # create a MUC client and associate it with profile' session
623 muc_client = client._muc_client = LiberviaMUCClient(self) 623 muc_client = client._muc_client = LiberviaMUCClient(self)
624 return muc_client 624 return muc_client
625 625
626 def kick(self, client, nick, room_jid, options=None): 626 def kick(self, client, nick, room_jid, options=None):
630 @param room_jid_s (JID): jid of the room 630 @param room_jid_s (JID): jid of the room
631 @param options (dict): attribute with extra info (reason, password) as in #XEP-0045 631 @param options (dict): attribute with extra info (reason, password) as in #XEP-0045
632 """ 632 """
633 if options is None: 633 if options is None:
634 options = {} 634 options = {}
635 self.checkRoomJoined(client, room_jid) 635 self.check_room_joined(client, room_jid)
636 return client._muc_client.kick(room_jid, nick, reason=options.get('reason', None)) 636 return client._muc_client.kick(room_jid, nick, reason=options.get('reason', None))
637 637
638 def ban(self, client, entity_jid, room_jid, options=None): 638 def ban(self, client, entity_jid, room_jid, options=None):
639 """Ban an entity from the room 639 """Ban an entity from the room
640 640
641 @param entity_jid (JID): bare jid of the entity to be banned 641 @param entity_jid (JID): bare jid of the entity to be banned
642 @param room_jid (JID): jid of the room 642 @param room_jid (JID): jid of the room
643 @param options: attribute with extra info (reason, password) as in #XEP-0045 643 @param options: attribute with extra info (reason, password) as in #XEP-0045
644 """ 644 """
645 self.checkRoomJoined(client, room_jid) 645 self.check_room_joined(client, room_jid)
646 if options is None: 646 if options is None:
647 options = {} 647 options = {}
648 assert not entity_jid.resource 648 assert not entity_jid.resource
649 assert not room_jid.resource 649 assert not room_jid.resource
650 return client._muc_client.ban(room_jid, entity_jid, reason=options.get('reason', None)) 650 return client._muc_client.ban(room_jid, entity_jid, reason=options.get('reason', None))
654 654
655 @param entity_jid (JID): bare jid of the entity 655 @param entity_jid (JID): bare jid of the entity
656 @param room_jid_s (JID): jid of the room 656 @param room_jid_s (JID): jid of the room
657 @param options: attribute with extra info (reason, nick) as in #XEP-0045 657 @param options: attribute with extra info (reason, nick) as in #XEP-0045
658 """ 658 """
659 self.checkRoomJoined(client, room_jid) 659 self.check_room_joined(client, room_jid)
660 assert not entity_jid.resource 660 assert not entity_jid.resource
661 assert not room_jid.resource 661 assert not room_jid.resource
662 assert 'affiliation' in options 662 assert 'affiliation' in options
663 # TODO: handles reason and nick 663 # TODO: handles reason and nick
664 return client._muc_client.modifyAffiliationList(room_jid, [entity_jid], options['affiliation']) 664 return client._muc_client.modifyAffiliationList(room_jid, [entity_jid], options['affiliation'])
684 @command (all): JID 684 @command (all): JID
685 - JID: room to join (on the same service if full jid is not specified) 685 - JID: room to join (on the same service if full jid is not specified)
686 """ 686 """
687 room_raw = mess_data["unparsed"].strip() 687 room_raw = mess_data["unparsed"].strip()
688 if room_raw: 688 if room_raw:
689 if self.isJoinedRoom(client, mess_data["to"]): 689 if self.is_joined_room(client, mess_data["to"]):
690 # we use the same service as the one from the room where the command has 690 # we use the same service as the one from the room where the command has
691 # been entered if full jid is not entered 691 # been entered if full jid is not entered
692 muc_service = mess_data["to"].host 692 muc_service = mess_data["to"].host
693 nick = self.getRoomNick(client, mess_data["to"]) or client.jid.user 693 nick = self.get_room_nick(client, mess_data["to"]) or client.jid.user
694 else: 694 else:
695 # the command has been entered in a one2one conversation, so we use 695 # the command has been entered in a one2one conversation, so we use
696 # our server MUC service as default service 696 # our server MUC service as default service
697 muc_service = client.muc_service or "" 697 muc_service = client.muc_service or ""
698 nick = client.jid.user 698 nick = client.jid.user
699 room_jid = self.text_cmds.getRoomJID(room_raw, muc_service) 699 room_jid = self.text_cmds.get_room_jid(room_raw, muc_service)
700 self.join(client, room_jid, nick, {}) 700 self.join(client, room_jid, nick, {})
701 701
702 return False 702 return False
703 703
704 def cmd_leave(self, client, mess_data): 704 def cmd_leave(self, client, mess_data):
707 @command (group): [ROOM_JID] 707 @command (group): [ROOM_JID]
708 - ROOM_JID: jid of the room to live (current room if not specified) 708 - ROOM_JID: jid of the room to live (current room if not specified)
709 """ 709 """
710 room_raw = mess_data["unparsed"].strip() 710 room_raw = mess_data["unparsed"].strip()
711 if room_raw: 711 if room_raw:
712 room = self.text_cmds.getRoomJID(room_raw, mess_data["to"].host) 712 room = self.text_cmds.get_room_jid(room_raw, mess_data["to"].host)
713 else: 713 else:
714 room = mess_data["to"] 714 room = mess_data["to"]
715 715
716 self.leave(client, room) 716 self.leave(client, room)
717 717
732 - ROOM_NICK: the nick of the person to kick 732 - ROOM_NICK: the nick of the person to kick
733 """ 733 """
734 options = mess_data["unparsed"].strip().split() 734 options = mess_data["unparsed"].strip().split()
735 try: 735 try:
736 nick = options[0] 736 nick = options[0]
737 assert self.isNickInRoom(client, mess_data["to"], nick) 737 assert self.is_nick_in_room(client, mess_data["to"], nick)
738 except (IndexError, AssertionError): 738 except (IndexError, AssertionError):
739 feedback = _("You must provide a member's nick to kick.") 739 feedback = _("You must provide a member's nick to kick.")
740 self.text_cmds.feedBack(client, feedback, mess_data) 740 self.text_cmds.feed_back(client, feedback, mess_data)
741 return False 741 return False
742 742
743 reason = ' '.join(options[1:]) if len(options) > 1 else None 743 reason = ' '.join(options[1:]) if len(options) > 1 else None
744 744
745 d = self.kick(client, nick, mess_data["to"], {"reason": reason}) 745 d = self.kick(client, nick, mess_data["to"], {"reason": reason})
748 feedback_msg = _('You have kicked {}').format(nick) 748 feedback_msg = _('You have kicked {}').format(nick)
749 if reason is not None: 749 if reason is not None:
750 feedback_msg += _(' for the following reason: {reason}').format( 750 feedback_msg += _(' for the following reason: {reason}').format(
751 reason=reason 751 reason=reason
752 ) 752 )
753 self.text_cmds.feedBack(client, feedback_msg, mess_data) 753 self.text_cmds.feed_back(client, feedback_msg, mess_data)
754 return True 754 return True
755 d.addCallback(cb) 755 d.addCallback(cb)
756 return d 756 return d
757 757
758 def cmd_ban(self, client, mess_data): 758 def cmd_ban(self, client, mess_data):
771 except (RuntimeError, jid.InvalidFormat, AttributeError, IndexError, 771 except (RuntimeError, jid.InvalidFormat, AttributeError, IndexError,
772 AssertionError): 772 AssertionError):
773 feedback = _( 773 feedback = _(
774 "You must provide a valid JID to ban, like in '/ban contact@example.net'" 774 "You must provide a valid JID to ban, like in '/ban contact@example.net'"
775 ) 775 )
776 self.text_cmds.feedBack(client, feedback, mess_data) 776 self.text_cmds.feed_back(client, feedback, mess_data)
777 return False 777 return False
778 778
779 reason = ' '.join(options[1:]) if len(options) > 1 else None 779 reason = ' '.join(options[1:]) if len(options) > 1 else None
780 780
781 d = self.ban(client, entity_jid, mess_data["to"], {"reason": reason}) 781 d = self.ban(client, entity_jid, mess_data["to"], {"reason": reason})
784 feedback_msg = _('You have banned {}').format(entity_jid) 784 feedback_msg = _('You have banned {}').format(entity_jid)
785 if reason is not None: 785 if reason is not None:
786 feedback_msg += _(' for the following reason: {reason}').format( 786 feedback_msg += _(' for the following reason: {reason}').format(
787 reason=reason 787 reason=reason
788 ) 788 )
789 self.text_cmds.feedBack(client, feedback_msg, mess_data) 789 self.text_cmds.feed_back(client, feedback_msg, mess_data)
790 return True 790 return True
791 d.addCallback(cb) 791 d.addCallback(cb)
792 return d 792 return d
793 793
794 def cmd_affiliate(self, client, mess_data): 794 def cmd_affiliate(self, client, mess_data):
808 entity_jid = jid.JID(jid_s).userhostJID() 808 entity_jid = jid.JID(jid_s).userhostJID()
809 assert(entity_jid.user) 809 assert(entity_jid.user)
810 assert(entity_jid.host) 810 assert(entity_jid.host)
811 except (RuntimeError, jid.InvalidFormat, AttributeError, IndexError, AssertionError): 811 except (RuntimeError, jid.InvalidFormat, AttributeError, IndexError, AssertionError):
812 feedback = _("You must provide a valid JID to affiliate, like in '/affiliate contact@example.net member'") 812 feedback = _("You must provide a valid JID to affiliate, like in '/affiliate contact@example.net member'")
813 self.text_cmds.feedBack(client, feedback, mess_data) 813 self.text_cmds.feed_back(client, feedback, mess_data)
814 return False 814 return False
815 815
816 affiliation = options[1] if len(options) > 1 else 'none' 816 affiliation = options[1] if len(options) > 1 else 'none'
817 if affiliation not in AFFILIATIONS: 817 if affiliation not in AFFILIATIONS:
818 feedback = _("You must provide a valid affiliation: %s") % ' '.join(AFFILIATIONS) 818 feedback = _("You must provide a valid affiliation: %s") % ' '.join(AFFILIATIONS)
819 self.text_cmds.feedBack(client, feedback, mess_data) 819 self.text_cmds.feed_back(client, feedback, mess_data)
820 return False 820 return False
821 821
822 d = self.affiliate(client, entity_jid, mess_data["to"], {'affiliation': affiliation}) 822 d = self.affiliate(client, entity_jid, mess_data["to"], {'affiliation': affiliation})
823 823
824 def cb(__): 824 def cb(__):
825 feedback_msg = _('New affiliation for {entity}: {affiliation}').format( 825 feedback_msg = _('New affiliation for {entity}: {affiliation}').format(
826 entity=entity_jid, affiliation=affiliation) 826 entity=entity_jid, affiliation=affiliation)
827 self.text_cmds.feedBack(client, feedback_msg, mess_data) 827 self.text_cmds.feed_back(client, feedback_msg, mess_data)
828 return True 828 return True
829 d.addCallback(cb) 829 d.addCallback(cb)
830 return d 830 return d
831 831
832 def cmd_title(self, client, mess_data): 832 def cmd_title(self, client, mess_data):
869 elif client.muc_service is not None: 869 elif client.muc_service is not None:
870 service = client.muc_service 870 service = client.muc_service
871 else: 871 else:
872 msg = D_("No known default MUC service {unparsed}").format( 872 msg = D_("No known default MUC service {unparsed}").format(
873 unparsed=unparsed) 873 unparsed=unparsed)
874 self.text_cmds.feedBack(client, msg, mess_data) 874 self.text_cmds.feed_back(client, msg, mess_data)
875 return False 875 return False
876 except jid.InvalidFormat: 876 except jid.InvalidFormat:
877 msg = D_("{} is not a valid JID!".format(unparsed)) 877 msg = D_("{} is not a valid JID!".format(unparsed))
878 self.text_cmds.feedBack(client, msg, mess_data) 878 self.text_cmds.feed_back(client, msg, mess_data)
879 return False 879 return False
880 d = self.host.getDiscoItems(client, service) 880 d = self.host.getDiscoItems(client, service)
881 d.addCallback(self._showListUI, client, service) 881 d.addCallback(self._show_list_ui, client, service)
882 882
883 return False 883 return False
884 884
885 def _whois(self, client, whois_msg, mess_data, target_jid): 885 def _whois(self, client, whois_msg, mess_data, target_jid):
886 """ Add MUC user information to whois """ 886 """ Add MUC user information to whois """
902 if user.status: 902 if user.status:
903 whois_msg.append(_("Status: %s") % user.status) 903 whois_msg.append(_("Status: %s") % user.status)
904 if user.show: 904 if user.show:
905 whois_msg.append(_("Show: %s") % user.show) 905 whois_msg.append(_("Show: %s") % user.show)
906 906
907 def presenceTrigger(self, presence_elt, client): 907 def presence_trigger(self, presence_elt, client):
908 # FIXME: should we add a privacy parameters in settings to activate before 908 # FIXME: should we add a privacy parameters in settings to activate before
909 # broadcasting the presence to all MUC rooms ? 909 # broadcasting the presence to all MUC rooms ?
910 muc_client = client._muc_client 910 muc_client = client._muc_client
911 for room_jid, room in muc_client.joined_rooms.items(): 911 for room_jid, room in muc_client.joined_rooms.items():
912 elt = xml_tools.elementCopy(presence_elt) 912 elt = xml_tools.element_copy(presence_elt)
913 elt['to'] = room_jid.userhost() + '/' + room.nick 913 elt['to'] = room_jid.userhost() + '/' + room.nick
914 client.presence.send(elt) 914 client.presence.send(elt)
915 return True 915 return True
916 916
917 def presenceReceivedTrigger(self, client, entity, show, priority, statuses): 917 def presence_received_trigger(self, client, entity, show, priority, statuses):
918 entity_bare = entity.userhostJID() 918 entity_bare = entity.userhostJID()
919 muc_client = client._muc_client 919 muc_client = client._muc_client
920 if entity_bare in muc_client.joined_rooms: 920 if entity_bare in muc_client.joined_rooms:
921 # presence is already handled in (un)availableReceived 921 # presence is already handled in (un)availableReceived
922 return False 922 return False
951 951
952 @property 952 @property
953 def _si(self): 953 def _si(self):
954 return self.plugin_parent._si 954 return self.plugin_parent._si
955 955
956 def changeRoomState(self, room, new_state): 956 def change_room_state(self, room, new_state):
957 """Check that room is in expected state, and change it 957 """Check that room is in expected state, and change it
958 958
959 @param new_state: one of ROOM_STATE_* 959 @param new_state: one of ROOM_STATE_*
960 """ 960 """
961 new_state_idx = ROOM_STATES.index(new_state) 961 new_state_idx = ROOM_STATES.index(new_state)
993 room_jid: jid.JID, 993 room_jid: jid.JID,
994 nick: str, 994 nick: str,
995 password: Optional[str] 995 password: Optional[str]
996 ) -> muc.Room: 996 ) -> muc.Room:
997 """Join room an retrieve history with legacy method""" 997 """Join room an retrieve history with legacy method"""
998 mess_data_list = await self.host.memory.historyGet( 998 mess_data_list = await self.host.memory.history_get(
999 room_jid, 999 room_jid,
1000 client.jid.userhostJID(), 1000 client.jid.userhostJID(),
1001 limit=1, 1001 limit=1,
1002 between=True, 1002 between=True,
1003 profile=client.profile 1003 profile=client.profile
1016 room._history_type = HISTORY_LEGACY 1016 room._history_type = HISTORY_LEGACY
1017 room._history_d = defer.Deferred() 1017 room._history_d = defer.Deferred()
1018 room._history_d.callback(None) 1018 room._history_d.callback(None)
1019 return room 1019 return room
1020 1020
1021 async def _get_MAM_history( 1021 async def _get_mam_history(
1022 self, 1022 self,
1023 client: SatXMPPEntity, 1023 client: SatXMPPEntity,
1024 room: muc.Room, 1024 room: muc.Room,
1025 room_jid: jid.JID 1025 room_jid: jid.JID
1026 ) -> None: 1026 ) -> None:
1028 history_d = room._history_d = defer.Deferred() 1028 history_d = room._history_d = defer.Deferred()
1029 # we trigger now the deferred so all callback are processed as soon as possible 1029 # we trigger now the deferred so all callback are processed as soon as possible
1030 # and in order 1030 # and in order
1031 history_d.callback(None) 1031 history_d.callback(None)
1032 1032
1033 last_mess = await self.host.memory.historyGet( 1033 last_mess = await self.host.memory.history_get(
1034 room_jid, 1034 room_jid,
1035 None, 1035 None,
1036 limit=1, 1036 limit=1,
1037 between=False, 1037 between=False,
1038 filters={ 1038 filters={
1054 mam_req = mam.MAMRequest(rsm_=rsm_req) 1054 mam_req = mam.MAMRequest(rsm_=rsm_req)
1055 complete = False 1055 complete = False
1056 count = 0 1056 count = 0
1057 while not complete: 1057 while not complete:
1058 try: 1058 try:
1059 mam_data = await self._mam.getArchives(client, mam_req, 1059 mam_data = await self._mam.get_archives(client, mam_req,
1060 service=room_jid) 1060 service=room_jid)
1061 except xmpp_error.StanzaError as e: 1061 except xmpp_error.StanzaError as e:
1062 if last_mess and e.condition == 'item-not-found': 1062 if last_mess and e.condition == 'item-not-found':
1063 log.info( 1063 log.info(
1064 f"requested item (with id {stanza_id!r}) can't be found in " 1064 f"requested item (with id {stanza_id!r}) can't be found in "
1081 else: 1081 else:
1082 count += len(elt_list) 1082 count += len(elt_list)
1083 1083
1084 for mess_elt in elt_list: 1084 for mess_elt in elt_list:
1085 try: 1085 try:
1086 fwd_message_elt = self._mam.getMessageFromResult( 1086 fwd_message_elt = self._mam.get_message_from_result(
1087 client, mess_elt, mam_req, service=room_jid) 1087 client, mess_elt, mam_req, service=room_jid)
1088 except exceptions.DataError: 1088 except exceptions.DataError:
1089 continue 1089 continue
1090 if fwd_message_elt.getAttribute("to"): 1090 if fwd_message_elt.getAttribute("to"):
1091 log.warning( 1091 log.warning(
1092 'Forwarded message element has a "to" attribute while it is ' 1092 'Forwarded message element has a "to" attribute while it is '
1093 'forbidden by specifications') 1093 'forbidden by specifications')
1094 fwd_message_elt["to"] = client.jid.full() 1094 fwd_message_elt["to"] = client.jid.full()
1095 try: 1095 try:
1096 mess_data = client.messageProt.parseMessage(fwd_message_elt) 1096 mess_data = client.messageProt.parse_message(fwd_message_elt)
1097 except Exception as e: 1097 except Exception as e:
1098 log.error( 1098 log.error(
1099 f"Can't parse message, ignoring it: {e}\n" 1099 f"Can't parse message, ignoring it: {e}\n"
1100 f"{fwd_message_elt.toXml()}" 1100 f"{fwd_message_elt.toXml()}"
1101 ) 1101 )
1102 continue 1102 continue
1103 # we attache parsed message data to element, to avoid parsing 1103 # we attache parsed message data to element, to avoid parsing
1104 # again in _addToHistory 1104 # again in _add_to_history
1105 fwd_message_elt._mess_data = mess_data 1105 fwd_message_elt._mess_data = mess_data
1106 # and we inject to MUC workflow 1106 # and we inject to MUC workflow
1107 client._muc_client._onGroupChat(fwd_message_elt) 1107 client._muc_client._onGroupChat(fwd_message_elt)
1108 1108
1109 if not count: 1109 if not count:
1116 .format(num_mess=count, room_jid=room_jid)) 1116 .format(num_mess=count, room_jid=room_jid))
1117 1117
1118 # for legacy history, the following steps are done in receivedSubject but for MAM 1118 # for legacy history, the following steps are done in receivedSubject but for MAM
1119 # the order is different (we have to join then get MAM archive, so subject 1119 # the order is different (we have to join then get MAM archive, so subject
1120 # is received before archive), so we change state and add the callbacks here. 1120 # is received before archive), so we change state and add the callbacks here.
1121 self.changeRoomState(room, ROOM_STATE_LIVE) 1121 self.change_room_state(room, ROOM_STATE_LIVE)
1122 history_d.addCallbacks(self._historyCb, self._historyEb, [room], 1122 history_d.addCallbacks(self._history_cb, self._history_eb, [room],
1123 errbackArgs=[room]) 1123 errbackArgs=[room])
1124 1124
1125 # we wait for all callbacks to be processed 1125 # we wait for all callbacks to be processed
1126 await history_d 1126 await history_d
1127 1127
1128 async def _join_MAM( 1128 async def _join_mam(
1129 self, 1129 self,
1130 client: SatXMPPEntity, 1130 client: SatXMPPEntity,
1131 room_jid: jid.JID, 1131 room_jid: jid.JID,
1132 nick: str, 1132 nick: str,
1133 password: Optional[str] 1133 password: Optional[str]
1138 room_jid, nick, muc.HistoryOptions(maxStanzas=0), password=password 1138 room_jid, nick, muc.HistoryOptions(maxStanzas=0), password=password
1139 ) 1139 )
1140 room._history_type = HISTORY_MAM 1140 room._history_type = HISTORY_MAM
1141 # MAM history retrieval can be very long, and doesn't need to be sync, so we don't 1141 # MAM history retrieval can be very long, and doesn't need to be sync, so we don't
1142 # wait for it 1142 # wait for it
1143 defer.ensureDeferred(self._get_MAM_history(client, room, room_jid)) 1143 defer.ensureDeferred(self._get_mam_history(client, room, room_jid))
1144 room.fully_joined.callback(room) 1144 room.fully_joined.callback(room)
1145 1145
1146 return room 1146 return room
1147 1147
1148 async def join(self, room_jid, nick, password=None): 1148 async def join(self, room_jid, nick, password=None):
1149 room_service = jid.JID(room_jid.host) 1149 room_service = jid.JID(room_jid.host)
1150 has_mam = await self.host.hasFeature(self.client, mam.NS_MAM, room_service) 1150 has_mam = await self.host.hasFeature(self.client, mam.NS_MAM, room_service)
1151 if not self._mam or not has_mam: 1151 if not self._mam or not has_mam:
1152 return await self._join_legacy(self.client, room_jid, nick, password) 1152 return await self._join_legacy(self.client, room_jid, nick, password)
1153 else: 1153 else:
1154 return await self._join_MAM(self.client, room_jid, nick, password) 1154 return await self._join_mam(self.client, room_jid, nick, password)
1155 1155
1156 ## presence/roster ## 1156 ## presence/roster ##
1157 1157
1158 def availableReceived(self, presence): 1158 def availableReceived(self, presence):
1159 """ 1159 """
1211 1211
1212 room.removeUser(user) 1212 room.removeUser(user)
1213 1213
1214 if muc.STATUS_CODE.NEW_NICK in presence.mucStatuses: 1214 if muc.STATUS_CODE.NEW_NICK in presence.mucStatuses:
1215 self._changing_nicks.add(presence.nick) 1215 self._changing_nicks.add(presence.nick)
1216 self.userChangedNick(room, user, presence.nick) 1216 self.user_changed_nick(room, user, presence.nick)
1217 else: 1217 else:
1218 self._changing_nicks.discard(presence.nick) 1218 self._changing_nicks.discard(presence.nick)
1219 self.userLeftRoom(room, user) 1219 self.userLeftRoom(room, user)
1220 1220
1221 def userJoinedRoom(self, room, user): 1221 def userJoinedRoom(self, room, user):
1222 if user.nick == room.nick: 1222 if user.nick == room.nick:
1223 # we have received our own nick, 1223 # we have received our own nick,
1224 # this mean that the full room roster was received 1224 # this mean that the full room roster was received
1225 self.changeRoomState(room, ROOM_STATE_SELF_PRESENCE) 1225 self.change_room_state(room, ROOM_STATE_SELF_PRESENCE)
1226 log.debug("room {room} joined with nick {nick}".format( 1226 log.debug("room {room} joined with nick {nick}".format(
1227 room=room.occupantJID.userhost(), nick=user.nick)) 1227 room=room.occupantJID.userhost(), nick=user.nick))
1228 # we set type so we don't have to use a deferred 1228 # we set type so we don't have to use a deferred
1229 # with disco to check entity type 1229 # with disco to check entity type
1230 self.host.memory.updateEntityData( 1230 self.host.memory.update_entity_data(
1231 self.client, room.roomJID, C.ENTITY_TYPE, C.ENTITY_TYPE_MUC 1231 self.client, room.roomJID, C.ENTITY_TYPE, C.ENTITY_TYPE_MUC
1232 ) 1232 )
1233 elif room.state not in (ROOM_STATE_OCCUPANTS, ROOM_STATE_LIVE): 1233 elif room.state not in (ROOM_STATE_OCCUPANTS, ROOM_STATE_LIVE):
1234 log.warning( 1234 log.warning(
1235 "Received user presence data in a room before its initialisation " 1235 "Received user presence data in a room before its initialisation "
1270 "timestamp": time.time(), 1270 "timestamp": time.time(),
1271 } 1271 }
1272 # FIXME: we disable presence in history as it's taking a lot of space 1272 # FIXME: we disable presence in history as it's taking a lot of space
1273 # while not indispensable. In the future an option may allow 1273 # while not indispensable. In the future an option may allow
1274 # to re-enable it 1274 # to re-enable it
1275 # self.client.messageAddToHistory(mess_data) 1275 # self.client.message_add_to_history(mess_data)
1276 self.client.messageSendToBridge(mess_data) 1276 self.client.message_send_to_bridge(mess_data)
1277 1277
1278 1278
1279 def userLeftRoom(self, room, user): 1279 def userLeftRoom(self, room, user):
1280 if not self.host.trigger.point("MUC user left", room, user, self.client.profile): 1280 if not self.host.trigger.point("MUC user left", room, user, self.client.profile):
1281 return 1281 return
1282 if user.nick == room.nick: 1282 if user.nick == room.nick:
1283 # we left the room 1283 # we left the room
1284 room_jid_s = room.roomJID.userhost() 1284 room_jid_s = room.roomJID.userhost()
1285 log.info(_("Room ({room}) left ({profile})").format( 1285 log.info(_("Room ({room}) left ({profile})").format(
1286 room = room_jid_s, profile = self.client.profile)) 1286 room = room_jid_s, profile = self.client.profile))
1287 self.host.memory.delEntityCache(room.roomJID, profile_key=self.client.profile) 1287 self.host.memory.del_entity_cache(room.roomJID, profile_key=self.client.profile)
1288 self.host.bridge.mucRoomLeft(room.roomJID.userhost(), self.client.profile) 1288 self.host.bridge.muc_room_left(room.roomJID.userhost(), self.client.profile)
1289 elif room.state != ROOM_STATE_LIVE: 1289 elif room.state != ROOM_STATE_LIVE:
1290 log.warning("Received user presence data in a room before its initialisation (current state: {state})," 1290 log.warning("Received user presence data in a room before its initialisation (current state: {state}),"
1291 "this is not standard! Ignoring it: {room} ({nick})".format( 1291 "this is not standard! Ignoring it: {room} ({nick})".format(
1292 state=room.state, 1292 state=room.state,
1293 room=room.roomJID.userhost(), 1293 room=room.roomJID.userhost(),
1313 "type": C.MESS_TYPE_INFO, 1313 "type": C.MESS_TYPE_INFO,
1314 "extra": extra, 1314 "extra": extra,
1315 "timestamp": time.time(), 1315 "timestamp": time.time(),
1316 } 1316 }
1317 # FIXME: disable history, see userJoinRoom comment 1317 # FIXME: disable history, see userJoinRoom comment
1318 # self.client.messageAddToHistory(mess_data) 1318 # self.client.message_add_to_history(mess_data)
1319 self.client.messageSendToBridge(mess_data) 1319 self.client.message_send_to_bridge(mess_data)
1320 1320
1321 def userChangedNick(self, room, user, new_nick): 1321 def user_changed_nick(self, room, user, new_nick):
1322 self.host.bridge.mucRoomUserChangedNick(room.roomJID.userhost(), user.nick, new_nick, self.client.profile) 1322 self.host.bridge.muc_room_user_changed_nick(room.roomJID.userhost(), user.nick, new_nick, self.client.profile)
1323 1323
1324 def userUpdatedStatus(self, room, user, show, status): 1324 def userUpdatedStatus(self, room, user, show, status):
1325 entity = jid.JID(tuple=(room.roomJID.user, room.roomJID.host, user.nick)) 1325 entity = jid.JID(tuple=(room.roomJID.user, room.roomJID.host, user.nick))
1326 if hasattr(room, "_cache_presence"): 1326 if hasattr(room, "_cache_presence"):
1327 # room has a cache for presence, meaning it has not been fully 1327 # room has a cache for presence, meaning it has not been fully
1335 "show": show, 1335 "show": show,
1336 "status": status, 1336 "status": status,
1337 } 1337 }
1338 return 1338 return
1339 statuses = {C.PRESENCE_STATUSES_DEFAULT: status or ''} 1339 statuses = {C.PRESENCE_STATUSES_DEFAULT: status or ''}
1340 self.host.bridge.presenceUpdate( 1340 self.host.bridge.presence_update(
1341 entity.full(), show or '', 0, statuses, self.client.profile) 1341 entity.full(), show or '', 0, statuses, self.client.profile)
1342 1342
1343 ## messages ## 1343 ## messages ##
1344 1344
1345 def receivedGroupChat(self, room, user, body): 1345 def receivedGroupChat(self, room, user, body):
1346 log.debug('receivedGroupChat: room=%s user=%s body=%s' % (room.roomJID.full(), user, body)) 1346 log.debug('receivedGroupChat: room=%s user=%s body=%s' % (room.roomJID.full(), user, body))
1347 1347
1348 def _addToHistory(self, __, user, message): 1348 def _add_to_history(self, __, user, message):
1349 try: 1349 try:
1350 # message can be already parsed (with MAM), in this case mess_data 1350 # message can be already parsed (with MAM), in this case mess_data
1351 # it attached to the element 1351 # it attached to the element
1352 mess_data = message.element._mess_data 1352 mess_data = message.element._mess_data
1353 except AttributeError: 1353 except AttributeError:
1354 mess_data = self.client.messageProt.parseMessage(message.element) 1354 mess_data = self.client.messageProt.parse_message(message.element)
1355 if mess_data['message'] or mess_data['subject']: 1355 if mess_data['message'] or mess_data['subject']:
1356 return defer.ensureDeferred( 1356 return defer.ensureDeferred(
1357 self.host.memory.addToHistory(self.client, mess_data) 1357 self.host.memory.add_to_history(self.client, mess_data)
1358 ) 1358 )
1359 else: 1359 else:
1360 return defer.succeed(None) 1360 return defer.succeed(None)
1361 1361
1362 def _addToHistoryEb(self, failure): 1362 def _add_to_history_eb(self, failure):
1363 failure.trap(exceptions.CancelError) 1363 failure.trap(exceptions.CancelError)
1364 1364
1365 def receivedHistory(self, room, user, message): 1365 def receivedHistory(self, room, user, message):
1366 """Called when history (backlog) message are received 1366 """Called when history (backlog) message are received
1367 1367
1384 log.warning(_("storing the unexpected message anyway, to avoid loss")) 1384 log.warning(_("storing the unexpected message anyway, to avoid loss"))
1385 # we have to restore URI which are stripped by wokkel parsing 1385 # we have to restore URI which are stripped by wokkel parsing
1386 for c in message.element.elements(): 1386 for c in message.element.elements():
1387 if c.uri is None: 1387 if c.uri is None:
1388 c.uri = C.NS_CLIENT 1388 c.uri = C.NS_CLIENT
1389 mess_data = self.client.messageProt.parseMessage(message.element) 1389 mess_data = self.client.messageProt.parse_message(message.element)
1390 message.element._mess_data = mess_data 1390 message.element._mess_data = mess_data
1391 self._addToHistory(None, user, message) 1391 self._add_to_history(None, user, message)
1392 if mess_data['message'] or mess_data['subject']: 1392 if mess_data['message'] or mess_data['subject']:
1393 self.host.bridge.messageNew( 1393 self.host.bridge.message_new(
1394 *self.client.messageGetBridgeArgs(mess_data), 1394 *self.client.message_get_bridge_args(mess_data),
1395 profile=self.client.profile 1395 profile=self.client.profile
1396 ) 1396 )
1397 return 1397 return
1398 room._history_d.addCallback(self._addToHistory, user, message) 1398 room._history_d.addCallback(self._add_to_history, user, message)
1399 room._history_d.addErrback(self._addToHistoryEb) 1399 room._history_d.addErrback(self._add_to_history_eb)
1400 1400
1401 ## subject ## 1401 ## subject ##
1402 1402
1403 def groupChatReceived(self, message): 1403 def groupChatReceived(self, message):
1404 """ 1404 """
1424 self.receivedHistory(room, user, message) 1424 self.receivedHistory(room, user, message)
1425 1425
1426 def subject(self, room, subject): 1426 def subject(self, room, subject):
1427 return muc.MUCClientProtocol.subject(self, room, subject) 1427 return muc.MUCClientProtocol.subject(self, room, subject)
1428 1428
1429 def _historyCb(self, __, room): 1429 def _history_cb(self, __, room):
1430 """Called when history have been written to database and subject is received 1430 """Called when history have been written to database and subject is received
1431 1431
1432 this method will finish joining by: 1432 this method will finish joining by:
1433 - sending message to bridge 1433 - sending message to bridge
1434 - calling fully_joined deferred (for legacy history) 1434 - calling fully_joined deferred (for legacy history)
1435 - sending stanza put in cache 1435 - sending stanza put in cache
1436 - cleaning variables not needed anymore 1436 - cleaning variables not needed anymore
1437 """ 1437 """
1438 args = self.plugin_parent._getRoomJoinedArgs(room, self.client.profile) 1438 args = self.plugin_parent._get_room_joined_args(room, self.client.profile)
1439 self.host.bridge.mucRoomJoined(*args) 1439 self.host.bridge.muc_room_joined(*args)
1440 if room._history_type == HISTORY_LEGACY: 1440 if room._history_type == HISTORY_LEGACY:
1441 room.fully_joined.callback(room) 1441 room.fully_joined.callback(room)
1442 del room._history_d 1442 del room._history_d
1443 del room._history_type 1443 del room._history_type
1444 cache = room._cache 1444 cache = room._cache
1447 del room._cache_presence 1447 del room._cache_presence
1448 for elem in cache: 1448 for elem in cache:
1449 self.client.xmlstream.dispatch(elem) 1449 self.client.xmlstream.dispatch(elem)
1450 for presence_data in cache_presence.values(): 1450 for presence_data in cache_presence.values():
1451 if not presence_data['show'] and not presence_data['status']: 1451 if not presence_data['show'] and not presence_data['status']:
1452 # occupants are already sent in mucRoomJoined, so if we don't have 1452 # occupants are already sent in muc_room_joined, so if we don't have
1453 # extra information like show or statuses, we can discard the signal 1453 # extra information like show or statuses, we can discard the signal
1454 continue 1454 continue
1455 else: 1455 else:
1456 self.userUpdatedStatus(**presence_data) 1456 self.userUpdatedStatus(**presence_data)
1457 1457
1458 def _historyEb(self, failure_, room): 1458 def _history_eb(self, failure_, room):
1459 log.error("Error while managing history: {}".format(failure_)) 1459 log.error("Error while managing history: {}".format(failure_))
1460 self._historyCb(None, room) 1460 self._history_cb(None, room)
1461 1461
1462 def receivedSubject(self, room, user, subject): 1462 def receivedSubject(self, room, user, subject):
1463 # when subject is received, we know that we have whole roster and history 1463 # when subject is received, we know that we have whole roster and history
1464 # cf. http://xmpp.org/extensions/xep-0045.html#enter-subject 1464 # cf. http://xmpp.org/extensions/xep-0045.html#enter-subject
1465 room.subject = subject # FIXME: subject doesn't handle xml:lang 1465 room.subject = subject # FIXME: subject doesn't handle xml:lang
1466 if room.state != ROOM_STATE_LIVE: 1466 if room.state != ROOM_STATE_LIVE:
1467 if room._history_type == HISTORY_LEGACY: 1467 if room._history_type == HISTORY_LEGACY:
1468 self.changeRoomState(room, ROOM_STATE_LIVE) 1468 self.change_room_state(room, ROOM_STATE_LIVE)
1469 room._history_d.addCallbacks(self._historyCb, self._historyEb, [room], errbackArgs=[room]) 1469 room._history_d.addCallbacks(self._history_cb, self._history_eb, [room], errbackArgs=[room])
1470 else: 1470 else:
1471 # the subject has been changed 1471 # the subject has been changed
1472 log.debug(_("New subject for room ({room_id}): {subject}").format(room_id = room.roomJID.full(), subject = subject)) 1472 log.debug(_("New subject for room ({room_id}): {subject}").format(room_id = room.roomJID.full(), subject = subject))
1473 self.host.bridge.mucRoomNewSubject(room.roomJID.userhost(), subject, self.client.profile) 1473 self.host.bridge.muc_room_new_subject(room.roomJID.userhost(), subject, self.client.profile)
1474 1474
1475 ## disco ## 1475 ## disco ##
1476 1476
1477 def getDiscoInfo(self, requestor, target, nodeIdentifier=''): 1477 def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
1478 return [disco.DiscoFeature(NS_MUC)] 1478 return [disco.DiscoFeature(NS_MUC)]