monolith/threshold

297 lines
9.8 KiB
Python
Executable File

#!/usr/bin/env python
from twisted.internet import reactor
from twisted.internet.ssl import DefaultOpenSSLContextFactory
from twisted.internet.protocol import Protocol, Factory
from json import load, dump, loads
from sys import exit
listener = None
connections = {}
def log(data):
print("[LOG]", data)
def debug(data):
print("[DEBUG]", data)
def warn(data):
print("[WARNING]", data)
def error(data):
print("[ERROR]", data)
exit(1)
def sendData(addr, data):
connections[addr].send(data)
def sendSuccess(addr, data):
sendData(addr, "[y] " + data)
def sendFailure(addr, data):
sendData(addr, "[n] " + data)
def sendInfo(addr, data):
sendData(addr, "[i] " + data)
class Base(Protocol):
def __init__(self, addr):
self.addr = addr
self.authed = False
if config["UsePassword"] == False:
self.authed = True
def send(self, data):
data += "\r\n"
data = data.encode("utf-8", "replace")
self.transport.write(data)
def dataReceived(self, data):
data = data.decode("utf-8", "replace")
log("Data received from %s:%s -- %s" % (self.addr.host, self.addr.port, repr(data)))
helper.parseCommand(self.addr, self.authed, data)
def connectionMade(self):
log("Connection from %s:%s" % (self.addr.host, self.addr.port))
self.send("Hello.")
def connectionLost(self, reason):
global connections
self.authed = False
log("Connection lost from %s:%s -- %s" % (self.addr.host, self.addr.port, reason.getErrorMessage()))
if not listener == None:
if self.addr in connections.keys():
del connections[self.addr]
else:
warn("Tried to remove a non-existant connection.")
else:
warn("Tried to remove a connection from a listener that wasn't running.")
class BaseFactory(Factory):
def buildProtocol(self, addr):
global connections
entry = Base(addr)
connections[addr] = entry
return entry
def send(self, addr, data):
global connections
if addr in connections.keys():
connection = connections[addr]
connection.send(data)
else:
return
class Helper(object):
def getConfig(self):
with open("config.json", "r") as f:
config = load(f)
if set(["Port", "BindAddress", "UseSSL", "UsePassword"]).issubset(set(config.keys())):
if config["UseSSL"] == True:
if not set(["ListenerKey", "ListenerCertificate"]).issubset(set(config.keys())):
error("SSL is on but certificate or key is not defined")
if config["UsePassword"] == True:
if not "Password" in config.keys():
error("Password authentication is on but password is not defined")
return config
else:
error("Mandatory values missing from config")
def getPool(self):
with open("pool.json", "r") as f:
data = f.read()
if not data == "":
pool = loads(data.strip())
return pool
else:
return {}
def savePool(self):
global pool
with open("pool.json", "w") as f:
dump(pool, f, indent=4)
return
def getHelp(self):
with open("help.json", "r") as f:
help = load(f)
return help
def incorrectUsage(self, addr, mode):
if mode == None:
sendFailure(addr, "Incorrect usage")
return
if mode in help.keys():
sendFailure(addr, "Usage: " + help[mode])
return
def parseCommand(self, addr, authed, data):
global pool
data = data.strip()
spl = data.split()
obj = connections[addr]
success = lambda data: sendSuccess(addr, data)
failure = lambda data: sendFailure(addr, data)
info = lambda data: sendInfo(addr, data)
incUsage = lambda mode: self.incorrectUsage(addr, mode)
length = len(spl)
if len(spl) > 0:
cmd = spl[0]
else:
failure("No text was sent")
return
if authed == True:
if cmd == "help":
helpMap = []
for i in help.keys():
helpMap.append("%s: %s" % (i, help[i]))
info("\n".join(helpMap))
return
elif cmd == "rehash":
global config
config = helper.getConfig()
success("Configuration rehashed successfully")
elif cmd == "pass":
info("You are already authenticated")
return
elif cmd == "logout":
obj.authed = False
success("Logged out")
return
elif cmd == "list":
poolMap = []
for i in pool.keys():
poolMap.append("Server: %s" % i)
for x in pool[i].keys():
poolMap.append("%s: %s" % (x, pool[i][x]))
poolMap.append("\n")
info("\n".join(poolMap))
return
elif cmd == "add":
if length == 6:
if spl[1] in pool.keys():
failure("Name already exists: %s" % spl[1])
return
protocol = spl[4].lower()
if not protocol in ["ssl", "plain"]:
incUsage("connect")
return
pool[spl[1]] = { "address": spl[2],
"port": spl[3],
"protocol": protocol,
"nickname": spl[5],
"username": None,
"realname": None,
"userinfo": None,
"finger": None,
"version": None,
"source": None,
"authtype": None,
"password": None,
"authentity": None,
"key": None,
"certificate": None,
}
success("Successfully created bot")
self.savePool()
return
else:
incUsage("add")
return
elif cmd == "del":
if length == 2:
if not spl[1] in pool.keys():
failure("Name does not exist: %s" % spl[1])
return
del pool[spl[1]]
success("Successfully removed bot")
self.savePool()
return
else:
incUsage("del")
return
elif cmd == "mod":
if length == 2:
if not spl[1] in pool.keys():
failure("Name does not exist: %s" % spl[1])
return
optionMap = ["Viewing options for %s" % spl[1]]
for i in pool[spl[1]].keys():
optionMap.append("%s: %s" % (i, pool[spl[1]][i]))
info("\n".join(optionMap))
return
elif length == 3:
if not spl[1] in pool.keys():
failure("Name does not exist: %s" % spl[1])
return
if not spl[2] in pool[spl[1]].keys():
failure("No such key: %s" % spl[2])
return
info("%s: %s" % (spl[2], pool[spl[1]][spl[2]]))
return
elif length == 4:
if not spl[1] in pool.keys():
failure("Name does not exist: %s" % spl[1])
return
if not spl[2] in pool[spl[1]].keys():
failure("No such key: %s" % spl[2])
return
if spl[3] == pool[spl[1]][spl[2]]:
failure("Value already exists: %s" % spl[3])
return
pool[spl[1]][spl[2]] = spl[3]
success("Successfully set key %s to %s on %s" % (spl[2], spl[3], spl[1]))
return
else:
incUsage("mod")
else:
incUsage(None)
return
else:
if cmd == "pass" and length == 2:
if spl[1] == config["Password"]:
success("Authenticated successfully")
obj.authed = True
return
else:
failure("Password incorrect")
obj.transport.loseConnection()
return
else:
incUsage(None)
return
if __name__ == "__main__":
helper = Helper()
config = helper.getConfig()
pool = helper.getPool()
help = helper.getHelp()
listener = BaseFactory()
if config["UseSSL"] == True:
reactor.listenSSL(config["Port"], listener, DefaultOpenSSLContextFactory(config["ListenerKey"], config["ListenerCertificate"]), interface=config["BindAddress"])
log("Threshold running with SSL on %s:%s" % (config["BindAddress"], config["Port"]))
else:
reactor.listenTCP(config["Port"], listener, interface=config["BindAddress"])
log("Threshold running on %s:%s" % (config["BindAddress"], config["Port"]))
reactor.run()