-# 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
-import utils
-from twisted.internet import task
-if(utils.checkTwisted()):
- from twisted.xish.domish import Element
-else:
- from tlib.domish import Element
-from tlib import msn
+import os.path
+from twisted.internet import task, error
+from twisted.words.xish.domish import Element
+
+from debug import LogEvent, INFO, WARN, ERROR
+from legacy import msn
+import disco
import groupchat
-import msnw
+import ft
+import avatar
import config
-import debug
import lang
-name = "MSN Transport" # The name of the transport
-version = "0.9.5" # The transport version
-mangle = True # XDB '@' -> '%' mangling
-id = "msn" # The transport identifier
+url = "http://msn-transport.jabberstudio.org"
+version = "0.11.2-dev" # The transport version
+mangle = True # XDB '@' -> '%' mangling
+id = "msn" # The transport identifier
+
+
+# Load the default avatars
+f = open(os.path.join("data", "defaultJabberAvatar.png"), "rb")
+defaultJabberAvatarData = f.read()
+f.close()
+f = open(os.path.join("data", "defaultMSNAvatar.png"), "rb")
+defaultAvatarData = f.read()
+f.close()
+defaultAvatar = avatar.AvatarCache().setAvatar(defaultAvatarData)
+def reloadConfig():
+ msn.MSNConnection.GETALLAVATARS = config.getAllAvatars
+ msn.setDebug(config._debugLevel >= 4)
+
def isGroupJID(jid):
""" Returns True if the JID passed is a valid groupchat JID (for MSN, does not contain '%') """
return (jid.find('%') == -1)
namespace = "jabber:iq:register"
-def formRegEntry(username, password, nickname):
+def formRegEntry(username, password):
""" Returns a domish.Element representation of the data passed. This element will be written to the XDB spool file """
reginfo = Element((None, "query"))
reginfo.attributes["xmlns"] = "jabber:iq:register"
passEl = reginfo.addElement("password")
passEl.addContent(password)
- nickEl = reginfo.addElement("nick")
- if(nickname): nickEl.addContent(nickname)
-
return reginfo
def getAttributes(base):
""" This function should, given a spool domish.Element, pull the username, password,
- and nickname out of it and return them """
+ and out of it and return them """
username = ""
password = ""
- nickname = ""
for child in base.elements():
try:
- if(child.name == "username"):
+ if child.name == "username":
username = child.__str__()
- elif(child.name == "password"):
+ elif child.name == "password":
password = child.__str__()
- elif(child.name == "nick"):
- nickname = child.__str__()
except AttributeError:
continue
- return username, password, nickname
+ return username, password
+def startStats(statistics):
+ stats = statistics.stats
+ stats["MessageCount"] = 0
+ stats["FailedMessageCount"] = 0
+ stats["AvatarCount"] = 0
+ stats["FailedAvatarCount"] = 0
+def updateStats(statistics):
+ stats = statistics.stats
+ # FIXME
+ #stats["AvatarCount"] = msnp2p.MSNP2P_Avatar.TRANSFER_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 """
- 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
+ msn2jid_cache[msnid] = jid
+ jid2msn_cache[jid] = msnid
+ return msn2jid(msnid, withResource)
+
+# 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 """
- 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):
""" Converts a Jabber presence into an MSN status code """
- if(ptype == "unavailable"):
+ if ptype == "unavailable":
return msn.STATUS_OFFLINE
- elif(show in [None, "online", "chat"]):
+ elif not show or show == "online" or show == "chat":
return msn.STATUS_ONLINE
- elif(show in ["dnd"]):
+ elif show == "dnd":
return msn.STATUS_BUSY
- elif(show in ["away", "xa"]):
+ elif show == "away" or show == "xa":
return msn.STATUS_AWAY
+ return msn.STATUS_ONLINE
def state2presence(state):
""" Converts a MSN status code into a Jabber presence """
- if(state == msn.STATUS_ONLINE):
+ if state == msn.STATUS_ONLINE:
return (None, None)
- elif(state == msn.STATUS_BUSY):
+ elif state == msn.STATUS_BUSY:
return ("dnd", None)
- elif(state == msn.STATUS_AWAY):
+ elif state == msn.STATUS_AWAY:
return ("away", None)
- elif(state == msn.STATUS_IDLE):
+ elif state == msn.STATUS_IDLE:
return ("away", None)
- elif(state == msn.STATUS_BRB):
+ elif state == msn.STATUS_BRB:
return ("away", None)
- elif(state == msn.STATUS_PHONE):
+ elif state == msn.STATUS_PHONE:
return ("dnd", None)
- elif(state == msn.STATUS_LUNCH):
+ elif state == msn.STATUS_LUNCH:
return ("away", None)
else:
return (None, "unavailable")
+def getGroupNames(msnContact, msnContactList):
+ """ Gets a list of groups that this contact is in """
+ groups = []
+ for groupGUID in msnContact.groups:
+ try:
+ groups.append(msnContactList.groups[groupGUID])
+ except KeyError:
+ pass
+ return groups
+
+def msnlist2jabsub(lists):
+ """ Converts MSN contact lists ORed together into the corresponding Jabber subscription state """
+ if lists & msn.FORWARD_LIST and lists & msn.REVERSE_LIST:
+ return "both"
+ elif lists & msn.REVERSE_LIST:
+ return "from"
+ elif lists & msn.FORWARD_LIST:
+ return "to"
+ else:
+ return "none"
+
+
+def jabsub2msnlist(sub):
+ """ Converts a Jabber subscription state into the corresponding MSN contact lists ORed together """
+ if sub == "to":
+ return msn.FORWARD_LIST
+ elif sub == "from":
+ return msn.REVERSE_LIST
+ elif sub == "both":
+ return (msn.FORWARD_LIST | msn.REVERSE_LIST)
+ else:
+ return 0
+
+
+
# 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)
- if(not existing):
- self.switchboardSession = msnw.GroupchatSwitchboardSession(self, makeSwitchboard=True)
- else:
+ if switchboardSession:
self.switchboardSession = switchboardSession
+ else:
+ self.switchboardSession = msn.MultiSwitchboardSession(self.session.legacycon)
+ self.switchboardSession.groupchat = self
- assert(self.switchboardSession != None)
-
- debug.log("LegacyGroupchat: \"%s\" created" % (self.roomJID()))
+ 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)
- debug.log("LegacyGroupchat: \"%s\" destroyed" % (self.roomJID()))
- utils.mutilateMe(self)
+ LogEvent(INFO, self.roomJID())
def sendLegacyMessage(self, message, noerror):
- debug.log("LegacyGroupchat: \"%s\" sendLegacyMessage(\"%s\")" % (self.roomJID(), message))
- self.switchboardSession.sendMessage(message, noerror)
+ LogEvent(INFO, self.roomJID())
+ self.switchboardSession.sendMessage(message.replace("\n", "\r\n"), noerror)
def sendContactInvite(self, contactJID):
- debug.log("LegacyGroupchat: \"%s\" sendContactInvite(\"%s\")" % (self.roomJID(), contactJID))
+ LogEvent(INFO, self.roomJID())
userHandle = jid2msn(contactJID)
self.switchboardSession.inviteUser(userHandle)
+
+ def gotMessage(self, userHandle, text):
+ LogEvent(INFO, self.roomJID())
+ self.messageReceived(userHandle, text)
# This class handles most interaction with the legacy protocol
-class LegacyConnection(msnw.MSNConnection):
+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
- # Get the latest listVersion to pass to MSNConnection
- result = self.session.pytrans.xdb.request(self.session.jabberID, "msn:listVersion")
- if(result):
- self.initialListVersion = int(str(result))
+ self.remoteShow = ""
+ self.remoteStatus = ""
+ self.remoteNick = ""
# Init the MSN bits
- msnw.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
self.userTypingSend = task.LoopingCall(self.sendTypingNotifications)
self.userTypingSend.start(5.0)
- import subscription # Is in here to prevent an ImportError loop
- self.subscriptions = subscription.SubscriptionManager(self.session)
+ self.legacyList = LegacyList(self.session)
- debug.log("LegacyConnection: \"%s\" - created" % (self.session.jabberID))
+ LogEvent(INFO, self.jabberID)
def removeMe(self):
- debug.log("LegacyConnection: \"%s\" - being deleted" % (self.session.jabberID))
+ LogEvent(INFO, self.jabberID)
self.userTypingSend.stop()
- if(self.getContacts()):
- for userHandle in self.getContacts().getContacts():
- msnContact = self.getContacts().getContact(userHandle)
- if(msnContact.status and msnContact.status != msn.STATUS_OFFLINE):
- msnContact.status = msn.STATUS_OFFLINE
- self.sendMSNContactPresence(msnContact)
-
-# msnw.MSNConnection.changeStatus(self, msn.STATUS_OFFLINE, self.session.nickname) # Change our nickname on the MSN network (stops users from appearing offline with a nickname "james - Online")
-
- # Save to XDB the current list version (for fast logins)
- if(self.notificationFactory and self.notificationFactory.contacts):
- listVersion = self.notificationFactory.contacts.version
- el = Element((None, "query"))
- el.addContent(str(listVersion))
- self.session.pytrans.xdb.set(self.session.jabberID, "msn:listVersion", el)
-
- msnw.MSNConnection.removeMe(self)
- self.subscriptions.removeMe()
- self.subscriptions = None
+ self.legacyList.removeMe()
+ self.legacyList = None
self.session = None
-
- utils.mutilateMe(self)
+ 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 sendMessage(self, dest, resource, body, noerror):
dest = jid2msn(dest)
- if(self.userTyping.has_key(dest)):
+ if self.userTyping.has_key(dest):
del self.userTyping[dest]
- msnw.MSNConnection.sendMessage(self, dest, resource, body, noerror)
+ try:
+ msn.MSNConnection.sendMessage(self, dest, body, noerror)
+ self.session.pytrans.statistics.stats["MessageCount"] += 1
+ except:
+ self.failedMessage(dest, body)
+ raise
- def buildFriendly(self, status):
- """ Constructs a friendly name from the user's registered nick, and their status message """
-
- if(not config.fancyFriendly):
- if(self.session.nickname and len(self.session.nickname) > 0):
- return self.session.nickname
- else:
- return self.session.jabberID[:self.session.jabberID.find('@')]
-
- if(self.session.nickname and len(self.session.nickname) > 0):
- friendly = self.session.nickname
- else:
- friendly = self.session.jabberID[:self.session.jabberID.find('@')]
- if(status and len(status) > 0):
- friendly += " - "
- friendly += status
- if(len(friendly) > 127):
- friendly = friendly[:124] + "..."
- debug.log("LegacyConnection: buildFriendly(%s) returning \"%s\"" % (self.session.jabberID, friendly))
- return friendly
-
- def msnAlert(self, text, actionurl, subscrurl):
- 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, show, status):
+ def setStatus(self, nickname, show, status):
statusCode = presence2state(show, None)
- msnw.MSNConnection.changeStatus(self, statusCode, self.buildFriendly(status))
+ msn.MSNConnection.changeStatus(self, statusCode, nickname, status)
- def newResourceOnline(self, resource):
- self.sendLists(resource)
-
- def jabberSubscriptionReceived(self, source, subtype):
- self.subscriptions.jabberSubscriptionReceived(source, subtype)
+ def updateAvatar(self, av=None):
+ global defaultJabberAvatarData
+
+ if av:
+ msn.MSNConnection.changeAvatar(self, av.getImageData)
+ else:
+ msn.MSNConnection.changeAvatar(self, lambda: defaultJabberAvatarData)
def sendTypingNotifications(self):
+ if not self.session: return
+
# Send any typing notification messages to the user's contacts
for contact in self.userTyping.keys():
- if(self.userTyping[contact]):
+ if self.userTyping[contact]:
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):
- # 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
dest = jid2msn(dest)
self.userTyping[dest] = composing
- if(composing): # Make it instant
+ 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 sendMSNContactPresence(self, msnContact, to=None):
- if(not to):
- to = self.session.jabberID
- source = msn2jid(msnContact.userHandle)
- show, ptype = state2presence(msnContact.status)
- status = msnContact.screenName.decode("utf-8")
- self.session.sendPresence(to=to, fro=source, show=show, status=status, ptype=ptype)
-
- def sendMSNUserPresence(self, userHandle, to=None):
- msnContact = self.getContacts().getContact(userHandle)
- if(msnContact):
- self.sendMSNContactPresence(msnContact, to)
-
- def sendLists(self, resource):
- """ Sends a copy of the MSN contact presences to this resource """
- debug.log("LegacyConnection: \"%s\" - sendLists(\"%s\")" % (self.session.jabberID, resource))
- fulljid = self.session.jabberID
- if(resource):
- fulljid += "/" + resource
- if(self.getContacts()):
- for userHandle in self.getContacts().getContacts():
- self.sendMSNUserPresence(userHandle, fulljid)
-
- def listSynchronized(self):
- if(self.session):
- self.session.sendPresence(to=self.session.jabberID, fro=config.jid)
- self.subscriptions.syncJabberLegacyLists()
- self.listSynced = True
- self.subscriptions.flushSubscriptionBuffer()
-
- def gotMessage(self, remoteUser, resource, text):
- source = msn2jid(remoteUser)
- self.session.sendMessage(self.jidRes(resource), fro=source, body=text, mtype="chat")
+ def connectionLost(self, reason):
+ LogEvent(INFO, self.jabberID)
+ if reason.type != error.ConnectionDone:
+ 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 loggedIn(self):
- if(self.session):
- debug.log("LegacyConnection: \"%s\" - loggedIn()" % (self.session.jabberID))
- self.session.ready = True
+ def serverGoingDown(self):
+ LogEvent(INFO, self.jabberID)
+ self.session.sendMessage(to=self.jabberID, fro=config.jid, body=lang.get(self.session.lang).msnMaintenance)
- def contactStatusChanged(self, remoteUser):
- if(self.session): # Make sure the transport isn't shutting down
- debug.log("LegacyConnection: \"%s\" - contactStatusChanged(\"%s\")" % (self.session.jabberID, remoteUser))
- self.sendMSNUserPresence(remoteUser)
-
- def ourStatusChanged(self, statusCode):
- # Send out a new presence packet to the Jabber user so that the MSN-t icon changes
- if(self.session):
- source = config.jid
- to = self.session.jabberID
- show, ptype = state2presence(statusCode)
- debug.log("LegacyConnection: \"%s\" - ourStatusChanged(\"%s\")" % (self.session.jabberID, statusCode))
- self.session.sendPresence(to=to, fro=source, show=show)
+ 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.session.jabberID, fro=msn2jid(passport), body=text)
+ self.session.sendMessage(to=self.jabberID, fro=msn2jid(passport, True), body=text)
- def userAddedMe(self, userHandle):
- self.subscriptions.msnContactAddedMe(userHandle)
-
- def userRemovedMe(self, userHandle):
- self.subscriptions.msnContactRemovedMe(userHandle)
+ def loggedIn(self):
+ LogEvent(INFO, self.jabberID)
- def serverGoingDown(self):
- if(self.session):
- self.session.sendMessage(to=self.session.jabberID, fro=config.jid, body=lang.get(self.session.lang).msnMaintenance)
+ def listSynchronized(self):
+ LogEvent(INFO, self.jabberID)
+ self.session.sendPresence(to=self.jabberID, fro=config.jid)
+ self.legacyList.syncJabberLegacyLists()
+ self.listSynced = True
+ self.session.ready = True
+ #self.legacyList.flushSubscriptionBuffer()
+
+ 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
+
+ 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)
+
+ # Reset the counter
+ self.contactTyping[contact] = 0
- def multipleLogin(self):
- if(self.session):
- self.session.sendMessage(to=self.session.jabberID, fro=config.jid, body=lang.get(self.session.lang).msnMultipleLogin)
- self.session.removeMe()
+ 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
+ c.updateAvatar(av)
+ else:
+ 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 accountNotVerified(self):
- if(self.session):
- text = lang.get(self.session.lang).msnNotVerified % (self.session.username)
- self.session.sendMessage(to=self.session.jabberID, fro=config.jid, body=text)
+ def contactStatusChanged(self, remoteUser):
+ LogEvent(INFO, self.jabberID)
+
+ msnContact = self.getContacts().getContact(remoteUser)
+ c = self.session.contactList.findContact(msn2jid(remoteUser, False))
+ if not (c and msnContact): return
+
+ show, ptype = state2presence(msnContact.status)
+ status = msnContact.personal.decode("utf-8")
+ screenName = msnContact.screenName.decode("utf-8")
+
+ c.updateNickname(screenName, push=False)
+ c.updatePresence(show, status, ptype, force=True)
+
+ def gotFileReceive(self, fileReceive):
+ LogEvent(INFO, self.jabberID)
+ # FIXME
+ ft.FTReceive(self.session, msn2jid(fileReceive.userHandle, True), fileReceive)
+
+ def contactAddedMe(self, userHandle):
+ LogEvent(INFO, self.jabberID)
+ self.session.contactList.getContact(msn2jid(userHandle, False)).contactRequestsAuth()
+
+ def contactRemovedMe(self, userHandle):
+ LogEvent(INFO, self.jabberID)
+ c = self.session.contactList.getContact(msn2jid(userHandle, True))
+ c.contactDerequestsAuth()
+ c.contactRemovesAuth()
+
+ 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 loginFailure(self, message):
- if(self.session):
- 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")
+ 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 failedMessage(self, remoteUser, message):
- if(self.session):
- 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 initialEmailNotification(self, inboxunread, foldersunread):
- text = lang.get(self.session.lang).msnInitialMail % (inboxunread, foldersunread)
- self.session.sendMessage(to=self.session.jabberID, fro=config.jid, body=text, mtype="headline")
+
+
+class LegacyList:
+ def __init__(self, session):
+ self.jabberID = session.jabberID
+ self.session = session
- def realtimeEmailNotification(self, mailfrom, fromaddr, subject):
- 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 removeMe(self):
+ self.session = None
+
+ def addContact(self, jid):
+ LogEvent(INFO, self.jabberID)
+ userHandle = jid2msn(jid)
+ self.session.legacycon.addContact(msn.FORWARD_LIST, userHandle)
+
+ # Handle adding a contact that has previously been removed
+ msnContact = self.session.legacycon.getContacts().getContact(userHandle)
+ if msnContact and msnContact.lists & msn.REVERSE_LIST:
+ self.session.legacycon.contactAddedMe(userHandle)
+ self.authContact(jid)
+ self.session.contactList.getContact(jid).contactGrantsAuth()
+
+ def removeContact(self, jid):
+ LogEvent(INFO, self.jabberID)
+ jid = jid2msn(jid)
+ self.session.legacycon.remContact(msn.FORWARD_LIST, jid)
+
+
+ def authContact(self, jid):
+ LogEvent(INFO, self.jabberID)
+ userHandle = jid2msn(jid)
+ d = self.session.legacycon.remContact(msn.PENDING_LIST, userHandle)
+ if d:
+ self.session.legacycon.addContact(msn.REVERSE_LIST, userHandle)
+ self.session.legacycon.remContact(msn.BLOCK_LIST, userHandle)
+ self.session.legacycon.addContact(msn.ALLOW_LIST, userHandle)
+
+ def deauthContact(self, jid):
+ LogEvent(INFO, self.jabberID)
+ 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)
+
+
+
+ def syncJabberLegacyLists(self):
+ """ Synchronises the MSN contact list on server with the Jabber contact list """
+
+ global defaultAvatar
+
+ # 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.jabberID, "Start.")
+ result = self.session.pytrans.xdb.request(self.jabberID, disco.IQROSTER)
+ oldContactList = msn.MSNContactList()
+ if result:
+ for item in result.elements():
+ user = item.getAttribute("jid")
+ sub = item.getAttribute("subscription")
+ lists = item.getAttribute("lists")
+ if not lists:
+ lists = jabsub2msnlist(sub) # Backwards compatible
+ lists = int(lists)
+ contact = msn.MSNContact(userHandle=user, screenName="", lists=lists)
+ oldContactList.addContact(contact)
- def connectionLost(self, reason):
- if(self.session):
- debug.log("LegacyConnection: \"%s\" - connectionLost(\"%s\")" % (self.session.jabberID, reason))
- 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
+ newXDB = Element((None, "query"))
+ newXDB.attributes["xmlns"] = disco.IQROSTER
+
+ contactList = self.session.legacycon.getContacts()
+
+ # Convienence functions
+ def addedToList(num):
+ return (not (oldLists & num) and (lists & num))
+ def removedFromList(num):
+ return ((oldLists & num) and not (lists & num))
+
+ for contact in contactList.contacts.values():
+ # Compare with the XDB <item/> entry
+ oldContact = oldContactList.getContact(contact.userHandle)
+ if oldContact == None:
+ oldLists = 0
+ else:
+ oldLists = oldContact.lists
+ lists = contact.lists
+
+ # 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, False), msnlist2jabsub(oldLists))
+ jabContact.updateAvatar(defaultAvatar, push=False)
+
+ if addedToList(msn.FORWARD_LIST):
+ jabContact.syncGroups(getGroupNames(contact, contactList), push=False)
+ jabContact.syncContactGrantedAuth()
+
+ if removedFromList(msn.FORWARD_LIST):
+ jabContact.syncContactRemovedAuth()
+
+ if addedToList(msn.ALLOW_LIST):
+ jabContact.syncUserGrantedAuth()
+
+ if addedToList(msn.BLOCK_LIST) or removedFromList(msn.ALLOW_LIST):
+ jabContact.syncUserRemovedAuth()
+
+ if (not (lists & msn.ALLOW_LIST) and not (lists & msn.BLOCK_LIST) and (lists & msn.REVERSE_LIST)) or (lists & msn.PENDING_LIST):
+ jabContact.contactRequestsAuth()
+
+ if removedFromList(msn.REVERSE_LIST):
+ jabContact.contactDerequestsAuth()
+
+ jabContact.syncRoster()
+
+ item = newXDB.addElement("item")
+ item.attributes["jid"] = contact.userHandle
+ item.attributes["subscription"] = msnlist2jabsub(lists)
+ item.attributes["lists"] = str(lists)
+
+ # Update the XDB
+ self.session.pytrans.xdb.set(self.jabberID, disco.IQROSTER, newXDB)
+ LogEvent(INFO, self.jabberID, "End.")
+
+ def saveLegacyList(self):
+ contactList = self.session.legacycon.getContacts()
+ if not contactList: return
+
+ newXDB = Element((None, "query"))
+ newXDB.attributes["xmlns"] = disco.IQROSTER
+
+ for contact in contactList.contacts.values():
+ item = newXDB.addElement("item")
+ item.attributes["jid"] = contact.userHandle
+ item.attributes["subscription"] = msnlist2jabsub(contact.lists) # Backwards compat
+ item.attributes["lists"] = str(contact.lists)
+
+ self.session.pytrans.xdb.set(self.jabberID, disco.IQROSTER, newXDB)
+ LogEvent(INFO, self.jabberID, "Finished saving list.")
+