import select
import socket
import time

class OneClient:
    def __init__(self, tcp, id):
        self.tcp = tcp
        self.inbuffer = b""
        self.atl = 0
        self.outbuffer = b""
        self.frames = []
        self.alive = True
        self.LRT = time.time()
        self.id = id
        self.echo = False
        self.access = False

    def sendframe(self, id, frame):
        try:
            out = id.to_bytes(1, "little") + frame
            self.outbuffer += out
        except:
            pass

    def sendbuf(self):
        try:
            n = self.tcp.send(self.outbuffer)
            self.outbuffer = self.outbuffer[n:]
        except BlockingIOError:
            return
        except OSError:
            self.alive = False

    def recvbuf(self):
        try:
            dta = self.tcp.recv(4096)
            if not dta:
                self.alive = False
        except:
            self.alive = False

        if self.alive:
            self.inbuffer += dta
            self.LRT = time.time()

    def workbuf(self, pw):
        ln = len(self.inbuffer)
        if self.atl == 0 and ln >= 2:
            self.atl = 2 + int.from_bytes(self.inbuffer[:2], byteorder="little")
            if self.atl == 2:
                self.inbuffer = self.inbuffer[2:]
                if self.echo:
                    self.echo = False
                else:
                    self.echo = True
                self.atl = 0
            elif self.atl == 1:
                self.alive = False
                return
            elif self.atl >= 1024:
                self.alive = False
                return
            else:
                self.workbuf(pw)
        elif self.atl != 0 and ln >= self.atl:
            if self.access:
                self.frames.append(self.inbuffer[:self.atl])
                self.inbuffer = self.inbuffer[self.atl:]
                self.atl = 0
                self.workbuf(pw)
            else:
                txt = self.inbuffer[2:self.atl]
                if txt == pw:
                    self.access = True
                    self.inbuffer = self.inbuffer[self.atl:]
                    self.atl = 0
                    self.workbuf(pw)
                else:
                    self.alive = False
                    return

class VarHolder:
    clients = []
    tcps = []
    ids = [x for x in range(32)]
    serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
    pw = b"hackme"

    def closeclient(self, client):
        self.tcps.remove(client.tcp)
        self.clients.remove(client)
        client.tcp.close()
        self.ids.append(client.id)

    def acceptclient(self):
        client, addr = self.serv.accept()
        if len(self.clients) > 30:
            client.close()
        else:
            client.setblocking(False)
            self.tcps.append(client)
            self.clients.append(OneClient(client, self.ids.pop(0)))

    def findclient(self, tcp):
        for c in self.clients:
            if c.tcp == tcp:
                self.handle(c)

    def handle(self, c):
        c.recvbuf()
        if len(c.inbuffer) > 0:
            c.workbuf(self.pw)
            if c.alive == False:
                self.closeclient(c)
                return
        for frame in c.frames:
            for r in self.clients:
                if r.access and r != c:
                    r.sendframe(c.id, frame)
                    r.sendbuf()
                elif r.echo:
                    r.sendframe(c.id, frame)
                    r.sendbuf()

        c.frames = []

def main():
    port = int(input("PORT> "))
    pw = input("PASSWORD> ").encode("utf-8")
    vars = VarHolder()
    vars.pw = pw
    vars.serv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    vars.serv.bind(("0.0.0.0", port))
    vars.serv.listen(5)
    vars.tcps.append(vars.serv)

    while True:
        r,w,e = select.select(vars.tcps, [], [], 0.01)

        for c in vars.clients:
            if len(c.outbuffer) > 0:
                c.sendbuf()
            if c.LRT + 5 < time.time():
                c.alive = False
            if c.alive == False:
                vars.closeclient(c)

        if not r:
            continue

        for s in r:
            if s == vars.serv:
                vars.acceptclient()
            else:
                vars.findclient(s)

if __name__ == '__main__':
    main()
