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