From 26da26f7a6d6fad5de92b44bf10eaf9d5d7d1c4a Mon Sep 17 00:00:00 2001 From: tuxmain Date: Mon, 31 Jan 2022 23:49:50 +0100 Subject: [PATCH] Compatibility proxy --- README.md | 12 +++- config.template.lua | 1 + init.lua | 10 ++- proxy.py | 147 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 166 insertions(+), 4 deletions(-) create mode 100644 proxy.py diff --git a/README.md b/README.md index a8e282b..2035792 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,6 @@ This is a server-side mod for Minetest, creating a bridge between a Matrix insta This mod does not use any additional lua library, only standard Minetest API. -**Compatibility note**: Fully functional with `>=5.4.0`. Due to a broken API in `5.3.0`, this version is not fully supported. - **Demo server**: `survival.juneland.fr` port `30000`; [see Juneland's Matrix room](https://txmn.tk/element/#/room/#juneland:matrix.txmn.tk) [Donate Libre Currency Ğ1](https://demo.cesium.app/#/app/wot/ENA89PPrBHS8wxhxgGMZxUXd53nRw1BaXBDvCVmZ6Tip/) @@ -28,6 +26,16 @@ Add `matrix_bridge` to `secure.http_mods` (e.g. in the file `/etc/minetest/minet secure.http_mods = matrix_bridge ``` +### Compatibility proxy + +If using Minetest `5.3.0` (probably any version strictly before `5.4.0`), because of a broken API, you need to run the proxy: + +```bash +python3 proxy.py -A -P # --help to print options +``` + +In `config.lua`, set `MATRIX_SERVER=http://127.0.0.1:18448` and `MATRIX_HACK_PROXY=true`. + ## Contributing See [Matrix API docs](https://www.matrix.org/docs/guides/client-server-api). diff --git a/config.template.lua b/config.template.lua index 449359e..74d1e57 100644 --- a/config.template.lua +++ b/config.template.lua @@ -6,3 +6,4 @@ MATRIX_SERVER = "https://matrix.txmn.tk:8448" MATRIX_ROOM = "!xReNhJbGJeGK4fysJU:matrix.txmn.tk" MATRIX_USERNAME = "minebot" MATRIX_PASSWORD = "PUT-YOUR-PASSWORD-HERE" +MATRIX_HACK_PROXY = false diff --git a/init.lua b/init.lua index 79c45a4..ea8bc52 100644 --- a/init.lua +++ b/init.lua @@ -14,7 +14,8 @@ local MatrixChat = { server = MATRIX_SERVER, username = MATRIX_USERNAME, password = MATRIX_PASSWORD, - room = MATRIX_ROOM, + room = MATRIX_ROOM, + proxy = MATRIX_HACK_PROXY, -- acquired on login userid = nil, token = nil, @@ -135,7 +136,12 @@ function MatrixChat:send(msg) local u = self.server .."/_matrix/client/r0/rooms/".. self.room .."/send/m.room.message/" .. txid -- ?access_token=..token local h = {"Content-Type: application/json", "Authorization: Bearer " .. self.token} local d = minetest.write_json({msgtype="m.text", body=msg}) - http.fetch({url=u, method="PUT", extra_headers=h, data=d}, + if self.proxy then + local req = {url=u, method="POST", extra_headers=h, post_data=d} + else + local req = {url=u, method="PUT", extra_headers=h, data=d} + end + http.fetch(req, function(res) if res.code == 200 then local data = minetest.parse_json(res.data) diff --git a/proxy.py b/proxy.py new file mode 100644 index 0000000..14fabb7 --- /dev/null +++ b/proxy.py @@ -0,0 +1,147 @@ +import http.client, re, socket, sys, time + +RECBUF = 1024 + +def getargv(arg, default="", n=1, args=sys.argv): + if arg in args and len(args) > args.index(arg)+n: + return args[args.index(arg)+n] + else: + return default + +p_clen = re.compile("\r?\ncontent-length: *(\d+)\r?\n?", re.IGNORECASE) + +if __name__ == "__main__": + if "--help" in sys.argv or "-h" in sys.argv: + print("""MineTest Matrix Bridge mod proxy +GNU AGPLv3, CopyLeft 2022 Pascal Engélibert + +This program is useful for <5.4.0 MineTest servers using the mod matrix_bridge. +For security reasons, please listen to a local address (default value is OK) and use a firewall if needed to prevent public access. + +Options: (with defaults) + -a 127.0.0.1 Listen address + -p 18448 Listen port + -A matrix.txmn.tk Matrix address (without protocol and port) + -P 8448 Matrix port + -s Use HTTP instead of HTTPS (default: no) +""") + exit() + + listen_addr = getargv("-a", "127.0.0.1") + listen_port = int(getargv("-p", 18448)) + matrix_addr = getargv("-A", "matrix.txmn.tk") + matrix_port = int(getargv("-P", 8448)) + matrix_https = not "-s" in sys.argv + + if matrix_https: + import ssl + + ssl_ctx = ssl.create_default_context() + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock.settimeout(60) + sock.bind((listen_addr, listen_port)) + sock.listen(1) + + try: + while True: + try: + client, addr = sock.accept() + except socket.timeout: + continue + + # Get request + paquet = b"" + header = b"" + content = b"" + content_len = 0 + resp = {} + lf = 0 + while True: + raw = client.recv(RECBUF) + if raw: + paquet += raw + if lf >= 0: + for c in raw: + if c == 10:# LF + lf += 1 + elif c != 13:# CR + lf = 0 + if lf > 1: + parts = paquet.split(b"\r\n\r\n") + header = parts[0] + content = parts[1] + try: + content_len = int(p_clen.search(header.decode()).group(1)) + except (AttributeError, ValueError): + content_len = 0 + break + if lf > 1: + break + else: + break + while len(content) < content_len: + raw = client.recv(RECBUF) + paquet += raw + content += raw + + httpreq = paquet.split(b" ", 2) + if len(httpreq) != 3: + client.close() + continue + method = httpreq[0] + url = httpreq[1] + rest = httpreq[2] + + if b"/send/m.room.message/" in url: + method = b"PUT" + + matrix_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + if matrix_https: + matrix_sock = ssl_ctx.wrap_socket(matrix_sock, server_hostname=matrix_addr) + matrix_sock.settimeout(60) + matrix_sock.connect((matrix_addr, matrix_port)) + matrix_sock.settimeout(None) + + matrix_sock.sendall(b" ".join([method, url, rest])) + + paquet = b"" + header = b"" + content = b"" + content_len = 0 + resp = {} + lf = 0 + while True: + raw = matrix_sock.recv(RECBUF) + if raw: + paquet += raw + if lf >= 0: + for c in raw: + if c == 10:# LF + lf += 1 + elif c != 13:# CR + lf = 0 + if lf > 1: + parts = paquet.split(b"\r\n\r\n") + header = parts[0] + content = parts[1] + try: + content_len = int(p_clen.search(header.decode()).group(1)) + except AttributeError: + content_len = 0 + break + if lf > 1: + break + else: + break + while len(content) < content_len: + raw = matrix_sock.recv(RECBUF) + paquet += raw + content += raw + + client.sendall(paquet) + + client.close() + sock.close() + except KeyboardInterrupt: + sock.shutdown(socket.SHUT_WR)