Compare commits

..

No commits in common. "master" and "wip" have entirely different histories.
master ... wip

19 changed files with 547 additions and 924 deletions

View File

@ -5,7 +5,6 @@
import shelve
from twisted.python import log
from frn.user import FRNUser
CLIENTS_ORDER = ['Crosslink', 'Parrot', 'Gateway', 'PC Only']
@ -40,7 +39,6 @@ class ClientTracker(object):
self._mute = {}
self._block = {}
self._admin = {}
self._acl = {}
self.cbClient = cbClient
self.cbNet = cbNet
self.cbMute = cbMute
@ -55,7 +53,6 @@ class ClientTracker(object):
self._mute = s['mute']
self._block = s['block']
self._admin = s['admin']
self._acl = s['acl']
except KeyError: pass
s.close()
@ -64,7 +61,6 @@ class ClientTracker(object):
s['mute'] = self._mute
s['block'] = self._block
s['admin'] = self._admin
s['acl'] = self._acl
s.close()
def isLoggedIn(self, clientId):
@ -82,7 +78,7 @@ class ClientTracker(object):
return l.index(clientId)+1
def getNetworkList(self):
return sorted(set(self._net.keys())|set(self._acl.keys()))
return self._net.keys()
def getClientList(self, network=[]):
if network:
@ -117,10 +113,7 @@ class ClientTracker(object):
def login(self, client, status=0):
clientId = client.user.ID
if clientId not in self._clientData:
if not self.aclTalkOk(client.user.NT, client.user.EA) and status < 1:
client.user.S = 1
else:
client.user.S = status
client.user.S = status
self._clientData[clientId] = client
net = client.user.NT
nc = self._net.get(net, [])
@ -140,10 +133,8 @@ class ClientTracker(object):
self._mute[clientId].AI = a
else:
client.user.M = 0
if not self.canLogin(client.user):
if clientId in self._block:
client.role = "BLOCK"
else:
self.aclUpdate(client.user)
if client.user.EA in self._admin:
client.role = "ADMIN"
self._admin[client.user.EA].update(client.user.dict())
@ -168,13 +159,11 @@ class ClientTracker(object):
self.cbClient(net, self.getClientList(net))
def setStatus(self, clientId, status):
user = self.getClient(clientId)
oldStatus = user.S
if not self.aclTalkOk(user.NT, user.EA) and status < 1:
status = 1
oldStatus = self._clientData[clientId].user.S
if oldStatus != str(status):
net = self._clientData[clientId].user.NT
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):
if clientId not in self._mute:
@ -221,91 +210,5 @@ class ClientTracker(object):
self.save()
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:

View File

@ -8,16 +8,16 @@ from zope.interface import Interface
class IManager(Interface):
def serverLogin(server):
def serverLogin(user):
"""Logs server on"""
def serverLogout(server):
def serverLogout(user):
"""Logs server out"""
def clientLogin(server, user):
def clientLogin(user):
"""Logs client in"""
def clientLogout(server, user):
def clientLogout(user):
"""Logs client out"""
def getClientList():

View File

@ -5,149 +5,33 @@
from zope.interface import implements
from frn.manager import IManager
from twisted.python import log
from twisted.python.failure import Failure
from twisted.mail.smtp import sendmail
import random, string, uuid
from frn.user import FRNUser
from frn.userstore.database import DatabaseUserStore
def rndpasswd():
return ''.join([random.choice(string.ascii_uppercase) for i in range(8)])
class DatabaseManager(object):
implements(IManager)
def __init__(self, pool):
self._pool = pool
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 __init__(self, store):
self._store = store
def serverLogin(self, server):
def checkauth(res):
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 serverLogin(self, user):
pass
def serverLogout(self, server):
self._pool.runOperation("UPDATE frn_users SET server=NULL, port=NULL, _nt=NULL WHERE server=? AND port=?",
(server.SN, server.PT))
self._pool.runOperation("DELETE FROM frn_servers WHERE _sn=? AND _pt=?",
(server.SN, server.PT))
def serverLogout(self, user):
pass
def clientLogin(self, server, user):
def userfound(data):
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 clientLogin(self, user):
pass
def clientLogout(self, server, user):
log.msg("Logging out client %s" % repr(user))
return self._pool.runOperation("UPDATE frn_users SET server=NULL, port=NULL, _nt=NULL WHERE _id=?",
(user.ID,)).addBoth(lambda x: "OK")
def clientLogout(self, user):
pass
def getClientList(self):
def buildlist(tr):
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)
pass
def registerUser(self, user):
def fetchdata(is_new):
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)
pass
# vim: set et ai sw=4 ts=4 sts=4:

View File

@ -16,20 +16,20 @@ class DummyManager(object):
def _randId(self):
return '.'.join([str(randint(1,254)) for i in range(4)])
def serverLogin(self, server):
def serverLogin(self, user):
return defer.succeed(0)
def serverLogout(self, server):
def serverLogout(self, user):
return defer.succeed(None)
def clientLogin(self, server, user):
def clientLogin(self, user):
return defer.succeed(self._randId())
def clientLogout(self, server, user):
def clientLogout(self, user):
return defer.succeed('OK')
def getClientList(self):
return defer.succeed({})
return defer.succeed([])
def registerUser(self, user):
return defer.succeed('OK')

View File

@ -23,7 +23,7 @@ class CustomManagerClientFactory(FRNManagerClientFactory):
def authReply(auth):
self.resetDelay()
self.authResult = auth
self.authDone = (auth['AL'] == '0')
self.authDone = (auth['al'] == '0')
if self.authDone:
self.deferred.callback(auth)
connection.sendServerLogin(self.user).addCallback(authReply)
@ -55,23 +55,23 @@ class RemoteManager(object):
def doConnect(self):
self.reactor.connectTCP(self.server, self.port, self.factory)
def serverLogin(self, server):
self.factory = CustomManagerClientFactory(server)
def serverLogin(self, user):
self.factory = CustomManagerClientFactory(user)
self.doConnect()
return self.factory.deferred
def serverLogout(self, server):
def serverLogout(self, user):
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':
u = user.copy(BC='PC Only')
else:
u = user.copy()
return self.factory.client.sendClientLogin(u)
def clientLogout(self, server, user):
def clientLogout(self, user):
if self.maskParrot and user.BC == 'Parrot':
u = user.copy(BC='PC Only')
else:

View File

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

View File

@ -6,7 +6,6 @@
from twisted.internet.defer import Deferred, succeed
from twisted.internet.protocol import ReconnectingClientFactory, ServerFactory
from twisted.protocols.basic import LineOnlyReceiver
from twisted.protocols.policies import TimeoutMixin
from twisted.internet.task import LoopingCall
from twisted.python import log
from frn.user import FRNUser
@ -53,10 +52,10 @@ class FRNManagerClient(LineOnlyReceiver):
def loginDone(result):
self.managerdata = parseSimpleXML(result)
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.managerdata['KP']))
if self.managerdata['AL'] != '0':
self.managerdata['kp']))
if self.managerdata['al'] != '0':
self.transport.loseConnection()
else:
self.pingtimer = LoopingCall(self.sendPing)
@ -108,22 +107,17 @@ class FRNManagerClientFactory(ReconnectingClientFactory):
pass
class FRNManagerServer(LineOnlyReceiver, TimeoutMixin):
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()
self.setTimeout(30.0)
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"
self.manager.serverLogout(None) # FIXME
def _authOnly(self):
if self._phase != "AUTHENTICATED":
@ -131,13 +125,10 @@ class FRNManagerServer(LineOnlyReceiver, TimeoutMixin):
self.transport.loseConnection()
def lineReceived(self, line):
self.resetTimeout()
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:
@ -155,19 +146,8 @@ class FRNManagerServer(LineOnlyReceiver, TimeoutMixin):
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)
self.sendLine('0') # TODO
def unimplemented(self, cmd, body):
log.err("Unimplemented command %s: %s" % (cmd, str(body)))
@ -176,16 +156,13 @@ class FRNManagerServer(LineOnlyReceiver, TimeoutMixin):
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', res['al']),
('KP', self.kp)
]))
log.msg("SC: %s" % str(body))
@ -195,13 +172,13 @@ class FRNManagerServer(LineOnlyReceiver, TimeoutMixin):
def decodeCC(self, body): # Client login
log.msg("CC: %s" % str(body))
self._authOnly()
self.manager.clientLogin(self.serverInfo, FRNUser(**body)).addCallback(
self.manager.clientLogin(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.manager.clientLogout(FRNUser(**body)).addCallback(
self.sendLine)
def decodeIG(self, body): # Account creation

View File

@ -3,7 +3,7 @@
# Copyright 2010 Maurizio Porrato <maurizio.porrato@gmail.com>
# 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.protocols.policies import TimeoutMixin
from twisted.internet.task import LoopingCall
@ -32,19 +32,20 @@ class FRNServer(BufferingLineReceiver, TimeoutMixin):
def connectionLost(self, reason):
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)
def timeoutConnection(self):
log.msg("Client dead: disconnecting %s" % self.user)
self.disconnect()
def lineReceived(self, line):
self.resetTimeout()
sline = line.strip()
if self.waitingKey:
if responseToChallange(self.kp) != sline:
self.disconnect()
self.transport.loseConnection()
return
else:
self.waitingKey = False
@ -53,13 +54,8 @@ class FRNServer(BufferingLineReceiver, TimeoutMixin):
self.sendMuteList(self.factory.tracker.getMuteList())
self.sendBlockList(self.factory.tracker.getBlockList())
if self.role == 'OWNER':
self.sendAdminList(self.factory.tracker.getAdminList())
ac, tx = self.factory.tracker.getAclFlags(self.user.NT)
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.sendAccessFlags(None)
self.sendAccessList([])
self.startPinging()
self.setTimeout(10.0)
return
@ -71,7 +67,7 @@ class FRNServer(BufferingLineReceiver, TimeoutMixin):
else:
command, body = sline.split(':', 1)
if command != 'CT' and self.user is None:
self.disconnect()
self.transport.loseConnection()
return
handler = getattr(self, 'decode'+command, None)
if body[0] == '<' and body[-1] == '>':
@ -102,20 +98,9 @@ class FRNServer(BufferingLineReceiver, TimeoutMixin):
else:
return (userId, "")
user.IP = self.clientAddress.host
if self.factory.tracker.canLogin(user):
return self.factory.manager.clientLogin(self.factory.serverAuth, user).addCallback(loginReturned)
else:
log.msg("BLOCK %s" % repr(user))
return succeed(("BLOCK", ""))
return self.factory.manager.clientLogin(user).addCallback(loginReturned)
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()
def decodeCT(self, body):
@ -127,10 +112,8 @@ class FRNServer(BufferingLineReceiver, TimeoutMixin):
if self.role == 'OK':
if self.user.EA == self.factory.serverAuth.OW:
self.role = 'OWNER'
log.msg("%s promoted to OWNER" % clientId)
elif self.factory.tracker.isAdmin(self.user.ID):
self.role = 'ADMIN'
log.msg("%s promoted to ADMIN" % clientId)
if versions.server > 2009004:
self.waitingKey = True
self.sendLine(str(versions.client))
@ -141,7 +124,7 @@ class FRNServer(BufferingLineReceiver, TimeoutMixin):
self.sendLine(
self.factory.serverAuth.asXML('MT','SV','AL','BN','BP','KP'))
if self.role not in ['OK', 'OWNER', 'ADMIN']:
self.disconnect()
self.transport.loseConnection()
else:
self.sendNetworkList(self.factory.tracker.getNetworkList())
self.transport.setTcpNoDelay(True)
@ -160,8 +143,6 @@ class FRNServer(BufferingLineReceiver, TimeoutMixin):
def decodeRX(self, body):
log.msg("RX%d" % int(body))
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):
cp = self.factory.tracker.getClientProtocol(c.ID).startPinging()
@ -173,101 +154,55 @@ class FRNServer(BufferingLineReceiver, TimeoutMixin):
def decodeTM(self, body):
log.msg("TM: %s" % str(body))
if body['ID'] == '':
if body['id'] == '':
msgtype = 'A'
else:
msgtype = 'P'
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':
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):
if body == '0':
if not self.factory.tracker.isMute(self.user.ID):
if not self.factory.talking.get(self.user.NT):
self.factory.talking[self.user.NT] = self
ih,il = divmod(self.getIndex(), 256)
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':
self.stopPinging()
self.expectRawData(325)
def decodeMC(self, body):
if self.role in ["OWNER","ADMIN"]:
self.factory.tracker.mute(self.user, body['IP'])
if self.factory.tracker.isLoggedIn(body['IP']):
self.factory.tracker.getClientProtocol(body['IP']).decodeRX('0')
self.factory.tracker.mute(self.user, body['ip'])
if self.factory.tracker.isLoggedIn(body['ip']):
self.factory.tracker.getClientProtocol(body['ip']).decodeRX('0')
def decodeUM(self, body):
if self.role in ["OWNER","ADMIN"]:
self.factory.tracker.unMute(body['IP'])
self.factory.tracker.unMute(body['ip'])
def decodeBC(self, body):
if self.role in ["OWNER","ADMIN"]:
self.factory.tracker.block(self.user, body['IP'])
self.factory.tracker.getClientProtocol(body['IP']).disconnect()
self.factory.tracker.block(self.user, body['ip'])
self.factory.tracker.getClientProtocol(body['ip']).disconnect()
def decodeUC(self, body):
if self.role in ["OWNER","ADMIN"]:
self.factory.tracker.unBlock(body['IP'])
self.factory.tracker.unBlock(body['ip'])
def decodeAA(self, body):
if self.role == "OWNER":
self.factory.tracker.admin(body['IP'])
self.factory.tracker.admin(body['ip'])
def decodeDA(self, body):
if self.role == "OWNER":
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))
self.factory.tracker.unAdmin(body['ip'])
def audioFrameReceived(self, frame):
#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()
for c in self.factory.tracker.getClientList(self.user.NT):
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'))
def sendAccessList(self, clients):
log.msg("Sending ACL to %s: %s" % (self.user.ON, str(clients)))
self.transport.write(chr(7))
self.sendLine(str(len(clients)))
for c in clients:
for c in clients: # FIXME
self.sendLine(c.asXML('AI','NN','CT','BC','ON','ID'))
def sendAccessFlags(self, access, talk):
log.msg("Sending ACL flags to %s: %s" % (self.user.ON, str((access, talk))))
FV = {True: '1', False: 'o'}
def sendAccessFlags(self, flags):
self.transport.write(chr(10))
self.sendLine('2')
self.sendLine(FV[access])
self.sendLine(FV[talk])
self.sendLine('2') # TODO
self.sendLine('o')
self.sendLine('o')
def sendTextMessage(self, clientId, message, target):
self.transport.write(chr(4))
@ -350,7 +282,7 @@ class FRNServerFactory(ServerFactory):
def __init__(self, trackerfile, manager, serverAuth):
self.manager = manager
self.serverAuth = serverAuth
self.talking = {}
self.talking = None
self.officialNets = []
self.tracker = ClientTracker(
trackerfile,

View File

@ -35,7 +35,7 @@ class FRNUser(object):
self._fields[field.lower()] = str(value)
def get(self, field, default=''):
return self._fields.get(field.lower(),'')
return self._fields[field.lower()]
def items(self, *fields):
if len(fields) == 0:

View File

@ -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:

84
frn/userstore/database.py Normal file
View File

@ -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:

View File

@ -3,7 +3,7 @@
# Copyright 2010 Maurizio Porrato <maurizio.porrato@gmail.com>
# See LICENSE.txt for copyright info
import re
from HTMLParser import HTMLParser
from random import choice
@ -19,10 +19,45 @@ def makeRandomChallange():
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):
return dict(re_tag.findall(xml))
p = SimpleXMLParser()
p.feed(xml)
return p.get_fields()
def formatSimpleXML(elements):
@ -30,6 +65,6 @@ def formatSimpleXML(elements):
items = elements.items()
else:
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:

View File

@ -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

View File

@ -9,7 +9,6 @@ from frn.protocol.manager import FRNManagerServer, FRNManagerServerFactory
from twisted.enterprise.adbapi import ConnectionPool
from frn.manager.dummy import DummyManager
from frn.manager.remote import RemoteManager
from frn.manager.database import DatabaseManager
from frn.user import FRNUser
from twisted.python import log
@ -18,22 +17,16 @@ if __name__ == '__main__':
log.startLogging(sys.stderr)
def dummyManagerFactory():
log.msg("Building DummyManager")
return DummyManager()
def remoteManagerFactory():
log.msg("Building RemoteManager")
def standardManagerFactory():
log.msg("Building Manager")
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(
databaseManagerFactory
# DatabaseUserStore(
# ConnectionPool("sqlite3", "frn_users.sqlite3",
# check_same_thread=False)),
# DummyManager()
standardManagerFactory
))
reactor.run()

View File

@ -27,7 +27,7 @@ class FRNParrot(FRNClient):
def getClientName(self, client_id):
if self.clientsById.has_key(client_id):
return self.clientsById[client_id]['ON']
return self.clientsById[client_id]['on']
else:
return client_id
@ -64,11 +64,11 @@ class FRNParrot(FRNClient):
def startRepeating(self, from_id):
log.msg("%s stopped talking: starting playback." %
self.clients[from_id-1]['ON'])
self.clients[from_id-1]['on'])
self.startStreaming()
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:
f.write(frames)
self.feedStreaming(frames)
@ -76,17 +76,17 @@ class FRNParrot(FRNClient):
self.parrot_timer.reset(PARROT_AUDIO_DELAY)
except:
log.msg("%s started talking" %
self.clients[from_id-1]['ON'])
self.clients[from_id-1]['on'])
self.parrot_timer = self.factory.reactor.callLater(
PARROT_AUDIO_DELAY, self.startRepeating, from_id)
self.pong()
def loginResponse(self, info):
log.msg("Login: %s" % info['AL'])
log.msg("Login: %s" % info['al'])
def clientsListUpdated(self, 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):

View File

@ -68,7 +68,7 @@ class FRNStream(FRNClient):
def getClientName(self, client_id):
if self.clientsById.has_key(client_id):
return self.clientsById[client_id]['ON']
return self.clientsById[client_id]['on']
else:
return client_id
@ -77,11 +77,11 @@ class FRNStream(FRNClient):
self.pong()
def loginResponse(self, info):
log.msg("Login: %s" % info['AL'])
log.msg("Login: %s" % info['al'])
def clientsListUpdated(self, 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):

View File

@ -34,7 +34,7 @@ class FRNCrosslinkClient(FRNClient):
def getClientName(self, client_id):
if self.clientsById.has_key(client_id):
return self.clientsById[client_id]['ON']
return self.clientsById[client_id]['on']
else:
return client_id
@ -60,16 +60,16 @@ class FRNCrosslinkClient(FRNClient):
for server, port, u, factory in clients:
cl = []
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']:
for c in factory.connection.clients:
ss = cStatus.get(c['S'], '?')
if c['M'] != '0':
ss = cStatus.get(c['s'], '?')
if c['m'] != '0':
ss = ss.lower()
cl.append(" %s%s %s " % (
cType.get(c['BC'], 'G'),
cType.get(c['bc'], 'G'),
ss,
c['ON'].replace('<', '&lt;'),
c['on'].replace('<', '&lt;'),
))
else:
cl.append(" :!: DISCONNECTED :!: ")
@ -100,7 +100,7 @@ class FRNCrosslinkClient(FRNClient):
global clients, talking, lastMessages
if talking is None or talking == self:
talking = self
talkingUser = self.clients[from_id-1]['ON']
talkingUser = self.clients[from_id-1]['on']
if len(lastMessages) > 0:
if lastMessages[0] == talkingUser:
talkingUser = None
@ -110,7 +110,7 @@ class FRNCrosslinkClient(FRNClient):
for _, _, _, factory in clients:
conn = factory.connection
if conn != self:
role = conn.serverdata.get('AL', None)
role = conn.serverdata.get('al', None)
if role in ['OK', 'ADMIN', 'OWNER']:
if conn.txOk:
conn.txReq = False
@ -126,15 +126,15 @@ class FRNCrosslinkClient(FRNClient):
self.pong()
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):
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:
for c in clients:
if c['ON'] == self.user.ON and c['BC'] == self.user.BC:
self.clientId = c['ID']
if c['on'] == self.user.ON and c['bc'] == self.user.BC:
self.clientId = c['id']
class FRNCrosslinkClientFactory(FRNClientFactory):
protocol = FRNCrosslinkClient

View File

@ -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:

View File

@ -52,8 +52,7 @@ class FRNServerServiceMaker(object):
return internet.TCPServer(options['port'],
FRNServerFactory(
pjoin(options['confdir'], 'tracker.shelve'),
# RemoteManager(), # Authenticate on frn.no-ip.info
RemoteManager(server='fri.no-ip.info', maskParrot=False),
RemoteManager(),
FRNUser(
SN=options['server'],PT=options['port'],
BN=options['backup_server'], BP=options['backup_port'],