2010-08-22 08:57:39 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
#
|
|
|
|
# Copyright 2010 Maurizio Porrato <maurizio.porrato@gmail.com>
|
|
|
|
# See LICENSE.txt for copyright info
|
|
|
|
|
2010-08-28 17:50:31 +00:00
|
|
|
from twisted.application.service import MultiService
|
|
|
|
from zope.interface import implements
|
|
|
|
from twisted.plugin import IPlugin
|
|
|
|
from twisted.application.service import IServiceMaker
|
|
|
|
from twisted.application import internet
|
|
|
|
from twisted.python import usage
|
|
|
|
from ConfigParser import ConfigParser
|
2010-08-22 08:57:39 +00:00
|
|
|
from frn.protocol.client import FRNClient, FRNClientFactory
|
|
|
|
from frn.user import FRNUser
|
|
|
|
from twisted.python import log
|
2010-08-28 17:50:31 +00:00
|
|
|
from os.path import curdir, join as pjoin
|
|
|
|
|
2010-09-04 11:29:01 +00:00
|
|
|
cType = {'PC Only': 'O', 'Crosslink': 'C', 'Parrot': 'P'}
|
2010-09-04 12:22:23 +00:00
|
|
|
cStatus = {'0': 'T', '1': 'R', '2': 'N'}
|
2010-08-22 08:57:39 +00:00
|
|
|
|
|
|
|
clients = []
|
2010-08-22 22:35:22 +00:00
|
|
|
talking = None
|
2010-09-05 14:43:05 +00:00
|
|
|
lastMessages = []
|
2010-08-22 08:57:39 +00:00
|
|
|
|
|
|
|
class FRNCrosslinkClient(FRNClient):
|
|
|
|
|
2010-08-22 22:35:22 +00:00
|
|
|
def connectionMade(self):
|
|
|
|
self.txOk = False
|
|
|
|
self.txReq = False
|
2010-08-24 13:56:37 +00:00
|
|
|
self.clientId = None
|
2010-08-28 17:50:31 +00:00
|
|
|
from twisted.internet import reactor
|
|
|
|
self.reactor = reactor
|
2010-08-22 22:35:22 +00:00
|
|
|
FRNClient.connectionMade(self)
|
|
|
|
|
2010-08-22 08:57:39 +00:00
|
|
|
def getClientName(self, client_id):
|
|
|
|
if self.clientsById.has_key(client_id):
|
2011-01-30 14:59:29 +00:00
|
|
|
return self.clientsById[client_id]['ON']
|
2010-08-22 08:57:39 +00:00
|
|
|
else:
|
|
|
|
return client_id
|
|
|
|
|
2010-09-05 14:43:05 +00:00
|
|
|
def sendMultiLineTextMessage(self, client, lines, maxLen=15):
|
|
|
|
chunks = [lines[i*maxLen:(i+1)*maxLen]
|
|
|
|
for i in range((len(lines)+maxLen-1)/maxLen)]
|
|
|
|
for m in chunks:
|
|
|
|
reply = '<br>'.join(m)
|
|
|
|
self.sendTextMessage(client, reply)
|
|
|
|
|
2010-08-22 08:57:39 +00:00
|
|
|
def textMessageReceived(self, client, message, target):
|
2010-09-05 14:43:05 +00:00
|
|
|
global clients, talking, lastMessages
|
2010-08-22 08:57:39 +00:00
|
|
|
if target == 'A':
|
2010-08-28 18:34:43 +00:00
|
|
|
msg = "[%s] %s" % (self.getClientName(client), message)
|
2010-08-24 13:56:37 +00:00
|
|
|
if client != self.clientId:
|
|
|
|
for _, _, _, factory in clients:
|
|
|
|
if factory != self.factory:
|
2010-08-28 18:34:43 +00:00
|
|
|
factory.connection.sendTextMessage('', msg)
|
2010-08-22 08:57:39 +00:00
|
|
|
else:
|
|
|
|
if message.startswith('!'):
|
|
|
|
cmd = message[1:]
|
|
|
|
if cmd == "who":
|
2010-08-24 13:56:37 +00:00
|
|
|
for server, port, u, factory in clients:
|
2010-08-22 08:57:39 +00:00
|
|
|
cl = []
|
2010-09-04 12:22:23 +00:00
|
|
|
cl.append("<u>%s@%s:%d</u>" % (u.NT,server,port))
|
2011-01-30 14:59:29 +00:00
|
|
|
role = factory.connection.serverdata.get('AL', None)
|
2010-09-05 14:43:05 +00:00
|
|
|
if role in ['OK', 'ADMIN', 'OWNER']:
|
|
|
|
for c in factory.connection.clients:
|
|
|
|
ss = cStatus.get(c['s'], '?')
|
2011-01-30 14:59:29 +00:00
|
|
|
if c['M'] != '0':
|
2010-09-05 14:43:05 +00:00
|
|
|
ss = ss.lower()
|
|
|
|
cl.append(" %s%s %s " % (
|
2011-01-30 14:59:29 +00:00
|
|
|
cType.get(c['BC'], 'G'),
|
2010-09-05 14:43:05 +00:00
|
|
|
ss,
|
2011-01-30 14:59:29 +00:00
|
|
|
c['ON'].replace('<', '<'),
|
2010-09-05 14:43:05 +00:00
|
|
|
))
|
|
|
|
else:
|
|
|
|
cl.append(" :!: DISCONNECTED :!: ")
|
|
|
|
self.sendMultiLineTextMessage(client, cl)
|
|
|
|
elif cmd == "last":
|
|
|
|
ml = []
|
|
|
|
ml.append("Last active talkers (most recent first):")
|
|
|
|
for n in lastMessages:
|
2010-09-06 21:26:46 +00:00
|
|
|
ml.append(" - "+n)
|
2010-09-05 14:43:05 +00:00
|
|
|
self.sendMultiLineTextMessage(client, ml)
|
2010-08-22 22:35:22 +00:00
|
|
|
|
|
|
|
def decodeTX(self, my_id):
|
|
|
|
log.msg("Got TX ack for %s" % self.user.ON)
|
|
|
|
self.txOk = True
|
2010-08-22 08:57:39 +00:00
|
|
|
|
|
|
|
def stopTransmission(self):
|
|
|
|
FRNClient.stopTransmission(self)
|
2010-08-22 22:35:22 +00:00
|
|
|
log.msg("Stopped TX on %s" % self.user.ON)
|
|
|
|
|
|
|
|
def goIdle(self):
|
2010-09-05 14:43:05 +00:00
|
|
|
global talking, lastMessages
|
2010-08-22 22:35:22 +00:00
|
|
|
self.txReq = False
|
|
|
|
self.txOk = False
|
|
|
|
talking = None
|
|
|
|
self.stopTransmission()
|
2010-08-22 08:57:39 +00:00
|
|
|
|
|
|
|
def audioFrameReceived(self, from_id, frames):
|
2010-09-05 16:16:00 +00:00
|
|
|
global clients, talking, lastMessages
|
2010-08-22 22:35:22 +00:00
|
|
|
if talking is None or talking == self:
|
|
|
|
talking = self
|
2011-01-30 14:59:29 +00:00
|
|
|
talkingUser = self.clients[from_id-1]['ON']
|
2010-09-05 16:16:00 +00:00
|
|
|
if len(lastMessages) > 0:
|
|
|
|
if lastMessages[0] == talkingUser:
|
|
|
|
talkingUser = None
|
|
|
|
if talkingUser is not None:
|
|
|
|
lastMessages.insert(0, talkingUser)
|
|
|
|
lastMessages = lastMessages[:5]
|
2010-08-22 22:35:22 +00:00
|
|
|
for _, _, _, factory in clients:
|
|
|
|
conn = factory.connection
|
|
|
|
if conn != self:
|
2010-08-28 21:15:05 +00:00
|
|
|
role = conn.serverdata.get('al', None)
|
2010-09-05 14:43:05 +00:00
|
|
|
if role in ['OK', 'ADMIN', 'OWNER']:
|
2010-08-28 21:15:05 +00:00
|
|
|
if conn.txOk:
|
|
|
|
conn.txReq = False
|
|
|
|
conn.sendAudioFrame(frames)
|
|
|
|
conn.busyTimer.reset(2.0)
|
|
|
|
else:
|
|
|
|
if not conn.txReq:
|
|
|
|
log.msg("Requesting TX for %s" % conn.user.ON)
|
|
|
|
conn.startTransmission()
|
|
|
|
conn.txReq = True
|
|
|
|
conn.busyTimer = self.reactor.callLater(
|
|
|
|
5.0, conn.goIdle)
|
2010-08-22 08:57:39 +00:00
|
|
|
self.pong()
|
|
|
|
|
|
|
|
def loginResponse(self, info):
|
2011-01-30 14:59:29 +00:00
|
|
|
log.msg("%s login: %s" % (self.user.ON, info['AL']))
|
2010-08-22 08:57:39 +00:00
|
|
|
|
|
|
|
def clientsListUpdated(self, clients):
|
|
|
|
self.clients = clients
|
2011-01-30 14:59:29 +00:00
|
|
|
self.clientsById = dict([(i['ID'], i) for i in clients])
|
2010-08-24 13:56:37 +00:00
|
|
|
if self.clientId is None:
|
|
|
|
for c in clients:
|
2011-01-30 14:59:29 +00:00
|
|
|
if c['ON'] == self.user.ON and c['BC'] == self.user.BC:
|
|
|
|
self.clientId = c['ID']
|
2010-08-22 08:57:39 +00:00
|
|
|
|
|
|
|
class FRNCrosslinkClientFactory(FRNClientFactory):
|
|
|
|
protocol = FRNCrosslinkClient
|
|
|
|
|
|
|
|
def buildProtocol(self, addr):
|
|
|
|
p = FRNClientFactory.buildProtocol(self, addr)
|
|
|
|
self.connection = p
|
|
|
|
return p
|
|
|
|
|
|
|
|
|
2010-08-28 17:50:31 +00:00
|
|
|
class Options(usage.Options):
|
|
|
|
|
|
|
|
optParameters = [
|
|
|
|
["confdir", "c", curdir, "Directory containing config files"]
|
|
|
|
]
|
|
|
|
|
|
|
|
def parseArgs(self, *serverdefs):
|
|
|
|
basedir = self['confdir']
|
|
|
|
acfg = ConfigParser()
|
|
|
|
acfg.read(['/etc/grn/accounts.conf',
|
|
|
|
pjoin(basedir,'accounts.conf'), 'accounts.conf'])
|
|
|
|
|
|
|
|
scfg = ConfigParser()
|
|
|
|
scfg.read(['/etc/grn/servers.conf',
|
|
|
|
pjoin(basedir,'servers.conf'), 'servers.conf'])
|
|
|
|
|
|
|
|
clients = []
|
|
|
|
for serverdef in serverdefs:
|
|
|
|
account_name, server_name, network_name = serverdef.split(':', 2)
|
|
|
|
|
|
|
|
sd = {}
|
|
|
|
sd['server'] = scfg.get(server_name, 'server')
|
|
|
|
sd['port'] = scfg.getint(server_name, 'port')
|
|
|
|
sd['backup_server'] = scfg.get(server_name, 'backup_server')
|
|
|
|
sd['backup_port'] = scfg.getint(server_name, 'backup_port')
|
|
|
|
sd['operator'] = acfg.get(account_name, 'operator')
|
|
|
|
sd['email'] = acfg.get(account_name, 'email')
|
|
|
|
sd['password'] = acfg.get(account_name, 'password')
|
|
|
|
sd['description'] = acfg.get(account_name, 'description')
|
|
|
|
sd['country'] = acfg.get(account_name, 'country')
|
|
|
|
sd['city'] = acfg.get(account_name, 'city')
|
|
|
|
sd['network'] = network_name
|
|
|
|
clients.append(sd)
|
|
|
|
self['clients'] = clients
|
|
|
|
|
|
|
|
|
|
|
|
class FRNCrosslinkServiceMaker(object):
|
|
|
|
implements(IServiceMaker, IPlugin)
|
|
|
|
tapname = "frncrosslink"
|
|
|
|
description = "Freeradionetwork crosslink"
|
|
|
|
options = Options
|
|
|
|
|
|
|
|
def makeService(self, options):
|
|
|
|
s = MultiService()
|
|
|
|
global clients # FIXME: Get rid of this hack
|
|
|
|
for c in options['clients']:
|
2010-08-22 08:57:39 +00:00
|
|
|
user = FRNUser(
|
2010-08-28 17:50:31 +00:00
|
|
|
EA=c['email'],
|
|
|
|
PW=c['password'], ON=c['operator'],
|
|
|
|
# BC=c['transmission'], DS=c['description'],
|
|
|
|
BC="Crosslink", DS=c['description'],
|
|
|
|
NN=c['country'], CT=c['city'], NT=c['network'])
|
2010-08-22 08:57:39 +00:00
|
|
|
factory = FRNCrosslinkClientFactory(user)
|
2010-08-28 17:50:31 +00:00
|
|
|
clients.append((c['server'], c['port'], user, factory))
|
|
|
|
s.addService(
|
|
|
|
internet.TCPClient(c['server'], c['port'], factory))
|
|
|
|
return s
|
|
|
|
|
2010-08-22 08:57:39 +00:00
|
|
|
|
2010-08-28 17:50:31 +00:00
|
|
|
serviceMaker = FRNCrosslinkServiceMaker()
|
2010-08-22 08:57:39 +00:00
|
|
|
|
|
|
|
# vim: set et ai sw=4 ts=4 sts=4:
|