gnuradionetwork/frn/protocol/server.py

395 lines
15 KiB
Python
Raw Normal View History

2010-08-18 15:46:58 +00:00
# -*- coding: utf-8 -*-
#
# Copyright 2010 Maurizio Porrato <maurizio.porrato@gmail.com>
# See LICENSE.txt for copyright info
from twisted.internet.defer import Deferred, succeed
2010-08-18 15:46:58 +00:00
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
2010-08-18 15:46:58 +00:00
from frn.protocol import versions
from frn.protocol.common import BufferingLineReceiver
from frn.utility import *
class FRNServer(BufferingLineReceiver, TimeoutMixin):
PING_PERIOD = 0.5
2010-08-18 15:46:58 +00:00
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()
2010-08-18 15:46:58 +00:00
BufferingLineReceiver.connectionLost(self, reason)
def timeoutConnection(self):
log.msg("Client dead: disconnecting %s" % self.user)
self.disconnect()
2010-08-18 15:46:58 +00:00
def lineReceived(self, line):
self.resetTimeout()
sline = line.strip()
if self.waitingKey:
if responseToChallange(self.kp) != sline:
self.disconnect()
2010-08-18 15:46:58 +00:00
return
else:
self.waitingKey = False
self.factory.tracker.login(self)
2010-08-18 15:46:58 +00:00
if self.role in ['OWNER', 'ADMIN']:
self.sendMuteList(self.factory.tracker.getMuteList())
self.sendBlockList(self.factory.tracker.getBlockList())
2010-08-18 15:46:58 +00:00
if self.role == 'OWNER':
self.sendAdminList(self.factory.tracker.getAdminList())
2010-10-31 22:08:32 +00:00
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()
2010-10-31 22:08:32 +00:00
return
self.startPinging()
2010-08-18 15:46:58 +00:00
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()
2010-08-18 15:46:58 +00:00
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() # ???
2010-08-18 15:46:58 +00:00
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)
2010-08-18 15:46:58 +00:00
def authenticate(self, user):
def loginReturned(userId):
if userId not in ['WRONG', 'DUPL']:
if self.factory.tracker.isBlocked(userId):
return ("BLOCK", userId)
2010-08-18 15:46:58 +00:00
return ("OK", userId) # FIXME: return OWNER or ADMIN eventually
else:
return (userId, "")
2010-08-18 15:46:58 +00:00
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", ""))
2010-08-18 15:46:58 +00:00
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()
2010-08-18 15:46:58 +00:00
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
2010-08-18 15:46:58 +00:00
if self.role == 'OK':
if self.user.EA == self.factory.serverAuth.OW:
self.role = 'OWNER'
2010-10-31 22:08:32 +00:00
log.msg("%s promoted to OWNER" % clientId)
2010-08-28 17:54:25 +00:00
elif self.factory.tracker.isAdmin(self.user.ID):
2010-08-18 15:46:58 +00:00
self.role = 'ADMIN'
2010-10-31 22:08:32 +00:00
log.msg("%s promoted to ADMIN" % clientId)
2010-08-18 15:46:58 +00:00
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
2010-08-18 15:46:58 +00:00
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()
2010-08-18 15:46:58 +00:00
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()
2010-08-18 15:46:58 +00:00
def decodeRX(self, body):
log.msg("RX%d" % int(body))
self.startPinging()
2011-02-20 11:14:09 +00:00
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()
2010-08-18 15:46:58 +00:00
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))
2010-08-18 15:46:58 +00:00
def decodeTM(self, body):
log.msg("TM: %s" % str(body))
if body['ID'] == '':
2010-08-18 15:46:58 +00:00
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)
2010-08-18 15:46:58 +00:00
def decodeTX(self, body):
if body == '0':
if not self.factory.tracker.isMute(self.user.ID):
2011-02-20 11:14:09 +00:00
if not self.factory.talking.get(self.user.NT):
self.factory.talking[self.user.NT] = self
2011-02-20 10:18:04 +00:00
ih,il = divmod(self.getIndex(), 256)
self.transport.write(chr(1)+chr(ih)+chr(il))
2010-08-18 15:46:58 +00:00
elif body == '1':
self.stopPinging()
2010-08-18 15:46:58 +00:00
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'])
2010-08-18 15:46:58 +00:00
2010-10-31 22:08:32 +00:00
def decodeETX(self, body):
log.msg("decodeETX(%s)" % str(body))
if self.role == "OWNER":
self.factory.tracker.setAclTx(self.user.NT, body['EA'], True)
2010-10-31 22:08:32 +00:00
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)
2010-10-31 22:08:32 +00:00
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'])
2010-10-31 22:08:32 +00:00
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'])
2010-10-31 22:08:32 +00:00
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))
2010-08-18 15:46:58 +00:00
def audioFrameReceived(self, frame):
#log.msg("audioFrameReceived")
2011-02-20 11:14:09 +00:00
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)
2010-08-18 15:46:58 +00:00
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):
2010-08-18 15:46:58 +00:00
log.msg("Send network list")
self.transport.write(chr(5))
self.sendLine(str(len(networks)))
for net in networks:
2010-08-18 15:46:58 +00:00
self.sendLine(net)
def sendMuteList(self, clients):
log.msg("Sending mute list to %s: %s" % (self.user.ON, str(clients)))
2010-08-18 15:46:58 +00:00
self.transport.write(chr(9))
self.sendLine(str(len(clients)))
for c in clients:
self.sendLine(c.asXML('AI','NN','CT','BC','ON','ID'))
2010-08-18 15:46:58 +00:00
def sendBlockList(self, clients):
log.msg("Sending block list to %s: %s" % (self.user.ON, str(clients)))
2010-08-18 15:46:58 +00:00
self.transport.write(chr(8))
self.sendLine(str(len(clients)))
for c in clients:
self.sendLine(c.asXML('AI','NN','CT','BC','ON','ID'))
2010-08-18 15:46:58 +00:00
def sendAdminList(self, clients):
log.msg("Sending admin list to %s: %s" % (self.user.ON, str(clients)))
2010-08-18 15:46:58 +00:00
self.transport.write(chr(6))
self.sendLine(str(len(clients)))
for c in clients:
self.sendLine(c.asXML('NN','CT','BC','ON','ID'))
2010-08-18 15:46:58 +00:00
def sendAccessList(self, clients):
2010-10-31 22:08:32 +00:00
log.msg("Sending ACL to %s: %s" % (self.user.ON, str(clients)))
2010-08-18 15:46:58 +00:00
self.transport.write(chr(7))
self.sendLine(str(len(clients)))
for c in clients:
self.sendLine(c.asXML('AI','NN','CT','BC','ON','ID'))
2010-08-18 15:46:58 +00:00
2010-10-31 22:08:32 +00:00
def sendAccessFlags(self, access, talk):
log.msg("Sending ACL flags to %s: %s" % (self.user.ON, str((access, talk))))
FV = {True: '1', False: 'o'}
2010-08-18 15:46:58 +00:00
self.transport.write(chr(10))
self.sendLine('2')
2010-10-31 22:08:32 +00:00
self.sendLine(FV[access])
self.sendLine(FV[talk])
2010-08-18 15:46:58 +00:00
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()
2010-08-18 15:46:58 +00:00
ih,il = divmod(clientIdx, 256)
self.transport.write(chr(2))
2010-08-18 15:46:58 +00:00
self.transport.write(chr(ih)+chr(il))
self.transport.write(frame)
class FRNServerFactory(ServerFactory):
protocol = FRNServer
def __init__(self, trackerfile, manager, serverAuth):
2010-08-18 15:46:58 +00:00
self.manager = manager
self.serverAuth = serverAuth
2011-02-20 11:14:09 +00:00
self.talking = {}
2010-08-18 15:46:58 +00:00
self.officialNets = []
self.tracker = ClientTracker(
trackerfile,
self.sendClientList, self.sendNetworkList,
self.sendMuteList, self.sendBlockList,
self.sendAdminList)
2010-08-18 15:46:58 +00:00
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)
2010-08-18 15:46:58 +00:00
# vim: set et ai sw=4 ts=4 sts=4: