X-Git-Url: https://code.delx.au/pymsnt/blobdiff_plain/8cf8ec9e82b032d969eb0cfe26cb12a0d661da09..38edf008bd005c4fc3a8cecba0379640f8bea4f6:/src/tlib/msn/msnw.py diff --git a/src/tlib/msn/msnw.py b/src/tlib/msn/msnw.py index 1e4f03f..1060293 100644 --- a/src/tlib/msn/msnw.py +++ b/src/tlib/msn/msnw.py @@ -7,17 +7,13 @@ from twisted.internet.defer import Deferred from twisted.internet.protocol import ClientFactory # System imports -import math, base64, binascii, math +import math, base64, binascii # Local imports from debug import LogEvent, INFO, WARN, ERROR from tlib.msn import msn -MAXMESSAGESIZE = 1400 -SWITCHBOARDTIMEOUT = 30.0*60.0 -GETALLAVATARS = False - """ All interaction should be with the MSNConnection and MultiSwitchboardSession classes. @@ -26,6 +22,10 @@ You should not directly instantiate any objects of other classes. class MSNConnection: """ Manages all the Twisted factories, etc """ + MAXMESSAGESIZE = 1400 + SWITCHBOARDTIMEOUT = 30.0*60.0 + GETALLAVATARS = False + def __init__(self, username, password, ident): """ Connects to the MSN servers. @param username: the MSN passport to connect with. @@ -36,6 +36,7 @@ class MSNConnection: self.password = password self.ident = ident self.timeout = None + self.notificationFactory = None self.notificationClient = None self.connect() LogEvent(INFO, self.ident) @@ -49,7 +50,10 @@ class MSNConnection: def _getNotificationReferral(self): def timeout(): - if not d.called: d.errback() + self.timeout = None + if not d.called: + d.errback(Exception("Timeout")) + self.logOut() # Clean up everything self.timeout = reactor.callLater(30, timeout) dispatchFactory = msn.DispatchFactory() dispatchFactory.userHandle = self.username @@ -62,6 +66,7 @@ class MSNConnection: def _gotNotificationReferral(self, (host, port)): self.timeout.cancel() + self.timeout = None # Create the NotificationClient self.notificationFactory = msn.NotificationFactory() self.notificationFactory.userHandle = self.username @@ -73,7 +78,6 @@ class MSNConnection: def _sendSavedEvents(self): self.savedEvents.send(self) - self.savedEvents = None def _notificationClientReady(self, notificationClient): self.notificationClient = notificationClient @@ -126,7 +130,7 @@ class MSNConnection: LogEvent(INFO, self.ident) if not self.notificationClient: return - if GETALLAVATARS: + if MSNConnection.GETALLAVATARS: self._ensureSwitchboardSession(userHandle) sb = self.switchboardSessions.get(userHandle) if sb: return sb.sendAvatarRequest() @@ -141,7 +145,7 @@ class MSNConnection: @return: A Deferred, which will fire with an argument of: (fileSend, d) A FileSend object and a Deferred. - The Deferred will pass one argument in a tuple, + The new Deferred will pass one argument in a tuple, whether or not the transfer is accepted. If you receive a True, then you can call write() on the fileSend object to send your file. Call close() @@ -172,8 +176,8 @@ class MSNConnection: if self.notificationClient: LogEvent(INFO, self.ident) self.notificationClient.changeAvatar(imageData, push=True) - else: - self.savedEvents.avatarImageData = imageData + # Save the avatar for reuse on disconnection + self.savedEvents.avatarImageData = imageData def changeStatus(self, statusCode, screenName, personal): """ @@ -186,22 +190,22 @@ class MSNConnection: """ if not screenName: screenName = self.username + if not statusCode: statusCode = msn.STATUS_ONLINE + if not personal: personal = "" if self.notificationClient: - self.changeCount = 0 + changeCount = [0] # Hack def cb(ignored=None): - self.changeCount += 1 - if self.changeCount == 3: + changeCount[0] += 1 + if changeCount[0] == 3: self.ourStatusChanged(statusCode, screenName, personal) - del self.changeCount LogEvent(INFO, self.ident) self.notificationClient.changeStatus(statusCode.encode("utf-8")).addCallback(cb) self.notificationClient.changeScreenName(screenName.encode("utf-8")).addCallback(cb) - if not personal: personal = "" self.notificationClient.changePersonalMessage(personal.encode("utf-8")).addCallback(cb) - else: - self.savedEvents.statusCode = statusCode - self.savedEvents.screenName = screenName - self.savedEvents.personal = personal + # Remember the saved status + self.savedEvents.statusCode = statusCode + self.savedEvents.screenName = screenName + self.savedEvents.personal = personal def addContact(self, listType, userHandle): """ See msn.NotificationClient.addContact """ @@ -219,7 +223,7 @@ class MSNConnection: def logOut(self): """ Shuts down the whole connection. Don't try to call any - other methods after this one. """ + other methods after this one. Except maybe connect() """ if self.notificationClient: self.notificationClient.logOut() for c in self.connectors: @@ -227,7 +231,13 @@ class MSNConnection: if self.notificationFactory: self.notificationFactory.msncon = None self.connectors = [] + for sbs in self.switchboardSessions.values(): + if hasattr(sbs, "transport") and sbs.transport: + sbs.transport.loseConnection() self.switchboardSessions = {} + if self.timeout: + self.timeout.cancel() + self.timeout = None LogEvent(INFO, self.ident) @@ -324,7 +334,7 @@ class MSNConnection: class SavedEvents: def __init__(self): - self.nickname = "" + self.screenName = "" self.statusCode = "" self.personal = "" self.avatarImageData = "" @@ -334,8 +344,8 @@ class SavedEvents: def send(self, msncon): if self.avatarImageData: msncon.notificationClient.changeAvatar(self.avatarImageData, push=False) - if self.nickname or self.statusCode or self.personal: - msncon.changeStatus(self.statusCode, self.nickname, self.personal) + if self.screenName or self.statusCode or self.personal: + msncon.changeStatus(self.statusCode, self.screenName, self.personal) for listType, userHandle in self.addContacts: msncon.addContact(listType, userHandle) for listType, userHandle in self.remContacts: @@ -350,6 +360,10 @@ class DispatchClient(msn.DispatchClient): class NotificationClient(msn.NotificationClient): + def doDisconnect(self, *args): + if hasattr(self, "transport") and self.transport: + self.transport.loseConnection() + def loginFailure(self, message): self.factory.msncon.loginFailed(message) @@ -363,13 +377,18 @@ class NotificationClient(msn.NotificationClient): def logOut(self): msn.NotificationClient.logOut(self) + # If we explicitly log out, then all of these events + # are now redundant + self.loginFailure = self.doDisconnect + self.loggedIn = self.doDisconnect + self.connectionLost = self.doDisconnect def connectionLost(self, reason): - if not self.factory.msncon: return # If we called logOut def wait(): LogEvent(INFO, self.factory.msncon.ident) msn.NotificationClient.connectionLost(self, reason) - self.factory.msncon.connectionLost(reason) + if self.factory.maxRetries >= self.factory.retries: + self.factory.msncon.connectionLost(reason) # Make sure this event is handled after any others reactor.callLater(0, wait) @@ -450,9 +469,16 @@ class SwitchboardSessionBase(msn.SwitchboardClient): self.ready = False def __del__(self): + self.ready = False LogEvent(INFO, self.ident) del self.msncon self.transport.disconnect() + + def connectionLost(self, reason): + self.ready = False + self.msncon = None + self.msnobj = None + self.ident = (self.ident[0], self.ident[1] + " Disconnected!") def loggedIn(self): LogEvent(INFO, self.ident) @@ -522,13 +548,13 @@ class SwitchboardSessionBase(msn.SwitchboardClient): self.messageBuffer.append((text, noerror)) else: LogEvent(INFO, self.ident) + text = str(text.replace("\n", "\r\n").encode("utf-8")) def failedMessage(ignored): if not noerror: self.failedMessage(text) - if len(text) < MAXMESSAGESIZE: - message = msn.MSNMessage(message=str(text.replace("\n", "\r\n").encode("utf-8"))) - message.setHeader("Content-Type", "text/plain; charset=UTF-8") + if len(text) < MSNConnection.MAXMESSAGESIZE: + message = msn.MSNMessage(message=text) message.ack = msn.MSNMessage.MESSAGE_NACK d = msn.SwitchboardClient.sendMessage(self, message) @@ -536,18 +562,19 @@ class SwitchboardSessionBase(msn.SwitchboardClient): d.addCallback(failedMessage) else: - chunks = int(math.ceil(len(text) / float(MAXMESSAGESIZE))) + chunks = int(math.ceil(len(text) / float(MSNConnection.MAXMESSAGESIZE))) chunk = 0 guid = msn.random_guid() while chunk < chunks: - offset = chunk * MAXMESSAGESIZE - text = message[offset : offset + MAXMESSAGESIZE] - message = msn.MSNMessage(message=str(text.replace("\n", "\r\n").encode("utf-8"))) + offset = chunk * MSNConnection.MAXMESSAGESIZE + message = msn.MSNMessage(message=text[offset : offset + MSNConnection.MAXMESSAGESIZE]) message.ack = msn.MSNMessage.MESSAGE_NACK + message.setHeader("Message-ID", guid) if chunk == 0: - message.setHeader("Content-Type", "text/plain; charset=UTF-8") message.setHeader("Chunks", str(chunks)) else: + message.delHeader("MIME-Version") + message.delHeader("Content-Type") message.setHeader("Chunk", str(chunk)) d = msn.SwitchboardClient.sendMessage(self, message) @@ -615,9 +642,12 @@ class OneSwitchboardSession(SwitchboardSessionBase): self.timeout = None def __del__(self): + if self.timeout: + self.timeout.cancel() + self.timeout = None for message, noerror in self.messageBuffer: if not noerror: - self.failedMessage(self.remoteUser, message) + self.failedMessage(message) def _ready(self): LogEvent(INFO, self.ident) @@ -636,9 +666,7 @@ class OneSwitchboardSession(SwitchboardSessionBase): del self.remoteUser self.contactCount = 0 self.msncon.gotGroupchat(self, userHandle) - if not self.groupchat: - LogEvent(ERROR, self.ident) - raise Exception("YouNeedAGroupchat-WeHaveAProblemError") # FIXME + assert self.groupchat def failedMessage(self, text): self.msncon.failedMessage(self.remoteUser, text) @@ -648,8 +676,11 @@ class OneSwitchboardSession(SwitchboardSessionBase): LogEvent(INFO, self.ident) if not self.reply: def failCB(arg=None): + if not (self.msncon and self.msncon.switchboardSessions.has_key(self.remoteUser)): + return LogEvent(INFO, self.ident, "User has not joined after 30 seconds.") del self.msncon.switchboardSessions[self.remoteUser] + self.timeout = None d = self.inviteUser(self.remoteUser) d.addErrback(failCB) self.timeout = reactor.callLater(30.0, failCB) @@ -681,7 +712,8 @@ class OneSwitchboardSession(SwitchboardSessionBase): def userLeft(self, userHandle): def wait(): if userHandle == self.remoteUser: - del self.msncon.switchboardSessions[self.remoteUser] + if self.msncon and self.msncon.switchboardSessions.has_key(self.remoteUser): + del self.msncon.switchboardSessions[self.remoteUser] reactor.callLater(0, wait) # Make sure this is handled after everything else def gotMessage(self, message): @@ -695,12 +727,12 @@ class OneSwitchboardSession(SwitchboardSessionBase): text = message.getMessage() self.msncon.gotMessage(self.remoteUser, text) except: - self.msncon.gotMessage("A message was lost.") + self.msncon.gotMessage(self.remoteUser, "A message was lost.") raise elif "text/x-clientcaps" == cTypes[0]: if message.hasHeader("JabberID"): jid = message.getHeader("JabberID") - self.switchboardSession.msncon.userMapping(message.userHandle, jid) + self.msncon.userMapping(message.userHandle, jid) else: LogEvent(INFO, self.ident, "Discarding unknown message type.")