]> code.delx.au - pymsnt/blob - src/session.py
Fixed nickname sending.
[pymsnt] / src / session.py
1 # Copyright 2004-2005 James Bunton <james@delx.cjb.net>
2 # Licensed for distribution under the GPL version 2, check COPYING for details
3
4 from tlib.xmlw import jid
5
6 import utils
7 import legacy
8 import jabw
9 import contact
10 import avatar
11 import config
12 import lang
13 import disco
14 from debug import LogEvent, INFO, WARN, ERROR
15
16
17
18 def makeSession(pytrans, jabberID, ulang):
19 """ Tries to create a session object for the corresponding JabberID. Retrieves information
20 from XDB to create the session. If it fails, then the user is most likely not registered with
21 the transport """
22 LogEvent(INFO, jabberID)
23 if pytrans.sessions.has_key(jabberID):
24 LogEvent(INFO, jabberID, "Removing existing session.")
25 pytrans.sessions[jabberID].removeMe()
26 result = pytrans.registermanager.getRegInfo(jabberID)
27 if result:
28 username, password = result
29 return Session(pytrans, jabberID, username, password, ulang)
30 else:
31 return None
32
33
34
35 class Session(jabw.JabberConnection):
36 """ A class to represent each registered user's session with the legacy network. Exists as long as there
37 is a Jabber resource for the user available """
38
39 def __init__(self, pytrans, jabberID, username, password, ulang):
40 """ Initialises the session object and connects to the legacy network """
41 jabw.JabberConnection.__init__(self, pytrans, jabberID)
42 LogEvent(INFO, jabberID)
43
44 self.pytrans = pytrans
45 self.alive = True
46 self.ready = False # Only ready when we're logged into the legacy service
47 self.jabberID = jabberID # the JabberID of the Session's user
48 self.username = username # the legacy network ID of the Session's user
49 self.password = password
50 self.nickname = ""
51 self.avatar = None
52 self.lang = ulang
53
54 self.show = None
55 self.status = None
56
57 self.resourceList = {}
58 self.groupchats = []
59
60 self.legacycon = legacy.LegacyConnection(self.username, self.password, self)
61 self.contactList = contact.ContactList(self)
62 self.contactList.legacyList = self.legacycon.legacyList
63
64 if config.sessionGreeting:
65 self.sendMessage(to=self.jabberID, fro=config.jid, body=config.sessionGreeting)
66
67 self.updateNickname("")
68 self.doVCardUpdate()
69 LogEvent(INFO, self.jabberID, "Created!")
70
71 def removeMe(self):
72 """ Safely removes the session object, including sending <presence type="unavailable"/> messages for each legacy related item on the user's contact list """
73 # Send offline presence to Jabber ID
74 # Delete all objects cleanly
75 # Remove this Session object from the pytrans
76
77 LogEvent(INFO, self.jabberID)
78
79 # Mark as dead
80 self.alive = False
81 self.ready = False
82
83 # Send offline presence to the user
84 if self.pytrans:
85 self.sendPresence(to=self.jabberID, fro=config.jid, ptype="unavailable")
86
87 # Clean up stuff on the legacy service end (including sending offline presences for all contacts)
88 if self.legacycon:
89 self.legacycon.removeMe()
90 self.legacycon = None
91
92 if self.contactList:
93 self.contactList.removeMe()
94 self.contactList = None
95
96 # Remove any groupchats we may be in
97 for groupchat in self.groupchats[:]:
98 groupchat.removeMe()
99
100 if self.pytrans:
101 # Remove us from the session list
102 del self.pytrans.sessions[self.jabberID]
103 # Clean up the no longer needed reference
104 self.pytrans = None
105
106 LogEvent(INFO, self.jabberID, "Removed!")
107
108 def doVCardUpdate(self):
109 def vCardReceived(el):
110 if not self.alive: return
111 LogEvent(INFO, self.jabberID)
112 vCard = None
113 for e in el.elements():
114 if e.name == "vCard" and e.uri == disco.VCARDTEMP:
115 vCard = e
116 break
117 else:
118 self.legacycon.updateAvatar() # Default avatar
119 return
120 avatarSet = False
121 name = ""
122 for e in vCard.elements():
123 if e.name == "NICKNAME" and e.__str__():
124 name = e.__str__()
125 if not name and e.name == "FN" and e.__str__():
126 # Give priority to nickname
127 name = e.__str__()
128 if e.name == "PHOTO":
129 imageData = avatar.parsePhotoEl(e)
130 if not imageData:
131 errback() # Possibly it wasn't in a supported format?
132 self.avatar = self.pytrans.avatarCache.setAvatar(imageData)
133 self.legacycon.updateAvatar(self.avatar)
134 avatarSet = True
135 if name:
136 self.updateNickname(name)
137 if not avatarSet:
138 self.legacycon.updateAvatar() # Default avatar
139
140 def errback(args=None):
141 LogEvent(INFO, self.jabberID, "Error fetching avatar.")
142 if self.alive:
143 self.legacycon.updateAvatar()
144
145 LogEvent(INFO, self.jabberID, "Fetching avatar.")
146 d = self.sendVCardRequest(to=self.jabberID, fro=config.jid + "/msn")
147 d.addCallback(vCardReceived)
148 d.addErrback(errback)
149
150 def updateNickname(self, nickname):
151 self.nickname = nickname
152 if not self.nickname:
153 j = jid.intern(self.jabberID)
154 self.nickname = j.user
155 self.setStatus(self.show, self.status)
156
157 def setStatus(self, show, status):
158 self.show = show
159 self.status = status
160 self.legacycon.setStatus(self.nickname, show, status)
161
162 def sendNotReadyError(self, source, resource, dest, body):
163 self.sendErrorMessage(source + '/' + resource, dest, "wait", "not-allowed", lang.get(self.lang).waitForLogin, body)
164
165 def findGroupchat(self, to):
166 pos = to.find('@')
167 if pos > 0:
168 roomID = to[:pos]
169 else:
170 roomID = to
171
172 for groupchat in self.groupchats:
173 if groupchat.ID == roomID:
174 return groupchat
175
176 return None
177
178 def nicknameReceived(self, source, dest, nickname):
179 if dest.find('@') > 0: return # Ignore presence packets sent to users
180
181 self.updateNickname(nickname)
182
183 def avatarHashReceived(self, source, dest, avatarHash):
184 if dest.find('@') > 0: return # Ignore presence packets sent to users
185
186 if avatarHash == " ": # Setting no avatar
187 self.legacycon.updateAvatar() # Default
188 elif (not self.avatar) or (self.avatar and self.avatar.getImageHash() != avatarHash):
189 av = self.pytrans.avatarCache.getAvatar(avatarHash)
190 if av:
191 self.avatar = av # Stuff in the cache is always PNG
192 self.legacycon.updateAvatar(self.avatar)
193 else:
194 self.doVCardUpdate()
195
196 def messageReceived(self, source, resource, dest, destr, mtype, body, noerror):
197 if dest == config.jid:
198 if body.lower().startswith("end"):
199 LogEvent(INFO, self.jabberID, "Received 'end' request.")
200 self.removeMe()
201 return
202
203 if not self.ready:
204 self.sendNotReadyError(source, resource, dest, body)
205 return
206
207 # Sends the message to the legacy translator
208 groupchat = self.findGroupchat(dest)
209 if groupchat:
210 # It's for a groupchat
211 if destr and len(destr) > 0 and not noerror:
212 self.sendErrorMessage(to=(source + "/" + resource), fro=dest, etype="cancel", condition="not-allowed", explanation=lang.get(self.lang).groupchatPrivateError, body=body)
213 else:
214 LogEvent(INFO, self.jabberID, "Groupchat.")
215 groupchat.sendMessage(body, noerror)
216 else:
217 LogEvent(INFO, self.jabberID, "Message.")
218 self.legacycon.sendMessage(dest, resource, body, noerror)
219
220 def inviteReceived(self, source, resource, dest, destr, roomjid):
221 if not self.ready:
222 self.sendNotReadyError(source, resource, dest, roomjid)
223 return
224
225 if not roomjid.endswith('@' + config.jid): # Inviting a MSN user to a Jabber chatroom
226 message = lang.get(self.lang).groupchatAdvocacy % (self.jabberID, config.website)
227 self.legacycon.sendMessage(dest, resource, message, True)
228 return
229
230 groupchat = self.findGroupchat(roomjid)
231 if groupchat:
232 LogEvent(INFO, self.jabberID, "Groupchat invitation.")
233 groupchat.sendContactInvite(dest)
234
235 def typingNotificationReceived(self, dest, resource, composing):
236 """ The user has sent typing notification to a contact on the legacy service """
237 self.legacycon.userTypingNotification(dest, resource, composing)
238
239 def presenceReceived(self, source, resource, to, tor, priority, ptype, show, status):
240 # Checks resources and priorities so that the highest priority resource always appears as the
241 # legacy services status. If there are no more resources then the session is deleted
242 # Additionally checks if the presence is to a groupchat room
243 groupchat = self.findGroupchat(to)
244 if groupchat:
245 # It's for an existing groupchat
246 if ptype == "unavailable":
247 # Kill the groupchat
248 LogEvent(INFO, self.jabberID, "Killing groupchat.")
249 groupchat.removeMe()
250 else:
251 if source == self.jabberID:
252 LogEvent(INFO, self.jabberID, "Groupchat presence.")
253 if ptype == "error":
254 groupchat.removeMe()
255 else:
256 groupchat.userJoined(tor)
257 else:
258 LogEvent(INFO, self.jabberID, "Sending groupchat error presence.")
259 self.sendPresence(to=(source + "/" + resource), fro=to, ptype="error")
260
261 elif legacy.isGroupJID(to) and to != config.jid and not ptype:
262 # Its to a groupchat JID, and the presence type is available
263 if not self.ready:
264 self.sendNotReadyError(source, resource, to, to)
265 return
266
267 # It's a new groupchat
268 gcID = to[:to.find('@')] # Grab the room name
269 LogEvent(INFO, self.jabberID, "Creating a new groupchat.")
270 groupchat = legacy.LegacyGroupchat(self, resource, gcID) # Creates an empty groupchat
271 groupchat.userJoined(tor)
272
273 elif ptype == "probe":
274 LogEvent(INFO, self.jabberID, "Responding to presence probe")
275 if to == config.jid:
276 self.legacycon.sendShowStatus(source)
277 else:
278 self.contactList.getContact(to).sendPresence(source)
279 else:
280 # Not for groupchat
281 self.handleResourcePresence(source, resource, to, tor, priority, ptype, show, status)
282
283
284 def handleResourcePresence(self, source, resource, to, tor, priority, ptype, show, status):
285 if ptype and ptype != "unavailable": return # Ignore presence errors, probes, etc
286 if to.find('@') > 0: return # Ignore presence packets sent to users
287
288 existing = self.resourceList.has_key(resource)
289 if ptype == "unavailable":
290 if existing:
291 LogEvent(INFO, self.jabberID, "Resource gone offline.")
292 self.resourceOffline(resource)
293 else:
294 return # I don't know the resource, and they're leaving, so it's all good
295 else:
296 if not existing:
297 LogEvent(INFO, self.jabberID, "Resource came online.")
298 self.contactList.resendLists(source + "/" + resource)
299 LogEvent(INFO, self.jabberID, "Setting status.")
300 self.resourceList[resource] = SessionResource(show, status, priority)
301
302 highestActive = self.highestResource()
303
304 if highestActive:
305 # If we're the highest active resource, we should update the legacy service
306 LogEvent(INFO, self.jabberID, "Updating status on legacy service.")
307 r = self.resourceList[highestActive]
308 self.setStatus(r.show, r.status)
309 else:
310 LogEvent(INFO, self.jabberID, "Last resource died. Calling removeMe in 0 seconds.")
311 #reactor.callLater(0, self.removeMe)
312 self.removeMe()
313 #FIXME Which of the above?
314
315 def highestResource(self):
316 """ Returns the highest priority resource """
317 highestActive = None
318 for checkR in self.resourceList.keys():
319 if highestActive == None or self.resourceList[checkR].priority > self.resourceList[highestActive].priority:
320 highestActive = checkR
321
322 # if highestActive:
323 # debug.log("Session %s - highest active resource is \"%s\" at %d" % (self.jabberID, highestActive, self.resourceList[highestActive].priority))
324
325 return highestActive
326
327
328 def resourceOffline(self, resource):
329 del self.resourceList[resource]
330 self.legacycon.resourceOffline(resource)
331
332 def subscriptionReceived(self, to, subtype):
333 """ Sends the subscription request to the legacy services handler """
334 if to.find('@') > 0:
335 LogEvent(INFO, self.jabberID, "Passing subscription to legacy service.")
336 self.contactList.jabberSubscriptionReceived(to, subtype)
337 else:
338 if subtype == "subscribe":
339 self.sendPresence(to=self.jabberID, fro=config.jid, ptype="subscribed")
340 elif subtype.startswith("unsubscribe"):
341 # They want to unregister.
342 jid = self.jabberID
343 LogEvent(INFO, jid, "About to unregister.")
344 self.pytrans.registermanager.removeRegInfo(jid)
345 LogEvent(INFO, jid, "Just unregistered.")
346
347
348
349
350
351
352
353 class SessionResource:
354 """ A convienence class to allow comparisons of Jabber resources """
355 def __init__(self, show=None, status=None, priority=None):
356 self.show = show
357 self.status = status
358 self.priority = 0
359 try:
360 self.priority = int(priority)
361 except TypeError: pass
362 except ValueError: pass
363
364