Compare commits
No commits in common. "master" and "wip" have entirely different histories.
|
@ -5,7 +5,6 @@
|
||||||
|
|
||||||
import shelve
|
import shelve
|
||||||
from twisted.python import log
|
from twisted.python import log
|
||||||
from frn.user import FRNUser
|
|
||||||
|
|
||||||
CLIENTS_ORDER = ['Crosslink', 'Parrot', 'Gateway', 'PC Only']
|
CLIENTS_ORDER = ['Crosslink', 'Parrot', 'Gateway', 'PC Only']
|
||||||
|
|
||||||
|
@ -40,7 +39,6 @@ class ClientTracker(object):
|
||||||
self._mute = {}
|
self._mute = {}
|
||||||
self._block = {}
|
self._block = {}
|
||||||
self._admin = {}
|
self._admin = {}
|
||||||
self._acl = {}
|
|
||||||
self.cbClient = cbClient
|
self.cbClient = cbClient
|
||||||
self.cbNet = cbNet
|
self.cbNet = cbNet
|
||||||
self.cbMute = cbMute
|
self.cbMute = cbMute
|
||||||
|
@ -55,7 +53,6 @@ class ClientTracker(object):
|
||||||
self._mute = s['mute']
|
self._mute = s['mute']
|
||||||
self._block = s['block']
|
self._block = s['block']
|
||||||
self._admin = s['admin']
|
self._admin = s['admin']
|
||||||
self._acl = s['acl']
|
|
||||||
except KeyError: pass
|
except KeyError: pass
|
||||||
s.close()
|
s.close()
|
||||||
|
|
||||||
|
@ -64,7 +61,6 @@ class ClientTracker(object):
|
||||||
s['mute'] = self._mute
|
s['mute'] = self._mute
|
||||||
s['block'] = self._block
|
s['block'] = self._block
|
||||||
s['admin'] = self._admin
|
s['admin'] = self._admin
|
||||||
s['acl'] = self._acl
|
|
||||||
s.close()
|
s.close()
|
||||||
|
|
||||||
def isLoggedIn(self, clientId):
|
def isLoggedIn(self, clientId):
|
||||||
|
@ -82,7 +78,7 @@ class ClientTracker(object):
|
||||||
return l.index(clientId)+1
|
return l.index(clientId)+1
|
||||||
|
|
||||||
def getNetworkList(self):
|
def getNetworkList(self):
|
||||||
return sorted(set(self._net.keys())|set(self._acl.keys()))
|
return self._net.keys()
|
||||||
|
|
||||||
def getClientList(self, network=[]):
|
def getClientList(self, network=[]):
|
||||||
if network:
|
if network:
|
||||||
|
@ -117,10 +113,7 @@ class ClientTracker(object):
|
||||||
def login(self, client, status=0):
|
def login(self, client, status=0):
|
||||||
clientId = client.user.ID
|
clientId = client.user.ID
|
||||||
if clientId not in self._clientData:
|
if clientId not in self._clientData:
|
||||||
if not self.aclTalkOk(client.user.NT, client.user.EA) and status < 1:
|
client.user.S = status
|
||||||
client.user.S = 1
|
|
||||||
else:
|
|
||||||
client.user.S = status
|
|
||||||
self._clientData[clientId] = client
|
self._clientData[clientId] = client
|
||||||
net = client.user.NT
|
net = client.user.NT
|
||||||
nc = self._net.get(net, [])
|
nc = self._net.get(net, [])
|
||||||
|
@ -140,10 +133,8 @@ class ClientTracker(object):
|
||||||
self._mute[clientId].AI = a
|
self._mute[clientId].AI = a
|
||||||
else:
|
else:
|
||||||
client.user.M = 0
|
client.user.M = 0
|
||||||
if not self.canLogin(client.user):
|
if clientId in self._block:
|
||||||
client.role = "BLOCK"
|
client.role = "BLOCK"
|
||||||
else:
|
|
||||||
self.aclUpdate(client.user)
|
|
||||||
if client.user.EA in self._admin:
|
if client.user.EA in self._admin:
|
||||||
client.role = "ADMIN"
|
client.role = "ADMIN"
|
||||||
self._admin[client.user.EA].update(client.user.dict())
|
self._admin[client.user.EA].update(client.user.dict())
|
||||||
|
@ -168,13 +159,11 @@ class ClientTracker(object):
|
||||||
self.cbClient(net, self.getClientList(net))
|
self.cbClient(net, self.getClientList(net))
|
||||||
|
|
||||||
def setStatus(self, clientId, status):
|
def setStatus(self, clientId, status):
|
||||||
user = self.getClient(clientId)
|
oldStatus = self._clientData[clientId].user.S
|
||||||
oldStatus = user.S
|
|
||||||
if not self.aclTalkOk(user.NT, user.EA) and status < 1:
|
|
||||||
status = 1
|
|
||||||
if oldStatus != str(status):
|
if oldStatus != str(status):
|
||||||
|
net = self._clientData[clientId].user.NT
|
||||||
self._clientData[clientId].user.S = status
|
self._clientData[clientId].user.S = status
|
||||||
self.cbClient(user.NT, self.getClientList(user.NT))
|
self.cbClient(net, self.getClientList(net))
|
||||||
|
|
||||||
def mute(self, admin, clientId):
|
def mute(self, admin, clientId):
|
||||||
if clientId not in self._mute:
|
if clientId not in self._mute:
|
||||||
|
@ -221,91 +210,5 @@ class ClientTracker(object):
|
||||||
self.save()
|
self.save()
|
||||||
self.cbAdmin(self.getAdminList())
|
self.cbAdmin(self.getAdminList())
|
||||||
|
|
||||||
def _getAcl(self, network):
|
|
||||||
return self._acl.get(network, (False, False, {}))
|
|
||||||
|
|
||||||
def _setAcl(self, network, ac, tx, l):
|
|
||||||
log.msg("Changing ACL for %s" % network)
|
|
||||||
if ac == tx == False and len(l) == 0:
|
|
||||||
log.msg("Default settings detected")
|
|
||||||
if network in self._acl:
|
|
||||||
log.msg("Deleting")
|
|
||||||
del self._acl[network]
|
|
||||||
else:
|
|
||||||
log.msg("Setting ACL for %s to %s" % (network, str((ac,tx,l))))
|
|
||||||
self._acl[network] = (ac, tx, l)
|
|
||||||
self.save()
|
|
||||||
|
|
||||||
def getAcl(self, network):
|
|
||||||
ac, tx, l = self._getAcl(network)
|
|
||||||
return l.values()
|
|
||||||
|
|
||||||
def getAclFlags(self, network):
|
|
||||||
ac, tx, l = self._getAcl(network)
|
|
||||||
return (ac, tx)
|
|
||||||
|
|
||||||
def setAclFlags(self, network, ac, tx):
|
|
||||||
oac, otx, l = self._getAcl(network)
|
|
||||||
self._setAcl(network, ac, tx, l)
|
|
||||||
|
|
||||||
def addAcl(self, network, email):
|
|
||||||
log.msg("Adding %s to ACL for %s" % (email, network))
|
|
||||||
ac, tx, l = self._getAcl(network)
|
|
||||||
if email not in l:
|
|
||||||
knownUsers = dict([(x.EA, x) for x in [y.user for y in self._clientData.values()] + self._mute.values() + self._block.values()])
|
|
||||||
user = knownUsers.get(email, FRNUser(EA=email)).copy(ID=email, AI=1)
|
|
||||||
l[email] = user
|
|
||||||
self._setAcl(network, ac, tx, l)
|
|
||||||
|
|
||||||
def delAcl(self, network, email):
|
|
||||||
log.msg("Removing %s from ACL for %s" % (email, network))
|
|
||||||
ac, tx, l = self._getAcl(network)
|
|
||||||
if email in l:
|
|
||||||
del l[email]
|
|
||||||
self._setAcl(network, ac, tx, l)
|
|
||||||
|
|
||||||
def setAclTx(self, network, email, tx):
|
|
||||||
oac, otx, l = self._getAcl(network)
|
|
||||||
if email in l:
|
|
||||||
if tx:
|
|
||||||
l[email].AI = 0
|
|
||||||
else:
|
|
||||||
l[email].AI = 1
|
|
||||||
self._setAcl(network, oac, otx, l)
|
|
||||||
|
|
||||||
def aclUpdate(self, user):
|
|
||||||
network = user.NT
|
|
||||||
email = user.EA
|
|
||||||
ac, tx, l = self._getAcl(network)
|
|
||||||
if email in l:
|
|
||||||
old = l[email]
|
|
||||||
new = user.copy(ID=email, AI=old.AI)
|
|
||||||
l[email] = new
|
|
||||||
self._setAcl(network, ac, tx, l)
|
|
||||||
|
|
||||||
def aclLoginOk(self, network, email):
|
|
||||||
ac, tx, l = self._getAcl(network)
|
|
||||||
if not ac:
|
|
||||||
return True
|
|
||||||
return email in l
|
|
||||||
|
|
||||||
def aclTalkOk(self, network, email):
|
|
||||||
ac, tx, l = self._getAcl(network)
|
|
||||||
if not tx:
|
|
||||||
return True
|
|
||||||
if email not in l:
|
|
||||||
return False
|
|
||||||
return l[email].AI == '0'
|
|
||||||
|
|
||||||
def canLogin(self, user):
|
|
||||||
if self.isBlocked(user.ID):
|
|
||||||
return False
|
|
||||||
return self.aclLoginOk(user.NT, user.EA)
|
|
||||||
|
|
||||||
def canTalk(self, clientId):
|
|
||||||
if self.isMute(clientId):
|
|
||||||
return False
|
|
||||||
u = self.getClient(clientId)
|
|
||||||
return self.aclTalkOk(u.NT, u.EA)
|
|
||||||
|
|
||||||
# vim: set et ai sw=4 ts=4 sts=4:
|
# vim: set et ai sw=4 ts=4 sts=4:
|
||||||
|
|
|
@ -8,16 +8,16 @@ from zope.interface import Interface
|
||||||
|
|
||||||
class IManager(Interface):
|
class IManager(Interface):
|
||||||
|
|
||||||
def serverLogin(server):
|
def serverLogin(user):
|
||||||
"""Logs server on"""
|
"""Logs server on"""
|
||||||
|
|
||||||
def serverLogout(server):
|
def serverLogout(user):
|
||||||
"""Logs server out"""
|
"""Logs server out"""
|
||||||
|
|
||||||
def clientLogin(server, user):
|
def clientLogin(user):
|
||||||
"""Logs client in"""
|
"""Logs client in"""
|
||||||
|
|
||||||
def clientLogout(server, user):
|
def clientLogout(user):
|
||||||
"""Logs client out"""
|
"""Logs client out"""
|
||||||
|
|
||||||
def getClientList():
|
def getClientList():
|
||||||
|
|
|
@ -5,149 +5,33 @@
|
||||||
|
|
||||||
from zope.interface import implements
|
from zope.interface import implements
|
||||||
from frn.manager import IManager
|
from frn.manager import IManager
|
||||||
from twisted.python import log
|
from frn.userstore.database import DatabaseUserStore
|
||||||
from twisted.python.failure import Failure
|
|
||||||
from twisted.mail.smtp import sendmail
|
|
||||||
import random, string, uuid
|
|
||||||
from frn.user import FRNUser
|
|
||||||
|
|
||||||
def rndpasswd():
|
|
||||||
return ''.join([random.choice(string.ascii_uppercase) for i in range(8)])
|
|
||||||
|
|
||||||
class DatabaseManager(object):
|
class DatabaseManager(object):
|
||||||
|
|
||||||
implements(IManager)
|
implements(IManager)
|
||||||
|
|
||||||
def __init__(self, pool):
|
def __init__(self, store):
|
||||||
self._pool = pool
|
self._store = store
|
||||||
self._pool.runOperation("""
|
|
||||||
CREATE TABLE IF NOT EXISTS frn_users (
|
|
||||||
_id VARCHAR(32) NOT NULL PRIMARY KEY,
|
|
||||||
_ea VARCHAR(30) UNIQUE NOT NULL,
|
|
||||||
_pw VARCHAR(20) NOT NULL,
|
|
||||||
_on VARCHAR(20) NOT NULL,
|
|
||||||
_bc VARCHAR(20) NOT NULL,
|
|
||||||
_nn VARCHAR(20) NOT NULL,
|
|
||||||
_ct VARCHAR(20) NOT NULL,
|
|
||||||
_nt VARCHAR(20),
|
|
||||||
_ds VARCHAR(20) NOT NULL,
|
|
||||||
_ip VARCHAR(20),
|
|
||||||
registration TIMESTAMP NOT NULL DEFAULT current_timestamp,
|
|
||||||
lastlogin TIMESTAMP,
|
|
||||||
server VARCHAR(20),
|
|
||||||
port INTEGER
|
|
||||||
);
|
|
||||||
""")
|
|
||||||
self._pool.runOperation("""
|
|
||||||
CREATE TABLE IF NOT EXISTS frn_servers (
|
|
||||||
_vx VARCHAR(7) NOT NULL,
|
|
||||||
_sn VARCHAR(20) NOT NULL,
|
|
||||||
_pt INTEGER NOT NULL,
|
|
||||||
_bn VARCHAR(20),
|
|
||||||
_bp integer,
|
|
||||||
_ow VARCHAR(30) NOT NULL,
|
|
||||||
registration TIMESTAMP NOT NULL DEFAULT current_timestamp,
|
|
||||||
PRIMARY KEY(_sn, _pt)
|
|
||||||
);
|
|
||||||
""")
|
|
||||||
|
|
||||||
def serverLogin(self, server):
|
def serverLogin(self, user):
|
||||||
def checkauth(res):
|
pass
|
||||||
if not isinstance(res, Failure):
|
|
||||||
if res[0] > 0:
|
|
||||||
return self._pool.runOperation("""
|
|
||||||
INSERT INTO frn_servers
|
|
||||||
(_vx, _sn, _pt, _bn, _bp, _ow) VALUES
|
|
||||||
(?,?,?,?,?,?)
|
|
||||||
""", (server.VX, server.SN, server.PT,
|
|
||||||
server.BN, server.BP, server.OW)).addCallbacks(
|
|
||||||
lambda x: 0, lambda x: -1)
|
|
||||||
else:
|
|
||||||
return -1
|
|
||||||
return self._pool.runQuery("SELECT count(*) FROM frn_users WHERE _ea=? AND _pw=?",
|
|
||||||
(server.OW, server.PW)).addBoth(checkauth)
|
|
||||||
|
|
||||||
def serverLogout(self, server):
|
def serverLogout(self, user):
|
||||||
self._pool.runOperation("UPDATE frn_users SET server=NULL, port=NULL, _nt=NULL WHERE server=? AND port=?",
|
pass
|
||||||
(server.SN, server.PT))
|
|
||||||
self._pool.runOperation("DELETE FROM frn_servers WHERE _sn=? AND _pt=?",
|
|
||||||
(server.SN, server.PT))
|
|
||||||
|
|
||||||
def clientLogin(self, server, user):
|
def clientLogin(self, user):
|
||||||
def userfound(data):
|
pass
|
||||||
log.msg("Client login: %s" % repr(data))
|
|
||||||
if data:
|
|
||||||
u = dict(zip(
|
|
||||||
('ID', 'EA', 'PW', 'ON', 'BC', 'NN', 'CT', 'NT', 'DS', 'IP', 'registration', 'lastlogin', 'server', 'port'),
|
|
||||||
data[0]))
|
|
||||||
if u['server']:
|
|
||||||
log.msg("Duplicate client %s" % repr(user))
|
|
||||||
return "DUPL"
|
|
||||||
self._pool.runQuery("""
|
|
||||||
UPDATE frn_users SET
|
|
||||||
_bc=?, _nn=?, _ct=?, _nt=?, _ds=?, _ip=?, server=?, port=?,
|
|
||||||
lastlogin=current_timestamp
|
|
||||||
WHERE _id=?
|
|
||||||
""", (user.BC, user.NN, user.CT, user.NT, user.DS, user.IP, server.SN, server.PT, u['ID']))
|
|
||||||
log.msg("Authenticated client %s" % repr(user))
|
|
||||||
return str(u['ID'])
|
|
||||||
else:
|
|
||||||
log.msg("Wrong client %s" % repr(user))
|
|
||||||
return "WRONG"
|
|
||||||
return self._pool.runQuery("SELECT * FROM frn_users WHERE _ea=? AND _on=? AND _pw=?",
|
|
||||||
(user.EA, user.ON, user.PW)).addCallbacks(userfound, lambda x: "WRONG")
|
|
||||||
|
|
||||||
def clientLogout(self, server, user):
|
def clientLogout(self, user):
|
||||||
log.msg("Logging out client %s" % repr(user))
|
pass
|
||||||
return self._pool.runOperation("UPDATE frn_users SET server=NULL, port=NULL, _nt=NULL WHERE _id=?",
|
|
||||||
(user.ID,)).addBoth(lambda x: "OK")
|
|
||||||
|
|
||||||
def getClientList(self):
|
def getClientList(self):
|
||||||
def buildlist(tr):
|
pass
|
||||||
tr.execute("SELECT _sn, _pt FROM frn_servers")
|
|
||||||
servers = tr.fetchall()
|
|
||||||
r = {}
|
|
||||||
for sn, sp in servers:
|
|
||||||
r[(sn,sp)] = {}
|
|
||||||
tr.execute("SELECT DISTINCT _nt FROM frn_users WHERE server=? AND port=?", (sn,sp))
|
|
||||||
networks = tr.fetchall()
|
|
||||||
for (n,) in networks:
|
|
||||||
r[(sn,sp)][n] = []
|
|
||||||
tr.execute("SELECT _id, _on, _bc, _ds, _nn, _ct FROM frn_users WHERE server=? AND port=? AND _nt=?",
|
|
||||||
(sn, sp, n))
|
|
||||||
clients = tr.fetchall()
|
|
||||||
for c in clients:
|
|
||||||
cu = FRNUser(**dict(zip(['EA','ON','BC','DS','NN','CT'],c)))
|
|
||||||
r[(sn,sp)][n].append(cu)
|
|
||||||
return r
|
|
||||||
return self._pool.runInteraction(buildlist)
|
|
||||||
|
|
||||||
def registerUser(self, user):
|
def registerUser(self, user):
|
||||||
def fetchdata(is_new):
|
pass
|
||||||
def maildata(data):
|
|
||||||
u = dict(zip(
|
|
||||||
('ID', 'EA', 'PW', 'ON', 'BC', 'NN', 'CT', 'NT', 'DS', 'IP', 'registration', 'lastlogin', 'server', 'port'),
|
|
||||||
data[0]))
|
|
||||||
log.msg("Mailing password to user %s" % str(u))
|
|
||||||
tplfile = open('mailtemplate.txt','r')
|
|
||||||
try:
|
|
||||||
tpl = tplfile.read()
|
|
||||||
mailbody = string.Template(tpl).safe_substitute(u)
|
|
||||||
sendmail('127.0.0.1',
|
|
||||||
'admin@gnuradionetwork.org',
|
|
||||||
[u['EA']],
|
|
||||||
mailbody, port=25)
|
|
||||||
finally:
|
|
||||||
tplfile.close()
|
|
||||||
return "OK"
|
|
||||||
return self._pool.runQuery(
|
|
||||||
"SELECT * FROM frn_users WHERE _ea=?", (user.EA,)
|
|
||||||
).addCallback(maildata).addErrback(lambda x: "ERROR")
|
|
||||||
return self._pool.runOperation("""
|
|
||||||
INSERT INTO frn_users (_id, _ea, _pw, _on, _bc, _ds, _nn, _ct)
|
|
||||||
VALUES (?,?,?,?,?,?,?,?)
|
|
||||||
""", (uuid.uuid4().get_hex()[:20], user.EA, rndpasswd(),
|
|
||||||
user.ON, user.BC, user.DS, user.NN, user.CT)
|
|
||||||
).addBoth(fetchdata)
|
|
||||||
|
|
||||||
# vim: set et ai sw=4 ts=4 sts=4:
|
# vim: set et ai sw=4 ts=4 sts=4:
|
||||||
|
|
|
@ -16,20 +16,20 @@ class DummyManager(object):
|
||||||
def _randId(self):
|
def _randId(self):
|
||||||
return '.'.join([str(randint(1,254)) for i in range(4)])
|
return '.'.join([str(randint(1,254)) for i in range(4)])
|
||||||
|
|
||||||
def serverLogin(self, server):
|
def serverLogin(self, user):
|
||||||
return defer.succeed(0)
|
return defer.succeed(0)
|
||||||
|
|
||||||
def serverLogout(self, server):
|
def serverLogout(self, user):
|
||||||
return defer.succeed(None)
|
return defer.succeed(None)
|
||||||
|
|
||||||
def clientLogin(self, server, user):
|
def clientLogin(self, user):
|
||||||
return defer.succeed(self._randId())
|
return defer.succeed(self._randId())
|
||||||
|
|
||||||
def clientLogout(self, server, user):
|
def clientLogout(self, user):
|
||||||
return defer.succeed('OK')
|
return defer.succeed('OK')
|
||||||
|
|
||||||
def getClientList(self):
|
def getClientList(self):
|
||||||
return defer.succeed({})
|
return defer.succeed([])
|
||||||
|
|
||||||
def registerUser(self, user):
|
def registerUser(self, user):
|
||||||
return defer.succeed('OK')
|
return defer.succeed('OK')
|
||||||
|
|
|
@ -23,7 +23,7 @@ class CustomManagerClientFactory(FRNManagerClientFactory):
|
||||||
def authReply(auth):
|
def authReply(auth):
|
||||||
self.resetDelay()
|
self.resetDelay()
|
||||||
self.authResult = auth
|
self.authResult = auth
|
||||||
self.authDone = (auth['AL'] == '0')
|
self.authDone = (auth['al'] == '0')
|
||||||
if self.authDone:
|
if self.authDone:
|
||||||
self.deferred.callback(auth)
|
self.deferred.callback(auth)
|
||||||
connection.sendServerLogin(self.user).addCallback(authReply)
|
connection.sendServerLogin(self.user).addCallback(authReply)
|
||||||
|
@ -55,23 +55,23 @@ class RemoteManager(object):
|
||||||
def doConnect(self):
|
def doConnect(self):
|
||||||
self.reactor.connectTCP(self.server, self.port, self.factory)
|
self.reactor.connectTCP(self.server, self.port, self.factory)
|
||||||
|
|
||||||
def serverLogin(self, server):
|
def serverLogin(self, user):
|
||||||
self.factory = CustomManagerClientFactory(server)
|
self.factory = CustomManagerClientFactory(user)
|
||||||
self.doConnect()
|
self.doConnect()
|
||||||
return self.factory.deferred
|
return self.factory.deferred
|
||||||
|
|
||||||
def serverLogout(self, server):
|
def serverLogout(self, user):
|
||||||
if self.factory.client is not None:
|
if self.factory.client is not None:
|
||||||
return self.factory.client.sendServerLogout(server)
|
return self.factory.client.sendServerLogout(user)
|
||||||
|
|
||||||
def clientLogin(self, server, user):
|
def clientLogin(self, user):
|
||||||
if self.maskParrot and user.BC == 'Parrot':
|
if self.maskParrot and user.BC == 'Parrot':
|
||||||
u = user.copy(BC='PC Only')
|
u = user.copy(BC='PC Only')
|
||||||
else:
|
else:
|
||||||
u = user.copy()
|
u = user.copy()
|
||||||
return self.factory.client.sendClientLogin(u)
|
return self.factory.client.sendClientLogin(u)
|
||||||
|
|
||||||
def clientLogout(self, server, user):
|
def clientLogout(self, user):
|
||||||
if self.maskParrot and user.BC == 'Parrot':
|
if self.maskParrot and user.BC == 'Parrot':
|
||||||
u = user.copy(BC='PC Only')
|
u = user.copy(BC='PC Only')
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -1,311 +1,311 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright 2010 Maurizio Porrato <maurizio.porrato@gmail.com>
|
# Copyright 2010 Maurizio Porrato <maurizio.porrato@gmail.com>
|
||||||
# See LICENSE.txt for copyright info
|
# See LICENSE.txt for copyright info
|
||||||
|
|
||||||
from Queue import Queue
|
from Queue import Queue
|
||||||
from twisted.internet.protocol import ReconnectingClientFactory
|
from twisted.internet.protocol import ReconnectingClientFactory
|
||||||
from twisted.protocols.policies import TimeoutMixin
|
from twisted.protocols.policies import TimeoutMixin
|
||||||
from twisted.internet.task import LoopingCall
|
from twisted.internet.task import LoopingCall
|
||||||
from twisted.python import log
|
from twisted.python import log
|
||||||
from frn.protocol import versions
|
from frn.protocol import versions
|
||||||
from frn.user import FRNUser
|
from frn.user import FRNUser
|
||||||
from frn.protocol.common import BufferingLineReceiver
|
from frn.protocol.common import BufferingLineReceiver
|
||||||
from frn.utility import *
|
from frn.utility import *
|
||||||
|
|
||||||
|
|
||||||
class FRNClient(BufferingLineReceiver, TimeoutMixin):
|
class FRNClient(BufferingLineReceiver, TimeoutMixin):
|
||||||
|
|
||||||
def connectionMade(self):
|
def connectionMade(self):
|
||||||
BufferingLineReceiver.connectionMade(self)
|
BufferingLineReceiver.connectionMade(self)
|
||||||
self.user.VX = versions.client
|
self.user.VX = versions.client
|
||||||
self.serverdata = {}
|
self.serverdata = {}
|
||||||
self.txq = Queue()
|
self.txq = Queue()
|
||||||
self.setTimeout(25.0)
|
self.setTimeout(25.0)
|
||||||
self.login()
|
self.login()
|
||||||
|
|
||||||
def connectionLost(self, reason):
|
def connectionLost(self, reason):
|
||||||
self.serverdata = {}
|
self.serverdata = {}
|
||||||
BufferingLineReceiver.connectionLost(self, reason)
|
BufferingLineReceiver.connectionLost(self, reason)
|
||||||
|
|
||||||
def ready(self):
|
def ready(self):
|
||||||
self.status = 'READY'
|
self.status = 'READY'
|
||||||
self.msgbuffer = []
|
self.msgbuffer = []
|
||||||
self.phase = 0
|
self.phase = 0
|
||||||
self.expectRawData(1)
|
self.expectRawData(1)
|
||||||
|
|
||||||
def startMultiLineMessage(self, msgtype):
|
def startMultiLineMessage(self, msgtype):
|
||||||
self.status = msgtype
|
self.status = msgtype
|
||||||
self.phase = None
|
self.phase = None
|
||||||
self.setLineMode()
|
self.setLineMode()
|
||||||
|
|
||||||
def stopMultiLineMessage(self):
|
def stopMultiLineMessage(self):
|
||||||
handler = getattr(self, 'decode'+self.status, None)
|
handler = getattr(self, 'decode'+self.status, None)
|
||||||
status = self.status
|
status = self.status
|
||||||
message = self.msgbuffer
|
message = self.msgbuffer
|
||||||
self.ready()
|
self.ready()
|
||||||
if handler is not None:
|
if handler is not None:
|
||||||
handler(message)
|
handler(message)
|
||||||
else:
|
else:
|
||||||
self.unimplemented(status, message)
|
self.unimplemented(status, message)
|
||||||
|
|
||||||
def collectMultiLineMessage(self, line):
|
def collectMultiLineMessage(self, line):
|
||||||
if self.phase is None:
|
if self.phase is None:
|
||||||
self.expected_lines = int(line.strip())
|
self.expected_lines = int(line.strip())
|
||||||
self.msgbuffer = []
|
self.msgbuffer = []
|
||||||
self.phase = 0
|
self.phase = 0
|
||||||
else:
|
else:
|
||||||
self.msgbuffer.append(line)
|
self.msgbuffer.append(line)
|
||||||
self.phase += 1
|
self.phase += 1
|
||||||
if self.phase >= self.expected_lines:
|
if self.phase >= self.expected_lines:
|
||||||
self.stopMultiLineMessage()
|
self.stopMultiLineMessage()
|
||||||
|
|
||||||
def lineReceived(self, line):
|
def lineReceived(self, line):
|
||||||
if self.status == 'AUTH':
|
if self.status == 'AUTH':
|
||||||
if self.phase == 0:
|
if self.phase == 0:
|
||||||
self.latest_client_version = int(line.strip())
|
self.latest_client_version = int(line.strip())
|
||||||
self.phase = 1
|
self.phase = 1
|
||||||
else:
|
else:
|
||||||
self.serverdata = parseSimpleXML(line.strip())
|
self.serverdata = parseSimpleXML(line.strip())
|
||||||
if int(self.serverdata['SV']) > 2009004:
|
if int(self.serverdata['sv']) > 2009004:
|
||||||
self.sendLine(responseToChallange(
|
self.sendLine(responseToChallange(
|
||||||
self.serverdata['KP']))
|
self.serverdata['kp']))
|
||||||
self.ready()
|
self.ready()
|
||||||
self.setTimeout(10.0)
|
self.setTimeout(10.0)
|
||||||
self.factory.resetDelay()
|
self.factory.resetDelay()
|
||||||
self.loginResponse(self.serverdata)
|
self.loginResponse(self.serverdata)
|
||||||
else:
|
else:
|
||||||
self.collectMultiLineMessage(line)
|
self.collectMultiLineMessage(line)
|
||||||
|
|
||||||
def expectedReceived(self, data):
|
def expectedReceived(self, data):
|
||||||
self.resetTimeout()
|
self.resetTimeout()
|
||||||
if self.status == 'READY':
|
if self.status == 'READY':
|
||||||
packet_type = ord(data[0])
|
packet_type = ord(data[0])
|
||||||
if packet_type == 0: # Keepalive
|
if packet_type == 0: # Keepalive
|
||||||
self.ready()
|
self.ready()
|
||||||
self.pong()
|
self.pong()
|
||||||
elif packet_type == 1: # TX ack
|
elif packet_type == 1: # TX ack
|
||||||
self.status = 'TX'
|
self.status = 'TX'
|
||||||
self.expectRawData(2)
|
self.expectRawData(2)
|
||||||
elif packet_type == 2: # Audio
|
elif packet_type == 2: # Audio
|
||||||
self.status = 'AUDIO'
|
self.status = 'AUDIO'
|
||||||
self.expectRawData(327) # Two ID bytes + 10 GSM frames
|
self.expectRawData(327) # Two ID bytes + 10 GSM frames
|
||||||
elif packet_type == 3: # Client list
|
elif packet_type == 3: # Client list
|
||||||
self.status = 'CLIENTS'
|
self.status = 'CLIENTS'
|
||||||
self.expectRawData(2) # Discard two null bytes
|
self.expectRawData(2) # Discard two null bytes
|
||||||
elif packet_type == 4: # Text
|
elif packet_type == 4: # Text
|
||||||
self.startMultiLineMessage('TEXT')
|
self.startMultiLineMessage('TEXT')
|
||||||
elif packet_type == 5: # Channel list
|
elif packet_type == 5: # Channel list
|
||||||
self.startMultiLineMessage('NETWORKS')
|
self.startMultiLineMessage('NETWORKS')
|
||||||
elif packet_type == 6: # Admin list
|
elif packet_type == 6: # Admin list
|
||||||
self.startMultiLineMessage('ADMIN')
|
self.startMultiLineMessage('ADMIN')
|
||||||
elif packet_type == 7: # Access list
|
elif packet_type == 7: # Access list
|
||||||
self.startMultiLineMessage('ACCESS')
|
self.startMultiLineMessage('ACCESS')
|
||||||
elif packet_type == 8: # Block list
|
elif packet_type == 8: # Block list
|
||||||
self.startMultiLineMessage('BLOCK')
|
self.startMultiLineMessage('BLOCK')
|
||||||
elif packet_type == 9: # Mute list
|
elif packet_type == 9: # Mute list
|
||||||
self.startMultiLineMessage('MUTE')
|
self.startMultiLineMessage('MUTE')
|
||||||
elif packet_type == 10: # Access list flags
|
elif packet_type == 10: # Access list flags
|
||||||
self.startMultiLineMessage('ACCESSFLAGS')
|
self.startMultiLineMessage('ACCESSFLAGS')
|
||||||
else:
|
else:
|
||||||
log.err("Unknown packet type %d" % packet_type)
|
log.err("Unknown packet type %d" % packet_type)
|
||||||
elif self.status == 'CLIENTS':
|
elif self.status == 'CLIENTS':
|
||||||
self.startMultiLineMessage('CLIENTS')
|
self.startMultiLineMessage('CLIENTS')
|
||||||
elif self.status == 'AUDIO':
|
elif self.status == 'AUDIO':
|
||||||
self.ready()
|
self.ready()
|
||||||
self.decodeAUDIO(ord(data[0])*256+ord(data[1]), data[2:])
|
self.decodeAUDIO(ord(data[0])*256+ord(data[1]), data[2:])
|
||||||
elif self.status == 'TX':
|
elif self.status == 'TX':
|
||||||
self.ready()
|
self.ready()
|
||||||
self.decodeTX(ord(data[0])*256+ord(data[1]))
|
self.decodeTX(ord(data[0])*256+ord(data[1]))
|
||||||
|
|
||||||
def login(self):
|
def login(self):
|
||||||
ap = "CT:"+self.user.asXML(
|
ap = "CT:"+self.user.asXML(
|
||||||
'VX','EA','PW','ON','BC','DS','NN','CT','NT')
|
'VX','EA','PW','ON','BC','DS','NN','CT','NT')
|
||||||
self.status = 'AUTH'
|
self.status = 'AUTH'
|
||||||
self.phase = 0
|
self.phase = 0
|
||||||
self.sendLine(ap)
|
self.sendLine(ap)
|
||||||
|
|
||||||
def pong(self):
|
def pong(self):
|
||||||
self.sendLine('P')
|
self.sendLine('P')
|
||||||
|
|
||||||
def setStatus(self, status):
|
def setStatus(self, status):
|
||||||
self.sendLine('ST:%s' % str(status))
|
self.sendLine('ST:%s' % str(status))
|
||||||
|
|
||||||
def stopTransmission(self):
|
def stopTransmission(self):
|
||||||
self.sendLine('RX0')
|
self.sendLine('RX0')
|
||||||
|
|
||||||
def startTransmission(self):
|
def startTransmission(self):
|
||||||
self.sendLine('TX0')
|
self.sendLine('TX0')
|
||||||
|
|
||||||
def sendAudioFrame(self, frame):
|
def sendAudioFrame(self, frame):
|
||||||
self.resetTimeout()
|
self.resetTimeout()
|
||||||
self.sendLine('TX1')
|
self.sendLine('TX1')
|
||||||
self.transport.write(frame)
|
self.transport.write(frame)
|
||||||
|
|
||||||
def streamStep(self, count):
|
def streamStep(self, count):
|
||||||
if count > 1:
|
if count > 1:
|
||||||
log.msg("WARNING: lost %d ticks" % (count-1))
|
log.msg("WARNING: lost %d ticks" % (count-1))
|
||||||
for i in range(count):
|
for i in range(count):
|
||||||
self.sendAudioFrame(self.txq.get_nowait())
|
self.sendAudioFrame(self.txq.get_nowait())
|
||||||
|
|
||||||
def stopStreaming(self):
|
def stopStreaming(self):
|
||||||
self.txtimer.stop()
|
self.txtimer.stop()
|
||||||
|
|
||||||
def _streamAck(self):
|
def _streamAck(self):
|
||||||
self.txtimer = LoopingCall.withCount(self.streamStep)
|
self.txtimer = LoopingCall.withCount(self.streamStep)
|
||||||
self.txtimer.start(0.20).addCallback(
|
self.txtimer.start(0.20).addCallback(
|
||||||
lambda _: self.stopTransmission()).addErrback(
|
lambda _: self.stopTransmission()).addErrback(
|
||||||
lambda _: self.stopTransmission())
|
lambda _: self.stopTransmission())
|
||||||
|
|
||||||
def feedStreaming(self, frames):
|
def feedStreaming(self, frames):
|
||||||
if type(frames) == list:
|
if type(frames) == list:
|
||||||
for frame in frames:
|
for frame in frames:
|
||||||
self.txq.put_nowait(frame)
|
self.txq.put_nowait(frame)
|
||||||
else:
|
else:
|
||||||
self.txq.put_nowait(frames)
|
self.txq.put_nowait(frames)
|
||||||
|
|
||||||
def startStreaming(self):
|
def startStreaming(self):
|
||||||
self.startTransmission()
|
self.startTransmission()
|
||||||
|
|
||||||
def sendTextMessage(self, dest, text):
|
def sendTextMessage(self, dest, text):
|
||||||
self.sendLine('TM:'+formatSimpleXML(dict(ID=dest, MS=text)))
|
self.sendLine('TM:'+formatSimpleXML(dict(ID=dest, MS=text)))
|
||||||
|
|
||||||
def addAdmin(self, client_ip):
|
def addAdmin(self, client_ip):
|
||||||
self.sendLine("AA:"+formatSimpleXML(dict(IP=client_ip)))
|
self.sendLine("AA:"+formatSimpleXML(dict(IP=client_ip)))
|
||||||
|
|
||||||
def removeAdmin(self, client_ip):
|
def removeAdmin(self, client_ip):
|
||||||
self.sendLine("DA:"+formatSimpleXML(dict(IP=client_ip)))
|
self.sendLine("DA:"+formatSimpleXML(dict(IP=client_ip)))
|
||||||
|
|
||||||
def addMute(self, client_ip):
|
def addMute(self, client_ip):
|
||||||
self.sendLine("MC:"+formatSimpleXML(dict(IP=client_ip)))
|
self.sendLine("MC:"+formatSimpleXML(dict(IP=client_ip)))
|
||||||
|
|
||||||
def removeMute(self, client_ip):
|
def removeMute(self, client_ip):
|
||||||
self.sendLine("UM:"+formatSimpleXML(dict(IP=client_ip)))
|
self.sendLine("UM:"+formatSimpleXML(dict(IP=client_ip)))
|
||||||
|
|
||||||
def addBlock(self, client_ip):
|
def addBlock(self, client_ip):
|
||||||
self.sendLine("BC:"+formatSimpleXML(dict(IP=client_ip)))
|
self.sendLine("BC:"+formatSimpleXML(dict(IP=client_ip)))
|
||||||
|
|
||||||
def removeBlock(self, client_ip):
|
def removeBlock(self, client_ip):
|
||||||
self.sendLine("UC:"+formatSimpleXML(dict(IP=client_ip)))
|
self.sendLine("UC:"+formatSimpleXML(dict(IP=client_ip)))
|
||||||
|
|
||||||
def addAccess(self, email):
|
def addAccess(self, email):
|
||||||
self.sendLine("AT:"+formatSimpleXML(dict(EA=email)))
|
self.sendLine("AT:"+formatSimpleXML(dict(EA=email)))
|
||||||
|
|
||||||
def removeAccess(self, email):
|
def removeAccess(self, email):
|
||||||
self.sendLine("DT:"+formatSimpleXML(dict(EA=email)))
|
self.sendLine("DT:"+formatSimpleXML(dict(EA=email)))
|
||||||
|
|
||||||
def addTalk(self, email):
|
def addTalk(self, email):
|
||||||
self.sendLine("ETX:"+formatSimpleXML(dict(EA=email)))
|
self.sendLine("ETX:"+formatSimpleXML(dict(EA=email)))
|
||||||
|
|
||||||
def removeTalk(self, email):
|
def removeTalk(self, email):
|
||||||
self.sendLine("RTX:"+formatSimpleXML(dict(EA=email)))
|
self.sendLine("RTX:"+formatSimpleXML(dict(EA=email)))
|
||||||
|
|
||||||
def accessFlagEnable(self, enable):
|
def accessFlagEnable(self, enable):
|
||||||
if enable:
|
if enable:
|
||||||
v = 1
|
v = 1
|
||||||
else:
|
else:
|
||||||
v = 0
|
v = 0
|
||||||
self.sendLine("ENA:%d" % v)
|
self.sendLine("ENA:%d" % v)
|
||||||
|
|
||||||
def accessFlagTalk(self, enable):
|
def accessFlagTalk(self, enable):
|
||||||
if enable:
|
if enable:
|
||||||
v = 1
|
v = 1
|
||||||
else:
|
else:
|
||||||
v = 0
|
v = 0
|
||||||
self.sendLine("TXR:%d" % v)
|
self.sendLine("TXR:%d" % v)
|
||||||
|
|
||||||
def unimplemented(self, status, msg):
|
def unimplemented(self, status, msg):
|
||||||
log.msg("Unimplemented: %s: %s" % (status, msg))
|
log.msg("Unimplemented: %s: %s" % (status, msg))
|
||||||
|
|
||||||
def decodeAUDIO(self, from_id, frames):
|
def decodeAUDIO(self, from_id, frames):
|
||||||
self.audioFrameReceived(from_id, frames)
|
self.audioFrameReceived(from_id, frames)
|
||||||
|
|
||||||
def decodeTX(self, my_id):
|
def decodeTX(self, my_id):
|
||||||
self._streamAck()
|
self._streamAck()
|
||||||
|
|
||||||
def decodeTEXT(self, msg):
|
def decodeTEXT(self, msg):
|
||||||
self.textMessageReceived(msg[0], msg[1], msg[2])
|
self.textMessageReceived(msg[0], msg[1], msg[2])
|
||||||
|
|
||||||
def decodeCLIENTS(self, msg):
|
def decodeCLIENTS(self, msg):
|
||||||
self.clientsListUpdated([parseSimpleXML(x) for x in msg])
|
self.clientsListUpdated([parseSimpleXML(x) for x in msg])
|
||||||
|
|
||||||
def decodeNETWORKS(self, msg):
|
def decodeNETWORKS(self, msg):
|
||||||
self.networksListUpdated(msg)
|
self.networksListUpdated(msg)
|
||||||
|
|
||||||
def decodeADMIN(self, msg):
|
def decodeADMIN(self, msg):
|
||||||
self.adminListUpdated([parseSimpleXML(x) for x in msg])
|
self.adminListUpdated([parseSimpleXML(x) for x in msg])
|
||||||
|
|
||||||
def decodeACCESS(self, msg):
|
def decodeACCESS(self, msg):
|
||||||
self.accessListUpdated([parseSimpleXML(x) for x in msg])
|
self.accessListUpdated([parseSimpleXML(x) for x in msg])
|
||||||
|
|
||||||
def decodeBLOCK(self, msg):
|
def decodeBLOCK(self, msg):
|
||||||
self.blockListUpdated([parseSimpleXML(x) for x in msg])
|
self.blockListUpdated([parseSimpleXML(x) for x in msg])
|
||||||
|
|
||||||
def decodeMUTE(self, msg):
|
def decodeMUTE(self, msg):
|
||||||
self.muteListUpdated([parseSimpleXML(x) for x in msg])
|
self.muteListUpdated([parseSimpleXML(x) for x in msg])
|
||||||
|
|
||||||
def decodeACCESSFLAGS(self, msg):
|
def decodeACCESSFLAGS(self, msg):
|
||||||
self.accessFlagsUpdated(msg[0], msg[1])
|
self.accessFlagsUpdated(msg[0], msg[1])
|
||||||
|
|
||||||
def decodeUNKNOWN(self, code, msg):
|
def decodeUNKNOWN(self, code, msg):
|
||||||
log.msg("%s: %s" % (code, msg))
|
log.msg("%s: %s" % (code, msg))
|
||||||
|
|
||||||
def loginResponse(self, info):
|
def loginResponse(self, info):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def audioFrameReceived(self, from_id, frame):
|
def audioFrameReceived(self, from_id, frame):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def textMessageReceived(self, client, message, target):
|
def textMessageReceived(self, client, message, target):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def clientsListUpdated(self, clients):
|
def clientsListUpdated(self, clients):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def networksListUpdated(self, networks):
|
def networksListUpdated(self, networks):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def adminListUpdated(self, admins):
|
def adminListUpdated(self, admins):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def accessListUpdated(self, access):
|
def accessListUpdated(self, access):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def blockListUpdated(self, blocks):
|
def blockListUpdated(self, blocks):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def muteListUpdated(self, mutes):
|
def muteListUpdated(self, mutes):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def accessFlagsUpdated(self, access, talk):
|
def accessFlagsUpdated(self, access, talk):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class FRNClientFactory(ReconnectingClientFactory):
|
class FRNClientFactory(ReconnectingClientFactory):
|
||||||
|
|
||||||
protocol = FRNClient
|
protocol = FRNClient
|
||||||
maxRetries = 10
|
maxRetries = 10
|
||||||
|
|
||||||
def __init__(self, user):
|
def __init__(self, user):
|
||||||
self.user = user
|
self.user = user
|
||||||
|
|
||||||
def startedConnecting(self, connector):
|
def startedConnecting(self, connector):
|
||||||
log.msg('Started to connect')
|
log.msg('Started to connect')
|
||||||
|
|
||||||
def buildProtocol(self, addr):
|
def buildProtocol(self, addr):
|
||||||
log.msg('Connected')
|
log.msg('Connected')
|
||||||
p = ReconnectingClientFactory.buildProtocol(self, addr)
|
p = ReconnectingClientFactory.buildProtocol(self, addr)
|
||||||
p.user = self.user
|
p.user = self.user
|
||||||
return p
|
return p
|
||||||
|
|
||||||
def clientConnectionLost(self, connector, reason):
|
def clientConnectionLost(self, connector, reason):
|
||||||
log.msg('Lost connection. Reason: %s' % reason)
|
log.msg('Lost connection. Reason: %s' % reason)
|
||||||
ReconnectingClientFactory.clientConnectionLost(
|
ReconnectingClientFactory.clientConnectionLost(
|
||||||
self, connector, reason)
|
self, connector, reason)
|
||||||
|
|
||||||
def clientConnectionFailed(self, connector, reason):
|
def clientConnectionFailed(self, connector, reason):
|
||||||
log.err('Connection failed. Reason: %s' % reason)
|
log.err('Connection failed. Reason: %s' % reason)
|
||||||
ReconnectingClientFactory.clientConnectionFailed(
|
ReconnectingClientFactory.clientConnectionFailed(
|
||||||
self, connector, reason)
|
self, connector, reason)
|
||||||
|
|
||||||
# vim: set et ai sw=4 ts=4 sts=4:
|
# vim: set et ai sw=4 ts=4 sts=4:
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
from twisted.internet.defer import Deferred, succeed
|
from twisted.internet.defer import Deferred, succeed
|
||||||
from twisted.internet.protocol import ReconnectingClientFactory, ServerFactory
|
from twisted.internet.protocol import ReconnectingClientFactory, ServerFactory
|
||||||
from twisted.protocols.basic import LineOnlyReceiver
|
from twisted.protocols.basic import LineOnlyReceiver
|
||||||
from twisted.protocols.policies import TimeoutMixin
|
|
||||||
from twisted.internet.task import LoopingCall
|
from twisted.internet.task import LoopingCall
|
||||||
from twisted.python import log
|
from twisted.python import log
|
||||||
from frn.user import FRNUser
|
from frn.user import FRNUser
|
||||||
|
@ -53,10 +52,10 @@ class FRNManagerClient(LineOnlyReceiver):
|
||||||
def loginDone(result):
|
def loginDone(result):
|
||||||
self.managerdata = parseSimpleXML(result)
|
self.managerdata = parseSimpleXML(result)
|
||||||
log.msg("Server login results: %s" % str(self.managerdata))
|
log.msg("Server login results: %s" % str(self.managerdata))
|
||||||
if int(self.managerdata['MC']) > 2009004:
|
if int(self.managerdata['mc']) > 2009004:
|
||||||
self.sendLine(responseToChallange(
|
self.sendLine(responseToChallange(
|
||||||
self.managerdata['KP']))
|
self.managerdata['kp']))
|
||||||
if self.managerdata['AL'] != '0':
|
if self.managerdata['al'] != '0':
|
||||||
self.transport.loseConnection()
|
self.transport.loseConnection()
|
||||||
else:
|
else:
|
||||||
self.pingtimer = LoopingCall(self.sendPing)
|
self.pingtimer = LoopingCall(self.sendPing)
|
||||||
|
@ -108,22 +107,17 @@ class FRNManagerClientFactory(ReconnectingClientFactory):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class FRNManagerServer(LineOnlyReceiver, TimeoutMixin):
|
class FRNManagerServer(LineOnlyReceiver):
|
||||||
|
|
||||||
def connectionMade(self):
|
def connectionMade(self):
|
||||||
log.msg("Manager client connected from %s" % self.transport.getPeer().host)
|
log.msg("Manager client connected from %s" % self.transport.getPeer().host)
|
||||||
self._phase = "CONNECTED"
|
self._phase = "CONNECTED"
|
||||||
self.serverInfo = None
|
|
||||||
self.kp = makeRandomChallange()
|
self.kp = makeRandomChallange()
|
||||||
self.setTimeout(30.0)
|
|
||||||
|
|
||||||
def connectionLost(self, reason):
|
def connectionLost(self, reason):
|
||||||
log.msg("Manager client disconnected from %s: %s" %
|
log.msg("Manager client disconnected from %s: %s" %
|
||||||
(self.transport.getPeer().host, reason))
|
(self.transport.getPeer().host, reason))
|
||||||
if self.serverInfo:
|
self.manager.serverLogout(None) # FIXME
|
||||||
self.manager.serverLogout(self.serverInfo)
|
|
||||||
self.serverInfo = None
|
|
||||||
self._phase = "DISCONNECTED"
|
|
||||||
|
|
||||||
def _authOnly(self):
|
def _authOnly(self):
|
||||||
if self._phase != "AUTHENTICATED":
|
if self._phase != "AUTHENTICATED":
|
||||||
|
@ -131,13 +125,10 @@ class FRNManagerServer(LineOnlyReceiver, TimeoutMixin):
|
||||||
self.transport.loseConnection()
|
self.transport.loseConnection()
|
||||||
|
|
||||||
def lineReceived(self, line):
|
def lineReceived(self, line):
|
||||||
self.resetTimeout()
|
|
||||||
sline = line.strip()
|
sline = line.strip()
|
||||||
if self._phase == "CHALLANGE":
|
if self._phase == "CHALLANGE":
|
||||||
if sline == responseToChallange(self.kp):
|
if sline == responseToChallange(self.kp):
|
||||||
self._phase = "AUTHENTICATED"
|
self._phase = "AUTHENTICATED"
|
||||||
self.serverInfo = self.tmpServerInfo
|
|
||||||
log.msg("Auth success: %s" % repr(self.serverInfo))
|
|
||||||
else:
|
else:
|
||||||
self.transport.loseConnection()
|
self.transport.loseConnection()
|
||||||
else:
|
else:
|
||||||
|
@ -155,19 +146,8 @@ class FRNManagerServer(LineOnlyReceiver, TimeoutMixin):
|
||||||
handler(xbody)
|
handler(xbody)
|
||||||
|
|
||||||
def sendClientList(self):
|
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")
|
log.msg("SM")
|
||||||
return self.manager.getClientList().addCallback(gotClientList)
|
self.sendLine('0') # TODO
|
||||||
|
|
||||||
|
|
||||||
def unimplemented(self, cmd, body):
|
def unimplemented(self, cmd, body):
|
||||||
log.err("Unimplemented command %s: %s" % (cmd, str(body)))
|
log.err("Unimplemented command %s: %s" % (cmd, str(body)))
|
||||||
|
@ -176,16 +156,13 @@ class FRNManagerServer(LineOnlyReceiver, TimeoutMixin):
|
||||||
def sendManagerInfo(res):
|
def sendManagerInfo(res):
|
||||||
if versions.manager > 2009004:
|
if versions.manager > 2009004:
|
||||||
self._phase = "CHALLANGE"
|
self._phase = "CHALLANGE"
|
||||||
self.tmpServerInfo = FRNUser(**body)
|
|
||||||
else:
|
else:
|
||||||
self._phase = "AUTHENTICATED"
|
self._phase = "AUTHENTICATED"
|
||||||
self.serverInfo = FRNUser(**body)
|
|
||||||
log.msg("Auth success: %s" % repr(self.serverInfo))
|
|
||||||
self.sendLine(formatSimpleXML([
|
self.sendLine(formatSimpleXML([
|
||||||
('SV', versions.server),
|
('SV', versions.server),
|
||||||
('CV', versions.client),
|
('CV', versions.client),
|
||||||
('MC', versions.manager),
|
('MC', versions.manager),
|
||||||
('AL', res),
|
('AL', res['al']),
|
||||||
('KP', self.kp)
|
('KP', self.kp)
|
||||||
]))
|
]))
|
||||||
log.msg("SC: %s" % str(body))
|
log.msg("SC: %s" % str(body))
|
||||||
|
@ -195,13 +172,13 @@ class FRNManagerServer(LineOnlyReceiver, TimeoutMixin):
|
||||||
def decodeCC(self, body): # Client login
|
def decodeCC(self, body): # Client login
|
||||||
log.msg("CC: %s" % str(body))
|
log.msg("CC: %s" % str(body))
|
||||||
self._authOnly()
|
self._authOnly()
|
||||||
self.manager.clientLogin(self.serverInfo, FRNUser(**body)).addCallback(
|
self.manager.clientLogin(FRNUser(**body)).addCallback(
|
||||||
self.sendLine)
|
self.sendLine)
|
||||||
|
|
||||||
def decodeCD(self, body): # Client logout
|
def decodeCD(self, body): # Client logout
|
||||||
log.msg("CD: %s" % str(body))
|
log.msg("CD: %s" % str(body))
|
||||||
self._authOnly()
|
self._authOnly()
|
||||||
self.manager.clientLogout(self.serverInfo, FRNUser(**body)).addCallback(
|
self.manager.clientLogout(FRNUser(**body)).addCallback(
|
||||||
self.sendLine)
|
self.sendLine)
|
||||||
|
|
||||||
def decodeIG(self, body): # Account creation
|
def decodeIG(self, body): # Account creation
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
# Copyright 2010 Maurizio Porrato <maurizio.porrato@gmail.com>
|
# Copyright 2010 Maurizio Porrato <maurizio.porrato@gmail.com>
|
||||||
# See LICENSE.txt for copyright info
|
# See LICENSE.txt for copyright info
|
||||||
|
|
||||||
from twisted.internet.defer import Deferred, succeed
|
from twisted.internet.defer import Deferred
|
||||||
from twisted.internet.protocol import ServerFactory
|
from twisted.internet.protocol import ServerFactory
|
||||||
from twisted.protocols.policies import TimeoutMixin
|
from twisted.protocols.policies import TimeoutMixin
|
||||||
from twisted.internet.task import LoopingCall
|
from twisted.internet.task import LoopingCall
|
||||||
|
@ -32,19 +32,20 @@ class FRNServer(BufferingLineReceiver, TimeoutMixin):
|
||||||
|
|
||||||
def connectionLost(self, reason):
|
def connectionLost(self, reason):
|
||||||
log.msg("Client disconnected: %s" % self.clientAddress.host)
|
log.msg("Client disconnected: %s" % self.clientAddress.host)
|
||||||
self.disconnect()
|
self.stopPinging()
|
||||||
|
if self.user is not None:
|
||||||
|
if self.user.ID:
|
||||||
|
log.msg("Logging out client %s" % self.user.ID)
|
||||||
|
self.factory.manager.clientLogout(self.user)
|
||||||
|
self.factory.tracker.logout(self)
|
||||||
BufferingLineReceiver.connectionLost(self, reason)
|
BufferingLineReceiver.connectionLost(self, reason)
|
||||||
|
|
||||||
def timeoutConnection(self):
|
|
||||||
log.msg("Client dead: disconnecting %s" % self.user)
|
|
||||||
self.disconnect()
|
|
||||||
|
|
||||||
def lineReceived(self, line):
|
def lineReceived(self, line):
|
||||||
self.resetTimeout()
|
self.resetTimeout()
|
||||||
sline = line.strip()
|
sline = line.strip()
|
||||||
if self.waitingKey:
|
if self.waitingKey:
|
||||||
if responseToChallange(self.kp) != sline:
|
if responseToChallange(self.kp) != sline:
|
||||||
self.disconnect()
|
self.transport.loseConnection()
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
self.waitingKey = False
|
self.waitingKey = False
|
||||||
|
@ -53,13 +54,8 @@ class FRNServer(BufferingLineReceiver, TimeoutMixin):
|
||||||
self.sendMuteList(self.factory.tracker.getMuteList())
|
self.sendMuteList(self.factory.tracker.getMuteList())
|
||||||
self.sendBlockList(self.factory.tracker.getBlockList())
|
self.sendBlockList(self.factory.tracker.getBlockList())
|
||||||
if self.role == 'OWNER':
|
if self.role == 'OWNER':
|
||||||
self.sendAdminList(self.factory.tracker.getAdminList())
|
self.sendAccessFlags(None)
|
||||||
ac, tx = self.factory.tracker.getAclFlags(self.user.NT)
|
self.sendAccessList([])
|
||||||
self.sendAccessFlags(ac,tx)
|
|
||||||
self.sendAccessList(self.factory.tracker.getAcl(self.user.NT))
|
|
||||||
if self.role not in ['OK', 'ADMIN', 'OWNER']:
|
|
||||||
self.disconnect()
|
|
||||||
return
|
|
||||||
self.startPinging()
|
self.startPinging()
|
||||||
self.setTimeout(10.0)
|
self.setTimeout(10.0)
|
||||||
return
|
return
|
||||||
|
@ -71,7 +67,7 @@ class FRNServer(BufferingLineReceiver, TimeoutMixin):
|
||||||
else:
|
else:
|
||||||
command, body = sline.split(':', 1)
|
command, body = sline.split(':', 1)
|
||||||
if command != 'CT' and self.user is None:
|
if command != 'CT' and self.user is None:
|
||||||
self.disconnect()
|
self.transport.loseConnection()
|
||||||
return
|
return
|
||||||
handler = getattr(self, 'decode'+command, None)
|
handler = getattr(self, 'decode'+command, None)
|
||||||
if body[0] == '<' and body[-1] == '>':
|
if body[0] == '<' and body[-1] == '>':
|
||||||
|
@ -102,20 +98,9 @@ class FRNServer(BufferingLineReceiver, TimeoutMixin):
|
||||||
else:
|
else:
|
||||||
return (userId, "")
|
return (userId, "")
|
||||||
user.IP = self.clientAddress.host
|
user.IP = self.clientAddress.host
|
||||||
if self.factory.tracker.canLogin(user):
|
return self.factory.manager.clientLogin(user).addCallback(loginReturned)
|
||||||
return self.factory.manager.clientLogin(self.factory.serverAuth, user).addCallback(loginReturned)
|
|
||||||
else:
|
|
||||||
log.msg("BLOCK %s" % repr(user))
|
|
||||||
return succeed(("BLOCK", ""))
|
|
||||||
|
|
||||||
def disconnect(self):
|
def disconnect(self):
|
||||||
self.stopPinging()
|
|
||||||
if self.user is not None:
|
|
||||||
log.msg("Logging out client %s" % self.user)
|
|
||||||
if self.factory.talking.get(self.user.NT) == self:
|
|
||||||
del self.factory.talking[self.user.NT]
|
|
||||||
self.factory.manager.clientLogout(self.factory.serverAuth, self.user)
|
|
||||||
self.factory.tracker.logout(self)
|
|
||||||
self.transport.loseConnection()
|
self.transport.loseConnection()
|
||||||
|
|
||||||
def decodeCT(self, body):
|
def decodeCT(self, body):
|
||||||
|
@ -127,10 +112,8 @@ class FRNServer(BufferingLineReceiver, TimeoutMixin):
|
||||||
if self.role == 'OK':
|
if self.role == 'OK':
|
||||||
if self.user.EA == self.factory.serverAuth.OW:
|
if self.user.EA == self.factory.serverAuth.OW:
|
||||||
self.role = 'OWNER'
|
self.role = 'OWNER'
|
||||||
log.msg("%s promoted to OWNER" % clientId)
|
|
||||||
elif self.factory.tracker.isAdmin(self.user.ID):
|
elif self.factory.tracker.isAdmin(self.user.ID):
|
||||||
self.role = 'ADMIN'
|
self.role = 'ADMIN'
|
||||||
log.msg("%s promoted to ADMIN" % clientId)
|
|
||||||
if versions.server > 2009004:
|
if versions.server > 2009004:
|
||||||
self.waitingKey = True
|
self.waitingKey = True
|
||||||
self.sendLine(str(versions.client))
|
self.sendLine(str(versions.client))
|
||||||
|
@ -141,7 +124,7 @@ class FRNServer(BufferingLineReceiver, TimeoutMixin):
|
||||||
self.sendLine(
|
self.sendLine(
|
||||||
self.factory.serverAuth.asXML('MT','SV','AL','BN','BP','KP'))
|
self.factory.serverAuth.asXML('MT','SV','AL','BN','BP','KP'))
|
||||||
if self.role not in ['OK', 'OWNER', 'ADMIN']:
|
if self.role not in ['OK', 'OWNER', 'ADMIN']:
|
||||||
self.disconnect()
|
self.transport.loseConnection()
|
||||||
else:
|
else:
|
||||||
self.sendNetworkList(self.factory.tracker.getNetworkList())
|
self.sendNetworkList(self.factory.tracker.getNetworkList())
|
||||||
self.transport.setTcpNoDelay(True)
|
self.transport.setTcpNoDelay(True)
|
||||||
|
@ -160,8 +143,6 @@ class FRNServer(BufferingLineReceiver, TimeoutMixin):
|
||||||
def decodeRX(self, body):
|
def decodeRX(self, body):
|
||||||
log.msg("RX%d" % int(body))
|
log.msg("RX%d" % int(body))
|
||||||
self.startPinging()
|
self.startPinging()
|
||||||
if self.factory.talking.get(self.user.NT) == self:
|
|
||||||
del self.factory.talking[self.user.NT]
|
|
||||||
for c in self.factory.tracker.getClientList(self.user.NT):
|
for c in self.factory.tracker.getClientList(self.user.NT):
|
||||||
cp = self.factory.tracker.getClientProtocol(c.ID).startPinging()
|
cp = self.factory.tracker.getClientProtocol(c.ID).startPinging()
|
||||||
|
|
||||||
|
@ -173,101 +154,55 @@ class FRNServer(BufferingLineReceiver, TimeoutMixin):
|
||||||
|
|
||||||
def decodeTM(self, body):
|
def decodeTM(self, body):
|
||||||
log.msg("TM: %s" % str(body))
|
log.msg("TM: %s" % str(body))
|
||||||
if body['ID'] == '':
|
if body['id'] == '':
|
||||||
msgtype = 'A'
|
msgtype = 'A'
|
||||||
else:
|
else:
|
||||||
msgtype = 'P'
|
msgtype = 'P'
|
||||||
for c in self.factory.tracker.getClientList(self.user.NT):
|
for c in self.factory.tracker.getClientList(self.user.NT):
|
||||||
if msgtype == 'A' or c.ID == body['ID']:
|
if msgtype == 'A' or c.ID == body['id']:
|
||||||
if c.ID != self.user.ID or msgtype == 'A':
|
if c.ID != self.user.ID or msgtype == 'A':
|
||||||
client = self.factory.tracker.getClientProtocol(c.ID)
|
client = self.factory.tracker.getClientProtocol(c.ID)
|
||||||
client.sendTextMessage(self.user.ID, body['MS'], msgtype)
|
client.sendTextMessage(self.user.ID, body['ms'], msgtype)
|
||||||
|
|
||||||
def decodeTX(self, body):
|
def decodeTX(self, body):
|
||||||
if body == '0':
|
if body == '0':
|
||||||
if not self.factory.tracker.isMute(self.user.ID):
|
if not self.factory.tracker.isMute(self.user.ID):
|
||||||
if not self.factory.talking.get(self.user.NT):
|
ih,il = divmod(self.getIndex(), 256)
|
||||||
self.factory.talking[self.user.NT] = self
|
self.transport.write(chr(1)+chr(ih)+chr(il))
|
||||||
ih,il = divmod(self.getIndex(), 256)
|
|
||||||
self.transport.write(chr(1)+chr(ih)+chr(il))
|
|
||||||
elif body == '1':
|
elif body == '1':
|
||||||
self.stopPinging()
|
self.stopPinging()
|
||||||
self.expectRawData(325)
|
self.expectRawData(325)
|
||||||
|
|
||||||
def decodeMC(self, body):
|
def decodeMC(self, body):
|
||||||
if self.role in ["OWNER","ADMIN"]:
|
if self.role in ["OWNER","ADMIN"]:
|
||||||
self.factory.tracker.mute(self.user, body['IP'])
|
self.factory.tracker.mute(self.user, body['ip'])
|
||||||
if self.factory.tracker.isLoggedIn(body['IP']):
|
if self.factory.tracker.isLoggedIn(body['ip']):
|
||||||
self.factory.tracker.getClientProtocol(body['IP']).decodeRX('0')
|
self.factory.tracker.getClientProtocol(body['ip']).decodeRX('0')
|
||||||
|
|
||||||
def decodeUM(self, body):
|
def decodeUM(self, body):
|
||||||
if self.role in ["OWNER","ADMIN"]:
|
if self.role in ["OWNER","ADMIN"]:
|
||||||
self.factory.tracker.unMute(body['IP'])
|
self.factory.tracker.unMute(body['ip'])
|
||||||
|
|
||||||
def decodeBC(self, body):
|
def decodeBC(self, body):
|
||||||
if self.role in ["OWNER","ADMIN"]:
|
if self.role in ["OWNER","ADMIN"]:
|
||||||
self.factory.tracker.block(self.user, body['IP'])
|
self.factory.tracker.block(self.user, body['ip'])
|
||||||
self.factory.tracker.getClientProtocol(body['IP']).disconnect()
|
self.factory.tracker.getClientProtocol(body['ip']).disconnect()
|
||||||
|
|
||||||
def decodeUC(self, body):
|
def decodeUC(self, body):
|
||||||
if self.role in ["OWNER","ADMIN"]:
|
if self.role in ["OWNER","ADMIN"]:
|
||||||
self.factory.tracker.unBlock(body['IP'])
|
self.factory.tracker.unBlock(body['ip'])
|
||||||
|
|
||||||
def decodeAA(self, body):
|
def decodeAA(self, body):
|
||||||
if self.role == "OWNER":
|
if self.role == "OWNER":
|
||||||
self.factory.tracker.admin(body['IP'])
|
self.factory.tracker.admin(body['ip'])
|
||||||
|
|
||||||
def decodeDA(self, body):
|
def decodeDA(self, body):
|
||||||
if self.role == "OWNER":
|
if self.role == "OWNER":
|
||||||
self.factory.tracker.unAdmin(body['IP'])
|
self.factory.tracker.unAdmin(body['ip'])
|
||||||
|
|
||||||
def decodeETX(self, body):
|
|
||||||
log.msg("decodeETX(%s)" % str(body))
|
|
||||||
if self.role == "OWNER":
|
|
||||||
self.factory.tracker.setAclTx(self.user.NT, body['EA'], True)
|
|
||||||
self.sendAccessList(self.factory.tracker.getAcl(self.user.NT))
|
|
||||||
|
|
||||||
def decodeRTX(self, body):
|
|
||||||
log.msg("decodeRTX(%s)" % str(body))
|
|
||||||
if self.role == "OWNER":
|
|
||||||
self.factory.tracker.setAclTx(self.user.NT, body['EA'], False)
|
|
||||||
self.sendAccessList(self.factory.tracker.getAcl(self.user.NT))
|
|
||||||
|
|
||||||
def decodeDT(self, body):
|
|
||||||
log.msg("decodeDT(%s)" % str(body))
|
|
||||||
if self.role == "OWNER":
|
|
||||||
self.factory.tracker.delAcl(self.user.NT, body['EA'])
|
|
||||||
self.sendAccessList(self.factory.tracker.getAcl(self.user.NT))
|
|
||||||
|
|
||||||
def decodeAT(self, body):
|
|
||||||
log.msg("decodeAT(%s)" % str(body))
|
|
||||||
if self.role == "OWNER":
|
|
||||||
self.factory.tracker.addAcl(self.user.NT, body['EA'])
|
|
||||||
self.sendAccessList(self.factory.tracker.getAcl(self.user.NT))
|
|
||||||
|
|
||||||
def decodeTXR(self, body):
|
|
||||||
log.msg("decodeTXR(%s)" % str(body))
|
|
||||||
if self.role == "OWNER":
|
|
||||||
en, _ = self.factory.tracker.getAclFlags(self.user.NT)
|
|
||||||
tx = body == '1'
|
|
||||||
self.factory.tracker.setAclFlags(self.user.NT, en, tx)
|
|
||||||
en,tx = self.factory.tracker.getAclFlags(self.user.NT)
|
|
||||||
self.sendAccessFlags(en, tx)
|
|
||||||
self.sendAccessList(self.factory.tracker.getAcl(self.user.NT))
|
|
||||||
|
|
||||||
def decodeENA(self, body):
|
|
||||||
log.msg("decodeENA(%s)" % str(body))
|
|
||||||
if self.role == "OWNER":
|
|
||||||
_, tx = self.factory.tracker.getAclFlags(self.user.NT)
|
|
||||||
en = body == '1'
|
|
||||||
self.factory.tracker.setAclFlags(self.user.NT, en, tx)
|
|
||||||
en,tx = self.factory.tracker.getAclFlags(self.user.NT)
|
|
||||||
self.sendAccessFlags(en, tx)
|
|
||||||
self.sendAccessList(self.factory.tracker.getAcl(self.user.NT))
|
|
||||||
|
|
||||||
def audioFrameReceived(self, frame):
|
def audioFrameReceived(self, frame):
|
||||||
#log.msg("audioFrameReceived")
|
#log.msg("audioFrameReceived")
|
||||||
if self.factory.tracker.canTalk(self.user.ID) and self.factory.talking.get(self.user.NT) == self:
|
if not self.factory.tracker.isMute(self.user.ID):
|
||||||
clientIdx = self.getIndex()
|
clientIdx = self.getIndex()
|
||||||
for c in self.factory.tracker.getClientList(self.user.NT):
|
for c in self.factory.tracker.getClientList(self.user.NT):
|
||||||
if int(c.S) < 2 and c.ID != self.user.ID:
|
if int(c.S) < 2 and c.ID != self.user.ID:
|
||||||
|
@ -314,19 +249,16 @@ class FRNServer(BufferingLineReceiver, TimeoutMixin):
|
||||||
self.sendLine(c.asXML('NN','CT','BC','ON','ID'))
|
self.sendLine(c.asXML('NN','CT','BC','ON','ID'))
|
||||||
|
|
||||||
def sendAccessList(self, clients):
|
def sendAccessList(self, clients):
|
||||||
log.msg("Sending ACL to %s: %s" % (self.user.ON, str(clients)))
|
|
||||||
self.transport.write(chr(7))
|
self.transport.write(chr(7))
|
||||||
self.sendLine(str(len(clients)))
|
self.sendLine(str(len(clients)))
|
||||||
for c in clients:
|
for c in clients: # FIXME
|
||||||
self.sendLine(c.asXML('AI','NN','CT','BC','ON','ID'))
|
self.sendLine(c.asXML('AI','NN','CT','BC','ON','ID'))
|
||||||
|
|
||||||
def sendAccessFlags(self, access, talk):
|
def sendAccessFlags(self, flags):
|
||||||
log.msg("Sending ACL flags to %s: %s" % (self.user.ON, str((access, talk))))
|
|
||||||
FV = {True: '1', False: 'o'}
|
|
||||||
self.transport.write(chr(10))
|
self.transport.write(chr(10))
|
||||||
self.sendLine('2')
|
self.sendLine('2') # TODO
|
||||||
self.sendLine(FV[access])
|
self.sendLine('o')
|
||||||
self.sendLine(FV[talk])
|
self.sendLine('o')
|
||||||
|
|
||||||
def sendTextMessage(self, clientId, message, target):
|
def sendTextMessage(self, clientId, message, target):
|
||||||
self.transport.write(chr(4))
|
self.transport.write(chr(4))
|
||||||
|
@ -350,7 +282,7 @@ class FRNServerFactory(ServerFactory):
|
||||||
def __init__(self, trackerfile, manager, serverAuth):
|
def __init__(self, trackerfile, manager, serverAuth):
|
||||||
self.manager = manager
|
self.manager = manager
|
||||||
self.serverAuth = serverAuth
|
self.serverAuth = serverAuth
|
||||||
self.talking = {}
|
self.talking = None
|
||||||
self.officialNets = []
|
self.officialNets = []
|
||||||
self.tracker = ClientTracker(
|
self.tracker = ClientTracker(
|
||||||
trackerfile,
|
trackerfile,
|
||||||
|
|
|
@ -35,7 +35,7 @@ class FRNUser(object):
|
||||||
self._fields[field.lower()] = str(value)
|
self._fields[field.lower()] = str(value)
|
||||||
|
|
||||||
def get(self, field, default=''):
|
def get(self, field, default=''):
|
||||||
return self._fields.get(field.lower(),'')
|
return self._fields[field.lower()]
|
||||||
|
|
||||||
def items(self, *fields):
|
def items(self, *fields):
|
||||||
if len(fields) == 0:
|
if len(fields) == 0:
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright 2010 Maurizio Porrato <maurizio.porrato@gmail.com>
|
||||||
|
# See LICENSE.txt for copyright info
|
||||||
|
|
||||||
|
class FRNUserNotFound(StandardError): pass
|
||||||
|
|
||||||
|
|
||||||
|
# vim: set et ai sw=4 ts=4 sts=4:
|
|
@ -0,0 +1,84 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright 2010 Maurizio Porrato <maurizio.porrato@gmail.com>
|
||||||
|
# See LICENSE.txt for copyright info
|
||||||
|
|
||||||
|
from twisted.python import log
|
||||||
|
from twisted.enterprise.adbapi import safe
|
||||||
|
from frn.userstore import FRNUserNotFound
|
||||||
|
from frn.user import FRNUser
|
||||||
|
|
||||||
|
|
||||||
|
def _asDict(curs, *args, **kw):
|
||||||
|
log.msg("Running query %s %s" % (str(args), str(kw)))
|
||||||
|
curs.execute(*args, **kw)
|
||||||
|
result = curs.fetchall()
|
||||||
|
columns = [d[0] for d in curs.description]
|
||||||
|
d = [dict(zip(columns, r)) for r in result]
|
||||||
|
log.msg("Query returned: %s" % str(d))
|
||||||
|
return d
|
||||||
|
|
||||||
|
|
||||||
|
def _returnId(curs, *args, **kw):
|
||||||
|
log.msg("Running query %s %s" % (str(args), str(kw)))
|
||||||
|
curs.execute(*args, **kw)
|
||||||
|
rowId = curs.lastrowid
|
||||||
|
log.msg("Query returned: %s" % str(rowId))
|
||||||
|
return rowId
|
||||||
|
|
||||||
|
|
||||||
|
class DatabaseUserStore(object):
|
||||||
|
|
||||||
|
def __init__(self, pool):
|
||||||
|
self._pool = pool
|
||||||
|
|
||||||
|
def _query(self, *args, **kw):
|
||||||
|
return self._pool.runInteraction(_asDict, *args, **kw)
|
||||||
|
|
||||||
|
def getByName(self, name, email):
|
||||||
|
try:
|
||||||
|
return self._query("""
|
||||||
|
SELECT * FROM frn_users
|
||||||
|
WHERE "on"=? AND "ea"=?""",
|
||||||
|
(name, email)).addCallback(
|
||||||
|
lambda x: FRNUser(**x[0]))
|
||||||
|
except IndexError:
|
||||||
|
raise FRNUserNotFound
|
||||||
|
|
||||||
|
def getById(self, userId):
|
||||||
|
return self._query("""
|
||||||
|
SELECT * FROM frn_users
|
||||||
|
WHERE "id"=?""",
|
||||||
|
(int(userId), )).addCallback(
|
||||||
|
lambda x: FRNUser(x[0]))
|
||||||
|
|
||||||
|
def update(self, userId, **kw):
|
||||||
|
assignments = ','.join(["\"%s\"='%s'" % (k,safe(v))
|
||||||
|
for k,v in kw.items()])
|
||||||
|
op = "UPDATE frn_users SET "+assignments+" WHERE \"id\"=?"
|
||||||
|
self._pool.runOperation(op, (int(userId),))
|
||||||
|
|
||||||
|
def create(self, user):
|
||||||
|
return self._query(
|
||||||
|
"""INSERT INTO frn_users ("on","ea") VALUES (?,?)""",
|
||||||
|
(user.ON, user.EA)).addCallback(lambda x: "%015s" % x)
|
||||||
|
|
||||||
|
def list(self):
|
||||||
|
return self._query("SELECT * FROM frn_users").addCallback(
|
||||||
|
lambda x: [FRNUser(y) for y in x])
|
||||||
|
|
||||||
|
def login(self, user):
|
||||||
|
def gotUser(u):
|
||||||
|
log.msg("Got user %s" % u.ID)
|
||||||
|
if u.PW == user.PW:
|
||||||
|
return u.ID
|
||||||
|
return "WRONG"
|
||||||
|
log.msg("Authenticating %s (%s)" % (user.ON, user.EA))
|
||||||
|
return self.getByName(user.ON, user.EA).addCallbacks(
|
||||||
|
gotUser, lambda x: "WRONG")
|
||||||
|
|
||||||
|
def logout(self, userId):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# vim: set et ai sw=4 ts=4 sts=4:
|
|
@ -3,7 +3,7 @@
|
||||||
# Copyright 2010 Maurizio Porrato <maurizio.porrato@gmail.com>
|
# Copyright 2010 Maurizio Porrato <maurizio.porrato@gmail.com>
|
||||||
# See LICENSE.txt for copyright info
|
# See LICENSE.txt for copyright info
|
||||||
|
|
||||||
import re
|
from HTMLParser import HTMLParser
|
||||||
from random import choice
|
from random import choice
|
||||||
|
|
||||||
|
|
||||||
|
@ -19,10 +19,45 @@ def makeRandomChallange():
|
||||||
return ''.join([choice('0123456789') for i in range(6)])
|
return ''.join([choice('0123456789') for i in range(6)])
|
||||||
|
|
||||||
|
|
||||||
re_tag = re.compile(r"<([A-Z]{1,2})>(.*)</\1>")
|
class SimpleXMLParser(HTMLParser):
|
||||||
|
"""Dirty FRN-specific hack to handle bogus one-level nesting only
|
||||||
|
XML-like syntax"""
|
||||||
|
|
||||||
|
def handle_starttag(self, tag, attrs):
|
||||||
|
if not hasattr(self, 'fields'):
|
||||||
|
self.fields = {}
|
||||||
|
self.next_field = None
|
||||||
|
self.next_data = ''
|
||||||
|
if self.next_field is None:
|
||||||
|
self.next_field = tag
|
||||||
|
self.next_data = ''
|
||||||
|
else:
|
||||||
|
if attrs:
|
||||||
|
a = ' '+' '.join(['%s="%s"' % (k,v) for k,v in attrs])
|
||||||
|
else:
|
||||||
|
a = ''
|
||||||
|
self.next_data += "<%s%s>" % (tag,a)
|
||||||
|
|
||||||
|
def handle_data(self, data):
|
||||||
|
if self.next_field is not None:
|
||||||
|
self.next_data += data
|
||||||
|
|
||||||
|
def handle_endtag(self, tag):
|
||||||
|
if tag == self.next_field:
|
||||||
|
self.fields[self.next_field] = self.next_data
|
||||||
|
self.next_field = None
|
||||||
|
self.next_data = ''
|
||||||
|
else:
|
||||||
|
self.next_data += "</%s>" % tag
|
||||||
|
|
||||||
|
def get_fields(self):
|
||||||
|
return self.fields
|
||||||
|
|
||||||
|
|
||||||
def parseSimpleXML(xml):
|
def parseSimpleXML(xml):
|
||||||
return dict(re_tag.findall(xml))
|
p = SimpleXMLParser()
|
||||||
|
p.feed(xml)
|
||||||
|
return p.get_fields()
|
||||||
|
|
||||||
|
|
||||||
def formatSimpleXML(elements):
|
def formatSimpleXML(elements):
|
||||||
|
@ -30,6 +65,6 @@ def formatSimpleXML(elements):
|
||||||
items = elements.items()
|
items = elements.items()
|
||||||
else:
|
else:
|
||||||
items = elements
|
items = elements
|
||||||
return ''.join(["<%s>%s</%s>" % (k.upper().encode('utf8'),unicode(v).encode('utf8'),k.upper().encode('utf8')) for k,v in items])
|
return ''.join(["<%s>%s</%s>" % (k.upper(),v,k.upper()) for k,v in items])
|
||||||
|
|
||||||
# vim: set et ai sw=4 ts=4 sts=4:
|
# vim: set et ai sw=4 ts=4 sts=4:
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
From: GNURadioNetwork System Manager <admin@gnuradionetwork.org>
|
|
||||||
To: $EA
|
|
||||||
Subject: [GRN] $ON account data
|
|
||||||
|
|
||||||
Callsign and operator name: $ON
|
|
||||||
Password: $PW
|
|
||||||
Operating mode: $BC
|
|
||||||
Description: $DS
|
|
||||||
City and locator: $CT
|
|
||||||
Country: $NN
|
|
||||||
|
|
||||||
Registration date: $registration
|
|
||||||
Last login: $lastlogin
|
|
21
manager.py
21
manager.py
|
@ -9,7 +9,6 @@ from frn.protocol.manager import FRNManagerServer, FRNManagerServerFactory
|
||||||
from twisted.enterprise.adbapi import ConnectionPool
|
from twisted.enterprise.adbapi import ConnectionPool
|
||||||
from frn.manager.dummy import DummyManager
|
from frn.manager.dummy import DummyManager
|
||||||
from frn.manager.remote import RemoteManager
|
from frn.manager.remote import RemoteManager
|
||||||
from frn.manager.database import DatabaseManager
|
|
||||||
from frn.user import FRNUser
|
from frn.user import FRNUser
|
||||||
from twisted.python import log
|
from twisted.python import log
|
||||||
|
|
||||||
|
@ -18,22 +17,16 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
log.startLogging(sys.stderr)
|
log.startLogging(sys.stderr)
|
||||||
|
|
||||||
def dummyManagerFactory():
|
def standardManagerFactory():
|
||||||
log.msg("Building DummyManager")
|
log.msg("Building Manager")
|
||||||
return DummyManager()
|
|
||||||
|
|
||||||
def remoteManagerFactory():
|
|
||||||
log.msg("Building RemoteManager")
|
|
||||||
return RemoteManager(reactor)
|
return RemoteManager(reactor)
|
||||||
|
|
||||||
pool = ConnectionPool("sqlite3", "/dev/shm/frnmanager.sqlite3", check_same_thread=False, cp_noisy=True)
|
|
||||||
|
|
||||||
def databaseManagerFactory():
|
|
||||||
log.msg("Building DatabaseManager")
|
|
||||||
return DatabaseManager(pool)
|
|
||||||
|
|
||||||
reactor.listenTCP(10025, FRNManagerServerFactory(
|
reactor.listenTCP(10025, FRNManagerServerFactory(
|
||||||
databaseManagerFactory
|
# DatabaseUserStore(
|
||||||
|
# ConnectionPool("sqlite3", "frn_users.sqlite3",
|
||||||
|
# check_same_thread=False)),
|
||||||
|
# DummyManager()
|
||||||
|
standardManagerFactory
|
||||||
))
|
))
|
||||||
reactor.run()
|
reactor.run()
|
||||||
|
|
||||||
|
|
12
parrot.py
12
parrot.py
|
@ -27,7 +27,7 @@ class FRNParrot(FRNClient):
|
||||||
|
|
||||||
def getClientName(self, client_id):
|
def getClientName(self, client_id):
|
||||||
if self.clientsById.has_key(client_id):
|
if self.clientsById.has_key(client_id):
|
||||||
return self.clientsById[client_id]['ON']
|
return self.clientsById[client_id]['on']
|
||||||
else:
|
else:
|
||||||
return client_id
|
return client_id
|
||||||
|
|
||||||
|
@ -64,11 +64,11 @@ class FRNParrot(FRNClient):
|
||||||
|
|
||||||
def startRepeating(self, from_id):
|
def startRepeating(self, from_id):
|
||||||
log.msg("%s stopped talking: starting playback." %
|
log.msg("%s stopped talking: starting playback." %
|
||||||
self.clients[from_id-1]['ON'])
|
self.clients[from_id-1]['on'])
|
||||||
self.startStreaming()
|
self.startStreaming()
|
||||||
|
|
||||||
def audioFrameReceived(self, from_id, frames):
|
def audioFrameReceived(self, from_id, frames):
|
||||||
recname = sanitizeFilename(self.clients[from_id-1]['ON'])
|
recname = sanitizeFilename(self.clients[from_id-1]['on'])
|
||||||
with file('recordings/%s.gsm' % recname, 'ab') as f:
|
with file('recordings/%s.gsm' % recname, 'ab') as f:
|
||||||
f.write(frames)
|
f.write(frames)
|
||||||
self.feedStreaming(frames)
|
self.feedStreaming(frames)
|
||||||
|
@ -76,17 +76,17 @@ class FRNParrot(FRNClient):
|
||||||
self.parrot_timer.reset(PARROT_AUDIO_DELAY)
|
self.parrot_timer.reset(PARROT_AUDIO_DELAY)
|
||||||
except:
|
except:
|
||||||
log.msg("%s started talking" %
|
log.msg("%s started talking" %
|
||||||
self.clients[from_id-1]['ON'])
|
self.clients[from_id-1]['on'])
|
||||||
self.parrot_timer = self.factory.reactor.callLater(
|
self.parrot_timer = self.factory.reactor.callLater(
|
||||||
PARROT_AUDIO_DELAY, self.startRepeating, from_id)
|
PARROT_AUDIO_DELAY, self.startRepeating, from_id)
|
||||||
self.pong()
|
self.pong()
|
||||||
|
|
||||||
def loginResponse(self, info):
|
def loginResponse(self, info):
|
||||||
log.msg("Login: %s" % info['AL'])
|
log.msg("Login: %s" % info['al'])
|
||||||
|
|
||||||
def clientsListUpdated(self, clients):
|
def clientsListUpdated(self, clients):
|
||||||
self.clients = clients
|
self.clients = clients
|
||||||
self.clientsById = dict([(i['ID'], i) for i in clients])
|
self.clientsById = dict([(i['id'], i) for i in clients])
|
||||||
|
|
||||||
|
|
||||||
class FRNParrotFactory(FRNClientFactory):
|
class FRNParrotFactory(FRNClientFactory):
|
||||||
|
|
|
@ -68,7 +68,7 @@ class FRNStream(FRNClient):
|
||||||
|
|
||||||
def getClientName(self, client_id):
|
def getClientName(self, client_id):
|
||||||
if self.clientsById.has_key(client_id):
|
if self.clientsById.has_key(client_id):
|
||||||
return self.clientsById[client_id]['ON']
|
return self.clientsById[client_id]['on']
|
||||||
else:
|
else:
|
||||||
return client_id
|
return client_id
|
||||||
|
|
||||||
|
@ -77,11 +77,11 @@ class FRNStream(FRNClient):
|
||||||
self.pong()
|
self.pong()
|
||||||
|
|
||||||
def loginResponse(self, info):
|
def loginResponse(self, info):
|
||||||
log.msg("Login: %s" % info['AL'])
|
log.msg("Login: %s" % info['al'])
|
||||||
|
|
||||||
def clientsListUpdated(self, clients):
|
def clientsListUpdated(self, clients):
|
||||||
self.clients = clients
|
self.clients = clients
|
||||||
self.clientsById = dict([(i['ID'], i) for i in clients])
|
self.clientsById = dict([(i['id'], i) for i in clients])
|
||||||
|
|
||||||
|
|
||||||
class FRNStreamFactory(FRNClientFactory):
|
class FRNStreamFactory(FRNClientFactory):
|
||||||
|
|
|
@ -34,7 +34,7 @@ class FRNCrosslinkClient(FRNClient):
|
||||||
|
|
||||||
def getClientName(self, client_id):
|
def getClientName(self, client_id):
|
||||||
if self.clientsById.has_key(client_id):
|
if self.clientsById.has_key(client_id):
|
||||||
return self.clientsById[client_id]['ON']
|
return self.clientsById[client_id]['on']
|
||||||
else:
|
else:
|
||||||
return client_id
|
return client_id
|
||||||
|
|
||||||
|
@ -60,16 +60,16 @@ class FRNCrosslinkClient(FRNClient):
|
||||||
for server, port, u, factory in clients:
|
for server, port, u, factory in clients:
|
||||||
cl = []
|
cl = []
|
||||||
cl.append("<u>%s@%s:%d</u>" % (u.NT,server,port))
|
cl.append("<u>%s@%s:%d</u>" % (u.NT,server,port))
|
||||||
role = factory.connection.serverdata.get('AL', None)
|
role = factory.connection.serverdata.get('al', None)
|
||||||
if role in ['OK', 'ADMIN', 'OWNER']:
|
if role in ['OK', 'ADMIN', 'OWNER']:
|
||||||
for c in factory.connection.clients:
|
for c in factory.connection.clients:
|
||||||
ss = cStatus.get(c['S'], '?')
|
ss = cStatus.get(c['s'], '?')
|
||||||
if c['M'] != '0':
|
if c['m'] != '0':
|
||||||
ss = ss.lower()
|
ss = ss.lower()
|
||||||
cl.append(" %s%s %s " % (
|
cl.append(" %s%s %s " % (
|
||||||
cType.get(c['BC'], 'G'),
|
cType.get(c['bc'], 'G'),
|
||||||
ss,
|
ss,
|
||||||
c['ON'].replace('<', '<'),
|
c['on'].replace('<', '<'),
|
||||||
))
|
))
|
||||||
else:
|
else:
|
||||||
cl.append(" :!: DISCONNECTED :!: ")
|
cl.append(" :!: DISCONNECTED :!: ")
|
||||||
|
@ -100,7 +100,7 @@ class FRNCrosslinkClient(FRNClient):
|
||||||
global clients, talking, lastMessages
|
global clients, talking, lastMessages
|
||||||
if talking is None or talking == self:
|
if talking is None or talking == self:
|
||||||
talking = self
|
talking = self
|
||||||
talkingUser = self.clients[from_id-1]['ON']
|
talkingUser = self.clients[from_id-1]['on']
|
||||||
if len(lastMessages) > 0:
|
if len(lastMessages) > 0:
|
||||||
if lastMessages[0] == talkingUser:
|
if lastMessages[0] == talkingUser:
|
||||||
talkingUser = None
|
talkingUser = None
|
||||||
|
@ -110,7 +110,7 @@ class FRNCrosslinkClient(FRNClient):
|
||||||
for _, _, _, factory in clients:
|
for _, _, _, factory in clients:
|
||||||
conn = factory.connection
|
conn = factory.connection
|
||||||
if conn != self:
|
if conn != self:
|
||||||
role = conn.serverdata.get('AL', None)
|
role = conn.serverdata.get('al', None)
|
||||||
if role in ['OK', 'ADMIN', 'OWNER']:
|
if role in ['OK', 'ADMIN', 'OWNER']:
|
||||||
if conn.txOk:
|
if conn.txOk:
|
||||||
conn.txReq = False
|
conn.txReq = False
|
||||||
|
@ -126,15 +126,15 @@ class FRNCrosslinkClient(FRNClient):
|
||||||
self.pong()
|
self.pong()
|
||||||
|
|
||||||
def loginResponse(self, info):
|
def loginResponse(self, info):
|
||||||
log.msg("%s login: %s" % (self.user.ON, info['AL']))
|
log.msg("%s login: %s" % (self.user.ON, info['al']))
|
||||||
|
|
||||||
def clientsListUpdated(self, clients):
|
def clientsListUpdated(self, clients):
|
||||||
self.clients = clients
|
self.clients = clients
|
||||||
self.clientsById = dict([(i['ID'], i) for i in clients])
|
self.clientsById = dict([(i['id'], i) for i in clients])
|
||||||
if self.clientId is None:
|
if self.clientId is None:
|
||||||
for c in clients:
|
for c in clients:
|
||||||
if c['ON'] == self.user.ON and c['BC'] == self.user.BC:
|
if c['on'] == self.user.ON and c['bc'] == self.user.BC:
|
||||||
self.clientId = c['ID']
|
self.clientId = c['id']
|
||||||
|
|
||||||
class FRNCrosslinkClientFactory(FRNClientFactory):
|
class FRNCrosslinkClientFactory(FRNClientFactory):
|
||||||
protocol = FRNCrosslinkClient
|
protocol = FRNCrosslinkClient
|
||||||
|
|
|
@ -1,180 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
# Copyright 2010 Maurizio Porrato <maurizio.porrato@gmail.com>
|
|
||||||
# See LICENSE.txt for copyright info
|
|
||||||
|
|
||||||
from __future__ import with_statement
|
|
||||||
from zope.interface import implements
|
|
||||||
from twisted.plugin import IPlugin
|
|
||||||
from twisted.application.service import IServiceMaker
|
|
||||||
from frn.protocol.client import FRNClient, FRNClientFactory
|
|
||||||
from frn.user import FRNUser
|
|
||||||
from twisted.application import internet
|
|
||||||
from twisted.internet import reactor, task
|
|
||||||
from twisted.internet.defer import DeferredList
|
|
||||||
from twisted.python import log, usage
|
|
||||||
import os, string, time, struct
|
|
||||||
from os.path import curdir, join as pjoin
|
|
||||||
from ConfigParser import ConfigParser
|
|
||||||
safe_chars = string.ascii_letters+string.digits+' :;.,+-=$@'
|
|
||||||
|
|
||||||
WAV_HEADER = "524946460c06000057415645666d74201400000031000100401f00005906000041000000020040016661637404000000000000006461746100000000".decode('hex')
|
|
||||||
AUDIO_TIMEOUT = 5.0
|
|
||||||
|
|
||||||
def sanitizeFilename(name):
|
|
||||||
r = ''
|
|
||||||
for c in name:
|
|
||||||
if c in safe_chars:
|
|
||||||
r += c
|
|
||||||
return r
|
|
||||||
|
|
||||||
|
|
||||||
class FRNRecorder(FRNClient):
|
|
||||||
|
|
||||||
def getClientName(self, client_id):
|
|
||||||
if self.clientsById.has_key(client_id):
|
|
||||||
return self.clientsById[client_id]['ON']
|
|
||||||
else:
|
|
||||||
return client_id
|
|
||||||
|
|
||||||
def buildRecordingName(self, client_id):
|
|
||||||
ts = time.localtime()
|
|
||||||
opname = sanitizeFilename(self.clients[client_id-1]['ON'])
|
|
||||||
dirname = self.factory.recdir+"/"+time.strftime("%Y%m/%d/%H", ts)
|
|
||||||
filename = time.strftime("%Y%m%d-%H%M%S-", ts) + opname + '.wav'
|
|
||||||
try:
|
|
||||||
os.makedirs(dirname)
|
|
||||||
except OSError: pass
|
|
||||||
return dirname+'/'+filename
|
|
||||||
|
|
||||||
def buildMessageLogName(self):
|
|
||||||
ts = time.localtime()
|
|
||||||
dirname = self.factory.recdir+"/"+time.strftime("%Y%m/%d/%H", ts)
|
|
||||||
filename = time.strftime("%Y%m%d-%H-", ts) + 'messages.txt'
|
|
||||||
try:
|
|
||||||
os.makedirs(dirname)
|
|
||||||
except OSError: pass
|
|
||||||
return dirname+'/'+filename
|
|
||||||
|
|
||||||
def textMessageReceived(self, client, message, target):
|
|
||||||
if target == 'A':
|
|
||||||
f = open(self.buildMessageLogName(), 'a')
|
|
||||||
ts = time.strftime("%Y-%m-%d %H:%M:%S")
|
|
||||||
f.write("[%s] [%s] %s\r\n" %
|
|
||||||
(ts, self.getClientName(client), message))
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
def closeFile(self):
|
|
||||||
size = self.recordingFile.tell()-len(WAV_HEADER)
|
|
||||||
time = size / 1625.0
|
|
||||||
if time > self.factory.mintime:
|
|
||||||
self.recordingFile.seek(0x30) # fact chunk value
|
|
||||||
self.recordingFile.write(struct.pack("<L", int(size/32.5)))
|
|
||||||
self.recordingFile.seek(0x38) # data chunk size
|
|
||||||
self.recordingFile.write(struct.pack("<L", size))
|
|
||||||
self.recordingFile.close()
|
|
||||||
else:
|
|
||||||
name = self.recordingFile.name
|
|
||||||
log.msg("Deleting short recording: %s" % (name,))
|
|
||||||
self.recordingFile.close()
|
|
||||||
os.remove(name)
|
|
||||||
|
|
||||||
def archiveRecording(self, from_id):
|
|
||||||
log.msg("%s stopped talking" %
|
|
||||||
self.clients[from_id-1]['ON'])
|
|
||||||
self.closeFile()
|
|
||||||
self.recordingFile = None
|
|
||||||
self.recordingOperator = None
|
|
||||||
|
|
||||||
def audioFrameReceived(self, from_id, frames):
|
|
||||||
if self.recordingOperator != self.clients[from_id-1]['ON']:
|
|
||||||
if self.recordingOperator is not None:
|
|
||||||
self.closeFile()
|
|
||||||
self.recordingOperator = self.clients[from_id-1]['ON']
|
|
||||||
self.recordingFile = file(self.buildRecordingName(from_id), 'wb')
|
|
||||||
self.recordingFile.write(WAV_HEADER)
|
|
||||||
self.recordingFile.write(frames)
|
|
||||||
try:
|
|
||||||
self.parrot_timer.reset(AUDIO_TIMEOUT)
|
|
||||||
except:
|
|
||||||
log.msg("%s started talking" %
|
|
||||||
self.clients[from_id-1]['ON'])
|
|
||||||
self.parrot_timer = self.factory.reactor.callLater(
|
|
||||||
AUDIO_TIMEOUT, self.archiveRecording, from_id)
|
|
||||||
self.pong()
|
|
||||||
|
|
||||||
def loginResponse(self, info):
|
|
||||||
log.msg("Login: %s" % info['AL'])
|
|
||||||
self.setStatus(1)
|
|
||||||
self.recordingOperator = None
|
|
||||||
self.recordingFile = None
|
|
||||||
|
|
||||||
def clientsListUpdated(self, clients):
|
|
||||||
self.clients = clients
|
|
||||||
self.clientsById = dict([(i['ID'], i) for i in clients])
|
|
||||||
|
|
||||||
|
|
||||||
class FRNRecorderFactory(FRNClientFactory):
|
|
||||||
protocol = FRNRecorder
|
|
||||||
reactor = reactor
|
|
||||||
|
|
||||||
def __init__(self, user, recdir="/var/spool/grn/recordings", mintime=2.0):
|
|
||||||
self.recdir = recdir
|
|
||||||
self.mintime = mintime
|
|
||||||
FRNClientFactory.__init__(self, user)
|
|
||||||
|
|
||||||
|
|
||||||
class Options(usage.Options):
|
|
||||||
|
|
||||||
optParameters = [
|
|
||||||
["confdir", "c", curdir, "Directory containing config files"],
|
|
||||||
["recdir", "d", "/var/spool/grn/recordings", "Directory where to save recordings"],
|
|
||||||
["mintime", "m", 2.0, "Minimun recording duration"]
|
|
||||||
]
|
|
||||||
|
|
||||||
def parseArgs(self, serverdef):
|
|
||||||
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'])
|
|
||||||
|
|
||||||
account_name, server_name, network_name = serverdef.split(':', 2)
|
|
||||||
|
|
||||||
self['server'] = scfg.get(server_name, 'server')
|
|
||||||
self['port'] = scfg.getint(server_name, 'port')
|
|
||||||
self['backup_server'] = scfg.get(server_name, 'backup_server')
|
|
||||||
self['backup_port'] = scfg.getint(server_name, 'backup_port')
|
|
||||||
self['operator'] = acfg.get(account_name, 'operator')
|
|
||||||
self['email'] = acfg.get(account_name, 'email')
|
|
||||||
self['password'] = acfg.get(account_name, 'password')
|
|
||||||
self['description'] = acfg.get(account_name, 'description')
|
|
||||||
self['country'] = acfg.get(account_name, 'country')
|
|
||||||
self['city'] = acfg.get(account_name, 'city')
|
|
||||||
self['network'] = network_name
|
|
||||||
|
|
||||||
|
|
||||||
class FRNRecorderServiceMaker(object):
|
|
||||||
implements(IServiceMaker, IPlugin)
|
|
||||||
tapname = "frnrecorder"
|
|
||||||
description = "Freeradionetwork recorder"
|
|
||||||
options = Options
|
|
||||||
|
|
||||||
def makeService(self, options):
|
|
||||||
return internet.TCPClient(options['server'], options['port'],
|
|
||||||
FRNRecorderFactory(
|
|
||||||
FRNUser(
|
|
||||||
EA=options['email'],
|
|
||||||
PW=options['password'], ON=options['operator'],
|
|
||||||
BC="Crosslink", DS=options['description'],
|
|
||||||
NN=options['country'], CT=options['city'], NT=options['network']),
|
|
||||||
options['recdir'], options['mintime']))
|
|
||||||
|
|
||||||
serviceMaker = FRNRecorderServiceMaker()
|
|
||||||
|
|
||||||
|
|
||||||
# vim: set et ai sw=4 ts=4 sts=4:
|
|
|
@ -52,8 +52,7 @@ class FRNServerServiceMaker(object):
|
||||||
return internet.TCPServer(options['port'],
|
return internet.TCPServer(options['port'],
|
||||||
FRNServerFactory(
|
FRNServerFactory(
|
||||||
pjoin(options['confdir'], 'tracker.shelve'),
|
pjoin(options['confdir'], 'tracker.shelve'),
|
||||||
# RemoteManager(), # Authenticate on frn.no-ip.info
|
RemoteManager(),
|
||||||
RemoteManager(server='fri.no-ip.info', maskParrot=False),
|
|
||||||
FRNUser(
|
FRNUser(
|
||||||
SN=options['server'],PT=options['port'],
|
SN=options['server'],PT=options['port'],
|
||||||
BN=options['backup_server'], BP=options['backup_port'],
|
BN=options['backup_server'], BP=options['backup_port'],
|
||||||
|
|
Loading…
Reference in New Issue