Mercurial > libervia-backend
comparison src/sat.tac @ 330:608a4a2ba94e
Core: created a new core module where xmpp classes are put
author | Goffi <goffi@goffi.org> |
---|---|
date | Mon, 23 May 2011 21:18:58 +0200 |
parents | b069055320b1 |
children | 0a8eb0461f31 |
comparison
equal
deleted
inserted
replaced
329:be9f682c53a5 | 330:608a4a2ba94e |
---|---|
46 from logging import debug, info, error | 46 from logging import debug, info, error |
47 | 47 |
48 import signal, sys | 48 import signal, sys |
49 import os.path | 49 import os.path |
50 | 50 |
51 from sat.core.xmpp import SatXMPPClient, SatMessageProtocol, SatRosterProtocol, SatPresenceProtocol, SatDiscoProtocol, SatFallbackHandler, RegisteringAuthenticator, SatVersionHandler | |
51 from sat.tools.memory import Memory | 52 from sat.tools.memory import Memory |
52 from sat.tools.xml_tools import tupleList2dataForm | 53 from sat.tools.xml_tools import tupleList2dataForm |
53 from sat.tools.misc import TriggerManager | 54 from sat.tools.misc import TriggerManager |
54 from glob import glob | 55 from glob import glob |
55 | 56 |
69 | 70 |
70 def sat_next_id(): | 71 def sat_next_id(): |
71 global sat_id | 72 global sat_id |
72 sat_id+=1 | 73 sat_id+=1 |
73 return "sat_id_"+str(sat_id) | 74 return "sat_id_"+str(sat_id) |
74 | |
75 class SatXMPPClient(client.XMPPClient): | |
76 | |
77 def __init__(self, host_app, profile, user_jid, password, host=None, port=5222): | |
78 client.XMPPClient.__init__(self, user_jid, password, host, port) | |
79 self.factory.clientConnectionLost = self.connectionLost | |
80 self.__connected=False | |
81 self.profile = profile | |
82 self.host_app = host_app | |
83 self.client_initialized = defer.Deferred() | |
84 | |
85 def _authd(self, xmlstream): | |
86 if not self.host_app.trigger.point("XML Initialized", xmlstream, self.profile): | |
87 return | |
88 client.XMPPClient._authd(self, xmlstream) | |
89 self.__connected=True | |
90 info (_("********** [%s] CONNECTED **********") % self.profile) | |
91 self.streamInitialized() | |
92 self.host_app.bridge.connected(self.profile) #we send the signal to the clients | |
93 | |
94 | |
95 def streamInitialized(self): | |
96 """Called after _authd""" | |
97 debug (_("XML stream is initialized")) | |
98 self.keep_alife = task.LoopingCall(self.xmlstream.send, " ") #Needed to avoid disconnection (specially with openfire) | |
99 self.keep_alife.start(180) | |
100 | |
101 self.disco = SatDiscoProtocol(self) | |
102 self.disco.setHandlerParent(self) | |
103 self.discoHandler = disco.DiscoHandler() | |
104 self.discoHandler.setHandlerParent(self) | |
105 | |
106 if not self.host_app.trigger.point("Disco Handled", self.profile): | |
107 return | |
108 | |
109 self.roster.requestRoster() | |
110 | |
111 self.presence.available() | |
112 | |
113 self.disco.requestInfo(jid.JID(self.host_app.memory.getParamA("Server", "Connection", profile_key=self.profile))).addCallback(self.host_app.serverDisco, self.profile) #FIXME: use these informations | |
114 self.disco.requestItems(jid.JID(self.host_app.memory.getParamA("Server", "Connection", profile_key=self.profile))).addCallback(self.host_app.serverDiscoItems, self.disco, self.profile, self.client_initialized) | |
115 | |
116 def initializationFailed(self, reason): | |
117 print ("initializationFailed: %s" % reason) | |
118 self.host_app.bridge.connectionError("AUTH_ERROR", self.profile) | |
119 try: | |
120 client.XMPPClient.initializationFailed(self, reason) | |
121 except: | |
122 #we already send an error signal, no need to raise an exception | |
123 pass | |
124 | |
125 def isConnected(self): | |
126 return self.__connected | |
127 | |
128 def connectionLost(self, connector, unused_reason): | |
129 self.__connected=False | |
130 info (_("********** [%s] DISCONNECTED **********") % self.profile) | |
131 try: | |
132 self.keep_alife.stop() | |
133 except AttributeError: | |
134 debug (_("No keep_alife")) | |
135 self.host_app.bridge.disconnected(self.profile) #we send the signal to the clients | |
136 | |
137 | |
138 class SatMessageProtocol(xmppim.MessageProtocol): | |
139 | |
140 def __init__(self, host): | |
141 xmppim.MessageProtocol.__init__(self) | |
142 self.host = host | |
143 | |
144 def onMessage(self, message): | |
145 debug (_(u"got message from: %s"), message["from"]) | |
146 if not self.host.trigger.point("MessageReceived",message, profile=self.parent.profile): | |
147 return | |
148 for e in message.elements(): | |
149 if e.name == "body": | |
150 mess_type = message['type'] if message.hasAttribute('type') else 'normal' | |
151 self.host.bridge.newMessage(message["from"], e.children[0], mess_type, message['to'], profile=self.parent.profile) | |
152 self.host.memory.addToHistory(self.parent.jid, jid.JID(message["from"]), self.parent.jid, "chat", e.children[0]) | |
153 break | |
154 | |
155 class SatRosterProtocol(xmppim.RosterClientProtocol): | |
156 | |
157 def __init__(self, host): | |
158 xmppim.RosterClientProtocol.__init__(self) | |
159 self.host = host | |
160 self._groups=set() | |
161 | |
162 def rosterCb(self, roster): | |
163 for raw_jid, item in roster.iteritems(): | |
164 self.onRosterSet(item) | |
165 | |
166 def requestRoster(self): | |
167 """ ask the server for Roster list """ | |
168 debug("requestRoster") | |
169 self.getRoster().addCallback(self.rosterCb) | |
170 | |
171 def removeItem(self, to): | |
172 """Remove a contact from roster list""" | |
173 xmppim.RosterClientProtocol.removeItem(self, to) | |
174 #TODO: check IQ result | |
175 | |
176 #XXX: disabled (cf http://wokkel.ik.nu/ticket/56)) | |
177 #def addItem(self, to): | |
178 #"""Add a contact to roster list""" | |
179 #xmppim.RosterClientProtocol.addItem(self, to) | |
180 #TODO: check IQ result""" | |
181 | |
182 def onRosterSet(self, item): | |
183 """Called when a new/update roster item is received""" | |
184 #TODO: send a signal to frontends | |
185 item_attr = {'to': str(item.subscriptionTo), | |
186 'from': str(item.subscriptionFrom), | |
187 'ask': str(item.ask) | |
188 } | |
189 if item.name: | |
190 item_attr['name'] = item.name | |
191 info (_("new contact in roster list: %s"), item.jid.full()) | |
192 self.host.memory.addContact(item.jid, item_attr, item.groups, self.parent.profile) | |
193 self.host.bridge.newContact(item.jid.full(), item_attr, item.groups, self.parent.profile) | |
194 self._groups.update(item.groups) | |
195 | |
196 def onRosterRemove(self, entity): | |
197 """Called when a roster removal event is received""" | |
198 #TODO: send a signal to frontends | |
199 print _("removing %s from roster list") % entity.full() | |
200 self.host.memory.delContact(entity, self.parent.profile) | |
201 | |
202 def getGroups(self): | |
203 """Return a set of groups""" | |
204 return self._groups | |
205 | |
206 class SatPresenceProtocol(xmppim.PresenceClientProtocol): | |
207 | |
208 def __init__(self, host): | |
209 xmppim.PresenceClientProtocol.__init__(self) | |
210 self.host = host | |
211 | |
212 def availableReceived(self, entity, show=None, statuses=None, priority=0): | |
213 debug (_("presence update for [%(entity)s] (available, show=%(show)s statuses=%(statuses)s priority=%(priority)d)") % {'entity':entity, 'show':show, 'statuses':statuses, 'priority':priority}) | |
214 | |
215 if statuses.has_key(None): #we only want string keys | |
216 statuses["default"] = statuses[None] | |
217 del statuses[None] | |
218 | |
219 self.host.memory.addPresenceStatus(entity, show or "", | |
220 int(priority), statuses, self.parent.profile) | |
221 | |
222 #now it's time to notify frontends | |
223 self.host.bridge.presenceUpdate(entity.full(), show or "", | |
224 int(priority), statuses, self.parent.profile) | |
225 | |
226 def unavailableReceived(self, entity, statuses=None): | |
227 debug (_("presence update for [%(entity)s] (unavailable, statuses=%(statuses)s)") % {'entity':entity, 'statuses':statuses}) | |
228 if statuses and statuses.has_key(None): #we only want string keys | |
229 statuses["default"] = statuses[None] | |
230 del statuses[None] | |
231 self.host.memory.addPresenceStatus(entity, "unavailable", 0, statuses, self.parent.profile) | |
232 | |
233 #now it's time to notify frontends | |
234 self.host.bridge.presenceUpdate(entity.full(), "unavailable", 0, statuses, self.parent.profile) | |
235 | |
236 | |
237 def available(self, entity=None, show=None, statuses=None, priority=0): | |
238 if statuses and statuses.has_key('default'): | |
239 statuses[None] = statuses['default'] | |
240 del statuses['default'] | |
241 xmppim.PresenceClientProtocol.available(self, entity, show, statuses, priority) | |
242 | |
243 def subscribedReceived(self, entity): | |
244 debug (_("subscription approved for [%s]") % entity.userhost()) | |
245 self.host.memory.delWaitingSub(entity.userhost(), self.parent.profile) | |
246 self.host.bridge.subscribe('subscribed', entity.userhost(), self.parent.profile) | |
247 | |
248 def unsubscribedReceived(self, entity): | |
249 debug (_("unsubscription confirmed for [%s]") % entity.userhost()) | |
250 self.host.memory.delWaitingSub(entity.userhost(), self.parent.profile) | |
251 self.host.bridge.subscribe('unsubscribed', entity.userhost(), self.parent.profile) | |
252 | |
253 def subscribeReceived(self, entity): | |
254 debug (_("subscription request for [%s]") % entity.userhost()) | |
255 self.host.memory.addWaitingSub('subscribe', entity.userhost(), self.parent.profile) | |
256 self.host.bridge.subscribe('subscribe', entity.userhost(), self.parent.profile) | |
257 | |
258 def unsubscribeReceived(self, entity): | |
259 debug (_("unsubscription asked for [%s]") % entity.userhost()) | |
260 self.host.memory.addWaitingSub('unsubscribe', entity.userhost(), self.parent.profile) | |
261 self.host.bridge.subscribe('unsubscribe', entity.userhost(), self.parent.profile) | |
262 | |
263 class SatDiscoProtocol(disco.DiscoClientProtocol): | |
264 def __init__(self, host): | |
265 disco.DiscoClientProtocol.__init__(self) | |
266 | |
267 class SatFallbackHandler(generic.FallbackHandler): | |
268 def __init__(self, host): | |
269 generic.FallbackHandler.__init__(self) | |
270 | |
271 def iqFallback(self, iq): | |
272 if iq.handled == True: | |
273 return | |
274 debug (u"iqFallback: xml = [%s]" % (iq.toXml())) | |
275 generic.FallbackHandler.iqFallback(self, iq) | |
276 | |
277 class RegisteringAuthenticator(xmlstream.ConnectAuthenticator): | |
278 | |
279 def __init__(self, host, jabber_host, user_login, user_pass, answer_id): | |
280 xmlstream.ConnectAuthenticator.__init__(self, jabber_host) | |
281 self.host = host | |
282 self.jabber_host = jabber_host | |
283 self.user_login = user_login | |
284 self.user_pass = user_pass | |
285 self.answer_id = answer_id | |
286 print _("Registration asked for"),user_login, user_pass, jabber_host | |
287 | |
288 def connectionMade(self): | |
289 print "connectionMade" | |
290 | |
291 self.xmlstream.namespace = "jabber:client" | |
292 self.xmlstream.sendHeader() | |
293 | |
294 iq = compat.IQ(self.xmlstream, 'set') | |
295 iq["to"] = self.jabber_host | |
296 query = iq.addElement(('jabber:iq:register', 'query')) | |
297 _user = query.addElement('username') | |
298 _user.addContent(self.user_login) | |
299 _pass = query.addElement('password') | |
300 _pass.addContent(self.user_pass) | |
301 reg = iq.send(self.jabber_host).addCallbacks(self.registrationAnswer, self.registrationFailure) | |
302 | |
303 def registrationAnswer(self, answer): | |
304 debug (_("registration answer: %s") % answer.toXml()) | |
305 answer_type = "SUCCESS" | |
306 answer_data={"message":_("Registration successfull")} | |
307 self.host.bridge.actionResult(answer_type, self.answer_id, answer_data) | |
308 self.xmlstream.sendFooter() | |
309 | |
310 def registrationFailure(self, failure): | |
311 info (_("Registration failure: %s") % str(failure.value)) | |
312 answer_type = "ERROR" | |
313 answer_data = {} | |
314 if failure.value.condition == 'conflict': | |
315 answer_data['reason'] = 'conflict' | |
316 answer_data={"message":_("Username already exists, please choose an other one")} | |
317 else: | |
318 answer_data['reason'] = 'unknown' | |
319 answer_data={"message":_("Registration failed (%s)") % str(failure.value.condition)} | |
320 self.host.bridge.actionResult(answer_type, self.answer_id, answer_data) | |
321 self.xmlstream.sendFooter() | |
322 | |
323 class SatVersionHandler(generic.VersionHandler): | |
324 | |
325 def getDiscoInfo(self, requestor, target, node): | |
326 #XXX: We need to work around wokkel's behavious (namespace not added if there is a | |
327 # node) as it cause issues with XEP-0115 & PEP (XEP-0163): there is a node when server | |
328 # ask for disco info, and not when we generate the key, so the hash is used with different | |
329 # disco features, and when the server (seen on ejabberd) generate its own hash for security check | |
330 # it reject our features (resulting in e.g. no notification on PEP) | |
331 return generic.VersionHandler.getDiscoInfo(self, requestor, target, None) | |
332 | 75 |
333 class SAT(service.Service): | 76 class SAT(service.Service): |
334 | 77 |
335 def get_next_id(self): | 78 def get_next_id(self): |
336 return sat_next_id() | 79 return sat_next_id() |