2010-08-18 15:46:58 +00:00
|
|
|
# -*- 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):
|
|
|
|
|
2010-08-21 15:32:22 +00:00
|
|
|
PING_PERIOD = 3.0 # Seconds between ping requests
|
|
|
|
|
2010-08-18 15:46:58 +00:00
|
|
|
def connectionMade(self):
|
|
|
|
log.msg("Connected to manager [%s]" % self.transport.getPeer().host)
|
|
|
|
self.notifications = []
|
2010-08-21 18:32:56 +00:00
|
|
|
self.factory.managerConnected(self)
|
2010-08-18 15:46:58 +00:00
|
|
|
|
|
|
|
def connectionLost(self, reason):
|
|
|
|
log.msg("Manager disconnected")
|
|
|
|
try:
|
|
|
|
self.pingtimer.stop()
|
|
|
|
except: pass
|
|
|
|
for d in self.notifications:
|
|
|
|
d.errback(reason)
|
2010-08-21 18:32:56 +00:00
|
|
|
self.factory.managerDisconnected(self)
|
2010-08-18 15:46:58 +00:00
|
|
|
|
|
|
|
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):
|
2010-08-21 15:32:22 +00:00
|
|
|
#log.msg("notifications: %s" % str(self.notifications))
|
2010-08-18 15:46:58 +00:00
|
|
|
if hasattr(self, 'serverlist'):
|
|
|
|
# TODO
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
self.finish(line.strip())
|
|
|
|
|
|
|
|
def sendServerLogin(self, user):
|
|
|
|
def loginDone(result):
|
|
|
|
self.managerdata = parseSimpleXML(result)
|
2010-08-21 15:32:22 +00:00
|
|
|
log.msg("Server login results: %s" % str(self.managerdata))
|
2011-01-30 14:59:29 +00:00
|
|
|
if int(self.managerdata['MC']) > 2009004:
|
2010-08-18 15:46:58 +00:00
|
|
|
self.sendLine(responseToChallange(
|
2011-01-30 14:59:29 +00:00
|
|
|
self.managerdata['KP']))
|
|
|
|
if self.managerdata['AL'] != '0':
|
2010-08-21 15:32:22 +00:00
|
|
|
self.transport.loseConnection()
|
|
|
|
else:
|
|
|
|
self.pingtimer = LoopingCall(self.sendPing)
|
|
|
|
self.pingtimer.start(self.PING_PERIOD, False)
|
|
|
|
self.factory.resetDelay()
|
2010-08-18 15:46:58 +00:00
|
|
|
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
|
|
|
|
|
2010-08-21 18:32:56 +00:00
|
|
|
def managerConnected(self, connection):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def managerDisconnected(self, connection):
|
|
|
|
pass
|
2010-08-18 15:46:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
class FRNManagerServer(LineOnlyReceiver):
|
|
|
|
|
|
|
|
def connectionMade(self):
|
|
|
|
log.msg("Manager client connected from %s" % self.transport.getPeer().host)
|
|
|
|
self._phase = "CONNECTED"
|
2011-01-30 01:29:31 +00:00
|
|
|
self.serverInfo = None
|
2010-08-18 15:46:58 +00:00
|
|
|
self.kp = makeRandomChallange()
|
|
|
|
|
|
|
|
def connectionLost(self, reason):
|
|
|
|
log.msg("Manager client disconnected from %s: %s" %
|
|
|
|
(self.transport.getPeer().host, reason))
|
2011-01-30 01:29:31 +00:00
|
|
|
if self.serverInfo:
|
|
|
|
self.manager.serverLogout(self.serverInfo)
|
|
|
|
self.serverInfo = None
|
|
|
|
self._phase = "DISCONNECTED"
|
2010-08-18 15:46:58 +00:00
|
|
|
|
|
|
|
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"
|
2011-01-30 01:29:31 +00:00
|
|
|
self.serverInfo = self.tmpServerInfo
|
|
|
|
log.msg("Auth success: %s" % repr(self.serverInfo))
|
2010-08-18 15:46:58 +00:00
|
|
|
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):
|
2011-01-30 01:29:31 +00:00
|
|
|
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'))
|
2010-08-18 15:46:58 +00:00
|
|
|
log.msg("SM")
|
2011-01-30 01:29:31 +00:00
|
|
|
return self.manager.getClientList().addCallback(gotClientList)
|
|
|
|
|
2010-08-18 15:46:58 +00:00
|
|
|
|
|
|
|
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"
|
2011-01-30 01:29:31 +00:00
|
|
|
self.tmpServerInfo = FRNUser(**body)
|
2010-08-18 15:46:58 +00:00
|
|
|
else:
|
|
|
|
self._phase = "AUTHENTICATED"
|
2011-01-30 01:29:31 +00:00
|
|
|
self.serverInfo = FRNUser(**body)
|
|
|
|
log.msg("Auth success: %s" % repr(self.serverInfo))
|
2010-08-18 15:46:58 +00:00
|
|
|
self.sendLine(formatSimpleXML([
|
|
|
|
('SV', versions.server),
|
|
|
|
('CV', versions.client),
|
|
|
|
('MC', versions.manager),
|
2011-01-30 01:29:31 +00:00
|
|
|
('AL', res),
|
2010-08-18 15:46:58 +00:00
|
|
|
('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()
|
2011-01-30 01:29:31 +00:00
|
|
|
self.manager.clientLogin(self.serverInfo, FRNUser(**body)).addCallback(
|
2010-08-18 15:46:58 +00:00
|
|
|
self.sendLine)
|
|
|
|
|
|
|
|
def decodeCD(self, body): # Client logout
|
|
|
|
log.msg("CD: %s" % str(body))
|
|
|
|
self._authOnly()
|
2011-01-30 01:29:31 +00:00
|
|
|
self.manager.clientLogout(self.serverInfo, FRNUser(**body)).addCallback(
|
2010-08-18 15:46:58 +00:00
|
|
|
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:
|