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