# -*- coding: utf-8 -*- # # Copyright 2010 Maurizio Porrato # See LICENSE.txt for copyright info 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 from frn.protocol.client import FRNClient, FRNClientFactory from frn.user import FRNUser from twisted.python import log from os.path import curdir, join as pjoin cType = {'PC Only': 'O', 'Crosslink': 'C', 'Parrot': 'P'} cStatus = {'0': 'T', '1': 'R', '2': 'N'} clients = [] talking = None class FRNCrosslinkClient(FRNClient): def connectionMade(self): self.txOk = False self.txReq = False self.clientId = None from twisted.internet import reactor self.reactor = reactor FRNClient.connectionMade(self) def getClientName(self, client_id): if self.clientsById.has_key(client_id): return self.clientsById[client_id]['on'] else: return client_id def textMessageReceived(self, client, message, target): global clients if target == 'A': msg = "[%s] %s" % (self.getClientName(client), message) if client != self.clientId: for _, _, _, factory in clients: if factory != self.factory: factory.connection.sendTextMessage('', msg) else: if message.startswith('!'): cmd = message[1:] if cmd == "who": for server, port, u, factory in clients: cl = [] cl.append("%s@%s:%d" % (u.NT,server,port)) for c in factory.connection.clients: ss = cStatus.get(c['s'], '?') if c['m'] != '0': ss = ss.lower() cl.append(" %s%s %s " % ( cType.get(c['bc'], 'G'), ss, c['on'].replace('<', '<'), )) sm = [cl[i*15:(i+1)*15] for i in range((len(cl)+14)/15)] for m in sm: reply = '
'.join(m) self.sendTextMessage(client, reply) def decodeTX(self, my_id): log.msg("Got TX ack for %s" % self.user.ON) self.txOk = True def stopTransmission(self): FRNClient.stopTransmission(self) log.msg("Stopped TX on %s" % self.user.ON) def goIdle(self): global talking self.txReq = False self.txOk = False talking = None self.stopTransmission() def audioFrameReceived(self, from_id, frames): global clients, talking if talking is None or talking == self: talking = self for _, _, _, factory in clients: conn = factory.connection if conn != self: role = conn.serverdata.get('al', None) if role in ['OK', 'ADMIN', 'OWNER']: 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) self.pong() def loginResponse(self, info): log.msg("%s login: %s" % (self.user.ON, info['al'])) def clientsListUpdated(self, clients): self.clients = clients self.clientsById = dict([(i['id'], i) for i in clients]) if self.clientId is None: for c in clients: if c['on'] == self.user.ON and c['bc'] == self.user.BC: self.clientId = c['id'] class FRNCrosslinkClientFactory(FRNClientFactory): protocol = FRNCrosslinkClient def buildProtocol(self, addr): p = FRNClientFactory.buildProtocol(self, addr) self.connection = p return p 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']: user = FRNUser( 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']) factory = FRNCrosslinkClientFactory(user) clients.append((c['server'], c['port'], user, factory)) s.addService( internet.TCPClient(c['server'], c['port'], factory)) return s serviceMaker = FRNCrosslinkServiceMaker() # vim: set et ai sw=4 ts=4 sts=4: