X-Git-Url: https://code.delx.au/pymsnt/blobdiff_plain/fb464b1db58890bd09d582de16282cebd0277021..7b5368b86bbb09516dd47ef8a651b893ac291df3:/src/legacy/glue.py diff --git a/src/legacy/glue.py b/src/legacy/glue.py index afd8038..efcd32f 100644 --- a/src/legacy/glue.py +++ b/src/legacy/glue.py @@ -1,13 +1,13 @@ # Copyright 2004-2005 James Bunton # Licensed for distribution under the GPL version 2, check COPYING for details -import utils -from twisted.internet import task -from tlib.xmlw 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 sha import groupchat import ft import avatar @@ -17,24 +17,28 @@ import lang -name = "MSN Transport" # The name of the transport url = "http://msn-transport.jabberstudio.org" -version = "0.11-dev" # The transport version +version = "0.11.3-dev" # The transport version 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() -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) +def reloadConfig(): + msn.MSNConnection.GETALLAVATARS = config.getAllAvatars + msn.MSNConnection.BINDADDRESS = config.host + 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) @@ -92,15 +96,45 @@ def updateStats(statistics): #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): @@ -113,6 +147,7 @@ def presence2state(show, ptype): return msn.STATUS_BUSY elif show == "away" or show == "xa": return msn.STATUS_AWAY + return msn.STATUS_ONLINE def state2presence(state): @@ -227,7 +262,7 @@ class LegacyConnection(msn.MSNConnection): self.remoteNick = "" # Init the MSN bits - msn.MSNConnection.__init__(self, username, password, self.session.jabberID) + 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 @@ -239,10 +274,10 @@ class LegacyConnection(msn.MSNConnection): self.legacyList = LegacyList(self.session) - LogEvent(INFO, self.session.jabberID) + LogEvent(INFO, self.jabberID) def removeMe(self): - LogEvent(INFO, self.session.jabberID) + LogEvent(INFO, self.jabberID) self.userTypingSend.stop() @@ -251,15 +286,15 @@ class LegacyConnection(msn.MSNConnection): self.session = None self.logOut() - def _sendShowStatus(self): + + # Implemented from baseproto + def sendShowStatus(self, jid=None): 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) + if not jid: + jid = self.jabberID + self.session.sendPresence(to=jid, fro=source, show=self.remoteShow, status=self.remoteStatus, nickname=self.remoteNick) - - - # Implemented from baseproto def resourceOffline(self, resource): pass @@ -278,6 +313,22 @@ class LegacyConnection(msn.MSNConnection): self.failedMessage(dest, body) raise + 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() + + d = msn.MSNConnection.sendFile(self, dest, ftSend.filename, ftSend.filesize) + d.addCallbacks(continueSendFile1, sendFileFail) + def setStatus(self, nickname, show, status): statusCode = presence2state(show, None) msn.MSNConnection.changeStatus(self, statusCode, nickname, status) @@ -286,9 +337,9 @@ class LegacyConnection(msn.MSNConnection): global defaultJabberAvatarData if av: - msn.MSNConnection.changeAvatar(self, av.getImageData()) + msn.MSNConnection.changeAvatar(self, av.getImageData) else: - msn.MSNConnection.changeAvatar(self, defaultJabberAvatarData) + msn.MSNConnection.changeAvatar(self, lambda: defaultJabberAvatarData) def sendTypingNotifications(self): if not self.session: return @@ -302,7 +353,7 @@ class LegacyConnection(msn.MSNConnection): for contact in self.contactTyping.keys(): self.contactTyping[contact] += 1 if self.contactTyping[contact] >= 3: - self.session.sendTypingNotification(self.session.jabberID, msn2jid(contact), False) + self.session.sendTypingNotification(self.jabberID, msn2jid(contact, True), False) del self.contactTyping[contact] def userTypingNotification(self, dest, resource, composing): @@ -316,90 +367,93 @@ class LegacyConnection(msn.MSNConnection): # Implement callbacks from msn.MSNConnection def connectionFailed(self, reason): - LogEvent(INFO, self.session.jabberID) + LogEvent(INFO, self.jabberID) text = lang.get(self.session.lang).msnConnectFailed % reason - self.session.sendMessage(to=self.session.jabberID, fro=config.jid, body=text) + self.session.sendMessage(to=self.jabberID, fro=config.jid, body=text) self.session.removeMe() def loginFailed(self, reason): - LogEvent(INFO, self.session.jabberID) + LogEvent(INFO, self.jabberID) 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.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.session.jabberID) - text = lang.get(self.session.lang).msnDisconnected % reason - self.session.sendMessage(to=self.session.jabberID, fro=config.jid, body=text) + 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.session.jabberID) - self.session.sendMessage(to=self.session.jabberID, fro=config.jid, body=lang.get(self.session.lang).msnMultipleLogin) + 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.session.jabberID) - self.session.sendMessage(to=self.session.jabberID, fro=config.jid, body=lang.get(self.session.lang).msnMaintenance) + 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.session.jabberID) + LogEvent(INFO, self.jabberID) text = lang.get(self.session.lang).msnNotVerified % (self.session.username) - self.session.sendMessage(to=self.session.jabberID, fro=config.jid, body=text) + self.session.sendMessage(to=self.jabberID, fro=config.jid, body=text) def userMapping(self, passport, jid): - LogEvent(INFO, self.session.jabberID) + 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 loggedIn(self): - LogEvent(INFO, self.session.jabberID) - self.session.ready = True + LogEvent(INFO, self.jabberID) def listSynchronized(self): - LogEvent(INFO, self.session.jabberID) - 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.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.session.jabberID) + LogEvent(INFO, self.jabberID) self.remoteShow, ptype = state2presence(statusCode) self.remoteStatus = personal self.remoteNick = screenName - self._sendShowStatus() + self.sendShowStatus() def gotMessage(self, remoteUser, text): - LogEvent(INFO, self.session.jabberID) - source = msn2jid(remoteUser) - self.session.sendMessage(self.session.jabberID, fro=source, body=text, mtype="chat") + 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.session.jabberID) + LogEvent(INFO, self.jabberID) msnGroupchat.groupchat = LegacyGroupchat(self.session, switchboardSession=msnGroupchat) - msnGroupchat.groupchat.sendUserInvite(msn2jid(userHandle)) + msnGroupchat.groupchat.sendUserInvite(msn2jid(userHandle, True)) def gotContactTyping(self, contact): - LogEvent(INFO, self.session.jabberID) + LogEvent(INFO, self.jabberID) # Check if the contact has only just started typing if not self.contactTyping.has_key(contact): - self.session.sendTypingNotification(self.session.jabberID, msn2jid(contact), True) + self.session.sendTypingNotification(self.jabberID, msn2jid(contact, True), True) # Reset the counter self.contactTyping[contact] = 0 def failedMessage(self, remoteUser, message): - LogEvent(INFO, self.session.jabberID) + LogEvent(INFO, self.jabberID) 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) + 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.session.jabberID) - jid = msn2jid(userHandle) + LogEvent(INFO, self.jabberID) + jid = msn2jid(userHandle, False) c = self.session.contactList.findContact(jid) if not c: return @@ -423,49 +477,51 @@ class LegacyConnection(msn.MSNConnection): c.updateAvatar(defaultAvatar) def contactStatusChanged(self, remoteUser): - LogEvent(INFO, self.session.jabberID) + LogEvent(INFO, self.jabberID) 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) - status = msnContact.personal.decode("utf-8") - screenName = msnContact.screenName.decode("utf-8") + status = msnContact.personal.decode("utf-8", "replace") + screenName = msnContact.screenName.decode("utf-8", "replace") c.updateNickname(screenName, push=False) c.updatePresence(show, status, ptype, force=True) def gotFileReceive(self, fileReceive): - LogEvent(INFO, self.session.jabberID) + LogEvent(INFO, self.jabberID) # FIXME - ft.FTReceive(self.session, msn2jid(fileReceive.userHandle), fileReceive) + ft.FTReceive(self.session, msn2jid(fileReceive.userHandle, True), fileReceive) def contactAddedMe(self, userHandle): - LogEvent(INFO, self.session.jabberID) - self.session.contactList.getContact(msn2jid(userHandle)).contactRequestsAuth() + LogEvent(INFO, self.jabberID) + self.session.contactList.getContact(msn2jid(userHandle, False)).contactRequestsAuth() def contactRemovedMe(self, userHandle): - LogEvent(INFO, self.session.jabberID) - c = self.session.contactList.getContact(msn2jid(userHandle)) + LogEvent(INFO, self.jabberID) + c = self.session.contactList.getContact(msn2jid(userHandle, True)) c.contactDerequestsAuth() c.contactRemovesAuth() def gotInitialEmailNotification(self, inboxunread, foldersunread): - LogEvent(INFO, self.session.jabberID) - text = lang.get(self.session.lang).msnInitialMail % (inboxunread, foldersunread) - self.session.sendMessage(to=self.session.jabberID, fro=config.jid, body=text, mtype="headline") + 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): - LogEvent(INFO, self.session.jabberID) - text = lang.get(self.session.lang).msnRealtimeMail % (mailfrom, fromaddr, subject) - self.session.sendMessage(to=self.session.jabberID, fro=config.jid, body=text, mtype="headline") + 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.session.jabberID) + LogEvent(INFO, self.jabberID) el = Element((None, "message")) - el.attributes["to"] = self.session.jabberID + el.attributes["to"] = self.jabberID el.attributes["from"] = config.jid el.attributes["type"] = "headline" body = el.addElement("body") @@ -484,9 +540,9 @@ class LegacyConnection(msn.MSNConnection): self.session.pytrans.send(el) def gotAvatarImageData(self, userHandle, imageData): - LogEvent(INFO, self.session.jabberID) + LogEvent(INFO, self.jabberID) av = self.session.pytrans.avatarCache.setAvatar(imageData) - jid = msn2jid(userHandle) + jid = msn2jid(userHandle, False) c = self.session.contactList.findContact(jid) c.updateAvatar(av) @@ -495,38 +551,44 @@ class LegacyConnection(msn.MSNConnection): class LegacyList: def __init__(self, session): + self.jabberID = session.jabberID self.session = session - self.subscriptionBuffer = [] def removeMe(self): - self.subscriptionBuffer = None 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) + + # 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.session.jabberID) + LogEvent(INFO, self.jabberID) jid = jid2msn(jid) self.session.legacycon.remContact(msn.FORWARD_LIST, jid) def authContact(self, jid): - LogEvent(INFO, self.session.jabberID) - jid = jid2msn(jid) - d = self.session.legacycon.remContact(msn.PENDING_LIST, 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, jid) - self.session.legacycon.remContact(msn.BLOCK_LIST, jid) - self.session.legacycon.addContact(msn.ALLOW_LIST, jid) + 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.session.jabberID) + 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) @@ -538,8 +600,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 - 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(): @@ -576,7 +638,7 @@ class LegacyList: # 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): @@ -597,6 +659,8 @@ class LegacyList: if removedFromList(msn.REVERSE_LIST): jabContact.contactDerequestsAuth() + + jabContact.syncRoster() item = newXDB.addElement("item") item.attributes["jid"] = contact.userHandle @@ -604,8 +668,8 @@ class LegacyList: 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() @@ -620,7 +684,7 @@ class LegacyList: 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.")