# -*- 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 ServerFactory from twisted.protocols.policies import TimeoutMixin from twisted.internet.task import LoopingCall from twisted.python import log from frn.user import FRNUser from frn.clienttracker import ClientTracker from frn.protocol import versions from frn.protocol.common import BufferingLineReceiver from frn.utility import * class FRNServer(BufferingLineReceiver, TimeoutMixin): PING_PERIOD = 0.5 def connectionMade(self): BufferingLineReceiver.connectionMade(self) log.msg("Connection from %s" % self.transport.getPeer().host) self.clientAddress = self.transport.getPeer() self.user = None self.role = None self.kp = makeRandomChallange() self.waitingKey = False self.pingTimer = LoopingCall(self.sendPing) self.setTimeout(25.0) def connectionLost(self, reason): log.msg("Client disconnected: %s" % self.clientAddress.host) self.disconnect() BufferingLineReceiver.connectionLost(self, reason) def timeoutConnection(self): log.msg("Client dead: disconnecting %s" % self.user) self.disconnect() def lineReceived(self, line): self.resetTimeout() sline = line.strip() if self.waitingKey: if responseToChallange(self.kp) != sline: self.disconnect() return else: self.waitingKey = False self.factory.tracker.login(self) if self.role in ['OWNER', 'ADMIN']: self.sendMuteList(self.factory.tracker.getMuteList()) self.sendBlockList(self.factory.tracker.getBlockList()) if self.role == 'OWNER': self.sendAdminList(self.factory.tracker.getAdminList()) ac, tx = self.factory.tracker.getAclFlags(self.user.NT) self.sendAccessFlags(ac,tx) self.sendAccessList(self.factory.tracker.getAcl(self.user.NT)) if self.role not in ['OK', 'ADMIN', 'OWNER']: self.disconnect() return self.startPinging() self.setTimeout(10.0) return if sline == 'P': # Pong #log.msg('Pong') return if sline in ['RX0', 'TX0', 'TX1']: command, body = sline[:2], sline[2:] else: command, body = sline.split(':', 1) if command != 'CT' and self.user is None: self.disconnect() return handler = getattr(self, 'decode'+command, None) if body[0] == '<' and body[-1] == '>': body = parseSimpleXML(body) if handler is not None: handler(body) else: self.unimplemented(command, body) #self.transport.loseConnection() # ??? def expectedReceived(self, data): self.resetTimeout() self.setLineMode() self.audioFrameReceived(data) def unimplemented(self, command, body): log.err("Unimplemented message: %s: %s" % (command, body)) def getIndex(self): return self.factory.tracker.getClientIndex(self.user.ID) def authenticate(self, user): def loginReturned(userId): if userId not in ['WRONG', 'DUPL']: if self.factory.tracker.isBlocked(userId): return ("BLOCK", userId) return ("OK", userId) # FIXME: return OWNER or ADMIN eventually else: return (userId, "") user.IP = self.clientAddress.host if self.factory.tracker.canLogin(user): return self.factory.manager.clientLogin(self.factory.serverAuth, user).addCallback(loginReturned) else: log.msg("BLOCK %s" % repr(user)) return succeed(("BLOCK", "")) def disconnect(self): self.stopPinging() if self.user is not None: log.msg("Logging out client %s" % self.user) if self.factory.talking.get(self.user.NT) == self: del self.factory.talking[self.user.NT] self.factory.manager.clientLogout(self.factory.serverAuth, self.user) self.factory.tracker.logout(self) self.transport.loseConnection() def decodeCT(self, body): def authReturned(result): self.role, clientId = result log.msg("AUTH result: %s %s" % (self.role, clientId)) self.user = FRNUser(**body) self.user.ID = clientId if self.role == 'OK': if self.user.EA == self.factory.serverAuth.OW: self.role = 'OWNER' log.msg("%s promoted to OWNER" % clientId) elif self.factory.tracker.isAdmin(self.user.ID): self.role = 'ADMIN' log.msg("%s promoted to ADMIN" % clientId) if versions.server > 2009004: self.waitingKey = True self.sendLine(str(versions.client)) self.factory.serverAuth.update(MT='', SV=versions.server, AL=self.role, KP=self.kp) if self.role == 'DUPL': self.factory.serverAuth.AL = 'BLOCK' # TODO: verify other results self.sendLine( self.factory.serverAuth.asXML('MT','SV','AL','BN','BP','KP')) if self.role not in ['OK', 'OWNER', 'ADMIN']: self.disconnect() else: self.sendNetworkList(self.factory.tracker.getNetworkList()) self.transport.setTcpNoDelay(True) self.startPinging() return self.authenticate(FRNUser(**body)).addCallback( authReturned) def startPinging(self): if not self.pingTimer.running: self.pingTimer.start(self.PING_PERIOD, True) def stopPinging(self): if self.pingTimer.running: self.pingTimer.stop() def decodeRX(self, body): log.msg("RX%d" % int(body)) self.startPinging() if self.factory.talking.get(self.user.NT) == self: del self.factory.talking[self.user.NT] for c in self.factory.tracker.getClientList(self.user.NT): cp = self.factory.tracker.getClientProtocol(c.ID).startPinging() def decodeST(self, body): log.msg("Set status = %d" % int(body)) if int(body) > 1: self.startPinging() self.factory.tracker.setStatus(self.user.ID, int(body)) def decodeTM(self, body): log.msg("TM: %s" % str(body)) if body['ID'] == '': msgtype = 'A' else: msgtype = 'P' for c in self.factory.tracker.getClientList(self.user.NT): if msgtype == 'A' or c.ID == body['ID']: if c.ID != self.user.ID or msgtype == 'A': client = self.factory.tracker.getClientProtocol(c.ID) client.sendTextMessage(self.user.ID, body['MS'], msgtype) def decodeTX(self, body): if body == '0': if not self.factory.tracker.isMute(self.user.ID): if not self.factory.talking.get(self.user.NT): self.factory.talking[self.user.NT] = self ih,il = divmod(self.getIndex(), 256) self.transport.write(chr(1)+chr(ih)+chr(il)) elif body == '1': self.stopPinging() self.expectRawData(325) def decodeMC(self, body): if self.role in ["OWNER","ADMIN"]: self.factory.tracker.mute(self.user, body['IP']) if self.factory.tracker.isLoggedIn(body['IP']): self.factory.tracker.getClientProtocol(body['IP']).decodeRX('0') def decodeUM(self, body): if self.role in ["OWNER","ADMIN"]: self.factory.tracker.unMute(body['IP']) def decodeBC(self, body): if self.role in ["OWNER","ADMIN"]: self.factory.tracker.block(self.user, body['IP']) self.factory.tracker.getClientProtocol(body['IP']).disconnect() def decodeUC(self, body): if self.role in ["OWNER","ADMIN"]: self.factory.tracker.unBlock(body['IP']) def decodeAA(self, body): if self.role == "OWNER": self.factory.tracker.admin(body['IP']) def decodeDA(self, body): if self.role == "OWNER": self.factory.tracker.unAdmin(body['IP']) def decodeETX(self, body): log.msg("decodeETX(%s)" % str(body)) if self.role == "OWNER": self.factory.tracker.setAclTx(self.user.NT, body['EA'], True) self.sendAccessList(self.factory.tracker.getAcl(self.user.NT)) def decodeRTX(self, body): log.msg("decodeRTX(%s)" % str(body)) if self.role == "OWNER": self.factory.tracker.setAclTx(self.user.NT, body['EA'], False) self.sendAccessList(self.factory.tracker.getAcl(self.user.NT)) def decodeDT(self, body): log.msg("decodeDT(%s)" % str(body)) if self.role == "OWNER": self.factory.tracker.delAcl(self.user.NT, body['EA']) self.sendAccessList(self.factory.tracker.getAcl(self.user.NT)) def decodeAT(self, body): log.msg("decodeAT(%s)" % str(body)) if self.role == "OWNER": self.factory.tracker.addAcl(self.user.NT, body['EA']) self.sendAccessList(self.factory.tracker.getAcl(self.user.NT)) def decodeTXR(self, body): log.msg("decodeTXR(%s)" % str(body)) if self.role == "OWNER": en, _ = self.factory.tracker.getAclFlags(self.user.NT) tx = body == '1' self.factory.tracker.setAclFlags(self.user.NT, en, tx) en,tx = self.factory.tracker.getAclFlags(self.user.NT) self.sendAccessFlags(en, tx) self.sendAccessList(self.factory.tracker.getAcl(self.user.NT)) def decodeENA(self, body): log.msg("decodeENA(%s)" % str(body)) if self.role == "OWNER": _, tx = self.factory.tracker.getAclFlags(self.user.NT) en = body == '1' self.factory.tracker.setAclFlags(self.user.NT, en, tx) en,tx = self.factory.tracker.getAclFlags(self.user.NT) self.sendAccessFlags(en, tx) self.sendAccessList(self.factory.tracker.getAcl(self.user.NT)) def audioFrameReceived(self, frame): #log.msg("audioFrameReceived") if self.factory.tracker.canTalk(self.user.ID) and self.factory.talking.get(self.user.NT) == self: clientIdx = self.getIndex() for c in self.factory.tracker.getClientList(self.user.NT): if int(c.S) < 2 and c.ID != self.user.ID: #log.msg("Sending to %s" % c.ON) self.factory.tracker.getClientProtocol(c.ID).sendAudioFrame(clientIdx, frame) def sendPing(self): self.transport.write(chr(0)) def sendClientList(self, clients): self.transport.write(chr(3)+chr(0)+chr(0)) self.sendLine(str(len(clients))) for client in clients: self.sendLine(client.asXML( 'S','M','NN','CT','BC','ON','ID','DS' )) def sendNetworkList(self, networks): log.msg("Send network list") self.transport.write(chr(5)) self.sendLine(str(len(networks))) for net in networks: self.sendLine(net) def sendMuteList(self, clients): log.msg("Sending mute list to %s: %s" % (self.user.ON, str(clients))) self.transport.write(chr(9)) self.sendLine(str(len(clients))) for c in clients: self.sendLine(c.asXML('AI','NN','CT','BC','ON','ID')) def sendBlockList(self, clients): log.msg("Sending block list to %s: %s" % (self.user.ON, str(clients))) self.transport.write(chr(8)) self.sendLine(str(len(clients))) for c in clients: self.sendLine(c.asXML('AI','NN','CT','BC','ON','ID')) def sendAdminList(self, clients): log.msg("Sending admin list to %s: %s" % (self.user.ON, str(clients))) self.transport.write(chr(6)) self.sendLine(str(len(clients))) for c in clients: self.sendLine(c.asXML('NN','CT','BC','ON','ID')) def sendAccessList(self, clients): log.msg("Sending ACL to %s: %s" % (self.user.ON, str(clients))) self.transport.write(chr(7)) self.sendLine(str(len(clients))) for c in clients: self.sendLine(c.asXML('AI','NN','CT','BC','ON','ID')) def sendAccessFlags(self, access, talk): log.msg("Sending ACL flags to %s: %s" % (self.user.ON, str((access, talk)))) FV = {True: '1', False: 'o'} self.transport.write(chr(10)) self.sendLine('2') self.sendLine(FV[access]) self.sendLine(FV[talk]) def sendTextMessage(self, clientId, message, target): self.transport.write(chr(4)) self.sendLine('3') self.sendLine(clientId) self.sendLine(message) self.sendLine(target) def sendAudioFrame(self, clientIdx, frame): self.stopPinging() ih,il = divmod(clientIdx, 256) self.transport.write(chr(2)) self.transport.write(chr(ih)+chr(il)) self.transport.write(frame) class FRNServerFactory(ServerFactory): protocol = FRNServer def __init__(self, trackerfile, manager, serverAuth): self.manager = manager self.serverAuth = serverAuth self.talking = {} self.officialNets = [] self.tracker = ClientTracker( trackerfile, self.sendClientList, self.sendNetworkList, self.sendMuteList, self.sendBlockList, self.sendAdminList) def startFactory(self): ServerFactory.startFactory(self) self.manager.serverLogin(self.serverAuth) def stopFactory(self): self.manager.serverLogout(self.serverAuth) ServerFactory.stopFactory(self) def sendNetworkList(self, networks): nets = self.officialNets+list(set(networks) - set(self.officialNets)) for c in self.tracker.getClientList(): self.tracker.getClientProtocol(c.ID).sendNetworkList(nets) def sendClientList(self, network, clients): for c in clients: self.tracker.getClientProtocol(c.ID).sendClientList(clients) def sendMuteList(self, clients): for c in self.tracker.getClientList(): if self.tracker.isAdmin(c.ID) or c.EA == self.serverAuth.OW: self.tracker.getClientProtocol(c.ID).sendMuteList(clients) def sendBlockList(self, clients): for c in self.tracker.getClientList(): if self.tracker.isAdmin(c.ID) or c.EA == self.serverAuth.OW: self.tracker.getClientProtocol(c.ID).sendBlockList(clients) def sendAdminList(self, clients): for c in self.tracker.getClientList(): if c.EA == self.serverAuth.OW: self.tracker.getClientProtocol(c.ID).sendAdminList(clients) # vim: set et ai sw=4 ts=4 sts=4: