]> code.delx.au - pymsnt/blob - src/session.py
0f9d91e002603ffbc240e06921546060a5d31633
[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.defaultUri == disco.VCARDTEMP:
115 vCard = e
116 break
117 else:
118 self.legacycon.updateAvatar() # Default avatar
119 return
120 avatarSet = False
121 for e in vCard.elements():
122 if e.name == "NICKNAME":
123 self.updateNickname(e.__str__())
124 if e.name == "PHOTO":
125 imageData = avatar.parsePhotoEl(e)
126 if not imageData:
127 errback() # Possibly it wasn't in a supported format?
128 self.avatar = self.pytrans.avatarCache.setAvatar(imageData)
129 self.legacycon.updateAvatar(self.avatar)
130 avatarSet = True
131 if not avatarSet:
132 self.legacycon.updateAvatar() # Default avatar
133
134 def errback(args=None):
135 LogEvent(INFO, self.jabberID, "Error fetching avatar.")
136 if self.alive:
137 self.legacycon.updateAvatar()
138
139 LogEvent(INFO, self.jabberID, "Fetching avatar.")
140 d = self.sendVCardRequest(to=self.jabberID, fro=config.jid)
141 d.addCallback(vCardReceived)
142 d.addErrback(errback)
143
144 def updateNickname(self, nickname):
145 self.nickname = nickname
146 if not self.nickname:
147 j = jid.intern(self.jabberID)
148 self.nickname = j.user
149 self.setStatus(self.show, self.status)
150
151 def setStatus(self, show, status):
152 self.show = show
153 self.status = status
154 self.legacycon.setStatus(self.nickname, show, status)
155
156 def sendNotReadyError(self, source, resource, dest, body):
157 self.sendErrorMessage(source + '/' + resource, dest, "wait", "not-allowed", lang.get(self.lang).waitForLogin, body)
158
159 def findGroupchat(self, to):
160 pos = to.find('@')
161 if pos > 0:
162 roomID = to[:pos]
163 else:
164 roomID = to
165
166 for groupchat in self.groupchats:
167 if groupchat.ID == roomID:
168 return groupchat
169
170 return None
171
172 def nicknameReceived(self, source, dest, nickname):
173 if dest.find('@') > 0: return # Ignore presence packets sent to users
174
175 self.updateNickname(nickname)
176
177 def avatarHashReceived(self, source, dest, avatarHash):
178 if dest.find('@') > 0: return # Ignore presence packets sent to users
179
180 if avatarHash == " ": # Setting no avatar
181 self.legacycon.updateAvatar() # Default
182 elif (not self.avatar) or (self.avatar and self.avatar.getImageHash() != avatarHash):
183 av = self.pytrans.avatarCache.getAvatar(avatarHash)
184 if av:
185 self.avatar = av # Stuff in the cache is always PNG
186 self.legacycon.updateAvatar(self.avatar)
187 else:
188 self.doVCardUpdate()
189
190 def messageReceived(self, source, resource, dest, destr, mtype, body, noerror):
191 if dest == config.jid:
192 if body.lower().startswith("end"):
193 LogEvent(INFO, self.jabberID, "Received 'end' request.")
194 self.removeMe()
195 return
196
197 if not self.ready:
198 self.sendNotReadyError(source, resource, dest, body)
199 return
200
201 # Sends the message to the legacy translator
202 groupchat = self.findGroupchat(dest)
203 if groupchat:
204 # It's for a groupchat
205 if destr and len(destr) > 0 and not noerror:
206 self.sendErrorMessage(to=(source + "/" + resource), fro=dest, etype="cancel", condition="not-allowed", explanation=lang.get(self.lang).groupchatPrivateError, body=body)
207 else:
208 LogEvent(INFO, self.jabberID, "Groupchat.")
209 groupchat.sendMessage(body, noerror)
210 else:
211 LogEvent(INFO, self.jabberID, "Message.")
212 self.legacycon.sendMessage(dest, resource, body, noerror)
213
214 def inviteReceived(self, source, resource, dest, destr, roomjid):
215 if not self.ready:
216 self.sendNotReadyError(source, resource, dest, roomjid)
217 return
218
219 if not roomjid.endswith('@' + config.jid): # Inviting a MSN user to a Jabber chatroom
220 message = lang.get(self.lang).groupchatAdvocacy % (self.jabberID, config.website)
221 self.legacycon.sendMessage(dest, resource, message, True)
222 return
223
224 groupchat = self.findGroupchat(roomjid)
225 if groupchat:
226 LogEvent(INFO, self.jabberID, "Groupchat invitation.")
227 groupchat.sendContactInvite(dest)
228
229 def typingNotificationReceived(self, dest, resource, composing):
230 """ The user has sent typing notification to a contact on the legacy service """
231 self.legacycon.userTypingNotification(dest, resource, composing)
232
233 def presenceReceived(self, source, resource, to, tor, priority, ptype, show, status):
234 # Checks resources and priorities so that the highest priority resource always appears as the
235 # legacy services status. If there are no more resources then the session is deleted
236 # Additionally checks if the presence is to a groupchat room
237 groupchat = self.findGroupchat(to)
238 if groupchat:
239 # It's for an existing groupchat
240 if ptype == "unavailable":
241 # Kill the groupchat
242 LogEvent(INFO, self.jabberID, "Killing groupchat.")
243 groupchat.removeMe()
244 else:
245 if source == self.jabberID:
246 LogEvent(INFO, self.jabberID, "Groupchat presence.")
247 if ptype == "error":
248 groupchat.removeMe()
249 else:
250 groupchat.userJoined(tor)
251 else:
252 LogEvent(INFO, self.jabberID, "Sending groupchat error presence.")
253 self.sendPresence(to=(source + "/" + resource), fro=to, ptype="error")
254
255 elif legacy.isGroupJID(to) and to != config.jid and not ptype:
256 # Its to a groupchat JID, and the presence type is available
257 if not self.ready:
258 self.sendNotReadyError(source, resource, to, to)
259 return
260
261 # It's a new groupchat
262 gcID = to[:to.find('@')] # Grab the room name
263 LogEvent(INFO, self.jabberID, "Creating a new groupchat.")
264 groupchat = legacy.LegacyGroupchat(self, resource, gcID) # Creates an empty groupchat
265 groupchat.userJoined(tor)
266
267 elif ptype == "probe":
268 LogEvent(INFO, self.jabberID, "Responding to presence probe")
269 self.contactList.getContact(to).sendPresence(source)
270 else:
271 # Not for groupchat
272 self.handleResourcePresence(source, resource, to, tor, priority, ptype, show, status)
273
274
275 def handleResourcePresence(self, source, resource, to, tor, priority, ptype, show, status):
276 if ptype and ptype != "unavailable": return # Ignore presence errors, probes, etc
277 if to.find('@') > 0: return # Ignore presence packets sent to users
278
279 existing = self.resourceList.has_key(resource)
280 if ptype == "unavailable":
281 if existing:
282 LogEvent(INFO, self.jabberID, "Resource gone offline.")
283 self.resourceOffline(resource)
284 else:
285 return # I don't know the resource, and they're leaving, so it's all good
286 else:
287 if not existing:
288 LogEvent(INFO, self.jabberID, "Resource came online.")
289 self.contactList.resendLists(source + "/" + resource)
290 LogEvent(INFO, self.jabberID, "Setting status.")
291 self.resourceList[resource] = SessionResource(show, status, priority)
292
293 highestActive = self.highestResource()
294
295 if highestActive:
296 # If we're the highest active resource, we should update the legacy service
297 LogEvent(INFO, self.jabberID, "Updating status on legacy service.")
298 r = self.resourceList[highestActive]
299 self.setStatus(r.show, r.status)
300 else:
301 LogEvent(INFO, self.jabberID, "Last resource died. Calling removeMe in 0 seconds.")
302 #reactor.callLater(0, self.removeMe)
303 self.removeMe()
304 #FIXME Which of the above?
305
306 def highestResource(self):
307 """ Returns the highest priority resource """
308 highestActive = None
309 for checkR in self.resourceList.keys():
310 if highestActive == None or self.resourceList[checkR].priority > self.resourceList[highestActive].priority:
311 highestActive = checkR
312
313 # if highestActive:
314 # debug.log("Session %s - highest active resource is \"%s\" at %d" % (self.jabberID, highestActive, self.resourceList[highestActive].priority))
315
316 return highestActive
317
318
319 def resourceOffline(self, resource):
320 del self.resourceList[resource]
321 self.legacycon.resourceOffline(resource)
322
323 def subscriptionReceived(self, to, subtype):
324 """ Sends the subscription request to the legacy services handler """
325 if to.find('@') > 0:
326 LogEvent(INFO, self.jabberID, "Passing subscription to legacy service.")
327 self.contactList.jabberSubscriptionReceived(to, subtype)
328 else:
329 if subtype == "subscribe":
330 self.sendPresence(to=self.jabberID, fro=config.jid, ptype="subscribed")
331 elif subtype.startswith("unsubscribe"):
332 # They want to unregister.
333 jid = self.jabberID
334 LogEvent(INFO, jid, "About to unregister.")
335 self.pytrans.registermanager.removeRegInfo(jid)
336 LogEvent(INFO, jid, "Just unregistered.")
337
338
339
340
341
342
343
344 class SessionResource:
345 """ A convienence class to allow comparisons of Jabber resources """
346 def __init__(self, show=None, status=None, priority=None):
347 self.show = show
348 self.status = status
349 self.priority = 0
350 try:
351 self.priority = int(priority)
352 except TypeError: pass
353 except ValueError: pass
354
355