# -*- coding: utf-8 -*- # # Copyright 2010 Maurizio Porrato # See LICENSE.txt for copyright info from twisted.internet.defer import Deferred, succeed from twisted.internet.protocol import ReconnectingClientFactory, ServerFactory from twisted.protocols.basic import LineOnlyReceiver from twisted.internet.task import LoopingCall from twisted.python import log from frn.user import FRNUser from frn.protocol import versions from frn.utility import * class FRNManagerClient(LineOnlyReceiver): PING_PERIOD = 3.0 # Seconds between ping requests def connectionMade(self): log.msg("Connected to manager [%s]" % self.transport.getPeer().host) self.notifications = [] if not self.factory.managerConnection.called: # FIXME: Why??? log.msg("Firing manager connection callback!") self.factory.managerConnection.callback(self) def connectionLost(self, reason): log.msg("Manager disconnected") try: self.pingtimer.stop() except: pass for d in self.notifications: d.errback(reason) self.factory.managerConnection = Deferred() def notifyFinish(self): self.notifications.append(Deferred()) return self.notifications[-1] def finish(self, result): d = self.notifications[0] del self.notifications[0] d.callback(result) def lineReceived(self, line): #log.msg("notifications: %s" % str(self.notifications)) if hasattr(self, 'serverlist'): # TODO pass else: self.finish(line.strip()) def sendServerLogin(self, user): def loginDone(result): self.managerdata = parseSimpleXML(result) log.msg("Server login results: %s" % str(self.managerdata)) if int(self.managerdata['mc']) > 2009004: self.sendLine(responseToChallange( self.managerdata['kp'])) if self.managerdata['al'] != '0': self.transport.loseConnection() else: self.pingtimer = LoopingCall(self.sendPing) self.pingtimer.start(self.PING_PERIOD, False) self.factory.resetDelay() return self.managerdata log.msg("Sending server login") user.VX = versions.server self.sendLine("SC:"+user.asXML( 'VX','SN','PT','OW','PW')) return self.notifyFinish().addCallback(loginDone) def sendServerLogout(self, user): self.transport.loseConnection() return succeed(None) def sendPing(self): self.sendLine("P") return self.notifyFinish() def sendClientLogin(self, client): self.sendLine("CC:"+client.asXML( 'EA','PW','ON','BC','NN','CT','NT','DS','IP')) return self.notifyFinish() def sendClientLogout(self, client): self.sendLine("CD:"+client.asXML('ID')) return self.notifyFinish() def getClientList(self): #self.sendLine('SM') raise NotImplementedError # TODO def sendRegisterUser(self, user): self.sendLine("IG:"+user.asXML( 'ON','EA','BC','DS','NN','CT')) return self.notifyFinish() class FRNManagerClientFactory(ReconnectingClientFactory): protocol = FRNManagerClient def startFactory(self): self.managerConnection = Deferred() class FRNManagerServer(LineOnlyReceiver): def connectionMade(self): log.msg("Manager client connected from %s" % self.transport.getPeer().host) self._phase = "CONNECTED" self.kp = makeRandomChallange() def connectionLost(self, reason): log.msg("Manager client disconnected from %s: %s" % (self.transport.getPeer().host, reason)) self.manager.serverLogout(None) # FIXME def _authOnly(self): if self._phase != "AUTHENTICATED": log.msg("Unauthorized action!") self.transport.loseConnection() def lineReceived(self, line): sline = line.strip() if self._phase == "CHALLANGE": if sline == responseToChallange(self.kp): self._phase = "AUTHENTICATED" else: self.transport.loseConnection() else: if sline == 'P': # Ping self.sendLine('F') elif sline == 'SM': # Client list self.sendClientList() else: cmd, body = sline.split(':', 1) xbody = parseSimpleXML(body) handler = getattr(self, 'decode'+cmd, None) if handler is None: self.unimplemented(cmd, xbody) else: handler(xbody) def sendClientList(self): log.msg("SM") self.sendLine('0') # TODO def unimplemented(self, cmd, body): log.err("Unimplemented command %s: %s" % (cmd, str(body))) def decodeSC(self, body): # Server login def sendManagerInfo(res): if versions.manager > 2009004: self._phase = "CHALLANGE" else: self._phase = "AUTHENTICATED" self.sendLine(formatSimpleXML([ ('SV', versions.server), ('CV', versions.client), ('MC', versions.manager), ('AL', res['al']), ('KP', self.kp) ])) log.msg("SC: %s" % str(body)) self.manager.serverLogin(FRNUser(**body)).addCallback( sendManagerInfo) # TODO: second authentication phase def decodeCC(self, body): # Client login log.msg("CC: %s" % str(body)) self._authOnly() self.manager.clientLogin(FRNUser(**body)).addCallback( self.sendLine) def decodeCD(self, body): # Client logout log.msg("CD: %s" % str(body)) self._authOnly() self.manager.clientLogout(FRNUser(**body)).addCallback( self.sendLine) def decodeIG(self, body): # Account creation log.msg("IG: %s" % str(body)) self.manager.registerUser(FRNUser(**body)).addCallback( self.sendLine) class FRNManagerServerFactory(ServerFactory): protocol = FRNManagerServer def __init__(self, managerFactory): self.managerFactory = managerFactory def buildProtocol(self, addr): p = ServerFactory.buildProtocol(self, addr) p.manager = self.managerFactory() return p # vim: set et ai sw=4 ts=4 sts=4: