diff --git a/frn/clienttracker.py b/frn/clienttracker.py index 9b25b13..4d953a9 100644 --- a/frn/clienttracker.py +++ b/frn/clienttracker.py @@ -5,6 +5,7 @@ import shelve from twisted.python import log +from frn.user import FRNUser CLIENTS_ORDER = ['Crosslink', 'Parrot', 'Gateway', 'PC Only'] @@ -39,6 +40,7 @@ class ClientTracker(object): self._mute = {} self._block = {} self._admin = {} + self._acl = {} self.cbClient = cbClient self.cbNet = cbNet self.cbMute = cbMute @@ -53,6 +55,7 @@ class ClientTracker(object): self._mute = s['mute'] self._block = s['block'] self._admin = s['admin'] + self._acl = s['acl'] except KeyError: pass s.close() @@ -61,6 +64,7 @@ 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): @@ -78,7 +82,7 @@ class ClientTracker(object): return l.index(clientId)+1 def getNetworkList(self): - return self._net.keys() + return sorted(set(self._net.keys())|set(self._acl.keys())) def getClientList(self, network=[]): if network: @@ -113,7 +117,10 @@ class ClientTracker(object): def login(self, client, status=0): clientId = client.user.ID if clientId not in self._clientData: - client.user.S = status + if not self.aclTalkOk(client.user.NT, client.user.EA) and status < 1: + client.user.S = 1 + else: + client.user.S = status self._clientData[clientId] = client net = client.user.NT nc = self._net.get(net, []) @@ -133,7 +140,7 @@ class ClientTracker(object): self._mute[clientId].AI = a else: client.user.M = 0 - if clientId in self._block: + if not self.canLogin(client.user): client.role = "BLOCK" if client.user.EA in self._admin: client.role = "ADMIN" @@ -159,11 +166,13 @@ class ClientTracker(object): self.cbClient(net, self.getClientList(net)) def setStatus(self, clientId, status): - oldStatus = self._clientData[clientId].user.S + user = self.getClient(clientId) + oldStatus = user.S + if not self.aclTalkOk(user.NT, user.EA) and status < 1: + status = 1 if oldStatus != str(status): - net = self._clientData[clientId].user.NT self._clientData[clientId].user.S = status - self.cbClient(net, self.getClientList(net)) + self.cbClient(user.NT, self.getClientList(user.NT)) def mute(self, admin, clientId): if clientId not in self._mute: @@ -210,5 +219,81 @@ 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.user.EA, x.user) for x 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 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: diff --git a/frn/protocol/server.py b/frn/protocol/server.py index f3bfe45..626d311 100644 --- a/frn/protocol/server.py +++ b/frn/protocol/server.py @@ -55,8 +55,12 @@ class FRNServer(BufferingLineReceiver, TimeoutMixin): self.sendBlockList(self.factory.tracker.getBlockList()) if self.role == 'OWNER': self.sendAdminList(self.factory.tracker.getAdminList()) - self.sendAccessFlags(None) - self.sendAccessList([]) + 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.transport.loseConnection() + return self.startPinging() self.setTimeout(10.0) return @@ -113,8 +117,10 @@ 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)) @@ -201,9 +207,53 @@ class FRNServer(BufferingLineReceiver, TimeoutMixin): 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)) + def audioFrameReceived(self, frame): #log.msg("audioFrameReceived") - if not self.factory.tracker.isMute(self.user.ID): + if self.factory.tracker.canTalk(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: @@ -250,16 +300,19 @@ 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: # FIXME self.sendLine(c.asXML('AI','NN','CT','BC','ON','ID')) - def sendAccessFlags(self, flags): + def sendAccessFlags(self, access, talk): + 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.sendLine('2') # TODO - self.sendLine('o') - self.sendLine('o') + self.sendLine(FV[access]) + self.sendLine(FV[talk]) def sendTextMessage(self, clientId, message, target): self.transport.write(chr(4)) diff --git a/frn/user.py b/frn/user.py index 2726d3e..5ca120d 100644 --- a/frn/user.py +++ b/frn/user.py @@ -35,7 +35,7 @@ class FRNUser(object): self._fields[field.lower()] = str(value) def get(self, field, default=''): - return self._fields[field.lower()] + return self._fields.get(field.lower(),'') def items(self, *fields): if len(fields) == 0: