# Twisted imports
from twisted.internet import reactor, task
from twisted.internet.defer import Deferred
-from twisted.internet.protocol import ClientFactory
+from twisted.internet.protocol import ReconnectingClientFactory, ClientFactory
try:
from twisted.internet.ssl import ClientContextFactory
except ImportError:
MSNEventBase.connectionMade(self)
self._setState('CONNECTED')
self.sendLine("VER %s %s" % (self._nextTransactionID(), MSN_PROTOCOL_VERSION))
+ self.factory.resetDelay()
def connectionLost(self, reason):
self._setState('DISCONNECTED')
def handle_OUT(self, params):
checkParamLen(len(params), 1, 'OUT')
+ self.factory.stopTrying()
if params[0] == "OTH": self.multipleLogin()
elif params[0] == "SSD": self.serverGoingDown()
else: raise MSNProtocolError, "Invalid Parameters received for OUT" # debug
if self.pingCheckTask:
self.pingCheckTask.stop()
self.pingCheckTask = None
+ self.factory.stopTrying()
self.sendLine("OUT")
self.transport.loseConnection()
-class NotificationFactory(ClientFactory):
+class NotificationFactory(ReconnectingClientFactory):
"""
Factory for the NotificationClient protocol.
This is basically responsible for keeping
(the whole URL is required)
@ivar status: The status of the client -- this is generally kept
up to date by the default command handlers
+ @ivar maxRetries: The number of times the factory will reconnect
+ if the connection dies because of a network error.
"""
contacts = None
passportServer = 'https://nexus.passport.com/rdr/pprdr.asp'
status = 'FLN'
protocol = NotificationClient
+ maxRetries = 5
class SwitchboardClient(MSNEventBase):
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):
"""
self.notificationClient.changeStatus(statusCode.encode("utf-8")).addCallback(cb)
self.notificationClient.changeScreenName(screenName.encode("utf-8")).addCallback(cb)
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 """
msn.NotificationClient.logOut(self)
def connectionLost(self, reason):
+ print "NotificationClient.connectionLost!!!"
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)
msn.PINGSPEED = 0.1
self.client = DummyPingNotificationClient()
self.server = DummyPingNotificationServer()
+ self.client.factory = msn.NotificationFactory()
+ self.server.factory = msn.NotificationFactory()
self.client.state = 'CONNECTED'
self.client.count = 0
self.loop = LoopbackCon(self.client, self.server)
log.startLogging(sys.stdout)
-checkCount = 0 # Uck!
def clearAccount(msncon):
""" Clears the contact list of the given MSNConnection. Returns a
Deferred which fires when the task is complete.
"""
d = Deferred()
count = 0
- global checkCount
- checkCount = 0
+ checkCount = [0]
def cb(ignored=None):
- global checkCount
- checkCount += 1
- if checkCount == count:
+ checkCount[0] += 1
+ if checkCount[0] == count:
d.callback(None)
for msnContact in msncon.getContacts().contacts.values():
self.loop("Adding user1 to user2's allow list.")
# Check the contacts have seen each other
- reactor.iterate(0.1) # One last chance to notice each other
+ reactor.iterate(0.5) # One last chance to notice each other
self.failUnless((self.user1.contactAdded == USER2 and self.user2.contactAdded == USER1), "Contacts can't see each other.")
def cb(self, ignored=None):
def tearDown(self):
TestsUtil.tearDown(self)
+ def testReconnect(self):
+ self.user1 = MSNConnection(USER1, PASS1, "user1", self)
+ self.loop("Looping user1.", cond="SYNCED")
+ self.user1.notificationClient.transport.loseConnection()
+ self.done = False
+ self.loop("Looping user1.", cond="SYNCED")
+ testReconnect.skip = FTRECEIVETEST or FTSENDTEST
+
def testConnect(self):
self.doLogins(both=False)
testConnect.skip = FTRECEIVETEST or FTSENDTEST