gnuradionetwork/frn/protocol/client.py

312 lines
9.3 KiB
Python

# -*- coding: utf-8 -*-
#
# Copyright 2010 Maurizio Porrato <maurizio.porrato@gmail.com>
# See LICENSE.txt for copyright info
from Queue import Queue
from twisted.internet.protocol import ReconnectingClientFactory
from twisted.protocols.policies import TimeoutMixin
from twisted.internet.task import LoopingCall
from twisted.python import log
from frn.protocol import versions
from frn.user import FRNUser
from frn.protocol.common import BufferingLineReceiver
from frn.utility import *
class FRNClient(BufferingLineReceiver, TimeoutMixin):
def connectionMade(self):
BufferingLineReceiver.connectionMade(self)
self.user.VX = versions.client
self.serverdata = {}
self.txq = Queue()
self.setTimeout(25.0)
self.login()
def connectionLost(self, reason):
self.serverdata = {}
BufferingLineReceiver.connectionLost(self, reason)
def ready(self):
self.status = 'READY'
self.msgbuffer = []
self.phase = 0
self.expectRawData(1)
def startMultiLineMessage(self, msgtype):
self.status = msgtype
self.phase = None
self.setLineMode()
def stopMultiLineMessage(self):
handler = getattr(self, 'decode'+self.status, None)
status = self.status
message = self.msgbuffer
self.ready()
if handler is not None:
handler(message)
else:
self.unimplemented(status, message)
def collectMultiLineMessage(self, line):
if self.phase is None:
self.expected_lines = int(line.strip())
self.msgbuffer = []
self.phase = 0
else:
self.msgbuffer.append(line)
self.phase += 1
if self.phase >= self.expected_lines:
self.stopMultiLineMessage()
def lineReceived(self, line):
if self.status == 'AUTH':
if self.phase == 0:
self.latest_client_version = int(line.strip())
self.phase = 1
else:
self.serverdata = parseSimpleXML(line.strip())
if int(self.serverdata['SV']) > 2009004:
self.sendLine(responseToChallange(
self.serverdata['KP']))
self.ready()
self.setTimeout(10.0)
self.factory.resetDelay()
self.loginResponse(self.serverdata)
else:
self.collectMultiLineMessage(line)
def expectedReceived(self, data):
self.resetTimeout()
if self.status == 'READY':
packet_type = ord(data[0])
if packet_type == 0: # Keepalive
self.ready()
self.pong()
elif packet_type == 1: # TX ack
self.status = 'TX'
self.expectRawData(2)
elif packet_type == 2: # Audio
self.status = 'AUDIO'
self.expectRawData(327) # Two ID bytes + 10 GSM frames
elif packet_type == 3: # Client list
self.status = 'CLIENTS'
self.expectRawData(2) # Discard two null bytes
elif packet_type == 4: # Text
self.startMultiLineMessage('TEXT')
elif packet_type == 5: # Channel list
self.startMultiLineMessage('NETWORKS')
elif packet_type == 6: # Admin list
self.startMultiLineMessage('ADMIN')
elif packet_type == 7: # Access list
self.startMultiLineMessage('ACCESS')
elif packet_type == 8: # Block list
self.startMultiLineMessage('BLOCK')
elif packet_type == 9: # Mute list
self.startMultiLineMessage('MUTE')
elif packet_type == 10: # Access list flags
self.startMultiLineMessage('ACCESSFLAGS')
else:
log.err("Unknown packet type %d" % packet_type)
elif self.status == 'CLIENTS':
self.startMultiLineMessage('CLIENTS')
elif self.status == 'AUDIO':
self.ready()
self.decodeAUDIO(ord(data[0])*256+ord(data[1]), data[2:])
elif self.status == 'TX':
self.ready()
self.decodeTX(ord(data[0])*256+ord(data[1]))
def login(self):
ap = "CT:"+self.user.asXML(
'VX','EA','PW','ON','BC','DS','NN','CT','NT')
self.status = 'AUTH'
self.phase = 0
self.sendLine(ap)
def pong(self):
self.sendLine('P')
def setStatus(self, status):
self.sendLine('ST:%s' % str(status))
def stopTransmission(self):
self.sendLine('RX0')
def startTransmission(self):
self.sendLine('TX0')
def sendAudioFrame(self, frame):
self.resetTimeout()
self.sendLine('TX1')
self.transport.write(frame)
def streamStep(self, count):
if count > 1:
log.msg("WARNING: lost %d ticks" % (count-1))
for i in range(count):
self.sendAudioFrame(self.txq.get_nowait())
def stopStreaming(self):
self.txtimer.stop()
def _streamAck(self):
self.txtimer = LoopingCall.withCount(self.streamStep)
self.txtimer.start(0.20).addCallback(
lambda _: self.stopTransmission()).addErrback(
lambda _: self.stopTransmission())
def feedStreaming(self, frames):
if type(frames) == list:
for frame in frames:
self.txq.put_nowait(frame)
else:
self.txq.put_nowait(frames)
def startStreaming(self):
self.startTransmission()
def sendTextMessage(self, dest, text):
self.sendLine('TM:'+formatSimpleXML(dict(ID=dest, MS=text)))
def addAdmin(self, client_ip):
self.sendLine("AA:"+formatSimpleXML(dict(IP=client_ip)))
def removeAdmin(self, client_ip):
self.sendLine("DA:"+formatSimpleXML(dict(IP=client_ip)))
def addMute(self, client_ip):
self.sendLine("MC:"+formatSimpleXML(dict(IP=client_ip)))
def removeMute(self, client_ip):
self.sendLine("UM:"+formatSimpleXML(dict(IP=client_ip)))
def addBlock(self, client_ip):
self.sendLine("BC:"+formatSimpleXML(dict(IP=client_ip)))
def removeBlock(self, client_ip):
self.sendLine("UC:"+formatSimpleXML(dict(IP=client_ip)))
def addAccess(self, email):
self.sendLine("AT:"+formatSimpleXML(dict(EA=email)))
def removeAccess(self, email):
self.sendLine("DT:"+formatSimpleXML(dict(EA=email)))
def addTalk(self, email):
self.sendLine("ETX:"+formatSimpleXML(dict(EA=email)))
def removeTalk(self, email):
self.sendLine("RTX:"+formatSimpleXML(dict(EA=email)))
def accessFlagEnable(self, enable):
if enable:
v = 1
else:
v = 0
self.sendLine("ENA:%d" % v)
def accessFlagTalk(self, enable):
if enable:
v = 1
else:
v = 0
self.sendLine("TXR:%d" % v)
def unimplemented(self, status, msg):
log.msg("Unimplemented: %s: %s" % (status, msg))
def decodeAUDIO(self, from_id, frames):
self.audioFrameReceived(from_id, frames)
def decodeTX(self, my_id):
self._streamAck()
def decodeTEXT(self, msg):
self.textMessageReceived(msg[0], msg[1], msg[2])
def decodeCLIENTS(self, msg):
self.clientsListUpdated([parseSimpleXML(x) for x in msg])
def decodeNETWORKS(self, msg):
self.networksListUpdated(msg)
def decodeADMIN(self, msg):
self.adminListUpdated([parseSimpleXML(x) for x in msg])
def decodeACCESS(self, msg):
self.accessListUpdated([parseSimpleXML(x) for x in msg])
def decodeBLOCK(self, msg):
self.blockListUpdated([parseSimpleXML(x) for x in msg])
def decodeMUTE(self, msg):
self.muteListUpdated([parseSimpleXML(x) for x in msg])
def decodeACCESSFLAGS(self, msg):
self.accessFlagsUpdated(msg[0], msg[1])
def decodeUNKNOWN(self, code, msg):
log.msg("%s: %s" % (code, msg))
def loginResponse(self, info):
pass
def audioFrameReceived(self, from_id, frame):
pass
def textMessageReceived(self, client, message, target):
pass
def clientsListUpdated(self, clients):
pass
def networksListUpdated(self, networks):
pass
def adminListUpdated(self, admins):
pass
def accessListUpdated(self, access):
pass
def blockListUpdated(self, blocks):
pass
def muteListUpdated(self, mutes):
pass
def accessFlagsUpdated(self, access, talk):
pass
class FRNClientFactory(ReconnectingClientFactory):
protocol = FRNClient
maxRetries = 10
def __init__(self, user):
self.user = user
def startedConnecting(self, connector):
log.msg('Started to connect')
def buildProtocol(self, addr):
log.msg('Connected')
p = ReconnectingClientFactory.buildProtocol(self, addr)
p.user = self.user
return p
def clientConnectionLost(self, connector, reason):
log.msg('Lost connection. Reason: %s' % reason)
ReconnectingClientFactory.clientConnectionLost(
self, connector, reason)
def clientConnectionFailed(self, connector, reason):
log.err('Connection failed. Reason: %s' % reason)
ReconnectingClientFactory.clientConnectionFailed(
self, connector, reason)
# vim: set et ai sw=4 ts=4 sts=4: