gnuradionetwork/frn/protocol/server.py

389 lines
14 KiB
Python

# -*- coding: utf-8 -*-
#
# Copyright 2010 Maurizio Porrato <maurizio.porrato@gmail.com>
# 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(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)
self.factory.manager.clientLogout(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()
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):
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):
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 = 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: