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
def _getNotificationReferral(self):
def timeout():
- if not d.called: d.errback()
+ self.timeout = None
+ dispatchFactory.d = 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
def _gotNotificationReferral(self, (host, port)):
self.timeout.cancel()
+ self.timeout = None
# Create the NotificationClient
self.notificationFactory = msn.NotificationFactory()
self.notificationFactory.userHandle = self.username
def _sendSavedEvents(self):
self.savedEvents.send(self)
- self.savedEvents = None
def _notificationClientReady(self, notificationClient):
self.notificationClient = notificationClient
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):
"""
"""
if not screenName: screenName = self.username
+ if not statusCode: statusCode = msn.STATUS_ONLINE
+ if not personal: personal = ""
if self.notificationClient:
- changeCount = [0] # Hack
+ changeCount = [0] # Hack for Python's limited scope :(
def cb(ignored=None):
changeCount[0] += 1
if changeCount[0] == 3:
self.ourStatusChanged(statusCode, screenName, personal)
+ def errcb(ignored=None):
+ pass # FIXME, should we do something here?
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
+ self.notificationClient.changeStatus(statusCode.encode("utf-8")).addCallbacks(cb, errcb)
+ self.notificationClient.changeScreenName(screenName.encode("utf-8")).addCallbacks(cb, errcb)
+ self.notificationClient.changePersonalMessage(personal.encode("utf-8")).addCallbacks(cb, errcb)
+ # 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 """
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:
c.disconnect()
+ self.connectors = []
if self.notificationFactory:
+ self.notificationFactory.stopTrying()
self.notificationFactory.msncon = None
- self.connectors = []
+ self.notificationFactory = None
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)
class DispatchClient(msn.DispatchClient):
def gotNotificationReferral(self, host, port):
- if self.factory.d.called: return # Too slow! We've already timed out
- self.factory.d.callback((host, port))
+ d = self.factory.d
+ self.factory.d = None
+ if not d or d.called:
+ return # Too slow! We've already timed out
+ d.callback((host, port))
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)
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 = lambda reason: msn.NotificationClient.connectionLost(self, reason)
def connectionLost(self, reason):
- if not self.factory.msncon: return # If we called logOut
+ if not self.factory.msncon:
+ # If MSNConnection.logOut is called before _notificationClientReady
+ return
+
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.stopTrying()
+ self.factory.msncon.connectionLost(reason)
# Make sure this event is handled after any others
reactor.callLater(0, wait)
sb = self.factory.msncon.switchboardSessions.get(userHandle)
if sb and sb.transport:
sb.transport.loseConnection()
- else:
- sb = OneSwitchboardSession(self.factory.msncon, userHandle)
- self.factory.msncon.switchboardSessions[userHandle] = sb
+ sb = OneSwitchboardSession(self.factory.msncon, userHandle)
+ self.factory.msncon.switchboardSessions[userHandle] = sb
sb.connectReply(host, port, key, sessionID)
def multipleLogin(self):
self.funcBuffer = []
self.ready = False
- def __del__(self):
+ def connectionLost(self, reason):
+ msn.SwitchboardClient.connectionLost(self, reason)
LogEvent(INFO, self.ident)
- del self.msncon
- self.transport.disconnect()
+ 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)
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) < MSNConnection.MAXMESSAGESIZE:
- message = msn.MSNMessage(message=str(text.replace("\n", "\r\n").encode("utf-8")))
- message.setHeader("Content-Type", "text/plain; charset=UTF-8")
+ message = msn.MSNMessage(message=text)
message.ack = msn.MSNMessage.MESSAGE_NACK
d = msn.SwitchboardClient.sendMessage(self, message)
if not noerror:
- d.addCallback(failedMessage)
+ d.addCallbacks(failedMessage, failedMessage)
else:
chunks = int(math.ceil(len(text) / float(MSNConnection.MAXMESSAGESIZE)))
guid = msn.random_guid()
while chunk < chunks:
offset = chunk * MSNConnection.MAXMESSAGESIZE
- text = message[offset : offset + MSNConnection.MAXMESSAGESIZE]
- message = msn.MSNMessage(message=str(text.replace("\n", "\r\n").encode("utf-8")))
+ 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)
if not noerror:
- d.addCallback(failedMessage)
+ d.addCallbacks(failedMessage, failedMessage)
chunk += 1
def __init__(self, msncon):
""" Automatically creates a new switchboard connection to the server """
SwitchboardSessionBase.__init__(self, msncon)
- self.ident = (self.msncon.ident, self)
+ self.ident = (self.msncon.ident, repr(self))
self.contactCount = 0
self.groupchat = None
self.connect()
self.chattingUsers = []
self.timeout = None
- def __del__(self):
+ def connectionLost(self, reason):
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)
+ self.messageBuffer = []
+
+ if self.msncon and self.msncon.switchboardSessions.has_key(self.remoteUser):
+ # Unexpected disconnection. Must remove us from msncon
+ self.msncon.switchboardSessions.pop(self.remoteUser)
+
+ SwitchboardSessionBase.connectionLost(self, reason)
def _ready(self):
LogEvent(INFO, self.ident)
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)
LogEvent(INFO, self.ident)
if not self.reply:
def failCB(arg=None):
+ self.timeout = None
+ self.transport.loseConnection()
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)
else:
text = message.getMessage()
self.msncon.gotMessage(self.remoteUser, text)
- except:
+ except UnicodeDecodeError:
+ LogEvent(WARN, self.ident, "Message lost!")
self.msncon.gotMessage(self.remoteUser, "A message was lost.")
raise
elif "text/x-clientcaps" == cTypes[0]: