]> code.delx.au - pymsnt/blob - src/session.py
OOB file sending works.
[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 imageData = self.pytrans.avatarCache.getAvatar(avatarHash)
184 if imageData:
185 self.avatar = avatar.Avatar(imageData, self.pytrans.avatarCache) # 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 else:
268 # Not for groupchat
269 self.handleResourcePresence(source, resource, to, tor, priority, ptype, show, status)
270
271
272 def handleResourcePresence(self, source, resource, to, tor, priority, ptype, show, status):
273 if ptype and ptype != "unavailable": return # Ignore presence errors, probes, etc
274 if to.find('@') > 0: return # Ignore presence packets sent to users
275
276 existing = self.resourceList.has_key(resource)
277 if ptype == "unavailable":
278 if existing:
279 LogEvent(INFO, self.jabberID, "Resource gone offline.")
280 self.resourceOffline(resource)
281 else:
282 return # I don't know the resource, and they're leaving, so it's all good
283 else:
284 if not existing:
285 LogEvent(INFO, self.jabberID, "Resource came online.")
286 self.contactList.resendLists(source + "/" + resource)
287 LogEvent(INFO, self.jabberID, "Setting status.")
288 self.resourceList[resource] = SessionResource(show, status, priority)
289
290 highestActive = self.highestResource()
291
292 if highestActive:
293 # If we're the highest active resource, we should update the legacy service
294 LogEvent(INFO, self.jabberID, "Updating status on legacy service.")
295 r = self.resourceList[highestActive]
296 self.setStatus(r.show, r.status)
297 else:
298 LogEvent(INFO, self.jabberID, "Last resource died. Calling removeMe in 0 seconds.")
299 #reactor.callLater(0, self.removeMe)
300 self.removeMe()
301 #FIXME Which of the above?
302
303 def highestResource(self):
304 """ Returns the highest priority resource """
305 highestActive = None
306 for checkR in self.resourceList.keys():
307 if highestActive == None or self.resourceList[checkR].priority > self.resourceList[highestActive].priority:
308 highestActive = checkR
309
310 # if highestActive:
311 # debug.log("Session %s - highest active resource is \"%s\" at %d" % (self.jabberID, highestActive, self.resourceList[highestActive].priority))
312
313 return highestActive
314
315
316 def resourceOffline(self, resource):
317 del self.resourceList[resource]
318 self.legacycon.resourceOffline(resource)
319
320 def subscriptionReceived(self, to, subtype):
321 """ Sends the subscription request to the legacy services handler """
322 if to.find('@') > 0:
323 LogEvent(INFO, self.jabberID, "Passing subscription to legacy service.")
324 self.contactList.jabberSubscriptionReceived(to, subtype)
325 else:
326 if subtype == "subscribe":
327 self.sendPresence(to=self.jabberID, fro=config.jid, ptype="subscribed")
328 elif subtype.startswith("unsubscribe"):
329 # They want to unregister.
330 jid = self.jabberID
331 LogEvent(INFO, jid, "About to unregister.")
332 self.pytrans.registermanager.removeRegInfo(jid)
333 LogEvent(INFO, jid, "Just unregistered.")
334
335
336
337
338
339
340
341 class SessionResource:
342 """ A convienence class to allow comparisons of Jabber resources """
343 def __init__(self, show=None, status=None, priority=None):
344 self.show = show
345 self.status = status
346 self.priority = 0
347 try:
348 self.priority = int(priority)
349 except TypeError: pass
350 except ValueError: pass
351
352