from json import dumps, loads import main from twisted.internet.protocol import Factory, Protocol from utils.logging.log import log, warn validTypes = [ "msg", "notice", "action", "who", "part", "join", "kick", "quit", "nick", "topic", "mode", "conn", "znc", "query", "self", "highlight", ] class Relay(Protocol): def __init__(self, addr): self.addr = addr self.authed = False self.subscriptions = [] def send(self, data): data += "\r\n" data = data.encode("utf-8", "replace") self.transport.write(data) def sendErr(self, data): self.send(dumps({"type": "error", "reason": data})) return def sendMsg(self, data): self.send(dumps(data)) def dataReceived(self, data): data = data.decode("utf-8", "replace") try: parsed = loads(data) except: # noqa: E722 self.sendErr("MALFORMED") return if "type" not in parsed.keys(): self.sendErr("NOTYPE") return if parsed["type"] == "hello": if set(["key", "hello"]).issubset(set(parsed)): self.handleHello(parsed) else: self.sendErr("WRONGFIELDS") return return elif parsed["type"] == "control": if "subscribe" in parsed.keys(): if self.authed: self.handleSubscribe(parsed["subscribe"]) return else: self.sendErr("DENIED") return elif "unsubscribe" in parsed.keys(): if self.authed: self.handleUnsubscribe(parsed["unsubscribe"]) return else: self.sendErr("DENIED") return else: self.sendErr("UNCLEAR") return else: self.sendErr("UNCLEAR") return def handleSubscribe(self, lst): if not isinstance(lst, list): self.sendErr("NOTLIST") return for i in lst: if i not in validTypes: self.sendErr("NONEXISTANT") return if i in self.subscriptions: self.sendErr("SUBSCRIBED") return self.subscriptions.append(i) self.sendMsg({"type": "success"}) return def handleUnubscribe(self, lst): if not isinstance(lst, list): self.sendErr("NOTLIST") return for i in lst: if i not in validTypes: self.sendErr("NONEXISTANT") return if i not in self.subscriptions: self.sendErr("NOTSUBSCRIBED") return del self.subscriptions[i] self.sendMsg({"type": "success"}) return def handleHello(self, parsed): if parsed["key"] in main.tokens.keys(): if ( parsed["hello"] == main.tokens[parsed["key"]]["hello"] and main.tokens[parsed["key"]]["usage"] == "relay" ): self.sendMsg( {"type": "hello", "hello": main.tokens[parsed["key"]]["counter"]} ) self.authed = True else: self.transport.loseConnection() return else: self.sendErr("NOKEY") return def connectionMade(self): log("Relay connection from %s:%s" % (self.addr.host, self.addr.port)) # self.send("Greetings.") def connectionLost(self, reason): self.authed = False log( "Relay connection lost from %s:%s -- %s" % (self.addr.host, self.addr.port, reason.getErrorMessage()) ) if self.addr in main.relayConnections.keys(): del main.relayConnections[self.addr] else: warn("Tried to remove a non-existant relay connection.") class RelayFactory(Factory): def buildProtocol(self, addr): entry = Relay(addr) main.relayConnections[addr] = entry return entry def send(self, addr, data): if addr in main.relayConnections.keys(): connection = main.relayConnections[addr] connection.send(data) else: return def sendRelayNotification(cast): for i in main.relayConnections.keys(): if main.relayConnections[i].authed: if cast["type"] in main.relayConnections[i].subscriptions: main.relayConnections[i].send(dumps(cast))