]> code.delx.au - pymsnt/blobdiff - src/legacy/glue.py
Sensible messages are now sent to both participants if a file is rejected for being...
[pymsnt] / src / legacy / glue.py
index 985d9dc723f0a60f4c3ce96fe8dc5742a2f692a2..0ba47b42685d96747e78353f707c0309b00e5061 100644 (file)
@@ -1,12 +1,13 @@
 # Copyright 2004-2005 James Bunton <james@delx.cjb.net>
 # Licensed for distribution under the GPL version 2, check COPYING for details
 
 # Copyright 2004-2005 James Bunton <james@delx.cjb.net>
 # Licensed for distribution under the GPL version 2, check COPYING for details
 
+import os.path
 import utils
 from twisted.internet import task
 from tlib.xmlw import Element
 from tlib import msn
 from debug import LogEvent, INFO, WARN, ERROR
 import utils
 from twisted.internet import task
 from tlib.xmlw import Element
 from tlib import msn
 from debug import LogEvent, INFO, WARN, ERROR
-import sha
+import disco
 import groupchat
 import ft
 import avatar
 import groupchat
 import ft
 import avatar
@@ -16,24 +17,26 @@ import lang
 
 
 
 
 
 
-name = "MSN Transport"   # The name of the transport
 url = "http://msn-transport.jabberstudio.org"
 url = "http://msn-transport.jabberstudio.org"
-version = "0.11-dev"     # The transport version
+version = "0.11-dev"         # The transport version
 mangle = True            # XDB '@' -> '%' mangling
 id = "msn"               # The transport identifier
 
 
 # Load the default avatars
 mangle = True            # XDB '@' -> '%' mangling
 id = "msn"               # The transport identifier
 
 
 # Load the default avatars
-f = open("src/legacy/defaultJabberAvatar.png")
+f = open(os.path.join("data", "defaultJabberAvatar.png"), "rb")
 defaultJabberAvatarData = f.read()
 f.close()
 
 defaultJabberAvatarData = f.read()
 f.close()
 
-f = open("src/legacy/defaultAvatar.png")
+f = open(os.path.join("data", "defaultMSNAvatar.png"), "rb")
 defaultAvatarData = f.read()
 f.close()
 defaultAvatar = avatar.AvatarCache().setAvatar(defaultAvatarData)
 
 
 defaultAvatarData = f.read()
 f.close()
 defaultAvatar = avatar.AvatarCache().setAvatar(defaultAvatarData)
 
 
+def reloadConfig():
+       msn.MSNConnection.GETALLAVATARS = config.getAllAvatars
+
 def isGroupJID(jid):
        """ Returns True if the JID passed is a valid groupchat JID (for MSN, does not contain '%') """
        return (jid.find('%') == -1)
 def isGroupJID(jid):
        """ Returns True if the JID passed is a valid groupchat JID (for MSN, does not contain '%') """
        return (jid.find('%') == -1)
@@ -91,15 +94,45 @@ def updateStats(statistics):
        #stats["FailedAvatarCount"] = msnp2p.MSNP2P_Avatar.ERROR_COUNT
 
 
        #stats["FailedAvatarCount"] = msnp2p.MSNP2P_Avatar.ERROR_COUNT
 
 
-def msn2jid(msnid):
+msn2jid_cache = {}
+def msn2jid(msnid, withResource):
        """ Converts a MSN passport into a JID representation to be used with the transport """
        """ Converts a MSN passport into a JID representation to be used with the transport """
-       return msnid.replace('@', '%') + "@" + config.jid
-
-translateAccount = msn2jid # Marks this as the function to be used in jabber:iq:gateway (Service ID Translation)
-
+       global msn2jid_cache
+       global jid2msn_cache
+
+       if msn2jid_cache.has_key(msnid):
+               jid = msn2jid_cache[msnid]
+               if withResource:
+                       jid += "/msn"
+               return jid
+       else:
+               if msnid.startswith("tel:+"):
+                       msnid = msnid.replace("tel:+", "") + "%tel"
+               jid = msnid.replace('@', '%') + "@" + config.jid + (withResource and "/msn" or "")
+               msn2jid_cache[msnid] = jid
+               jid2msn_cache[jid] = msnid
+               return jid
+
+# Marks this as the function to be used in jabber:iq:gateway (Service ID Translation)
+def translateAccount(msnid):
+       return msn2jid(msnid, False)
+
+jid2msn_cache = {}
 def jid2msn(jid):
        """ Converts a JID representation of a MSN passport into the original MSN passport """
 def jid2msn(jid):
        """ Converts a JID representation of a MSN passport into the original MSN passport """
-       return unicode(jid[:jid.find('@')].replace('%', '@'))
+       global jid2msn_cache
+       global msn2jid_cache
+
+       if jid2msn_cache.has_key(jid):
+               msnid = jid2msn_cache[jid]
+               return msnid
+       else:
+               if jid.find("%tel@") > 0:
+                       jid = "tel:+" + jid.replace("%tel@", "@")
+               msnid = unicode(jid[:jid.find('@')].replace('%', '@')).split("/")[0]
+               jid2msn_cache[jid] = msnid
+               msn2jid_cache[msnid] = jid
+               return msnid
 
 
 def presence2state(show, ptype): 
 
 
 def presence2state(show, ptype): 
@@ -112,6 +145,7 @@ def presence2state(show, ptype):
                return msn.STATUS_BUSY
        elif show == "away" or show == "xa":
                return msn.STATUS_AWAY
                return msn.STATUS_BUSY
        elif show == "away" or show == "xa":
                return msn.STATUS_AWAY
+       return msn.STATUS_ONLINE
 
 
 def state2presence(state):
 
 
 def state2presence(state):
@@ -173,25 +207,26 @@ def jabsub2msnlist(sub):
 
 # This class handles groupchats with the legacy protocol
 class LegacyGroupchat(groupchat.BaseGroupchat):
 
 # This class handles groupchats with the legacy protocol
 class LegacyGroupchat(groupchat.BaseGroupchat):
-       def __init__(self, session, resource, ID=None, existing=False, switchboardSession=None):
+       def __init__(self, session, resource=None, ID=None, switchboardSession=None):
                """ Possible entry points for groupchat
                        - User starts an empty switchboard session by sending presence to a blank room
                        - An existing switchboard session is joined by another MSN user
                        - User invited to an existing switchboard session with more than one user
                """
                groupchat.BaseGroupchat.__init__(self, session, resource, ID)
                """ Possible entry points for groupchat
                        - User starts an empty switchboard session by sending presence to a blank room
                        - An existing switchboard session is joined by another MSN user
                        - User invited to an existing switchboard session with more than one user
                """
                groupchat.BaseGroupchat.__init__(self, session, resource, ID)
-               if not existing:
-                       self.switchboardSession = msn.GroupchatSwitchboardSession(self, makeSwitchboard=True)
-               else:
+               if switchboardSession:
                        self.switchboardSession = switchboardSession
                        self.switchboardSession = switchboardSession
+               else:
+                       self.switchboardSession = msn.MultiSwitchboardSession(self.session.legacycon)
+                       self.switchboardSession.groupchat = self
                        
                        
-               assert(self.switchboardSession != None)
-               
                LogEvent(INFO, self.roomJID())
        
        def removeMe(self):
                LogEvent(INFO, self.roomJID())
        
        def removeMe(self):
-               self.switchboardSession.removeMe()
-               self.switchboardSession = None
+               if self.switchboardSession.transport:
+                       self.switchboardSession.transport.loseConnection()
+               self.switchboardSession.groupchat = None
+               del self.switchboardSession
                groupchat.BaseGroupchat.removeMe(self)
                LogEvent(INFO, self.roomJID())
        
                groupchat.BaseGroupchat.removeMe(self)
                LogEvent(INFO, self.roomJID())
        
@@ -203,6 +238,10 @@ class LegacyGroupchat(groupchat.BaseGroupchat):
                LogEvent(INFO, self.roomJID())
                userHandle = jid2msn(contactJID)
                self.switchboardSession.inviteUser(userHandle)
                LogEvent(INFO, self.roomJID())
                userHandle = jid2msn(contactJID)
                self.switchboardSession.inviteUser(userHandle)
+       
+       def gotMessage(self, userHandle, text):
+               LogEvent(INFO, self.roomJID())
+               self.messageReceived(userHandle, text)
 
 
 
 
 
 
@@ -210,6 +249,8 @@ class LegacyGroupchat(groupchat.BaseGroupchat):
 class LegacyConnection(msn.MSNConnection):
        """ A glue class that connects to the legacy network """
        def __init__(self, username, password, session):
 class LegacyConnection(msn.MSNConnection):
        """ A glue class that connects to the legacy network """
        def __init__(self, username, password, session):
+               self.jabberID = session.jabberID
+
                self.session = session
                self.listSynced = False
                self.initialListVersion = 0
                self.session = session
                self.listSynced = False
                self.initialListVersion = 0
@@ -219,7 +260,7 @@ class LegacyConnection(msn.MSNConnection):
                self.remoteNick = ""
 
                # Init the MSN bits
                self.remoteNick = ""
 
                # Init the MSN bits
-               msn.MSNConnection.__init__(self, username, password)
+               msn.MSNConnection.__init__(self, username, password, self.jabberID)
 
                # User typing notification stuff
                self.userTyping = dict() # Indexed by contact MSN ID, stores whether the user is typing to this contact
 
                # User typing notification stuff
                self.userTyping = dict() # Indexed by contact MSN ID, stores whether the user is typing to this contact
@@ -231,25 +272,30 @@ class LegacyConnection(msn.MSNConnection):
                
                self.legacyList = LegacyList(self.session)
        
                
                self.legacyList = LegacyList(self.session)
        
-               LogEvent(INFO, self.session.jabberID)
+               LogEvent(INFO, self.jabberID)
        
        def removeMe(self):
        
        def removeMe(self):
-               LogEvent(INFO, self.session.jabberID)
+               LogEvent(INFO, self.jabberID)
        
                self.userTypingSend.stop()
        
        
                self.userTypingSend.stop()
        
-               msn.MSNConnection.removeMe(self)
                self.legacyList.removeMe()
                self.legacyList = None
                self.session = None
                self.legacyList.removeMe()
                self.legacyList = None
                self.session = None
+               self.logOut()
+       
 
 
-       def jidRes(self, resource):
-               to = self.session.jabberID
-               if resource:
-                       to += "/" + resource
-
-               return to
+       # Implemented from baseproto
+       def sendShowStatus(self, jid=None):
+               if not self.session: return
+               source = config.jid
+               if not jid:
+                       jid = self.jabberID
+               self.session.sendPresence(to=jid, fro=source, show=self.remoteShow, status=self.remoteStatus, nickname=self.remoteNick)
        
        
+       def resourceOffline(self, resource):
+               pass
+
        def highestResource(self):
                """ Returns highest priority resource """
                return self.session.highestResource()
        def highestResource(self):
                """ Returns highest priority resource """
                return self.session.highestResource()
@@ -259,33 +305,27 @@ class LegacyConnection(msn.MSNConnection):
                if self.userTyping.has_key(dest):
                        del self.userTyping[dest]
                try:
                if self.userTyping.has_key(dest):
                        del self.userTyping[dest]
                try:
-                       msn.MSNConnection.sendMessage(self, dest, resource, body, noerror)
+                       msn.MSNConnection.sendMessage(self, dest, body, noerror)
                        self.session.pytrans.statistics.stats["MessageCount"] += 1
                except:
                        self.failedMessage(dest, body)
                        raise
        
                        self.session.pytrans.statistics.stats["MessageCount"] += 1
                except:
                        self.failedMessage(dest, body)
                        raise
        
-       def msnAlert(self, text, actionurl, subscrurl):
-               if not self.session: return
-
-               el = Element((None, "message"))
-               el.attributes["to"] = self.session.jabberID
-               el.attributes["from"] = config.jid
-               el.attributes["type"] = "headline"
-               body = el.addElement("body")
-               body.addContent(text)
-               
-               x = el.addElement("x")
-               x.attributes["xmlns"] = "jabber:x:oob"
-               x.addElement("desc").addContent("More information on this notice.")
-               x.addElement("url").addContent(actionurl)
-
-               x = el.addElement("x")
-               x.attributes["xmlns"] = "jabber:x:oob"
-               x.addElement("desc").addContent("Manage subscriptions to alerts.")
-               x.addElement("url").addContent(subscrurl)
+       def sendFile(self, dest, ftSend):
+               dest = jid2msn(dest)
+               def continueSendFile1((msnFileSend, d)):
+                       def continueSendFile2((success, )):
+                               if success:
+                                       ftSend.accept(msnFileSend)
+                               else:
+                                       sendFileFail()
+                       d.addCallbacks(continueSendFile2, sendFileFail)
+       
+               def sendFileFail():
+                       ftSend.reject()
 
 
-               self.session.pytrans.send(el)
+               d = msn.MSNConnection.sendFile(self, dest, ftSend.filename, ftSend.filesize)
+               d.addCallbacks(continueSendFile1, sendFileFail)
        
        def setStatus(self, nickname, show, status):
                statusCode = presence2state(show, None)
        
        def setStatus(self, nickname, show, status):
                statusCode = presence2state(show, None)
@@ -308,20 +348,11 @@ class LegacyConnection(msn.MSNConnection):
                                self.sendTypingToContact(contact)
 
                # Send any typing notification messages from contacts to the user
                                self.sendTypingToContact(contact)
 
                # Send any typing notification messages from contacts to the user
-               for contact, resource in self.contactTyping.keys():
-                       self.contactTyping[(contact, resource)] += 1
-                       if self.contactTyping[(contact, resource)] >= 3:
-                               self.session.sendTypingNotification(self.jidRes(resource), msn2jid(contact), False)
-                               del self.contactTyping[(contact, resource)]
-       
-       def gotContactTyping(self, contact, resource):
-               if not self.session: return
-               # Check if the contact has only just started typing
-               if not self.contactTyping.has_key((contact, resource)):
-                       self.session.sendTypingNotification(self.jidRes(resource), msn2jid(contact), True)
-
-               # Reset the counter
-               self.contactTyping[(contact, resource)] = 0
+               for contact in self.contactTyping.keys():
+                       self.contactTyping[contact] += 1
+                       if self.contactTyping[contact] >= 3:
+                               self.session.sendTypingNotification(self.jabberID, msn2jid(contact, True), False)
+                               del self.contactTyping[contact]
        
        def userTypingNotification(self, dest, resource, composing):
                if not self.session: return
        
        def userTypingNotification(self, dest, resource, composing):
                if not self.session: return
@@ -330,64 +361,123 @@ class LegacyConnection(msn.MSNConnection):
                if composing: # Make it instant
                        self.sendTypingToContact(dest)
        
                if composing: # Make it instant
                        self.sendTypingToContact(dest)
        
+
+
+       # Implement callbacks from msn.MSNConnection
+       def connectionFailed(self, reason):
+               LogEvent(INFO, self.jabberID)
+               text = lang.get(self.session.lang).msnConnectFailed % reason
+               self.session.sendMessage(to=self.jabberID, fro=config.jid, body=text)
+               self.session.removeMe()
+
+       def loginFailed(self, reason):
+               LogEvent(INFO, self.jabberID)
+               text = lang.get(self.session.lang).msnLoginFailure % (self.session.username)
+               self.session.sendErrorMessage(to=self.jabberID, fro=config.jid, etype="auth", condition="not-authorized", explanation=text, body="Login Failure")
+               self.session.removeMe()
+       
+       def connectionLost(self, reason):
+               LogEvent(INFO, self.jabberID)
+               text = lang.get(self.session.lang).msnDisconnected % reason
+               self.session.sendMessage(to=self.jabberID, fro=config.jid, body=text)
+               self.session.removeMe() # Tear down the session
+
+       def multipleLogin(self):
+               LogEvent(INFO, self.jabberID)
+               self.session.sendMessage(to=self.jabberID, fro=config.jid, body=lang.get(self.session.lang).msnMultipleLogin)
+               self.session.removeMe()
+       
+       def serverGoingDown(self):
+               LogEvent(INFO, self.jabberID)
+               self.session.sendMessage(to=self.jabberID, fro=config.jid, body=lang.get(self.session.lang).msnMaintenance)
+       
+       def accountNotVerified(self):
+               LogEvent(INFO, self.jabberID)
+               text = lang.get(self.session.lang).msnNotVerified % (self.session.username)
+               self.session.sendMessage(to=self.jabberID, fro=config.jid, body=text)
+       
+       def userMapping(self, passport, jid):
+               LogEvent(INFO, self.jabberID)
+               text = lang.get(self.session.lang).userMapping % (passport, jid)
+               self.session.sendMessage(to=self.jabberID, fro=msn2jid(passport, True), body=text)
+       
+       def loggedIn(self):
+               LogEvent(INFO, self.jabberID)
+               self.session.ready = True
+       
        def listSynchronized(self):
        def listSynchronized(self):
-               if not self.session: return
-               self.session.sendPresence(to=self.session.jabberID, fro=config.jid)
+               LogEvent(INFO, self.jabberID)
+               self.session.sendPresence(to=self.jabberID, fro=config.jid)
                self.legacyList.syncJabberLegacyLists()
                self.listSynced = True
                #self.legacyList.flushSubscriptionBuffer()
        
                self.legacyList.syncJabberLegacyLists()
                self.listSynced = True
                #self.legacyList.flushSubscriptionBuffer()
        
-       def gotMessage(self, remoteUser, resource, text):
-               if not self.session: return
-               source = msn2jid(remoteUser)
-               self.session.sendMessage(self.jidRes(resource), fro=source, body=text, mtype="chat")
+       def ourStatusChanged(self, statusCode, screenName, personal):
+               # Send out a new presence packet to the Jabber user so that the transport icon changes
+               LogEvent(INFO, self.jabberID)
+               self.remoteShow, ptype = state2presence(statusCode)
+               self.remoteStatus = personal
+               self.remoteNick = screenName
+               self.sendShowStatus()
+
+       def gotMessage(self, remoteUser, text):
+               LogEvent(INFO, self.jabberID)
+               source = msn2jid(remoteUser, True)
+               if self.contactTyping.has_key(remoteUser):
+                       del self.contactTyping[remoteUser]
+               self.session.sendMessage(self.jabberID, fro=source, body=text, mtype="chat")
                self.session.pytrans.statistics.stats["MessageCount"] += 1
        
                self.session.pytrans.statistics.stats["MessageCount"] += 1
        
-       def avatarHashChanged(self, userHandle, hash):
-               if not self.session: return
+       def gotGroupchat(self, msnGroupchat, userHandle):
+               LogEvent(INFO, self.jabberID)
+               msnGroupchat.groupchat = LegacyGroupchat(self.session, switchboardSession=msnGroupchat)
+               msnGroupchat.groupchat.sendUserInvite(msn2jid(userHandle, True))
+       
+       def gotContactTyping(self, contact):
+               LogEvent(INFO, self.jabberID)
+               # Check if the contact has only just started typing
+               if not self.contactTyping.has_key(contact):
+                       self.session.sendTypingNotification(self.jabberID, msn2jid(contact, True), True)
 
 
-               if not hash:
-                       # They've turned off their avatar
-                       c = self.session.contactList.findContact(jid)
-                       if not c: return
-                       c.updateAvatar(av)
-               else:
+               # Reset the counter
+               self.contactTyping[contact] = 0
+       
+       def failedMessage(self, remoteUser, message):
+               LogEvent(INFO, self.jabberID)
+               self.session.pytrans.statistics.stats["FailedMessageCount"] += 1
+               fro = msn2jid(remoteUser, True)
+               self.session.sendErrorMessage(to=self.jabberID, fro=fro, etype="wait", condition="recipient-unavailable", explanation=lang.get(self.session.lang).msnFailedMessage, body=message)
+       
+       def contactAvatarChanged(self, userHandle, hash):
+               LogEvent(INFO, self.jabberID)
+               jid = msn2jid(userHandle, False)
+               c = self.session.contactList.findContact(jid)
+               if not c: return
+
+               if hash:
                        # New avatar
                        av = self.session.pytrans.avatarCache.getAvatar(hash)
                        if av:
                                msnContact = self.getContacts().getContact(userHandle)
                                msnContact.msnobjGot = True
                        # New avatar
                        av = self.session.pytrans.avatarCache.getAvatar(hash)
                        if av:
                                msnContact = self.getContacts().getContact(userHandle)
                                msnContact.msnobjGot = True
-                               jid = msn2jid(userHandle)
-                               c = self.session.contactList.findContact(jid)
-                               if not c: return
                                c.updateAvatar(av)
                        else:
                                c.updateAvatar(av)
                        else:
-                               self.requestAvatar(userHandle)
-       
-       def gotAvatarImage(self, userHandle, imageData):
-               if not self.session: return
-               jid = msn2jid(userHandle)
-               c = self.session.contactList.findContact(jid)
-               if not c: return
-               av = self.session.pytrans.avatarCache.setAvatar(imageData)
-               c.updateAvatar(av)
-       
-       def gotSendRequest(self, fileReceive):
-               if not self.session: return
-               LogEvent(INFO, self.session.jabberID)
-               ft.FTReceive(self.session, msn2jid(fileReceive.userHandle), fileReceive)
-       
-       def loggedIn(self):
-               if not self.session: return
-               LogEvent(INFO, self.session.jabberID)
-               self.session.ready = True
+                               def updateAvatarCB((imageData,)):
+                                       av = self.session.pytrans.avatarCache.setAvatar(imageData)
+                                       c.updateAvatar(av)
+                               d = self.sendAvatarRequest(userHandle)
+                               if d:
+                                       d.addCallback(updateAvatarCB)
+               else:
+                       # They've turned off their avatar
+                       global defaultAvatar
+                       c.updateAvatar(defaultAvatar)
        
        def contactStatusChanged(self, remoteUser):
        
        def contactStatusChanged(self, remoteUser):
-               if not (self.session and self.getContacts()): return
-               LogEvent(INFO, self.session.jabberID)
+               LogEvent(INFO, self.jabberID)
                
                msnContact = self.getContacts().getContact(remoteUser)
                
                msnContact = self.getContacts().getContact(remoteUser)
-               c = self.session.contactList.findContact(msn2jid(remoteUser))
+               c = self.session.contactList.findContact(msn2jid(remoteUser, False))
                if not (c and msnContact): return
 
                show, ptype = state2presence(msnContact.status)
                if not (c and msnContact): return
 
                show, ptype = state2presence(msnContact.status)
@@ -397,92 +487,68 @@ class LegacyConnection(msn.MSNConnection):
                c.updateNickname(screenName, push=False)
                c.updatePresence(show, status, ptype, force=True)
        
                c.updateNickname(screenName, push=False)
                c.updatePresence(show, status, ptype, force=True)
        
-       def ourStatusChanged(self, statusCode):
-               # Send out a new presence packet to the Jabber user so that the MSN-t icon changes
-               if not self.session: return
-               LogEvent(INFO, self.session.jabberID)
-               self.remoteShow, ptype = state2presence(statusCode)
-               self.sendShowStatus()
+       def gotFileReceive(self, fileReceive):
+               LogEvent(INFO, self.jabberID)
+               # FIXME
+               ft.FTReceive(self.session, msn2jid(fileReceive.userHandle, True), fileReceive)
        
        
-       def ourPersonalChanged(self, statusMessage):
-               if not self.session: return
-               LogEvent(INFO, self.session.jabberID)
-               self.remoteStatus = statusMessage
-               self.sendShowStatus()
-       
-       def ourNickChanged(self, nick):
-               if not self.session: return
-               LogEvent(INFO, self.session.jabberID)
-               self.remoteNick = nick
-               self.sendShowStatus()
-       
-       def sendShowStatus(self):
-               if not self.session: return
-               source = config.jid
-               to = self.session.jabberID
-               self.session.sendPresence(to=to, fro=source, show=self.remoteShow, status=self.remoteStatus, nickname=self.remoteNick)
-       
-       def userMapping(self, passport, jid):
-               if not self.session: return
-               text = lang.get(self.session.lang).userMapping % (passport, jid)
-               self.session.sendMessage(to=self.session.jabberID, fro=msn2jid(passport), body=text)
-       
-       def userAddedMe(self, userHandle):
-               if not self.session: return
-               self.session.contactList.getContact(msn2jid(userHandle)).contactRequestsAuth()
+       def contactAddedMe(self, userHandle):
+               LogEvent(INFO, self.jabberID)
+               self.session.contactList.getContact(msn2jid(userHandle, False)).contactRequestsAuth()
        
        
-       def userRemovedMe(self, userHandle):
-               if not self.session: return
-               c = self.session.contactList.getContact(msn2jid(userHandle))
+       def contactRemovedMe(self, userHandle):
+               LogEvent(INFO, self.jabberID)
+               c = self.session.contactList.getContact(msn2jid(userHandle, True))
                c.contactDerequestsAuth()
                c.contactRemovesAuth()
        
                c.contactDerequestsAuth()
                c.contactRemovesAuth()
        
-       def serverGoingDown(self):
-               if not self.session: return
-               self.session.sendMessage(to=self.session.jabberID, fro=config.jid, body=lang.get(self.session.lang).msnMaintenance)
-       
-       def multipleLogin(self):
-               if not self.session: return
-               self.session.sendMessage(to=self.session.jabberID, fro=config.jid, body=lang.get(self.session.lang).msnMultipleLogin)
-               self.session.removeMe()
-       
-       def accountNotVerified(self):
-               if not self.session: return
-               text = lang.get(self.session.lang).msnNotVerified % (self.session.username)
-               self.session.sendMessage(to=self.session.jabberID, fro=config.jid, body=text)
-       
-       def loginFailure(self, message):
-               if not self.session: return
-               text = lang.get(self.session.lang).msnLoginFailure % (self.session.username)
-               self.session.sendErrorMessage(to=self.session.jabberID, fro=config.jid, etype="auth", condition="not-authorized", explanation=text, body="Login Failure")
-               self.session.removeMe()
+       def gotInitialEmailNotification(self, inboxunread, foldersunread):
+               if config.mailNotifications:
+                       LogEvent(INFO, self.jabberID)
+                       text = lang.get(self.session.lang).msnInitialMail % (inboxunread, foldersunread)
+                       self.session.sendMessage(to=self.jabberID, fro=config.jid, body=text, mtype="headline")
+       
+       def gotRealtimeEmailNotification(self, mailfrom, fromaddr, subject):
+               if config.mailNotifications:
+                       LogEvent(INFO, self.jabberID)
+                       text = lang.get(self.session.lang).msnRealtimeMail % (mailfrom, fromaddr, subject)
+                       self.session.sendMessage(to=self.jabberID, fro=config.jid, body=text, mtype="headline")
+               
+       def gotMSNAlert(self, text, actionurl, subscrurl):
+               LogEvent(INFO, self.jabberID)
+
+               el = Element((None, "message"))
+               el.attributes["to"] = self.jabberID
+               el.attributes["from"] = config.jid
+               el.attributes["type"] = "headline"
+               body = el.addElement("body")
+               body.addContent(text)
+               
+               x = el.addElement("x")
+               x.attributes["xmlns"] = "jabber:x:oob"
+               x.addElement("desc").addContent("More information on this notice.")
+               x.addElement("url").addContent(actionurl)
+
+               x = el.addElement("x")
+               x.attributes["xmlns"] = "jabber:x:oob"
+               x.addElement("desc").addContent("Manage subscriptions to alerts.")
+               x.addElement("url").addContent(subscrurl)
+
+               self.session.pytrans.send(el)
        
        
-       def failedMessage(self, remoteUser, message):
-               if not self.session: return
-               self.session.pytrans.statistics.stats["FailedMessageCount"] += 1
-               fro = msn2jid(remoteUser)
-               self.session.sendErrorMessage(to=self.session.jabberID, fro=fro, etype="wait", condition="recipient-unavailable", explanation=lang.get(self.session.lang).msnFailedMessage, body=message)
+       def gotAvatarImageData(self, userHandle, imageData):
+               LogEvent(INFO, self.jabberID)
+               av = self.session.pytrans.avatarCache.setAvatar(imageData)
+               jid = msn2jid(userHandle, False)
+               c = self.session.contactList.findContact(jid)
+               c.updateAvatar(av)
        
        
-       def initialEmailNotification(self, inboxunread, foldersunread):
-               if not self.session: return
-               text = lang.get(self.session.lang).msnInitialMail % (inboxunread, foldersunread)
-               self.session.sendMessage(to=self.session.jabberID, fro=config.jid, body=text, mtype="headline")
        
        
-       def realtimeEmailNotification(self, mailfrom, fromaddr, subject):
-               if not self.session: return
-               text = lang.get(self.session.lang).msnRealtimeMail % (mailfrom, fromaddr, subject)
-               self.session.sendMessage(to=self.session.jabberID, fro=config.jid, body=text, mtype="headline")
-               
-       def connectionLost(self, reason):
-               if not self.session: return
-               LogEvent(INFO, self.session.jabberID)
-               text = lang.get(self.session.lang).msnDisconnected % ("Error") # FIXME, a better error would be nice =P
-               self.session.sendMessage(to=self.session.jabberID, fro=config.jid, body=text)
-               self.session.removeMe() # Tear down the session
 
 
 class LegacyList:
        def __init__(self, session):
 
 
 class LegacyList:
        def __init__(self, session):
+               self.jabberID = session.jabberID
                self.session = session
                self.subscriptionBuffer = []
        
                self.session = session
                self.subscriptionBuffer = []
        
@@ -491,19 +557,19 @@ class LegacyList:
                self.session = None
 
        def addContact(self, jid):
                self.session = None
 
        def addContact(self, jid):
-               LogEvent(INFO, self.session.jabberID)
+               LogEvent(INFO, self.jabberID)
                userHandle = jid2msn(jid)
                self.session.legacycon.addContact(msn.FORWARD_LIST, userHandle)
                self.session.contactList.getContact(jid).contactGrantsAuth()
        
        def removeContact(self, jid):
                userHandle = jid2msn(jid)
                self.session.legacycon.addContact(msn.FORWARD_LIST, userHandle)
                self.session.contactList.getContact(jid).contactGrantsAuth()
        
        def removeContact(self, jid):
-               LogEvent(INFO, self.session.jabberID)
+               LogEvent(INFO, self.jabberID)
                jid = jid2msn(jid)
                self.session.legacycon.remContact(msn.FORWARD_LIST, jid)
        
        
        def authContact(self, jid):
                jid = jid2msn(jid)
                self.session.legacycon.remContact(msn.FORWARD_LIST, jid)
        
        
        def authContact(self, jid):
-               LogEvent(INFO, self.session.jabberID)
+               LogEvent(INFO, self.jabberID)
                jid = jid2msn(jid)
                d = self.session.legacycon.remContact(msn.PENDING_LIST, jid)
                if d:
                jid = jid2msn(jid)
                d = self.session.legacycon.remContact(msn.PENDING_LIST, jid)
                if d:
@@ -512,9 +578,10 @@ class LegacyList:
                self.session.legacycon.addContact(msn.ALLOW_LIST, jid)
        
        def deauthContact(self, jid):
                self.session.legacycon.addContact(msn.ALLOW_LIST, jid)
        
        def deauthContact(self, jid):
-               LogEvent(INFO, self.session.jabberID)
+               LogEvent(INFO, self.jabberID)
                jid = jid2msn(jid)
                self.session.legacycon.remContact(msn.ALLOW_LIST, jid)
                jid = jid2msn(jid)
                self.session.legacycon.remContact(msn.ALLOW_LIST, jid)
+               self.session.legacycon.remContact(msn.PENDING_LIST, jid)
                self.session.legacycon.addContact(msn.BLOCK_LIST, jid)
 
 
                self.session.legacycon.addContact(msn.BLOCK_LIST, jid)
 
 
@@ -526,8 +593,8 @@ class LegacyList:
 
                # We have to make an MSNContactList from the XDB data, then compare it with the one the server sent
                # Any subscription changes must be sent to the client, as well as changed in the XDB
 
                # We have to make an MSNContactList from the XDB data, then compare it with the one the server sent
                # Any subscription changes must be sent to the client, as well as changed in the XDB
-               LogEvent(INFO, self.session.jabberID, "Start.")
-               result = self.session.pytrans.xdb.request(self.session.jabberID, disco.IQROSTER)
+               LogEvent(INFO, self.jabberID, "Start.")
+               result = self.session.pytrans.xdb.request(self.jabberID, disco.IQROSTER)
                oldContactList = msn.MSNContactList()
                if result:
                        for item in result.elements():
                oldContactList = msn.MSNContactList()
                if result:
                        for item in result.elements():
@@ -564,7 +631,7 @@ class LegacyList:
                        # Create the Jabber representation of the
                        # contact base on the old list data and then
                        # sync it with current
                        # Create the Jabber representation of the
                        # contact base on the old list data and then
                        # sync it with current
-                       jabContact = self.session.contactList.createContact(msn2jid(contact.userHandle), msnlist2jabsub(oldLists))
+                       jabContact = self.session.contactList.createContact(msn2jid(contact.userHandle, False), msnlist2jabsub(oldLists))
                        jabContact.updateAvatar(defaultAvatar, push=False)
 
                        if addedToList(msn.FORWARD_LIST):
                        jabContact.updateAvatar(defaultAvatar, push=False)
 
                        if addedToList(msn.FORWARD_LIST):
@@ -585,6 +652,8 @@ class LegacyList:
 
                        if removedFromList(msn.REVERSE_LIST):
                                jabContact.contactDerequestsAuth()
 
                        if removedFromList(msn.REVERSE_LIST):
                                jabContact.contactDerequestsAuth()
+
+                       jabContact.syncRoster()
                        
                        item = newXDB.addElement("item")
                        item.attributes["jid"] = contact.userHandle
                        
                        item = newXDB.addElement("item")
                        item.attributes["jid"] = contact.userHandle
@@ -592,8 +661,8 @@ class LegacyList:
                        item.attributes["lists"] = str(lists)
                
                # Update the XDB
                        item.attributes["lists"] = str(lists)
                
                # Update the XDB
-               self.session.pytrans.xdb.set(self.session.jabberID, disco.IQROSTER, newXDB)
-               LogEvent(INFO, self.session.jabberID, "End.")
+               self.session.pytrans.xdb.set(self.jabberID, disco.IQROSTER, newXDB)
+               LogEvent(INFO, self.jabberID, "End.")
        
        def saveLegacyList(self):
                contactList = self.session.legacycon.getContacts()
        
        def saveLegacyList(self):
                contactList = self.session.legacycon.getContacts()
@@ -608,7 +677,7 @@ class LegacyList:
                        item.attributes["subscription"] = msnlist2jabsub(contact.lists) # Backwards compat
                        item.attributes["lists"] = str(contact.lists)
 
                        item.attributes["subscription"] = msnlist2jabsub(contact.lists) # Backwards compat
                        item.attributes["lists"] = str(contact.lists)
 
-               self.session.pytrans.xdb.set(self.session.jabberID, disco.IQROSTER, newXDB)
-               LogEvent(INFO, self.session.jabberID, "Finished saving list.")
+               self.session.pytrans.xdb.set(self.jabberID, disco.IQROSTER, newXDB)
+               LogEvent(INFO, self.jabberID, "Finished saving list.")