You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

189 lines
6.7KB

  1. # -*- coding: utf-8 -*-
  2. #
  3. # Copyright 2010 Maurizio Porrato <maurizio.porrato@gmail.com>
  4. # See LICENSE.txt for copyright info
  5. from twisted.application.service import MultiService
  6. from zope.interface import implements
  7. from twisted.plugin import IPlugin
  8. from twisted.application.service import IServiceMaker
  9. from twisted.application import internet
  10. from twisted.python import usage
  11. from ConfigParser import ConfigParser
  12. from frn.protocol.client import FRNClient, FRNClientFactory
  13. from frn.user import FRNUser
  14. from twisted.python import log
  15. from os.path import curdir, join as pjoin
  16. cType = {'PC Only': 'O', 'Crosslink': 'C', 'Parrot': 'P'}
  17. cStatus = {'0': 'T', '1': 'R', '2': 'N'}
  18. clients = []
  19. talking = None
  20. class FRNCrosslinkClient(FRNClient):
  21. def connectionMade(self):
  22. self.txOk = False
  23. self.txReq = False
  24. self.clientId = None
  25. from twisted.internet import reactor
  26. self.reactor = reactor
  27. FRNClient.connectionMade(self)
  28. def getClientName(self, client_id):
  29. if self.clientsById.has_key(client_id):
  30. return self.clientsById[client_id]['on']
  31. else:
  32. return client_id
  33. def textMessageReceived(self, client, message, target):
  34. global clients
  35. if target == 'A':
  36. msg = "[%s] %s" % (self.getClientName(client), message)
  37. if client != self.clientId:
  38. for _, _, _, factory in clients:
  39. if factory != self.factory:
  40. factory.connection.sendTextMessage('', msg)
  41. else:
  42. if message.startswith('!'):
  43. cmd = message[1:]
  44. if cmd == "who":
  45. for server, port, u, factory in clients:
  46. cl = []
  47. cl.append("<u>%s@%s:%d</u>" % (u.NT,server,port))
  48. for c in factory.connection.clients:
  49. ss = cStatus.get(c['s'], '?')
  50. if c['m'] != '0':
  51. ss = ss.lower()
  52. cl.append(" %s%s %s " % (
  53. cType.get(c['bc'], 'G'),
  54. ss,
  55. c['on'].replace('<', '&lt;'),
  56. ))
  57. sm = [cl[i*15:(i+1)*15]
  58. for i in range((len(cl)+14)/15)]
  59. for m in sm:
  60. reply = '<br>'.join(m)
  61. self.sendTextMessage(client, reply)
  62. def decodeTX(self, my_id):
  63. log.msg("Got TX ack for %s" % self.user.ON)
  64. self.txOk = True
  65. def stopTransmission(self):
  66. FRNClient.stopTransmission(self)
  67. log.msg("Stopped TX on %s" % self.user.ON)
  68. def goIdle(self):
  69. global talking
  70. self.txReq = False
  71. self.txOk = False
  72. talking = None
  73. self.stopTransmission()
  74. def audioFrameReceived(self, from_id, frames):
  75. global clients, talking
  76. if talking is None or talking == self:
  77. talking = self
  78. for _, _, _, factory in clients:
  79. conn = factory.connection
  80. if conn != self:
  81. role = conn.serverdata.get('al', None)
  82. if role in ['OK', 'ADMIN', 'OWNER']:
  83. if conn.txOk:
  84. conn.txReq = False
  85. conn.sendAudioFrame(frames)
  86. conn.busyTimer.reset(2.0)
  87. else:
  88. if not conn.txReq:
  89. log.msg("Requesting TX for %s" % conn.user.ON)
  90. conn.startTransmission()
  91. conn.txReq = True
  92. conn.busyTimer = self.reactor.callLater(
  93. 5.0, conn.goIdle)
  94. self.pong()
  95. def loginResponse(self, info):
  96. log.msg("%s login: %s" % (self.user.ON, info['al']))
  97. def clientsListUpdated(self, clients):
  98. self.clients = clients
  99. self.clientsById = dict([(i['id'], i) for i in clients])
  100. if self.clientId is None:
  101. for c in clients:
  102. if c['on'] == self.user.ON and c['bc'] == self.user.BC:
  103. self.clientId = c['id']
  104. class FRNCrosslinkClientFactory(FRNClientFactory):
  105. protocol = FRNCrosslinkClient
  106. def buildProtocol(self, addr):
  107. p = FRNClientFactory.buildProtocol(self, addr)
  108. self.connection = p
  109. return p
  110. class Options(usage.Options):
  111. optParameters = [
  112. ["confdir", "c", curdir, "Directory containing config files"]
  113. ]
  114. def parseArgs(self, *serverdefs):
  115. basedir = self['confdir']
  116. acfg = ConfigParser()
  117. acfg.read(['/etc/grn/accounts.conf',
  118. pjoin(basedir,'accounts.conf'), 'accounts.conf'])
  119. scfg = ConfigParser()
  120. scfg.read(['/etc/grn/servers.conf',
  121. pjoin(basedir,'servers.conf'), 'servers.conf'])
  122. clients = []
  123. for serverdef in serverdefs:
  124. account_name, server_name, network_name = serverdef.split(':', 2)
  125. sd = {}
  126. sd['server'] = scfg.get(server_name, 'server')
  127. sd['port'] = scfg.getint(server_name, 'port')
  128. sd['backup_server'] = scfg.get(server_name, 'backup_server')
  129. sd['backup_port'] = scfg.getint(server_name, 'backup_port')
  130. sd['operator'] = acfg.get(account_name, 'operator')
  131. sd['email'] = acfg.get(account_name, 'email')
  132. sd['password'] = acfg.get(account_name, 'password')
  133. sd['description'] = acfg.get(account_name, 'description')
  134. sd['country'] = acfg.get(account_name, 'country')
  135. sd['city'] = acfg.get(account_name, 'city')
  136. sd['network'] = network_name
  137. clients.append(sd)
  138. self['clients'] = clients
  139. class FRNCrosslinkServiceMaker(object):
  140. implements(IServiceMaker, IPlugin)
  141. tapname = "frncrosslink"
  142. description = "Freeradionetwork crosslink"
  143. options = Options
  144. def makeService(self, options):
  145. s = MultiService()
  146. global clients # FIXME: Get rid of this hack
  147. for c in options['clients']:
  148. user = FRNUser(
  149. EA=c['email'],
  150. PW=c['password'], ON=c['operator'],
  151. # BC=c['transmission'], DS=c['description'],
  152. BC="Crosslink", DS=c['description'],
  153. NN=c['country'], CT=c['city'], NT=c['network'])
  154. factory = FRNCrosslinkClientFactory(user)
  155. clients.append((c['server'], c['port'], user, factory))
  156. s.addService(
  157. internet.TCPClient(c['server'], c['port'], factory))
  158. return s
  159. serviceMaker = FRNCrosslinkServiceMaker()
  160. # vim: set et ai sw=4 ts=4 sts=4: