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
|
|
|
|
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
|
2010-08-21 15:32:22 +00:00
|
|
|
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):
|
|
|
|
|
|
|
|
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.pingCount = 0
|
|
|
|
self.setTimeout(25.0)
|
|
|
|
|
|
|
|
def connectionLost(self, reason):
|
|
|
|
log.msg("Client disconnected: %s" % self.clientAddress.host)
|
|
|
|
try:
|
|
|
|
self.pingTimer.stop()
|
|
|
|
except AssertionError: pass
|
2010-08-21 15:32:22 +00:00
|
|
|
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)
|
2010-08-18 15:46:58 +00:00
|
|
|
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
|
2010-08-21 15:32:22 +00:00
|
|
|
self.factory.tracker.login(self)
|
2010-08-18 15:46:58 +00:00
|
|
|
if self.role in ['OWNER', 'ADMIN']:
|
2010-08-21 15:32:22 +00:00
|
|
|
self.sendMuteList(self.factory.tracker.getMuteList())
|
|
|
|
self.sendBlockList(self.factory.tracker.getBlockList())
|
2010-08-18 15:46:58 +00:00
|
|
|
if self.role == 'OWNER':
|
2010-08-21 15:32:22 +00:00
|
|
|
self.sendAccessFlags(None)
|
|
|
|
self.sendAccessList([])
|
2010-08-18 15:46:58 +00:00
|
|
|
self.pingCount += 1
|
|
|
|
self.pingTimer.start(0.5)
|
|
|
|
self.setTimeout(10.0)
|
|
|
|
return
|
|
|
|
if sline == 'P': # Pong
|
|
|
|
#log.msg('Pong')
|
|
|
|
self.pingCount -= 1
|
|
|
|
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)
|
2010-08-21 15:32:22 +00:00
|
|
|
#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))
|
|
|
|
|
2010-08-21 15:32:22 +00:00
|
|
|
def getIndex(self):
|
2010-08-22 08:57:39 +00:00
|
|
|
return self.factory.tracker.getClientIndex(self.user.ID)
|
2010-08-21 15:32:22 +00:00
|
|
|
|
2010-08-18 15:46:58 +00:00
|
|
|
def authenticate(self, user):
|
|
|
|
def loginReturned(userId):
|
2010-08-21 15:32:22 +00:00
|
|
|
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:
|
2010-08-21 15:32:22 +00:00
|
|
|
return (userId, "")
|
2010-08-18 15:46:58 +00:00
|
|
|
user.IP = self.clientAddress.host
|
|
|
|
return self.factory.manager.clientLogin(user).addCallback(loginReturned)
|
|
|
|
|
2010-08-21 15:32:22 +00:00
|
|
|
def disconnect(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))
|
2010-08-21 18:32:56 +00:00
|
|
|
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-08-21 15:32:22 +00:00
|
|
|
elif self.factory.tracker.isAdmin(self.user.EA):
|
2010-08-18 15:46:58 +00:00
|
|
|
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()
|
|
|
|
return self.authenticate(FRNUser(**body)).addCallback(
|
|
|
|
authReturned)
|
|
|
|
|
|
|
|
def decodeRX(self, body):
|
|
|
|
log.msg("RX%d" % int(body))
|
|
|
|
if not self.pingTimer.running:
|
|
|
|
self.pingTimer.start(0.5)
|
|
|
|
|
|
|
|
def decodeST(self, body):
|
|
|
|
log.msg("Set status = %d" % int(body))
|
2010-08-21 15:32:22 +00:00
|
|
|
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'] == '':
|
|
|
|
msgtype = 'A'
|
|
|
|
else:
|
|
|
|
msgtype = 'P'
|
2010-08-21 15:32:22 +00:00
|
|
|
for c in self.factory.tracker.getClientList(self.user.NT):
|
|
|
|
if msgtype == 'A' or c.ID == body['id']:
|
|
|
|
if c.ID != self.user.ID:
|
|
|
|
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):
|
2010-08-21 15:32:22 +00:00
|
|
|
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))
|
2010-08-18 15:46:58 +00:00
|
|
|
elif body == '1':
|
2010-08-21 15:32:22 +00:00
|
|
|
if self.pingTimer.running:
|
|
|
|
self.pingTimer.stop()
|
2010-08-18 15:46:58 +00:00
|
|
|
self.expectRawData(325)
|
2010-08-21 15:32:22 +00:00
|
|
|
|
|
|
|
def decodeMC(self, body):
|
|
|
|
if self.role in ["OWNER","ADMIN"]:
|
|
|
|
self.factory.tracker.mute(self.user, body['ip'])
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
def audioFrameReceived(self, frame):
|
2010-08-21 15:32:22 +00:00
|
|
|
#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)
|
2010-08-18 15:46:58 +00:00
|
|
|
|
|
|
|
def sendPing(self):
|
|
|
|
if self.pingCount > 20:
|
|
|
|
log.msg("Client %s is dead: disconnecting" %
|
|
|
|
self.clientAddress.host)
|
|
|
|
self.transport.loseConnection()
|
|
|
|
# log.msg(self.pingCount)
|
|
|
|
self.pingCount += 1
|
|
|
|
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'
|
|
|
|
))
|
|
|
|
self.pingCount += 1
|
|
|
|
|
2010-08-21 15:32:22 +00:00
|
|
|
def sendNetworkList(self, networks):
|
2010-08-18 15:46:58 +00:00
|
|
|
log.msg("Send network list")
|
|
|
|
self.transport.write(chr(5))
|
2010-08-21 15:32:22 +00:00
|
|
|
self.sendLine(str(len(networks)))
|
|
|
|
for net in networks:
|
2010-08-18 15:46:58 +00:00
|
|
|
self.sendLine(net)
|
|
|
|
self.pingCount += 1
|
|
|
|
|
2010-08-21 15:32:22 +00:00
|
|
|
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))
|
2010-08-21 15:32:22 +00:00
|
|
|
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-08-21 15:32:22 +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))
|
2010-08-21 15:32:22 +00:00
|
|
|
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-08-21 15:32:22 +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))
|
2010-08-21 15:32:22 +00:00
|
|
|
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
|
|
|
|
2010-08-21 15:32:22 +00:00
|
|
|
def sendAccessList(self, clients):
|
2010-08-18 15:46:58 +00:00
|
|
|
self.transport.write(chr(7))
|
2010-08-21 15:32:22 +00:00
|
|
|
self.sendLine(str(len(clients)))
|
|
|
|
for c in clients: # FIXME
|
|
|
|
self.sendLine(c.asXML('AI','NN','CT','BC','ON','ID'))
|
2010-08-18 15:46:58 +00:00
|
|
|
|
2010-08-21 15:32:22 +00:00
|
|
|
def sendAccessFlags(self, flags):
|
2010-08-18 15:46:58 +00:00
|
|
|
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)
|
|
|
|
self.pingCount += 1
|
|
|
|
|
|
|
|
def sendAudioFrame(self, clientIdx, frame):
|
|
|
|
self.transport.write(chr(2))
|
|
|
|
ih,il = divmod(clientIdx, 256)
|
|
|
|
self.transport.write(chr(ih)+chr(il))
|
|
|
|
self.transport.write(frame)
|
|
|
|
|
|
|
|
|
|
|
|
class FRNServerFactory(ServerFactory):
|
|
|
|
|
|
|
|
protocol = FRNServer
|
|
|
|
|
2010-08-21 18:32:56 +00:00
|
|
|
def __init__(self, trackerfile, manager, serverAuth):
|
2010-08-18 15:46:58 +00:00
|
|
|
self.manager = manager
|
|
|
|
self.serverAuth = serverAuth
|
|
|
|
self.talking = None
|
|
|
|
self.officialNets = []
|
2010-08-21 15:32:22 +00:00
|
|
|
self.tracker = ClientTracker(
|
2010-08-21 18:32:56 +00:00
|
|
|
trackerfile,
|
2010-08-21 15:32:22 +00:00
|
|
|
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)
|
|
|
|
|
2010-08-21 15:32:22 +00:00
|
|
|
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:
|