gnuradionetwork/frn/protocol/server.py

321 lines
12 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
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: