# -*- 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 = [] self.factory.managerConnected(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.managerDisconnected(self) 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 managerConnected(self, connection): pass def managerDisconnected(self, connection): pass class FRNManagerServer(LineOnlyReceiver): def connectionMade(self): log.msg("Manager client connected from %s" % self.transport.getPeer().host) self._phase = "CONNECTED" self.serverInfo = None self.kp = makeRandomChallange() def connectionLost(self, reason): log.msg("Manager client disconnected from %s: %s" % (self.transport.getPeer().host, reason)) if self.serverInfo: self.manager.serverLogout(self.serverInfo) self.serverInfo = None self._phase = "DISCONNECTED" 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" self.serverInfo = self.tmpServerInfo log.msg("Auth success: %s" % repr(self.serverInfo)) 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): def gotClientList(cl): self.sendLine(str(len(cl))) for sn, sp in cl: self.sendLine("%s - Port: %d" % (sn, sp)) self.sendLine(str(len(cl[(sn,sp)]))) for nt in cl[(sn,sp)]: self.sendLine(nt) self.sendLine(str(len(cl[(sn,sp)][nt]))) for u in cl[(sn,sp)][nt]: self.sendLine(u.asXML('EA','ON','BC','DS','NN','CT')) log.msg("SM") return self.manager.getClientList().addCallback(gotClientList) 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" self.tmpServerInfo = FRNUser(**body) else: self._phase = "AUTHENTICATED" self.serverInfo = FRNUser(**body) log.msg("Auth success: %s" % repr(self.serverInfo)) self.sendLine(formatSimpleXML([ ('SV', versions.server), ('CV', versions.client), ('MC', versions.manager), # ('AL', res['al']), ('AL', res), ('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(self.serverInfo, FRNUser(**body)).addCallback( self.sendLine) def decodeCD(self, body): # Client logout log.msg("CD: %s" % str(body)) self._authOnly() self.manager.clientLogout(self.serverInfo, 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: