Beispiel #1
0
def serve(sockname, incoming):
    if os.path.exists(sockname):
        os.remove(sockname)

    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    sock.bind(sockname)
    sock.listen(1)

    def listen(sock):
        while True:
            connection, client = sock.accept()
            def handle(connection, client):
                try:
                    for octets in connection.makefile("rb"):
                        try:
                            text = octets.decode("ascii", "replace")
                            text = text.strip("\n")

                            if " " in text:
                                instruction, data = text.split(" ", 1)
                                args = common.b64unpickle(data)
                            else:
                                instruction, args = text, tuple()

                            incoming.put((instruction,) + args)
                        except Exception as err:
                            debug("ERROR!", err.__class__.__name__, err)
                finally:
                    connection.close()
            common.thread(handle, connection, client)
    common.thread(listen, sock)
Beispiel #2
0
 def run(name, safe, irc):
     if name in self.events:
         for function in self.events[name]:
             if not function.saxo_synchronous:
                 common.thread(safe, function, irc)
             else:
                 safe(function, irc)
Beispiel #3
0
def start(base):
    # TODO: Check when two clients are running
    common.exit_cleanly()
    # http://stackoverflow.com/questions/11423225
    # IGN rather than DFL, otherwise Popen.communicate can quit saxo
    signal.signal(signal.SIGPIPE, signal.SIG_IGN)

    opt = configparser.ConfigParser(interpolation=None)
    config = os.path.join(base, "config")
    if not os.path.isfile(config):
        error("missing config file in: `%s`" % config, E_NO_CONFIG)
    opt.read(config)
    # TODO: Defaulting?
    # TODO: Warn if the config file is widely readable?

    common.populate(saxo_path, base)

    sockname =  os.path.join(base, "client.sock")
    serve(sockname, incoming)
    os.chmod(sockname, 0o600)

    # NOTE: If using os._exit, this doesn't work
    def remove_sock(sockname):
        if os.path.exists(sockname):
            os.remove(sockname)
    atexit.register(remove_sock, sockname)

    common.thread(scheduler.start, base, incoming)

    saxo = Saxo(base, opt)
    saxo.run()
Beispiel #4
0
def start(base):
    # TODO: Check when two clients are running
    common.exit_cleanly()
    # http://stackoverflow.com/questions/11423225
    # IGN rather than DFL, otherwise Popen.communicate can quit saxo
    signal.signal(signal.SIGPIPE, signal.SIG_IGN)

    opt = configparser.ConfigParser(interpolation=None)
    config = os.path.join(base, "config")
    if not os.path.isfile(config):
        error("missing config file in: `%s`" % config, E_NO_CONFIG)
    opt.read(config)
    # TODO: Defaulting?
    # TODO: Warn if the config file is widely readable?

    sockname = os.path.join(base, "client.sock")
    serve(sockname, incoming)
    os.chmod(sockname, 0o600)

    # NOTE: If using os._exit, this doesn't work
    def remove_sock(sockname):
        if os.path.exists(sockname):
            os.remove(sockname)

    atexit.register(remove_sock, sockname)

    sched = scheduler.Scheduler(incoming)
    common.thread(sched.start, base)

    saxo = Saxo(base, opt)
    saxo.run()
Beispiel #5
0
Datei: irc.py Projekt: zort/saxo
    def command(self, msg):
        cmd, arg = msg.cmd, msg.arg
        path = command_path(self.base, cmd)
        if path is None:
            return

        if random.random() < .001 and msg.nick.lower().strip("_-`") == "ramond":
            self.send("PRIVMSG",
                      msg.sender,
                      random.choice(["Mum's the word.",
                                     "Silence is golden.",
                                     "Don't flood the channel."]))
            return

        def command_process(env, cmd, path, arg):
            outs = process(env, cmd, path, arg)
            if outs:
                self.send("PRIVMSG", msg.sender, outs)

        env = self.environment_cache.copy()
        env["SAXO_NICK"] = msg.nick
        env["SAXO_SENDER"] = msg.sender
        if msg.sender in self.links:
            env["SAXO_URL"] = self.links[msg.sender]
        if msg.authorised():
            env["SAXO_AUTHORISED"] = "1"
        env["SAXO_LF2STREAM"] = self.lf2stream
        common.thread(command_process, env, cmd, path, arg)
Beispiel #6
0
def serve(sockname, incoming):
    if os.path.exists(sockname):
        os.remove(sockname)

    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    sock.bind(sockname)
    sock.listen(1)

    def listen(sock):
        while True:
            connection, client = sock.accept()

            def handle(connection, client):
                try:
                    for octets in connection.makefile("rb"):
                        try:
                            text = octets.decode("ascii", "replace")
                            text = text.strip("\n")

                            if " " in text:
                                instruction, data = text.split(" ", 1)
                                args = common.b64unpickle(data)
                            else:
                                instruction, args = text, tuple()

                            incoming.put((instruction, ) + args)
                        except Exception as err:
                            debug("ERROR!", err.__class__.__name__, err)
                finally:
                    connection.close()

            common.thread(handle, connection, client)

    common.thread(listen, sock)
Beispiel #7
0
def start(base):
    # TODO: Check when two clients are running
    common.exit_cleanly()
    # http://stackoverflow.com/questions/11423225
    signal.signal(signal.SIGPIPE, signal.SIG_DFL)

    plugins = os.path.join(base, "plugins")
    if not os.path.isdir(plugins):
        common.error("no plugins directory: `%s`" % plugins, E_NO_PLUGINS)

    # TODO: Check for broken symlinks
    common.populate(saxo_path, base)

    opt = configparser.ConfigParser(interpolation=None)
    config = os.path.join(base, "config")
    opt.read(config)
    # TODO: Defaulting?
    # TODO: Warn if the config file is widely readable?

    sockname =  os.path.join(base, "client.sock")
    serve(sockname, incoming)
    os.chmod(sockname, 0o600)

    # NOTE: If using os._exit, this doesn't work
    def remove_sock(sockname):
        if os.path.exists(sockname):
            os.remove(sockname)
    atexit.register(remove_sock, sockname)

    common.thread(scheduler.start, base, incoming)

    saxo = Saxo(base, opt)
    saxo.run()
Beispiel #8
0
    def connect(self):
        try: self.connect_sock()
        except Exception as err:
           raise SaxoConnectionError(str(err))

        self.first = True
        # TODO: Reset other state? e.g. self.address

        receiving = (socket_receive, self.sock)
        self.receiving_thread = common.thread(*receiving)

        sending = (socket_send, self.sock, "flood" in self.opt["client"])
        self.sending_thread = common.thread(*sending)
Beispiel #9
0
    def scheduled_command(self, cmd, arg, sender=None):
        path = command_path(self.base, cmd)
        if path is None:
            return

        def command_process(env, cmd, path, arg):
            outs = process(env, cmd, path, arg)
            if outs and (sender is not None):
                self.send("PRIVMSG", sender, outs)

        env = self.environment_cache.copy()
        env["SAXO_SCHEDULED"] = "1"
        common.thread(command_process, env, cmd, path, arg)
Beispiel #10
0
Datei: irc.py Projekt: zort/saxo
    def scheduled_command(self, cmd, arg, sender=None):
        path = command_path(self.base, cmd)
        if path is None:
            return

        def command_process(env, cmd, path, arg):
            outs = process(env, cmd, path, arg)
            if outs and (sender is not None):
                self.send("PRIVMSG", sender, outs)

        env = self.environment_cache.copy()
        env["SAXO_SCHEDULED"] = "1"
        common.thread(command_process, env, cmd, path, arg)
Beispiel #11
0
    def connect(self):
        try:
            self.connect_sock()
        except Exception as err:
            raise SaxoConnectionError(str(err))

        self.first = True
        # TODO: Reset other state? e.g. self.address

        receiving = (socket_receive, self.sock)
        self.receiving_thread = common.thread(*receiving)

        sending = (socket_send, self.sock, "flood" in self.opt["client"])
        self.sending_thread = common.thread(*sending)
Beispiel #12
0
    def command(self, prefix, sender, identified, cmd, arg):
        if ("\x00" in cmd) or (os.sep in cmd) or ("." in cmd):
            return

        path = os.path.join(self.base, "commands", cmd)

        def process(env, path, arg):
            octets = arg.encode("utf-8", "replace")

            try: proc = subprocess.Popen([path, octets], env=env,
                stdin=subprocess.PIPE, stdout=subprocess.PIPE)
            except PermissionError:
                outs = "The command file does not have executable permissions"
            except FileNotFoundError:
                # Might have been removed just after running this thread
                return
            else:
                try: outs, errs = proc.communicate(octets + b"\n", timeout=12)
                except subprocess.TimeoutExpired:
                    proc.kill()
                    # TODO: Use actual prefix
                    outs = "Sorry, %s took too long" % cmd
                else:
                    outs = outs.decode("utf-8", "replace")
                    if "\n" in outs:
                        outs = outs.splitlines()[0]

                code = proc.returncode or 0
                # Otherwise: TypeError: unorderable types: NoneType() > int()
                if (code > 0) and (not outs):
                    # TODO: Use actual prefix
                    outs = "Sorry, %s responded with an error" % cmd

            if outs:
                self.send("PRIVMSG", sender, outs)

        if os.path.isfile(path):
            env = self.environment_cache.copy()
            env["SAXO_NICK"] = prefix[0]
            env["SAXO_SENDER"] = sender
            if identified == True:
                env["SAXO_IDENTIFIED"] = "1"
            elif identified == False:
                env["SAXO_IDENTIFIED"] = "0"
            if sender in self.links:
                env["SAXO_URL"] = self.links[sender]
            common.thread(process, env, path, arg)
Beispiel #13
0
    def connect(self):
        host = self.opt["server"]["host"]
        port = int(self.opt["server"]["port"])

        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        if "ssl" in self.opt["server"]:
            import ssl
            debug("Warning: Using SSL, but not validating the cert!")
            self.sock = ssl.wrap_socket(
                self.sock,
                server_side=False,
                cert_reqs=ssl.CERT_NONE) # TODO: or CERT_REQUIRED

        debug("Connecting to %s:%s" % (host, port))
        self.sock.connect((host, port))
        self.first = True

        common.thread(socket_receive, self.sock)
        common.thread(socket_send, self.sock, "flood" in self.opt["client"])
Beispiel #14
0
    def command(self, msg):
        cmd, arg = msg.cmd, msg.arg
        path = command_path(self.base, cmd)
        if path is None:
            return

        def command_process(env, cmd, path, arg):
            outs = process(env, cmd, path, arg)
            if outs:
                self.send("PRIVMSG", msg.sender, outs)

        env = self.environment_cache.copy()
        env["SAXO_NICK"] = msg.nick
        env["SAXO_SENDER"] = msg.sender
        if msg.sender in self.links:
            env["SAXO_URL"] = self.links[msg.sender]
        if msg.authorised():
            env["SAXO_AUTHORISED"] = "1"
        common.thread(command_process, env, cmd, path, arg)
Beispiel #15
0
Datei: irc.py Projekt: dpk/saxo
    def command(self, msg):
        cmd, arg = msg.cmd, msg.arg
        path = command_path(self.base, cmd)
        if path is None:
            return

        def command_process(env, cmd, path, arg):
            outs = process(env, cmd, path, arg)
            if outs:
                self.send("PRIVMSG", msg.sender, outs)

        env = self.environment_cache.copy()
        env["SAXO_NICK"] = msg.nick
        env["SAXO_SENDER"] = msg.sender
        if msg.sender in self.links:
            env["SAXO_URL"] = self.links[msg.sender]
        if msg.authorised():
            env["SAXO_AUTHORISED"] = "1"
        common.thread(command_process, env, cmd, path, arg)
Beispiel #16
0
    def listen(sock):
        while True:
            connection, client = sock.accept()
            def handle(connection, client):
                try:
                    for octets in connection.makefile("rb"):
                        try:
                            text = octets.decode("ascii", "replace")
                            text = text.strip("\n")

                            if " " in text:
                                instruction, data = text.split(" ", 1)
                                args = common.b64unpickle(data)
                            else:
                                instruction, args = text, tuple()

                            incoming.put((instruction,) + args)
                        except Exception as err:
                            debug("ERROR!", err.__class__.__name__, err)
                finally:
                    connection.close()
            common.thread(handle, connection, client)
Beispiel #17
0
    def listen(sock):
        while True:
            connection, client = sock.accept()

            def handle(connection, client):
                try:
                    for octets in connection.makefile("rb"):
                        try:
                            text = octets.decode("ascii", "replace")
                            text = text.strip("\n")

                            if " " in text:
                                instruction, data = text.split(" ", 1)
                                args = common.b64unpickle(data)
                            else:
                                instruction, args = text, tuple()

                            incoming.put((instruction, ) + args)
                        except Exception as err:
                            debug("ERROR!", err.__class__.__name__, err)
                finally:
                    connection.close()

            common.thread(handle, connection, client)
Beispiel #18
0
def test(args):
    if args.directory is not None:
        common.error("Tests cannot be run in conjunction with a directory")

    import queue
    import shutil
    import socket
    import subprocess
    import tempfile

    # Save PEP 3122!
    if "." in __name__:
        from . import saxo
    else:
        import saxo

    saxo_script = sys.modules["__main__"].__file__
    saxo_test_server = os.path.join(saxo.path, "test", "server.py")

    tmp = tempfile.mkdtemp()
    outgoing = queue.Queue()

    if not sys.executable:
        common.error("Couldn't find the python executable")

    if not os.path.isdir(tmp):
        common.error("There is no %s directory" % tmp)

    print("python executable:", sys.executable)
    print("saxo path:", saxo.path)
    print("saxo script:", saxo_script)
    print("saxo test server:", saxo_test_server)
    print()

    def run_server():
        server = subprocess.Popen([sys.executable, "-u", saxo_test_server],
            stdout=subprocess.PIPE)

        for line in server.stdout:
            line = line.decode("utf-8", "replace")
            line = line.rstrip("\n")
            outgoing.put("S: " + line)

        outgoing.put("Server finished")

    def run_client():
        saxo_test = os.path.join(tmp, "saxo-test")
        outgoing.put("Running in %s" % saxo_test)

        cmd = [sys.executable, saxo_script, "create", saxo_test]
        code = subprocess.call(cmd)
        if code:
            print("Error creating the client configuration")
            sys.exit(1)

        test_config = os.path.join(saxo.path, "test", "config")
        saxo_test_config = os.path.join(saxo_test, "config")
        with open(test_config) as f:
            with open(saxo_test_config, "w") as w:
                for line in f:
                    line = line.replace("localhost", socket.gethostname())
                    w.write(line)
        # shutil.copy2(test_config, saxo_test_config)

        client = subprocess.Popen([sys.executable, "-u",
                saxo_script, "-f", "start", saxo_test],
            stdout=subprocess.PIPE)

        for line in client.stdout:
            line = line.decode("utf-8", "replace")
            line = line.rstrip("\n")
            outgoing.put("C: " + line)

        manifest01 = {"commands", "config", "database.sqlite3",
            "pid", "plugins"}
        manifest02 = manifest01 | {"client.sock"}

        if set(os.listdir(saxo_test)) == manifest01:
            shutil.rmtree(saxo_test)
        elif set(os.listdir(saxo_test)) == manifest02:
            outgoing.put("Warning: client.sock had not been removed")
            shutil.rmtree(saxo_test)
        else:
            outgoing.put("Refusing to delete the saxo test directory")
            outgoing.put("Data was found which does not match the manifest")
            outgoing.put(saxo_test)

    common.thread(run_server)
    common.thread(run_client)

    error = False
    completed = False
    client_buffer = []
    while True:
        line = outgoing.get()

        if line.startswith("S: "):
            print(line)
            if line.startswith("S: ERROR"):
               error = True
            if line.startswith("S: Tests complete"):
               completed = True
            if not line.startswith("S: Test"):
                for c in client_buffer:
                    print(c)
            del client_buffer[:]

        elif line.startswith("C: "):
            client_buffer.append(line)

        else:
            print(line)

        sys.stdout.flush()

        if line == "Server finished":
            break

    if not os.listdir(tmp):
        os.rmdir(tmp)
    else:
        print("Warning: Did not remove:", tmp)

    if completed and (not error):
        sys.exit(0)
    else:
        sys.exit(1)
Beispiel #19
0
def test(args):
    if args.directory is not None:
        common.error("Tests cannot be run in conjunction with a directory")

    import queue
    import shutil
    import socket
    import subprocess
    import tempfile

    # Save PEP 3122!
    if "." in __name__:
        from . import saxo
    else:
        import saxo

    saxo_script = sys.modules["__main__"].__file__
    saxo_test_server = os.path.join(saxo.path, "test", "server.py")

    tmp = tempfile.mkdtemp()
    outgoing = queue.Queue()

    if not sys.executable:
        common.error("Couldn't find the python executable")

    if not os.path.isdir(tmp):
        common.error("There is no %s directory" % tmp)

    print("python executable:", sys.executable)
    print("saxo path:", saxo.path)
    print("saxo script:", saxo_script)
    print("saxo test server:", saxo_test_server)
    print()

    def run_server():
        server = subprocess.Popen([sys.executable, "-u", saxo_test_server],
                                  stdout=subprocess.PIPE)

        for line in server.stdout:
            line = line.decode("utf-8", "replace")
            line = line.rstrip("\n")
            outgoing.put("S: " + line)

        outgoing.put("Server finished")

    def run_client():
        saxo_test = os.path.join(tmp, "saxo-test")
        outgoing.put("Running in %s" % saxo_test)

        cmd = [sys.executable, saxo_script, "create", saxo_test]
        code = subprocess.call(cmd)
        if code:
            print("Error creating the client configuration")
            sys.exit(1)

        test_config = os.path.join(saxo.path, "test", "config")
        saxo_test_config = os.path.join(saxo_test, "config")
        with open(test_config) as f:
            with open(saxo_test_config, "w") as w:
                for line in f:
                    line = line.replace("localhost", socket.gethostname())
                    w.write(line)
        # shutil.copy2(test_config, saxo_test_config)

        client = subprocess.Popen(
            [sys.executable, "-u", saxo_script, "-f", "start", saxo_test],
            stdout=subprocess.PIPE)

        for line in client.stdout:
            line = line.decode("utf-8", "replace")
            line = line.rstrip("\n")
            outgoing.put("C: " + line)

        manifest01 = {
            "commands", "config", "database.sqlite3", "pid", "plugins"
        }
        manifest02 = manifest01 | {"client.sock"}

        if set(os.listdir(saxo_test)) == manifest01:
            shutil.rmtree(saxo_test)
        elif set(os.listdir(saxo_test)) == manifest02:
            outgoing.put("Warning: client.sock had not been removed")
            shutil.rmtree(saxo_test)
        else:
            outgoing.put("Refusing to delete the saxo test directory")
            outgoing.put("Data was found which does not match the manifest")
            outgoing.put(saxo_test)

    common.thread(run_server)
    common.thread(run_client)

    error = False
    completed = False
    client_buffer = []
    while True:
        line = outgoing.get()

        if line.startswith("S: "):
            print(line)
            if line.startswith("S: ERROR"):
                error = True
            if line.startswith("S: Tests complete"):
                completed = True
            if not line.startswith("S: Test"):
                for c in client_buffer:
                    print(c)
            del client_buffer[:]

        elif line.startswith("C: "):
            client_buffer.append(line)

        else:
            print(line)

        sys.stdout.flush()

        if line == "Server finished":
            break

    if not os.listdir(tmp):
        os.rmdir(tmp)
    else:
        print("Warning: Did not remove:", tmp)

    if completed and (not error):
        sys.exit(0)
    else:
        sys.exit(1)