From 86f2346165890fdb47f130876ff0f0a16c425362 Mon Sep 17 00:00:00 2001 From: Kay Faraday Date: Thu, 21 Sep 2023 07:23:34 +0000 Subject: [PATCH] initial commit --- .gitignore | 2 ++ fingerd.example.toml | 8 ++++++ fingerd.py | 64 ++++++++++++++++++++++++++++++++++++++++++++ fingerd.toml | 2 ++ requirements.txt | 2 ++ 5 files changed, 78 insertions(+) create mode 100644 .gitignore create mode 100644 fingerd.example.toml create mode 100755 fingerd.py create mode 100644 fingerd.toml create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a230a78 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.venv/ +__pycache__/ diff --git a/fingerd.example.toml b/fingerd.example.toml new file mode 100644 index 0000000..19daef4 --- /dev/null +++ b/fingerd.example.toml @@ -0,0 +1,8 @@ +instance_name = 'freak.university' + +[db] +# Accepts all parameters accepted by https://magicstack.github.io/asyncpg/current/api/index.html#connection. +# Or leave this section blank to use the defaults. +username='mastodon' +password='...' +database='...' diff --git a/fingerd.py b/fingerd.py new file mode 100755 index 0000000..546441c --- /dev/null +++ b/fingerd.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python + +import os +import asyncio +import asyncpg +import qtoml as toml +from contextvars import ContextVar + +FINGER_PORT = 79 +MASTODON_USERNAME_MAX_LENGTH = 30 + +db = ContextVar('db') + +with open('fingerd.toml') as f: + config = toml.load(f) + +async def handle_finger(reader, writer): + try: + data = await reader.read( + len('@') * 2 + + MASTODON_USERNAME_MAX_LENGTH + + len(config['instance_name']) + ) + handle = data.decode().rstrip() + username = handle.removeprefix('@').removesuffix('@' + config['instance_name']) + if '@' in username: + writer.write(b'Error: user must be local to this instance\n') + writer.write(username.encode()) + writer.write(b'\n') + writer.write(config['instance_name'].encode()) + writer.write(b'\n') + return + + resp = await db.get().fetchval( + 'SELECT note FROM accounts WHERE username = $1 AND domain IS NULL', + username, + ) + if resp is None: + writer.write(b'Error: user not found\n') + return + + writer.write(resp.encode()) + writer.write(b'\n') + finally: + print('running cleanup') + await writer.drain() + writer.close() + await writer.wait_closed() + +async def main(): + db.set(await asyncpg.create_pool(**config['db'])) + + async with await asyncio.start_server( + handle_finger, + '0.0.0.0', + os.getenv('PORT', FINGER_PORT), + ) as server: + addrs = ', '.join(str(sock.getsockname()) for sock in server.sockets) + print(f'Serving on {addrs}') + + await server.serve_forever() + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/fingerd.toml b/fingerd.toml new file mode 100644 index 0000000..372e32e --- /dev/null +++ b/fingerd.toml @@ -0,0 +1,2 @@ +instance_name = 'freak.university' +[db] diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..602cab9 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +asyncpg ~= 0.28.0 +qtoml ~= 0.3.1