gnuradionetwork/frn/protocol/manager.py

225 lines
7.3 KiB
Python

# -*- coding: utf-8 -*-
#
# Copyright 2010 Maurizio Porrato <maurizio.porrato@gmail.com>
# See LICENSE.txt for copyright info
from twisted.internet.defer import Deferred, succeed
from twisted.internet.protocol import ReconnectingClientFactory, ServerFactory
from twisted.protocols.basic import LineOnlyReceiver
from twisted.internet.task import LoopingCall
from twisted.python import log
from frn.user import FRNUser
from frn.protocol import versions
from frn.utility import *
class FRNManagerClient(LineOnlyReceiver):
PING_PERIOD = 3.0 # Seconds between ping requests
def connectionMade(self):
log.msg("Connected to manager [%s]" % self.transport.getPeer().host)
self.notifications = []
self.factory.managerConnected(self)
def connectionLost(self, reason):
log.msg("Manager disconnected")
try:
self.pingtimer.stop()
except: pass
for d in self.notifications:
d.errback(reason)
self.factory.managerDisconnected(self)
def notifyFinish(self):
self.notifications.append(Deferred())
return self.notifications[-1]
def finish(self, result):
d = self.notifications[0]
del self.notifications[0]
d.callback(result)
def lineReceived(self, line):
#log.msg("notifications: %s" % str(self.notifications))
if hasattr(self, 'serverlist'):
# TODO
pass
else:
self.finish(line.strip())
def sendServerLogin(self, user):
def loginDone(result):
self.managerdata = parseSimpleXML(result)
log.msg("Server login results: %s" % str(self.managerdata))
if int(self.managerdata['mc']) > 2009004:
self.sendLine(responseToChallange(
self.managerdata['kp']))
if self.managerdata['al'] != '0':
self.transport.loseConnection()
else:
self.pingtimer = LoopingCall(self.sendPing)
self.pingtimer.start(self.PING_PERIOD, False)
self.factory.resetDelay()
return self.managerdata
log.msg("Sending server login")
user.VX = versions.server
self.sendLine("SC:"+user.asXML(
'VX','SN','PT','OW','PW'))
return self.notifyFinish().addCallback(loginDone)
def sendServerLogout(self, user):
self.transport.loseConnection()
return succeed(None)
def sendPing(self):
self.sendLine("P")
return self.notifyFinish()
def sendClientLogin(self, client):
self.sendLine("CC:"+client.asXML(
'EA','PW','ON','BC','NN','CT','NT','DS','IP'))
return self.notifyFinish()
def sendClientLogout(self, client):
self.sendLine("CD:"+client.asXML('ID'))
return self.notifyFinish()
def getClientList(self):
#self.sendLine('SM')
raise NotImplementedError # TODO
def sendRegisterUser(self, user):
self.sendLine("IG:"+user.asXML(
'ON','EA','BC','DS','NN','CT'))
return self.notifyFinish()
class FRNManagerClientFactory(ReconnectingClientFactory):
protocol = FRNManagerClient
def managerConnected(self, connection):
pass
def managerDisconnected(self, connection):
pass
class FRNManagerServer(LineOnlyReceiver):
def connectionMade(self):
log.msg("Manager client connected from %s" % self.transport.getPeer().host)
self._phase = "CONNECTED"
self.serverInfo = None
self.kp = makeRandomChallange()
def connectionLost(self, reason):
log.msg("Manager client disconnected from %s: %s" %
(self.transport.getPeer().host, reason))
if self.serverInfo:
self.manager.serverLogout(self.serverInfo)
self.serverInfo = None
self._phase = "DISCONNECTED"
def _authOnly(self):
if self._phase != "AUTHENTICATED":
log.msg("Unauthorized action!")
self.transport.loseConnection()
def lineReceived(self, line):
sline = line.strip()
if self._phase == "CHALLANGE":
if sline == responseToChallange(self.kp):
self._phase = "AUTHENTICATED"
self.serverInfo = self.tmpServerInfo
log.msg("Auth success: %s" % repr(self.serverInfo))
else:
self.transport.loseConnection()
else:
if sline == 'P': # Ping
self.sendLine('F')
elif sline == 'SM': # Client list
self.sendClientList()
else:
cmd, body = sline.split(':', 1)
xbody = parseSimpleXML(body)
handler = getattr(self, 'decode'+cmd, None)
if handler is None:
self.unimplemented(cmd, xbody)
else:
handler(xbody)
def sendClientList(self):
def gotClientList(cl):
self.sendLine(str(len(cl)))
for sn, sp in cl:
self.sendLine("%s - Port: %d" % (sn, sp))
self.sendLine(str(len(cl[(sn,sp)])))
for nt in cl[(sn,sp)]:
self.sendLine(nt)
self.sendLine(str(len(cl[(sn,sp)][nt])))
for u in cl[(sn,sp)][nt]:
self.sendLine(u.asXML('EA','ON','BC','DS','NN','CT'))
log.msg("SM")
return self.manager.getClientList().addCallback(gotClientList)
def unimplemented(self, cmd, body):
log.err("Unimplemented command %s: %s" % (cmd, str(body)))
def decodeSC(self, body): # Server login
def sendManagerInfo(res):
if versions.manager > 2009004:
self._phase = "CHALLANGE"
self.tmpServerInfo = FRNUser(**body)
else:
self._phase = "AUTHENTICATED"
self.serverInfo = FRNUser(**body)
log.msg("Auth success: %s" % repr(self.serverInfo))
self.sendLine(formatSimpleXML([
('SV', versions.server),
('CV', versions.client),
('MC', versions.manager),
# ('AL', res['al']),
('AL', res),
('KP', self.kp)
]))
log.msg("SC: %s" % str(body))
self.manager.serverLogin(FRNUser(**body)).addCallback(
sendManagerInfo) # TODO: second authentication phase
def decodeCC(self, body): # Client login
log.msg("CC: %s" % str(body))
self._authOnly()
self.manager.clientLogin(self.serverInfo, FRNUser(**body)).addCallback(
self.sendLine)
def decodeCD(self, body): # Client logout
log.msg("CD: %s" % str(body))
self._authOnly()
self.manager.clientLogout(self.serverInfo, FRNUser(**body)).addCallback(
self.sendLine)
def decodeIG(self, body): # Account creation
log.msg("IG: %s" % str(body))
self.manager.registerUser(FRNUser(**body)).addCallback(
self.sendLine)
class FRNManagerServerFactory(ServerFactory):
protocol = FRNManagerServer
def __init__(self, managerFactory):
self.managerFactory = managerFactory
def buildProtocol(self, addr):
p = ServerFactory.buildProtocol(self, addr)
p.manager = self.managerFactory()
return p
# vim: set et ai sw=4 ts=4 sts=4: