comparison src/plugins/plugin_xep_0045.py @ 223:86d249b6d9b7

Files reorganisation
author Goffi <goffi@goffi.org>
date Wed, 29 Dec 2010 01:06:29 +0100
parents plugins/plugin_xep_0045.py@c5274bf5e18b
children b1794cbb88e5
comparison
equal deleted inserted replaced
222:3198bfd66daa 223:86d249b6d9b7
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3
4 """
5 SAT plugin for managing xep-0045
6 Copyright (C) 2009, 2010 Jérôme Poisson (goffi@goffi.org)
7
8 This program is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 """
21
22 from logging import debug, info, warning, error
23 from twisted.words.xish import domish
24 from twisted.internet import protocol, defer, threads, reactor
25 from twisted.words.protocols.jabber import client, jid, xmlstream
26 from twisted.words.protocols.jabber import error as jab_error
27 from twisted.words.protocols.jabber.xmlstream import IQ
28 import os.path
29 import pdb
30
31 from zope.interface import implements
32
33 from wokkel import disco, iwokkel, muc
34
35 from base64 import b64decode
36 from hashlib import sha1
37 from time import sleep
38
39 try:
40 from twisted.words.protocols.xmlstream import XMPPHandler
41 except ImportError:
42 from wokkel.subprotocols import XMPPHandler
43
44 AVATAR_PATH = "/avatars"
45
46 IQ_GET = '/iq[@type="get"]'
47 NS_VCARD = 'vcard-temp'
48 VCARD_REQUEST = IQ_GET + '/vCard[@xmlns="' + NS_VCARD + '"]' #TODO: manage requests
49
50 PRESENCE = '/presence'
51 NS_VCARD_UPDATE = 'vcard-temp:x:update'
52 VCARD_UPDATE = PRESENCE + '/x[@xmlns="' + NS_VCARD_UPDATE + '"]'
53
54 PLUGIN_INFO = {
55 "name": "XEP 0045 Plugin",
56 "import_name": "XEP_0045",
57 "type": "XEP",
58 "protocols": ["XEP-0045"],
59 "dependencies": [],
60 "main": "XEP_0045",
61 "handler": "yes",
62 "description": _("""Implementation of Multi-User Chat""")
63 }
64
65 class XEP_0045():
66
67 def __init__(self, host):
68 info(_("Plugin XEP_0045 initialization"))
69 self.host = host
70 self.clients={}
71 host.bridge.addMethod("joinMUC", ".communication", in_sign='ssss', out_sign='', method=self.join)
72 host.bridge.addMethod("getRoomJoined", ".communication", in_sign='s', out_sign='a(ssass)', method=self.getRoomJoined)
73 host.bridge.addMethod("getRoomSubjects", ".communication", in_sign='s', out_sign='a(sss)', method=self.getRoomSubjects)
74 host.bridge.addSignal("roomJoined", ".communication", signature='ssasss') #args: room_id, room_service, room_nicks, user_nick, profile
75 host.bridge.addSignal("roomUserJoined", ".communication", signature='sssa{ss}s') #args: room_id, room_service, user_nick, user_data, profile
76 host.bridge.addSignal("roomUserLeft", ".communication", signature='sssa{ss}s') #args: room_id, room_service, user_nick, user_data, profile
77 host.bridge.addSignal("roomNewSubject", ".communication", signature='ssss') #args: room_id, room_service, subject, profile
78
79 def __check_profile(self, profile):
80 """check if profile is used and connected
81 if profile known but disconnected, remove it from known profiles
82 @param profile: profile to check
83 @return: True if the profile is known and connected, else False"""
84 if not profile or not self.clients.has_key(profile) or not self.host.isConnected(profile):
85 error (_('Unknown or disconnected profile (%s)') % profile)
86 if self.clients.has_key(profile):
87 del self.clients[profile]
88 return False
89 return True
90
91 def __room_joined(self, room, profile):
92 """Called when the user is in the requested room"""
93 room_jid = room.roomIdentifier+'@'+room.service
94 self.clients[profile].joined_rooms[room_jid] = room
95 self.host.bridge.roomJoined(room.roomIdentifier, room.service, [user.nick for user in room.roster.values()], room.nick, profile)
96
97 def __err_joining_room(self, failure, profile):
98 """Called when something is going wrong when joining the room"""
99 mess = _("Error when joining the room")
100 error (mess)
101 self.host.bridge.newAlert(mess, _("Group chat error"), "ERROR", profile)
102
103 def getRoomJoined(self, profile_key='@DEFAULT@'):
104 """Return room where user is"""
105 profile = self.host.memory.getProfileName(profile_key)
106 result = []
107 if not self.__check_profile(profile):
108 return result
109 for room in self.clients[profile].joined_rooms.values():
110 result.append((room.roomIdentifier, room.service, [user.nick for user in room.roster.values()], room.nick))
111 return result
112
113 def getRoomNick(self, room_jid, profile_key='@DEFAULT@'):
114 """return nick used in room by user
115 @param room_jid: unicode room id
116 @profile_key: profile
117 @return: nick or empty string in case of error"""
118 profile = self.host.memory.getProfileName(profile_key)
119 if not self.__check_profile(profile) or not self.clients[profile].joined_rooms.has_key(room_jid):
120 return ''
121 return self.clients[profile].joined_rooms[room_jid].nick
122
123
124 def getRoomSubjects(self, profile_key='@DEFAULT@'):
125 """Return received subjects of rooms"""
126 profile = self.host.memory.getProfileName(profile_key)
127 if not self.__check_profile(profile):
128 return []
129 return self.clients[profile].rec_subjects.values()
130
131 def join(self, service, roomId, nick, profile_key='@DEFAULT@'):
132 profile = self.host.memory.getProfileName(profile_key)
133 if not self.__check_profile(profile):
134 return
135 room_jid = roomId+'@'+service
136 if self.clients[profile].joined_rooms.has_key(room_jid):
137 warning(_('%(profile)s is already in room %(room_jid)s') % {'profile':profile, 'room_jid':room_jid})
138 return
139 info (_("[%(profile)s] is joining room %(room)s with nick %(nick)s") % {'profile':profile,'room':roomId+'@'+service, 'nick':nick})
140 try:
141 self.clients[profile].join(service, roomId, nick).addCallbacks(self.__room_joined, self.__err_joining_room, callbackKeywords={'profile':profile}, errbackKeywords={'profile':profile})
142 except:
143 #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)
144 #FIXME: must be removed when MUCClient manage this better
145 self.__err_joining_room(None, profile)
146
147 def getHandler(self, profile):
148 #reactor.callLater(15,self.join,"conference.necton2.int", "test", "Goffi \o/", profile)
149 self.clients[profile] = SatMUCClient(self)
150 return self.clients[profile]
151
152
153
154 class SatMUCClient (muc.MUCClient):
155 #implements(iwokkel.IDisco)
156
157 def __init__(self, plugin_parent):
158 self.plugin_parent = plugin_parent
159 self.host = plugin_parent.host
160 muc.MUCClient.__init__(self)
161 self.joined_rooms = {}
162 self.rec_subjects = {}
163 print "init SatMUCClient OK"
164
165 def receivedGroupChat(self, room, user, body):
166 debug('receivedGroupChat: room=%s user=%s body=%s', room, user, body)
167
168 def userJoinedRoom(self, room, user):
169 debug (_("user %(nick)s has joined room (%(room_id)s)") % {'nick':user.nick, 'room_id':room.occupantJID.userhost()})
170 user_data={'entity':user.entity or '', 'affiliation':user.affiliation, 'role':user.role}
171 self.host.bridge.roomUserJoined(room.roomIdentifier, room.service, user.nick, user_data, self.parent.profile)
172
173 def userLeftRoom(self, room, user):
174 debug (_("user %(nick)s left room (%(room_id)s)") % {'nick':user.nick, 'room_id':room.occupantJID.userhost()})
175 user_data={'entity':user.entity or '', 'affiliation':user.affiliation, 'role':user.role}
176 self.host.bridge.roomUserLeft(room.roomIdentifier, room.service, user.nick, user_data, self.parent.profile)
177
178 def userUpdatedStatus(self, room, user, show, status):
179 print("FIXME: MUC status not managed yet")
180 #FIXME: gof
181
182 def receivedSubject(self, occupantJID, subject):
183 room = self._getRoom(occupantJID)
184 debug (_("New subject for room (%(room_id)s): %(subject)s") % {'room_id':room.occupantJID.userhost(),'subject':subject})
185 room_jid = room.roomIdentifier+'@'+room.service
186 self.rec_subjects[room_jid] = (room.roomIdentifier, room.service, subject)
187 self.host.bridge.roomNewSubject(room.roomIdentifier, room.service, subject, self.parent.profile)
188
189 #def connectionInitialized(self):
190 #pass
191
192 #def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
193 #return [disco.DiscoFeature(NS_VCARD)]
194
195 #def getDiscoItems(self, requestor, target, nodeIdentifier=''):
196 #return []
197