]> code.delx.au - pymsnt/blobdiff - src/session.py
Fixed tel:+ jid being invalid. Added cache to (hopefully) speed things up.
[pymsnt] / src / session.py
index 59b672af53207d8283ffecb33d47f29fe01b1eb3..c876fe7a3658721ec60287ba8128eb87fd7b0799 100644 (file)
@@ -1,12 +1,17 @@
-# Copyright 2004 James Bunton <james@delx.cjb.net>
+# Copyright 2004-2005 James Bunton <james@delx.cjb.net>
 # Licensed for distribution under the GPL version 2, check COPYING for details
 
+from tlib.xmlw import jid
+
 import utils
 import legacy
 import jabw
-import debug
+import contact
+import avatar
 import config
 import lang
+import disco
+from debug import LogEvent, INFO, WARN, ERROR
 
 
 
@@ -14,14 +19,14 @@ def makeSession(pytrans, jabberID, ulang):
        """ Tries to create a session object for the corresponding JabberID. Retrieves information
        from XDB to create the session. If it fails, then the user is most likely not registered with
        the transport """
-       debug.log("session: makeSession(\"%s\")" % (jabberID))
-       if(pytrans.sessions.has_key(jabberID)):
-               debug.log("session: makeSession() - removing existing session")
+       LogEvent(INFO, jabberID)
+       if pytrans.sessions.has_key(jabberID):
+               LogEvent(INFO, jabberID, "Removing existing session.")
                pytrans.sessions[jabberID].removeMe()
        result = pytrans.registermanager.getRegInfo(jabberID)
-       if(result):
-               username, password, nickname = result
-               return Session(pytrans, jabberID, username, password, nickname, ulang)
+       if result:
+               username, password = result
+               return Session(pytrans, jabberID, username, password, ulang)
        else:
                return None
 
@@ -31,10 +36,10 @@ class Session(jabw.JabberConnection):
        """ A class to represent each registered user's session with the legacy network. Exists as long as there
        is a Jabber resource for the user available """
        
-       def __init__(self, pytrans, jabberID, username, password, nickname, ulang):
+       def __init__(self, pytrans, jabberID, username, password, ulang):
                """ Initialises the session object and connects to the legacy network """
                jabw.JabberConnection.__init__(self, pytrans, jabberID)
-               debug.log("Session: Creating new session \"%s\"" % (jabberID))
+               LogEvent(INFO, jabberID)
                
                self.pytrans = pytrans
                self.alive = True
@@ -42,7 +47,8 @@ class Session(jabw.JabberConnection):
                self.jabberID = jabberID # the JabberID of the Session's user
                self.username = username # the legacy network ID of the Session's user
                self.password = password
-               self.nickname = nickname
+               self.nickname = ""
+               self.avatar = None
                self.lang = ulang
                
                self.show = None
@@ -52,10 +58,15 @@ class Session(jabw.JabberConnection):
                self.groupchats = []
                
                self.legacycon = legacy.LegacyConnection(self.username, self.password, self)
+               self.contactList = contact.ContactList(self)
+               self.contactList.legacyList = self.legacycon.legacyList
                
-               if(config.sessionGreeting):
+               if config.sessionGreeting:
                        self.sendMessage(to=self.jabberID, fro=config.jid, body=config.sessionGreeting)
-               debug.log("Session: New session created \"%s\" \"%s\" \"%s\" \"%s\"" % (jabberID, username, password, nickname))
+
+               self.updateNickname("")
+               self.doVCardUpdate()
+               LogEvent(INFO, self.jabberID, "Created!")
        
        def removeMe(self):
                """ Safely removes the session object, including sending <presence type="unavailable"/> messages for each legacy related item on the user's contact list """
@@ -63,96 +74,162 @@ class Session(jabw.JabberConnection):
                # Delete all objects cleanly
                # Remove this Session object from the pytrans
                
-               debug.log("Session: Removing \"%s\"" % (self.jabberID))
+               LogEvent(INFO, self.jabberID)
                
                # Mark as dead
                self.alive = False
                self.ready = False
                
                # Send offline presence to the user
-               if(self.pytrans):
+               if self.pytrans:
                        self.sendPresence(to=self.jabberID, fro=config.jid, ptype="unavailable")
-               
+
                # Clean up stuff on the legacy service end (including sending offline presences for all contacts)
-               if(self.legacycon):
+               if self.legacycon:
                        self.legacycon.removeMe()
                        self.legacycon = None
-               
+
+               if self.contactList:
+                       self.contactList.removeMe()
+                       self.contactList = None
+
                # Remove any groupchats we may be in
-               for groupchat in utils.copyList(self.groupchats):
+               for groupchat in self.groupchats[:]:
                        groupchat.removeMe()
                
-               if(self.pytrans):
+               if self.pytrans:
                        # Remove us from the session list
                        del self.pytrans.sessions[self.jabberID]
                        # Clean up the no longer needed reference
                        self.pytrans = None
                
-               debug.log("Session: Completed removal \"%s\"" % (self.jabberID))
-               utils.mutilateMe(self)
+               LogEvent(INFO, self.jabberID, "Removed!")
+       
+       def doVCardUpdate(self):
+               def vCardReceived(el):
+                       if not self.alive: return
+                       LogEvent(INFO, self.jabberID)
+                       vCard = None
+                       for e in el.elements():
+                               if e.name == "vCard" and e.defaultUri == disco.VCARDTEMP:
+                                       vCard = e
+                                       break
+                       else:
+                               self.legacycon.updateAvatar() # Default avatar
+                               return
+                       avatarSet = False
+                       name = ""
+                       for e in vCard.elements():
+                               if e.name == "NICKNAME":
+                                       name = e.__str__()
+                               if not name and e.name == "FN":
+                                       # Give priority to nickname
+                                       name = e.__str__()
+                               if e.name == "PHOTO":
+                                       imageData = avatar.parsePhotoEl(e)
+                                       if not imageData:
+                                               errback() # Possibly it wasn't in a supported format?
+                                       self.avatar = self.pytrans.avatarCache.setAvatar(imageData)
+                                       self.legacycon.updateAvatar(self.avatar)
+                                       avatarSet = True
+                       if name:
+                               self.updateNickname(e.__str__())
+                       if not avatarSet:
+                               self.legacycon.updateAvatar() # Default avatar
+
+               def errback(args=None):
+                       LogEvent(INFO, self.jabberID, "Error fetching avatar.")
+                       if self.alive:
+                               self.legacycon.updateAvatar()
+
+               LogEvent(INFO, self.jabberID, "Fetching avatar.")
+               d = self.sendVCardRequest(to=self.jabberID, fro=config.jid)
+               d.addCallback(vCardReceived)
+               d.addErrback(errback)
        
        def updateNickname(self, nickname):
                self.nickname = nickname
+               if not self.nickname:
+                       j = jid.intern(self.jabberID)
+                       self.nickname = j.user
                self.setStatus(self.show, self.status)
        
        def setStatus(self, show, status):
                self.show = show
                self.status = status
-               self.legacycon.setStatus(show, status)
+               self.legacycon.setStatus(self.nickname, show, status)
        
        def sendNotReadyError(self, source, resource, dest, body):
                self.sendErrorMessage(source + '/' + resource, dest, "wait", "not-allowed", lang.get(self.lang).waitForLogin, body)
        
        def findGroupchat(self, to):
                pos = to.find('@')
-               if(pos > 0):
+               if pos > 0:
                        roomID = to[:pos]
                else:
                        roomID = to
                
                for groupchat in self.groupchats:
-                       if(groupchat.ID == roomID):
+                       if groupchat.ID == roomID:
                                return groupchat
                
                return None
+       
+       def nicknameReceived(self, source, dest, nickname):
+               if dest.find('@') > 0: return # Ignore presence packets sent to users
+               
+               self.updateNickname(nickname)
+       
+       def avatarHashReceived(self, source, dest, avatarHash):
+               if dest.find('@') > 0: return # Ignore presence packets sent to users
+
+               if avatarHash == " ": # Setting no avatar
+                       self.legacycon.updateAvatar() # Default
+               elif (not self.avatar) or (self.avatar and self.avatar.getImageHash() != avatarHash):
+                       av = self.pytrans.avatarCache.getAvatar(avatarHash)
+                       if av:
+                               self.avatar = av # Stuff in the cache is always PNG
+                               self.legacycon.updateAvatar(self.avatar)
+                       else:
+                               self.doVCardUpdate()
                
        def messageReceived(self, source, resource, dest, destr, mtype, body, noerror):
-               if(dest == config.jid):
-                       if(body.lower().startswith("end")):
-                               debug.log("Session: Received 'end' request. Killing session %s" % (self.jabberID))
+               if dest == config.jid:
+                       if body.lower().startswith("end"):
+                               LogEvent(INFO, self.jabberID, "Received 'end' request.")
                                self.removeMe()
                        return
                
-               if(not self.ready):
+               if not self.ready:
                        self.sendNotReadyError(source, resource, dest, body)
                        return
                
                # Sends the message to the legacy translator
                groupchat = self.findGroupchat(dest)
-               if(groupchat):
+               if groupchat:
                        # It's for a groupchat
-                       if(destr and len(destr) > 0 and not noerror):
+                       if destr and len(destr) > 0 and not noerror:
                                self.sendErrorMessage(to=(source + "/" + resource), fro=dest, etype="cancel", condition="not-allowed", explanation=lang.get(self.lang).groupchatPrivateError, body=body)
                        else:
-                               debug.log("Session: Message received for groupchat \"%s\" \"%s\"" % (self.jabberID, groupchat.ID))
+                               LogEvent(INFO, self.jabberID, "Groupchat.")
                                groupchat.sendMessage(body, noerror)
                else:
-                       debug.log("Session: messageReceived(), passing onto legacycon.sendMessage()")
+                       LogEvent(INFO, self.jabberID, "Message.")
                        self.legacycon.sendMessage(dest, resource, body, noerror)
        
        def inviteReceived(self, source, resource, dest, destr, roomjid):
-               if(not self.ready):
+               if not self.ready:
                        self.sendNotReadyError(source, resource, dest, roomjid)
                        return
-               
-               if(not roomjid.endswith('@' + config.jid)):
+
+               if not roomjid.endswith('@' + config.jid): # Inviting a MSN user to a Jabber chatroom
                        message = lang.get(self.lang).groupchatAdvocacy % (self.jabberID, config.website)
                        self.legacycon.sendMessage(dest, resource, message, True)
                        return
-               
+
                groupchat = self.findGroupchat(roomjid)
-               if(groupchat):
-                       debug.log("Session: inviteReceived(\"%s\", \"%s\", \"%s\", \"%s\", \"%s\")" % (source, resource, dest, destr, roomjid))
+               if groupchat:
+                       LogEvent(INFO, self.jabberID, "Groupchat invitation.")
                        groupchat.sendContactInvite(dest)
        
        def typingNotificationReceived(self, dest, resource, composing):
@@ -164,76 +241,86 @@ class Session(jabw.JabberConnection):
                # legacy services status. If there are no more resources then the session is deleted
                # Additionally checks if the presence is to a groupchat room
                groupchat = self.findGroupchat(to)
-               if(groupchat):
+               if groupchat:
                        # It's for an existing groupchat
-                       if(ptype == "unavailable"):
+                       if ptype == "unavailable":
                                # Kill the groupchat
-                               debug.log("Session: Presence received to kill groupchat \"%s\" \"%s\"" % (self.jabberID, groupchat.ID))
+                               LogEvent(INFO, self.jabberID, "Killing groupchat.")
                                groupchat.removeMe()
                        else:
-                               if(source == self.jabberID):
-                                       debug.log("Session: Presence for groupchat \"%s\" \"%s\"" % (self.jabberID, groupchat.ID))
-                                       if(ptype == "error"):
+                               if source == self.jabberID:
+                                       LogEvent(INFO, self.jabberID, "Groupchat presence.")
+                                       if ptype == "error":
                                                groupchat.removeMe()
                                        else:
                                                groupchat.userJoined(tor)
                                else:
-                                       debug.log("Session: Sending error presence for groupchat (user not allowed) \"%s\" \"%s\"" % (self.jabberID, groupchat.ID))
+                                       LogEvent(INFO, self.jabberID, "Sending groupchat error presence.")
                                        self.sendPresence(to=(source + "/" + resource), fro=to, ptype="error")
                
-               elif(legacy.isGroupJID(to) and to != config.jid and ptype not in ["error", "unavailable"]):
-                       if(not self.ready):
+               elif legacy.isGroupJID(to) and to != config.jid and not ptype:
+                       # Its to a groupchat JID, and the presence type is available
+                       if not self.ready:
                                self.sendNotReadyError(source, resource, to, to)
                                return
+
                        # It's a new groupchat
                        gcID = to[:to.find('@')] # Grab the room name
-                       debug.log("Session: Creating a new groupchat \"%s\" \"%s\"" % (self.jabberID, gcID))
+                       LogEvent(INFO, self.jabberID, "Creating a new groupchat.")
                        groupchat = legacy.LegacyGroupchat(self, resource, gcID) # Creates an empty groupchat
                        groupchat.userJoined(tor)
                
+               elif ptype == "probe":
+                       LogEvent(INFO, self.jabberID, "Responding to presence probe")
+                       if to == config.jid:
+                               self.legacycon.sendShowStatus(source)
+                       else:
+                               self.contactList.getContact(to).sendPresence(source)
                else:
                        # Not for groupchat
                        self.handleResourcePresence(source, resource, to, tor, priority, ptype, show, status)
 
                
        def handleResourcePresence(self, source, resource, to, tor, priority, ptype, show, status):
-               if(not ptype in [None, "unavailable"]): return # Ignore presence errors, probes, etc
-               if(to.find('@') > 0): return # Ignore presence packets sent to users
+               if ptype and ptype != "unavailable": return # Ignore presence errors, probes, etc
+               if to.find('@') > 0: return # Ignore presence packets sent to users
                
                existing = self.resourceList.has_key(resource)
-               if(ptype == "unavailable"):
-                       if(existing):
-                               debug.log("Session: %s - resource \"%s\" gone offline" % (self.jabberID, resource))
+               if ptype == "unavailable":
+                       if existing:
+                               LogEvent(INFO, self.jabberID, "Resource gone offline.")
                                self.resourceOffline(resource)
                        else:
                                return # I don't know the resource, and they're leaving, so it's all good
                else:
-                       if(not existing):
-                               debug.log("Session %s - resource \"%s\" has come online" % (self.jabberID, resource))
-                               self.legacycon.newResourceOnline(resource)
-                       debug.log("Session %s - resource \"%s\" setting \"%s\" \"%s\" \"%s\"" % (self.jabberID, resource, show, status, priority)) 
+                       if not existing:
+                               LogEvent(INFO, self.jabberID, "Resource came online.")
+                               self.contactList.resendLists(source + "/" + resource)
+                       LogEvent(INFO, self.jabberID, "Setting status.")
                        self.resourceList[resource] = SessionResource(show, status, priority)
 
                highestActive = self.highestResource()
 
-               if(highestActive):
+               if highestActive:
                        # If we're the highest active resource, we should update the legacy service
-                       debug.log("Session %s - updating status on legacy service, resource %s" % (self.jabberID, highestActive))
+                       LogEvent(INFO, self.jabberID, "Updating status on legacy service.")
                        r = self.resourceList[highestActive]
                        self.setStatus(r.show, r.status)
                else:
-                       debug.log("Session %s - tearing down, last resource gone offline")
+                       LogEvent(INFO, self.jabberID, "Last resource died. Calling removeMe in 0 seconds.")
+                       #reactor.callLater(0, self.removeMe)
                        self.removeMe()
+                       #FIXME Which of the above?
        
        def highestResource(self):
                """ Returns the highest priority resource """
                highestActive = None
                for checkR in self.resourceList.keys():
-                       if(highestActive == None or self.resourceList[checkR].priority > self.resourceList[highestActive].priority)
+                       if highestActive == None or self.resourceList[checkR].priority > self.resourceList[highestActive].priority
                                highestActive = checkR
                
-               if(highestActive):
-                       debug.log("Session %s - highest active resource is \"%s\" at %d" % (self.jabberID, highestActive, self.resourceList[highestActive].priority))
+#              if highestActive:
+#                      debug.log("Session %s - highest active resource is \"%s\" at %d" % (self.jabberID, highestActive, self.resourceList[highestActive].priority))
 
                return highestActive
                
@@ -244,8 +331,19 @@ class Session(jabw.JabberConnection):
        
        def subscriptionReceived(self, to, subtype):
                """ Sends the subscription request to the legacy services handler """
-               debug.log("Session: \"%s\" subscriptionReceived(), passing onto legacycon.jabberSubscriptionReceived()" % (self.jabberID))
-               self.legacycon.jabberSubscriptionReceived(to, subtype)
+               if to.find('@') > 0:
+                       LogEvent(INFO, self.jabberID, "Passing subscription to legacy service.")
+                       self.contactList.jabberSubscriptionReceived(to, subtype)
+               else:
+                       if subtype == "subscribe":
+                               self.sendPresence(to=self.jabberID, fro=config.jid, ptype="subscribed")
+                       elif subtype.startswith("unsubscribe"):
+                               # They want to unregister.
+                               jid = self.jabberID
+                               LogEvent(INFO, jid, "About to unregister.")
+                               self.pytrans.registermanager.removeRegInfo(jid)
+                               LogEvent(INFO, jid, "Just unregistered.")
+