# -*- coding: utf-8 -*- # # Copyright 2010 Maurizio Porrato # See LICENSE.txt for copyright info from twisted.internet.defer import Deferred 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.stopPinging() if self.user is not None: if self.user.ID: log.msg("Logging out client %s" % self.user.ID) self.factory.manager.clientLogout(self.user) self.factory.tracker.logout(self) BufferingLineReceiver.connectionLost(self, reason) def lineReceived(self, line): self.resetTimeout() sline = line.strip() if self.waitingKey: if responseToChallange(self.kp) != sline: self.transport.loseConnection() 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.sendAccessFlags(None) self.sendAccessList([]) 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.transport.loseConnection() 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 return self.factory.manager.clientLogin(user).addCallback(loginReturned) def disconnect(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' elif self.factory.tracker.isAdmin(self.user.ID): self.role = 'ADMIN' 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) self.sendLine( self.factory.serverAuth.asXML('MT','SV','AL','BN','BP','KP')) if self.role not in ['OK', 'OWNER', 'ADMIN']: self.transport.loseConnection() else: 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() 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)) 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): 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 audioFrameReceived(self, frame): #log.msg("audioFrameReceived") if not self.factory.tracker.isMute(self.user.ID): 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): self.transport.write(chr(7)) self.sendLine(str(len(clients))) for c in clients: # FIXME self.sendLine(c.asXML('AI','NN','CT','BC','ON','ID')) def sendAccessFlags(self, flags): self.transport.write(chr(10)) self.sendLine('2') # TODO self.sendLine('o') self.sendLine('o') 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 = None 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: