]> code.delx.au - pymsnt/blob - src/session.py
File transfer nearly working...
[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 if utils.checkTwisted():
14 from twisted.words.protocols.jabber import jid
15 else:
16 from tlib.jabber import jid
17
18
19
20 def makeSession(pytrans, jabberID, ulang):
21 """ Tries to create a session object for the corresponding JabberID. Retrieves information
22 from XDB to create the session. If it fails, then the user is most likely not registered with
23 the transport """
24 LogEvent(INFO, jabberID)
25 if pytrans.sessions.has_key(jabberID):
26 LogEvent(INFO, jabberID, "Removing existing session.")
27 pytrans.sessions[jabberID].removeMe()
28 result = pytrans.registermanager.getRegInfo(jabberID)
29 if result:
30 username, password = result
31 return Session(pytrans, jabberID, username, password, ulang)
32 else:
33 return None
34
35
36
37 class Session(jabw.JabberConnection):
38 """ A class to represent each registered user's session with the legacy network. Exists as long as there
39 is a Jabber resource for the user available """
40
41 def __init__(self, pytrans, jabberID, username, password, ulang):
42 """ Initialises the session object and connects to the legacy network """
43 jabw.JabberConnection.__init__(self, pytrans, jabberID)
44 LogEvent(INFO, jabberID)
45
46 self.pytrans = pytrans
47 self.alive = True
48 self.ready = False # Only ready when we're logged into the legacy service
49 self.jabberID = jabberID # the JabberID of the Session's user
50 self.username = username # the legacy network ID of the Session's user
51 self.password = password
52 self.nickname = ""
53 self.avatar = None
54 self.lang = ulang
55
56 self.show = None
57 self.status = None
58
59 self.resourceList = {}
60 self.groupchats = []
61
62 self.legacycon = legacy.LegacyConnection(self.username, self.password, self)
63 self.contactList = contact.ContactList(self)
64 self.contactList.legacyList = self.legacycon.legacyList
65
66 if config.sessionGreeting:
67 self.sendMessage(to=self.jabberID, fro=config.jid, body=config.sessionGreeting)
68
69 self.updateNickname("")
70 self.doVCardUpdate()
71 LogEvent(INFO, self.jabberID, "Created!")
72
73 def removeMe(self):
74 """ Safely removes the session object, including sending <presence type="unavailable"/> messages for each legacy related item on the user's contact list """
75 # Send offline presence to Jabber ID
76 # Delete all objects cleanly
77 # Remove this Session object from the pytrans
78
79 LogEvent(INFO, self.jabberID)
80
81 # Mark as dead
82 self.alive = False
83 self.ready = False
84
85 # Send offline presence to the user
86 if self.pytrans:
87 self.sendPresence(to=self.jabberID, fro=config.jid, ptype="unavailable")
88
89 # Clean up stuff on the legacy service end (including sending offline presences for all contacts)
90 if self.legacycon:
91 self.legacycon.removeMe()
92 self.legacycon = None
93
94 if self.contactList:
95 self.contactList.removeMe()
96 self.contactList = None
97
98 # Remove any groupchats we may be in
99 for groupchat in self.groupchats[:]:
100 groupchat.removeMe()
101
102 if self.pytrans:
103 # Remove us from the session list
104 del self.pytrans.sessions[self.jabberID]
105 # Clean up the no longer needed reference
106 self.pytrans = None
107
108 LogEvent(INFO, self.jabberID, "Removed!")
109 utils.mutilateMe(self)
110
111 def doVCardUpdate(self):
112 def vCardReceived(el):
113 if not self.alive: return
114 LogEvent(INFO, self.jabberID)
115 vCard = None
116 for e in el.elements():
117 if e.name == "vCard" and e.defaultUri == disco.VCARDTEMP:
118 vCard = e
119 break
120 else:
121 self.legacycon.updateAvatar() # Default avatar
122 return
123 avatarSet = False
124 for e in vCard.elements():
125 if e.name == "NICKNAME":
126 self.updateNickname(e.__str__())
127 if e.name == "PHOTO":
128 imageData = avatar.parsePhotoEl(e)
129 if not imageData:
130 errback() # Possibly it wasn't in a supported format?
131 self.avatar = self.pytrans.avatarCache.setAvatar(imageData)
132 self.legacycon.updateAvatar(self.avatar)
133 avatarSet = True
134 if not avatarSet:
135 self.legacycon.updateAvatar() # Default avatar
136
137 def errback(args=None):
138 LogEvent(INFO, self.jabberID, "Error fetching avatar.")
139 if self.alive:
140 self.legacycon.updateAvatar()
141
142 LogEvent(INFO, self.jabberID, "Fetching avatar.")
143 d = self.sendVCardRequest(to=self.jabberID, fro=config.jid)
144 d.addCallback(vCardReceived)
145 d.addErrback(errback)
146
147 def updateNickname(self, nickname):
148 self.nickname = nickname
149 if not self.nickname:
150 j = jid.JID(self.jabberID)
151 self.nickname = j.user
152 self.setStatus(self.show, self.status)
153
154 def setStatus(self, show, status):
155 self.show = show
156 self.status = status
157 self.legacycon.setStatus(self.nickname, show, status)
158
159 def sendNotReadyError(self, source, resource, dest, body):
160 self.sendErrorMessage(source + '/' + resource, dest, "wait", "not-allowed", lang.get(self.lang).waitForLogin, body)
161
162 def findGroupchat(self, to):
163 pos = to.find('@')
164 if pos > 0:
165 roomID = to[:pos]
166 else:
167 roomID = to
168
169 for groupchat in self.groupchats:
170 if groupchat.ID == roomID:
171 return groupchat
172
173 return None
174
175 def nicknameReceived(self, source, dest, nickname):
176 if dest.find('@') > 0: return # Ignore presence packets sent to users
177
178 self.updateNickname(nickname)
179
180 def avatarHashReceived(self, source, dest, avatarHash):
181 if dest.find('@') > 0: return # Ignore presence packets sent to users
182
183 if avatarHash == " ": # Setting no avatar
184 self.legacycon.updateAvatar() # Default
185 elif (not self.avatar) or (self.avatar and self.avatar.getImageHash() != avatarHash):
186 imageData = self.pytrans.avatarCache.getAvatar(avatarHash)
187 if imageData:
188 self.avatar = avatar.Avatar(imageData) # Stuff in the cache is always PNG
189 self.legacycon.updateAvatar(self.avatar)
190 else:
191 self.doVCardUpdate()
192
193 def messageReceived(self, source, resource, dest, destr, mtype, body, noerror):
194 if dest == config.jid:
195 if body.lower().startswith("end"):
196 LogEvent(INFO, self.jabberID, "Received 'end' request.")
197 self.removeMe()
198 return
199
200 if not self.ready:
201 self.sendNotReadyError(source, resource, dest, body)
202 return
203
204 # Sends the message to the legacy translator
205 groupchat = self.findGroupchat(dest)
206 if groupchat:
207 # It's for a groupchat
208 if destr and len(destr) > 0 and not noerror:
209 self.sendErrorMessage(to=(source + "/" + resource), fro=dest, etype="cancel", condition="not-allowed", explanation=lang.get(self.lang).groupchatPrivateError, body=body)
210 else:
211 LogEvent(INFO, self.jabberID, "Groupchat.")
212 groupchat.sendMessage(body, noerror)
213 else:
214 LogEvent(INFO, self.jabberID, "Message.")
215 self.legacycon.sendMessage(dest, resource, body, noerror)
216
217 def inviteReceived(self, source, resource, dest, destr, roomjid):
218 if not self.ready:
219 self.sendNotReadyError(source, resource, dest, roomjid)
220 return
221
222 if not roomjid.endswith('@' + config.jid): # Inviting a MSN user to a Jabber chatroom
223 message = lang.get(self.lang).groupchatAdvocacy % (self.jabberID, config.website)
224 self.legacycon.sendMessage(dest, resource, message, True)
225 return
226
227 groupchat = self.findGroupchat(roomjid)
228 if groupchat:
229 LogEvent(INFO, self.jabberID, "Groupchat invitation.")
230 groupchat.sendContactInvite(dest)
231
232 def typingNotificationReceived(self, dest, resource, composing):
233 """ The user has sent typing notification to a contact on the legacy service """
234 self.legacycon.userTypingNotification(dest, resource, composing)
235
236 def presenceReceived(self, source, resource, to, tor, priority, ptype, show, status):
237 # Checks resources and priorities so that the highest priority resource always appears as the
238 # legacy services status. If there are no more resources then the session is deleted
239 # Additionally checks if the presence is to a groupchat room
240 groupchat = self.findGroupchat(to)
241 if groupchat:
242 # It's for an existing groupchat
243 if ptype == "unavailable":
244 # Kill the groupchat
245 LogEvent(INFO, self.jabberID, "Killing groupchat.")
246 groupchat.removeMe()
247 else:
248 if source == self.jabberID:
249 LogEvent(INFO, self.jabberID, "Groupchat presence.")
250 if ptype == "error":
251 groupchat.removeMe()
252 else:
253 groupchat.userJoined(tor)
254 else:
255 LogEvent(INFO, self.jabberID, "Sending groupchat error presence.")
256 self.sendPresence(to=(source + "/" + resource), fro=to, ptype="error")
257
258 elif legacy.isGroupJID(to) and to != config.jid and not ptype:
259 # Its to a groupchat JID, and the presence type is available
260 if not self.ready:
261 self.sendNotReadyError(source, resource, to, to)
262 return
263
264 # It's a new groupchat
265 gcID = to[:to.find('@')] # Grab the room name
266 LogEvent(INFO, self.jabberID, "Creating a new groupchat.")
267 groupchat = legacy.LegacyGroupchat(self, resource, gcID) # Creates an empty groupchat
268 groupchat.userJoined(tor)
269
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