Mercurial > libervia-backend
view src/plugins/plugin_xep_0045.py @ 260:c8406fe5e81e
Added SMTP server plugin, for sending messages from classic MUA \o/
- added subject managing in sendMessage
author | Goffi <goffi@goffi.org> |
---|---|
date | Tue, 18 Jan 2011 03:59:59 +0100 |
parents | b1794cbb88e5 |
children | 51085ed613c3 |
line wrap: on
line source
#!/usr/bin/python # -*- coding: utf-8 -*- """ SAT plugin for managing xep-0045 Copyright (C) 2009, 2010, 2011 Jérôme Poisson (goffi@goffi.org) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. """ from logging import debug, info, warning, error from twisted.words.xish import domish from twisted.internet import protocol, defer, threads, reactor from twisted.words.protocols.jabber import client, jid, xmlstream from twisted.words.protocols.jabber import error as jab_error from twisted.words.protocols.jabber.xmlstream import IQ import os.path import pdb from zope.interface import implements from wokkel import disco, iwokkel, muc from base64 import b64decode from hashlib import sha1 from time import sleep try: from twisted.words.protocols.xmlstream import XMPPHandler except ImportError: from wokkel.subprotocols import XMPPHandler AVATAR_PATH = "/avatars" IQ_GET = '/iq[@type="get"]' NS_VCARD = 'vcard-temp' VCARD_REQUEST = IQ_GET + '/vCard[@xmlns="' + NS_VCARD + '"]' #TODO: manage requests PRESENCE = '/presence' NS_VCARD_UPDATE = 'vcard-temp:x:update' VCARD_UPDATE = PRESENCE + '/x[@xmlns="' + NS_VCARD_UPDATE + '"]' PLUGIN_INFO = { "name": "XEP 0045 Plugin", "import_name": "XEP_0045", "type": "XEP", "protocols": ["XEP-0045"], "dependencies": [], "main": "XEP_0045", "handler": "yes", "description": _("""Implementation of Multi-User Chat""") } class XEP_0045(): def __init__(self, host): info(_("Plugin XEP_0045 initialization")) self.host = host self.clients={} host.bridge.addMethod("joinMUC", ".communication", in_sign='ssss', out_sign='', method=self.join) host.bridge.addMethod("getRoomJoined", ".communication", in_sign='s', out_sign='a(ssass)', method=self.getRoomJoined) host.bridge.addMethod("getRoomSubjects", ".communication", in_sign='s', out_sign='a(sss)', method=self.getRoomSubjects) host.bridge.addSignal("roomJoined", ".communication", signature='ssasss') #args: room_id, room_service, room_nicks, user_nick, profile host.bridge.addSignal("roomUserJoined", ".communication", signature='sssa{ss}s') #args: room_id, room_service, user_nick, user_data, profile host.bridge.addSignal("roomUserLeft", ".communication", signature='sssa{ss}s') #args: room_id, room_service, user_nick, user_data, profile host.bridge.addSignal("roomNewSubject", ".communication", signature='ssss') #args: room_id, room_service, subject, profile def __check_profile(self, profile): """check if profile is used and connected if profile known but disconnected, remove it from known profiles @param profile: profile to check @return: True if the profile is known and connected, else False""" if not profile or not self.clients.has_key(profile) or not self.host.isConnected(profile): error (_('Unknown or disconnected profile (%s)') % profile) if self.clients.has_key(profile): del self.clients[profile] return False return True def __room_joined(self, room, profile): """Called when the user is in the requested room""" room_jid = room.roomIdentifier+'@'+room.service self.clients[profile].joined_rooms[room_jid] = room self.host.bridge.roomJoined(room.roomIdentifier, room.service, [user.nick for user in room.roster.values()], room.nick, profile) def __err_joining_room(self, failure, profile): """Called when something is going wrong when joining the room""" mess = _("Error when joining the room") error (mess) self.host.bridge.newAlert(mess, _("Group chat error"), "ERROR", profile) def getRoomJoined(self, profile_key='@DEFAULT@'): """Return room where user is""" profile = self.host.memory.getProfileName(profile_key) result = [] if not self.__check_profile(profile): return result for room in self.clients[profile].joined_rooms.values(): result.append((room.roomIdentifier, room.service, [user.nick for user in room.roster.values()], room.nick)) return result def getRoomNick(self, room_jid, profile_key='@DEFAULT@'): """return nick used in room by user @param room_jid: unicode room id @profile_key: profile @return: nick or empty string in case of error""" profile = self.host.memory.getProfileName(profile_key) if not self.__check_profile(profile) or not self.clients[profile].joined_rooms.has_key(room_jid): return '' return self.clients[profile].joined_rooms[room_jid].nick def getRoomSubjects(self, profile_key='@DEFAULT@'): """Return received subjects of rooms""" profile = self.host.memory.getProfileName(profile_key) if not self.__check_profile(profile): return [] return self.clients[profile].rec_subjects.values() def join(self, service, roomId, nick, profile_key='@DEFAULT@'): profile = self.host.memory.getProfileName(profile_key) if not self.__check_profile(profile): return room_jid = roomId+'@'+service if self.clients[profile].joined_rooms.has_key(room_jid): warning(_('%(profile)s is already in room %(room_jid)s') % {'profile':profile, 'room_jid':room_jid}) return info (_("[%(profile)s] is joining room %(room)s with nick %(nick)s") % {'profile':profile,'room':roomId+'@'+service, 'nick':nick}) try: self.clients[profile].join(service, roomId, nick).addCallbacks(self.__room_joined, self.__err_joining_room, callbackKeywords={'profile':profile}, errbackKeywords={'profile':profile}) except: #XXX: this is a ugly workaround as MUCClient thrown an error if there is invalid chars in the room jid (like with the default string) #FIXME: must be removed when MUCClient manage this better self.__err_joining_room(None, profile) def getHandler(self, profile): #reactor.callLater(15,self.join,"conference.necton2.int", "test", "Goffi \o/", profile) self.clients[profile] = SatMUCClient(self) return self.clients[profile] class SatMUCClient (muc.MUCClient): #implements(iwokkel.IDisco) def __init__(self, plugin_parent): self.plugin_parent = plugin_parent self.host = plugin_parent.host muc.MUCClient.__init__(self) self.joined_rooms = {} self.rec_subjects = {} print "init SatMUCClient OK" def receivedGroupChat(self, room, user, body): debug('receivedGroupChat: room=%s user=%s body=%s', room, user, body) def userJoinedRoom(self, room, user): debug (_("user %(nick)s has joined room (%(room_id)s)") % {'nick':user.nick, 'room_id':room.occupantJID.userhost()}) user_data={'entity':user.entity or '', 'affiliation':user.affiliation, 'role':user.role} self.host.bridge.roomUserJoined(room.roomIdentifier, room.service, user.nick, user_data, self.parent.profile) def userLeftRoom(self, room, user): debug (_("user %(nick)s left room (%(room_id)s)") % {'nick':user.nick, 'room_id':room.occupantJID.userhost()}) user_data={'entity':user.entity or '', 'affiliation':user.affiliation, 'role':user.role} self.host.bridge.roomUserLeft(room.roomIdentifier, room.service, user.nick, user_data, self.parent.profile) def userUpdatedStatus(self, room, user, show, status): print("FIXME: MUC status not managed yet") #FIXME: gof def receivedSubject(self, occupantJID, subject): room = self._getRoom(occupantJID) debug (_("New subject for room (%(room_id)s): %(subject)s") % {'room_id':room.occupantJID.userhost(),'subject':subject}) room_jid = room.roomIdentifier+'@'+room.service self.rec_subjects[room_jid] = (room.roomIdentifier, room.service, subject) self.host.bridge.roomNewSubject(room.roomIdentifier, room.service, subject, self.parent.profile) #def connectionInitialized(self): #pass #def getDiscoInfo(self, requestor, target, nodeIdentifier=''): #return [disco.DiscoFeature(NS_VCARD)] #def getDiscoItems(self, requestor, target, nodeIdentifier=''): #return []