From 0702cd4a15e291fb84ad7049c112157dadc102a9 Mon Sep 17 00:00:00 2001 From: Lin2D2 Date: Thu, 12 Aug 2021 19:57:52 +0200 Subject: [PATCH 01/64] added setup bot function --- src/bot.py | 0 src/mailadm/cmdline.py | 58 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 src/bot.py diff --git a/src/bot.py b/src/bot.py new file mode 100644 index 00000000..e69de29b diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index b593caca..0369097b 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -16,14 +16,18 @@ from .conn import DBError, UserInfo from .mailcow import MailcowError import mailadm.util +import argparse +import socket +import time +import segno +import os -option_dryrun = click.option( - "-n", "--dryrun", is_flag=True, - help="don't change any files, only show what would be changed.") +from deltachat import Account +from db import DB -@click.command(cls=click.Group, context_settings=dict(help_option_names=["-h", "--help"])) +k.command(cls=click.Group, context_settings=dict(help_option_names=["-h", "--help"])) @click.version_option() @click.pass_context def mailadm_main(context): @@ -50,6 +54,52 @@ def get_mailadm_db(ctx, show=False, fail_missing_config=True): return db +@click.command() +@click.option("--email", type=str, default=None, help="name of email") +@click.option("--password", type=str, default=None, help="name of password") +@click.pass_context +@account_hookimpl +def setup_bot(ctx, email): + parser = argparse.ArgumentParser(prog=ctx[0] if ctx else None) + parser.add_argument("db", action="store", help="database file") + parser.add_argument("--show-ffi", action="store_true", help="show low level ffi events") + parser.add_argument("--email", action="store", help="email address") + parser.add_argument("--password", action="store", help="password") + + args = parser.parse_args(ctx[1:]) + + ac = Account(args.db) + + if not ac.is_configured(): + assert args.email and args.password, ( + "you must specify --email and --password once to configure this database/account" + ) + ac.set_config("addr", args.email) + ac.set_config("mail_pw", args.password) + ac.set_config("mvbox_move", "0") + ac.set_config("mvbox_watch", "0") + ac.set_config("sentbox_watch", "0") + ac.set_config("bot", "1") + configtracker = ac.configure() + configtracker.wait_finish() + + chat = ac.create_group_chat("Admin group on {}".format(socket.gethostname()), contacts=[], verified=True) + + qr = segno.make(chat.get_join_qr()) + print("\nPlease scan this qr code to join a verified admin group chat:\n\n") + qr.terminal() + + + print("looping until more than one group member") + while chat.num_contacts() < 2: + time.sleep(1) + + db = DB(os.getenv("MAILADM_DB")) + with db.read_connection() as conn: + conn.set_config("admingrpid", chat.id) + + + @click.command() @click.pass_context def config(ctx): From bf6a3ae5e3afdac35eeb8c82833b492b74bf0d04 Mon Sep 17 00:00:00 2001 From: Lin2D2 Date: Thu, 12 Aug 2021 20:09:46 +0200 Subject: [PATCH 02/64] added admingrpid --- src/bot.py | 0 src/mailadm/bot.py | 49 +++++++++++++++++++++++++++++++++++++++++++++ src/mailadm/conn.py | 4 +++- 3 files changed, 52 insertions(+), 1 deletion(-) delete mode 100644 src/bot.py create mode 100644 src/mailadm/bot.py diff --git a/src/bot.py b/src/bot.py deleted file mode 100644 index e69de29b..00000000 diff --git a/src/mailadm/bot.py b/src/mailadm/bot.py new file mode 100644 index 00000000..be153ebe --- /dev/null +++ b/src/mailadm/bot.py @@ -0,0 +1,49 @@ +# content of echo_and_quit.py + +from deltachat import account_hookimpl, run_cmdline +import mailadm.db +from mailadm import cmdline + + +class AdmBot: + @account_hookimpl + def ac_incoming_message(self, command): + print("process_incoming message", command) + if command.text.strip() == "/help": + pass + elif command.text.strip() == "/add-token": + chat = command.message.chat + if chat.is_group() and int(dbot.get('admgrpid')) == chat.id: + if chat.is_protected() and int(chat.num_contacts) >= 2: + command.create_chat() + text = '' #TODO add Token + command.chat.send_text(text) + + elif command.text.strip() == "/add-user": + chat = command.message.chat + if chat.is_group() and int(dbot.get('admgrpid')) == chat.id: + if chat.is_protected() and int(chat.num_contacts) >= 2: + command.create_chat() + text = '' # TODO add User + command.chat.send_text(text) + else: + # unconditionally accept the chat + command.create_chat() + addr = command.get_sender_contact().addr + if command.is_system_message(): + command.chat.send_text("echoing system message from {}:\n{}".format(addr, command)) + else: + text = command.text + command.chat.send_text("echoing from {}:\n{}".format(addr, text)) + + @account_hookimpl + def ac_message_delivered(self, message): + print("ac_message_delivered", message) + + +def main(argv=None): + run_cmdline(argv=argv, account_plugins=[AdmBot()]) + + +if __name__ == "__main__": + main() diff --git a/src/mailadm/conn.py b/src/mailadm/conn.py index a309e53d..9d5f8258 100644 --- a/src/mailadm/conn.py +++ b/src/mailadm/conn.py @@ -292,11 +292,13 @@ class Config: :param dbversion: the version of the mailadm database schema :param mailcow_endpoint: the URL to the mailcow API :param mailcow_token: the token to authenticate with the mailcow API + :param admingrpid: the ID of the admin group """ def __init__(self, mail_domain, web_endpoint, dbversion, mailcow_endpoint, - mailcow_token): + mailcow_token, admingrpid=None): self.mail_domain = mail_domain self.web_endpoint = web_endpoint self.dbversion = dbversion self.mailcow_endpoint = mailcow_endpoint self.mailcow_token = mailcow_token + self.admingrpid = admingrpid From 1b1af2aa908b8b82ad20c89a30ab7832302cbc36 Mon Sep 17 00:00:00 2001 From: missytake Date: Thu, 12 Aug 2021 18:59:10 +0200 Subject: [PATCH 03/64] added setup instructions for the mailadm bot --- install_mailadm.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/install_mailadm.sh b/install_mailadm.sh index 5bc5df7c..d94f978b 100644 --- a/install_mailadm.sh +++ b/install_mailadm.sh @@ -27,6 +27,9 @@ export MAILADM_HOME=/var/lib/mailadm export WEB_ENDPOINT=https://example.org/new_email export LOCALHOST_WEB_PORT=3691 +export BOT_EMAIL=bot@example.org +export BOT_PASSWORD=p4ssw0rd + # check if vmail user exists if ! getent passwd $VMAIL_USER > /dev/null 2>&1; then echo "user $VMAIL_USER does not exist, do you have a doveocot virtual user setup?" @@ -65,6 +68,10 @@ $MAILADM_HOME/venv/bin/mailadm gen-sysconfig \ --localhost-web-port=$LOCALHOST_WEB_PORT \ --mailadm-user $MAILADM_USER +$MAILADM_HOME/venv/bin/mailadm setup-bot \ + --email $BOT_EMAIL \ + --password $BOT_PASSWORD \ + $MAILADM_HOME/admbot.sqlite systemctl daemon-reload systemctl enable mailadm-web mailadm-prune From 079e48008f6bfa2c0540ed60f86befa4f2a65bf8 Mon Sep 17 00:00:00 2001 From: missytake Date: Thu, 12 Aug 2021 19:52:05 +0200 Subject: [PATCH 04/64] refactoring bot.py --- src/mailadm/bot.py | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/src/mailadm/bot.py b/src/mailadm/bot.py index be153ebe..56b8d5e9 100644 --- a/src/mailadm/bot.py +++ b/src/mailadm/bot.py @@ -8,24 +8,23 @@ class AdmBot: @account_hookimpl def ac_incoming_message(self, command): - print("process_incoming message", command) + print("process_incoming message:", command) + chat = command.message.chat if command.text.strip() == "/help": pass elif command.text.strip() == "/add-token": - chat = command.message.chat - if chat.is_group() and int(dbot.get('admgrpid')) == chat.id: - if chat.is_protected() and int(chat.num_contacts) >= 2: - command.create_chat() - text = '' #TODO add Token - command.chat.send_text(text) + if self.check_privileges(chat): + command.create_chat() + arguments = command.text.split(" ") + text = cmdline.add_token(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]) + command.chat.send_text(text) elif command.text.strip() == "/add-user": - chat = command.message.chat - if chat.is_group() and int(dbot.get('admgrpid')) == chat.id: - if chat.is_protected() and int(chat.num_contacts) >= 2: - command.create_chat() - text = '' # TODO add User - command.chat.send_text(text) + if self.check_privileges(chat): + command.create_chat() + arguments = command.text.split(" ") + text = cmdline.add_token(arguments[0], arguments[1], arguments[2]) + command.chat.send_text(text) else: # unconditionally accept the chat command.create_chat() @@ -38,7 +37,21 @@ def ac_incoming_message(self, command): @account_hookimpl def ac_message_delivered(self, message): - print("ac_message_delivered", message) + print("ac_message_delivered:", message) + + def check_privileges(self, chat): + """ + Checks whether the incoming message was in the admin group. + """ + if chat.is_group() and self.admingrpid == chat.id: + if chat.is_protected() and int(chat.num_contacts) >= 2: + return True + else: + print("admin chat is broken. Group ID:" + self.admingrpid) + raise Exception + else: + # reply "This command needs to be sent to the admin group" + return False def main(argv=None): From eb3ed2bb6e27350d415971828c6e7f172ec9783f Mon Sep 17 00:00:00 2001 From: missytake Date: Thu, 12 Aug 2021 20:01:25 +0200 Subject: [PATCH 05/64] removed clutter and unused imports --- src/mailadm/bot.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/mailadm/bot.py b/src/mailadm/bot.py index 56b8d5e9..d389603e 100644 --- a/src/mailadm/bot.py +++ b/src/mailadm/bot.py @@ -1,8 +1,7 @@ -# content of echo_and_quit.py - from deltachat import account_hookimpl, run_cmdline -import mailadm.db -from mailadm import cmdline +from db import DB +import cmdline +import os class AdmBot: From 76800b1e414289f32f82a9a91e6b635d513c3bba Mon Sep 17 00:00:00 2001 From: missytake Date: Thu, 19 Aug 2021 13:59:16 +0200 Subject: [PATCH 06/64] save configuration env variables in a file --- install_mailadm.sh | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) mode change 100644 => 100755 install_mailadm.sh diff --git a/install_mailadm.sh b/install_mailadm.sh old mode 100644 new mode 100755 index d94f978b..73e0baf8 --- a/install_mailadm.sh +++ b/install_mailadm.sh @@ -14,10 +14,10 @@ if [[ $EUID -ne 0 ]]; then exit 1 fi -set -xe - -# modify the following variables -export MAIL_DOMAIN=example.org +SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" +# load variables from config file +if [ ! -f $SCRIPTPATH/.env ]; then + echo "export MAIL_DOMAIN=example.org export VMAIL_USER=vmail export VMAIL_HOME=/home/vmail @@ -28,7 +28,14 @@ export WEB_ENDPOINT=https://example.org/new_email export LOCALHOST_WEB_PORT=3691 export BOT_EMAIL=bot@example.org -export BOT_PASSWORD=p4ssw0rd +export BOT_PASSWORD=p4ssw0rd" > $SCRIPTPATH/.env + echo "Can't get settings from $SCRIPTPATH/.env, please set environment variables there." + exit 1 +else + . ./.env +fi + +set -xe # check if vmail user exists if ! getent passwd $VMAIL_USER > /dev/null 2>&1; then From 93ab826d86976138806f3260d400a1d80c55bfac Mon Sep 17 00:00:00 2001 From: missytake Date: Thu, 19 Aug 2021 14:29:13 +0200 Subject: [PATCH 07/64] fixing import error --- src/mailadm/cmdline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index 0369097b..64659efa 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -24,7 +24,7 @@ import os from deltachat import Account -from db import DB +from .db import DB k.command(cls=click.Group, context_settings=dict(help_option_names=["-h", "--help"])) From b7e305deb49e4cb9b04b5c202fdf38b04440ad6d Mon Sep 17 00:00:00 2001 From: missytake Date: Thu, 19 Aug 2021 15:47:07 +0200 Subject: [PATCH 08/64] fixed cmdline.py; created commands.py to do logic --- src/mailadm/cmdline.py | 34 ++++++++++++++++++++-------------- src/mailadm/commands.py | 12 ++++++++++++ 2 files changed, 32 insertions(+), 14 deletions(-) create mode 100644 src/mailadm/commands.py diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index 64659efa..a32f1e23 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -19,15 +19,27 @@ import argparse import socket import time - -import segno +import pwd +import grp import os +import sys +import click +from click import style +import segno -from deltachat import Account -from .db import DB +import mailadm.db +import mailadm.commands +import mailadm.util +from .conn import DBError + +from deltachat import Account, account_hookimpl -k.command(cls=click.Group, context_settings=dict(help_option_names=["-h", "--help"])) +option_dryrun = click.option( + "-n", "--dryrun", is_flag=True, + help="don't change any files, only show what would be changed.") + +@click.command(cls=click.Group, context_settings=dict(help_option_names=["-h", "--help"])) @click.version_option() @click.pass_context def mailadm_main(context): @@ -94,7 +106,7 @@ def setup_bot(ctx, email): while chat.num_contacts() < 2: time.sleep(1) - db = DB(os.getenv("MAILADM_DB")) + db = mailadm.db.DB(os.getenv("MAILADM_DB")) with db.read_connection() as conn: conn.set_config("admingrpid", chat.id) @@ -155,19 +167,13 @@ def dump_token_info(token_info): help="prefix for all e-mail addresses for this token") @click.option("--token", type=str, default=None, help="name of token to be used") @click.pass_context -def add_token(ctx, name, expiry, prefix, token, maxuse): +def add_token(ctx, name, expiry, maxuse, prefix, token): """add new token for generating new e-mail addresses """ from mailadm.util import get_human_readable_id db = get_mailadm_db(ctx) - if token is None: - token = expiry + "_" + get_human_readable_id(len=15) - with db.write_transaction() as conn: - info = conn.add_token(name=name, token=token, expiry=expiry, - maxuse=maxuse, prefix=prefix) - tc = conn.get_tokeninfo_by_name(info.name) - dump_token_info(tc) + mailadm.commands.add_token(name, expiry, maxuse, prefix, token, db) @click.command() diff --git a/src/mailadm/commands.py b/src/mailadm/commands.py new file mode 100644 index 00000000..4a65620c --- /dev/null +++ b/src/mailadm/commands.py @@ -0,0 +1,12 @@ +from mailadm.util import get_human_readable_id +from mailadm.conn import DBError + + +def add_token(name, expiry, maxuse, prefix, token, db): + if token is None: + token = expiry + "_" + get_human_readable_id(len=15) + with db.write_transaction() as conn: + info = conn.add_token(name=name, token=token, expiry=expiry, + maxuse=maxuse, prefix=prefix) + tc = conn.get_tokeninfo_by_name(info.name) + dump_token_info(tc) From ecf5641bc6877cf5eaaa2da890bf58b680d333f3 Mon Sep 17 00:00:00 2001 From: missytake Date: Thu, 19 Aug 2021 15:49:53 +0200 Subject: [PATCH 09/64] bot: call add_token from commands instead of cmdline --- src/mailadm/bot.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mailadm/bot.py b/src/mailadm/bot.py index d389603e..4e9080c5 100644 --- a/src/mailadm/bot.py +++ b/src/mailadm/bot.py @@ -1,6 +1,6 @@ from deltachat import account_hookimpl, run_cmdline -from db import DB -import cmdline +from mailadm.db import DB +import commands import os @@ -15,14 +15,14 @@ def ac_incoming_message(self, command): if self.check_privileges(chat): command.create_chat() arguments = command.text.split(" ") - text = cmdline.add_token(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]) + text = commands.add_token(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], self.db) command.chat.send_text(text) elif command.text.strip() == "/add-user": if self.check_privileges(chat): command.create_chat() arguments = command.text.split(" ") - text = cmdline.add_token(arguments[0], arguments[1], arguments[2]) + text = cmdline.add_user(arguments[0], arguments[1], arguments[2]) command.chat.send_text(text) else: # unconditionally accept the chat From 4cb057f10b1ff316d5d3dcbd5e7a6f565a5b10e9 Mon Sep 17 00:00:00 2001 From: missytake Date: Thu, 19 Aug 2021 16:07:45 +0200 Subject: [PATCH 10/64] fixing cmdline for now --- src/mailadm/cmdline.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index a32f1e23..6fe36e91 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -71,7 +71,7 @@ def get_mailadm_db(ctx, show=False, fail_missing_config=True): @click.option("--password", type=str, default=None, help="name of password") @click.pass_context @account_hookimpl -def setup_bot(ctx, email): +def setup_bot(ctx, email, password): parser = argparse.ArgumentParser(prog=ctx[0] if ctx else None) parser.add_argument("db", action="store", help="database file") parser.add_argument("--show-ffi", action="store_true", help="show low level ffi events") @@ -106,7 +106,7 @@ def setup_bot(ctx, email): while chat.num_contacts() < 2: time.sleep(1) - db = mailadm.db.DB(os.getenv("MAILADM_DB")) + db = get_mailadm_db(ctx) with db.read_connection() as conn: conn.set_config("admingrpid", chat.id) From 70a7ea94695a425a01996730ea1a92ed09d4dabd Mon Sep 17 00:00:00 2001 From: missytake Date: Thu, 19 Aug 2021 16:42:45 +0200 Subject: [PATCH 11/64] made DB connections more accessible --- src/mailadm/db.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/mailadm/db.py b/src/mailadm/db.py index f61691f3..d658b1ba 100644 --- a/src/mailadm/db.py +++ b/src/mailadm/db.py @@ -8,6 +8,16 @@ from .conn import Connection +def read_connection(): + mailadm_db = DB(get_db_path()) + return mailadm_db.read_connection() + + +def write_connection(): + mailadm_db = DB(get_db_path()) + return mailadm_db.write_transaction() + + def get_db_path(): db_path = os.environ.get("MAILADM_DB", "/mailadm/docker-data/mailadm.db") try: From 4023013209c4c7ad6ab988b023527099cc0472e4 Mon Sep 17 00:00:00 2001 From: missytake Date: Thu, 19 Aug 2021 16:43:13 +0200 Subject: [PATCH 12/64] finishing add_token command --- src/mailadm/commands.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/mailadm/commands.py b/src/mailadm/commands.py index 4a65620c..b7965f11 100644 --- a/src/mailadm/commands.py +++ b/src/mailadm/commands.py @@ -1,12 +1,26 @@ from mailadm.util import get_human_readable_id -from mailadm.conn import DBError +from mailadm.db import write_connection -def add_token(name, expiry, maxuse, prefix, token, db): +def add_token(name, expiry, maxuse, prefix, token): if token is None: token = expiry + "_" + get_human_readable_id(len=15) - with db.write_transaction() as conn: + with write_connection() as conn: info = conn.add_token(name=name, token=token, expiry=expiry, maxuse=maxuse, prefix=prefix) tc = conn.get_tokeninfo_by_name(info.name) - dump_token_info(tc) + return dump_token_info(tc) + + +def dump_token_info(token_info): + """Format token info into a string + """ + return """token: {} + address prefix: {} + accounts expire after: {} + token was used {} of {} times + token: {} + - url: + - + """.format(token_info.name, token_info.prefix, token_info.expiry, token_info.usecount, token_info.maxuse, + token_info.token, token_info.get_web_url(), token_info.get_qr_url()) From 6dfa6d95c770419ae5b1ebc20ce1fdb3dedf57f1 Mon Sep 17 00:00:00 2001 From: missytake Date: Thu, 19 Aug 2021 16:55:33 +0200 Subject: [PATCH 13/64] moving db initializations out of cmdline.py --- src/mailadm/cmdline.py | 68 ++++++++++++++---------------------------- 1 file changed, 23 insertions(+), 45 deletions(-) diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index 6fe36e91..6d588769 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -27,7 +27,7 @@ from click import style import segno -import mailadm.db +from mailadm.db import write_connection, read_connection, get_db_path import mailadm.commands import mailadm.util from .conn import DBError @@ -39,6 +39,7 @@ "-n", "--dryrun", is_flag=True, help="don't change any files, only show what would be changed.") + @click.command(cls=click.Group, context_settings=dict(help_option_names=["-h", "--help"])) @click.version_option() @click.pass_context @@ -60,7 +61,7 @@ def get_mailadm_db(ctx, show=False, fail_missing_config=True): if show: click.secho("using db: {}".format(db_path), file=sys.stderr) if fail_missing_config: - with db.read_connection() as conn: + with read_connection() as conn: if not conn.is_initialized(): ctx.fail("database not initialized, use 'init' subcommand to do so") return db @@ -101,35 +102,29 @@ def setup_bot(ctx, email, password): print("\nPlease scan this qr code to join a verified admin group chat:\n\n") qr.terminal() - print("looping until more than one group member") while chat.num_contacts() < 2: time.sleep(1) - db = get_mailadm_db(ctx) - with db.read_connection() as conn: + with read_connection() as conn: conn.set_config("admingrpid", chat.id) @click.command() -@click.pass_context -def config(ctx): +def config(): """show and manipulate config settings. """ - db = get_mailadm_db(ctx) - with db.read_connection() as conn: + with read_connection() as conn: click.secho("** mailadm version: {}".format(mailadm.__version__)) - click.secho("** mailadm database path: {}".format(db.path)) + click.secho("** mailadm database path: {}".format(get_db_path())) for name, val in conn.get_config_items(): click.secho("{:22s} {}".format(name, val)) @click.command() -@click.pass_context -def list_tokens(ctx): +def list_tokens(): """list available tokens """ - db = get_mailadm_db(ctx) - with db.read_connection() as conn: + with read_connection() as conn: for name in conn.get_token_list(): token_info = conn.get_tokeninfo_by_name(name) dump_token_info(token_info) @@ -137,11 +132,9 @@ def list_tokens(ctx): @click.command() @click.option("--token", type=str, default=None, help="name of token") -@click.pass_context -def list_users(ctx, token): +def list_users(token): """list users """ - db = get_mailadm_db(ctx) - with db.read_connection() as conn: + with read_connection() as conn: for user_info in conn.get_user_list(token=token): click.secho("{} [token={}]".format(user_info.addr, user_info.token_name)) @@ -166,14 +159,10 @@ def dump_token_info(token_info): @click.option("--prefix", type=str, default="tmp.", help="prefix for all e-mail addresses for this token") @click.option("--token", type=str, default=None, help="name of token to be used") -@click.pass_context -def add_token(ctx, name, expiry, maxuse, prefix, token): +def add_token(name, expiry, maxuse, prefix, token): """add new token for generating new e-mail addresses """ - from mailadm.util import get_human_readable_id - - db = get_mailadm_db(ctx) - mailadm.commands.add_token(name, expiry, maxuse, prefix, token, db) + click.secho(mailadm.commands.add_token(name, expiry, maxuse, prefix, token)) @click.command() @@ -184,13 +173,10 @@ def add_token(ctx, name, expiry, maxuse, prefix, token): help="maximum number of accounts this token can create, default is not to change") @click.option("--prefix", type=str, default=None, help="prefix for all e-mail addresses for this token, default is not to change") -@click.pass_context -def mod_token(ctx, name, expiry, prefix, maxuse): +def mod_token(name, expiry, prefix, maxuse): """modify a token selectively """ - db = get_mailadm_db(ctx) - - with db.write_transaction() as conn: + with write_connection() as conn: conn.mod_token(name=name, expiry=expiry, maxuse=maxuse, prefix=prefix) tc = conn.get_tokeninfo_by_name(name) dump_token_info(tc) @@ -198,11 +184,9 @@ def mod_token(ctx, name, expiry, prefix, maxuse): @click.command() @click.argument("name", type=str, required=True) -@click.pass_context -def del_token(ctx, name): +def del_token(name): """remove named token""" - db = get_mailadm_db(ctx) - with db.write_transaction() as conn: + with write_connection() as conn: conn.del_token(name=name) @@ -213,8 +197,7 @@ def gen_qr(ctx, tokenname): """generate qr code image for a token. """ from .gen_qr import gen_qr - db = get_mailadm_db(ctx) - with db.read_connection() as conn: + with read_connection() as conn: token_info = conn.get_tokeninfo_by_name(tokenname) config = conn.config @@ -266,7 +249,7 @@ def init(ctx, web_endpoint, mail_domain, mailcow_endpoint, mailcow_token): def add_user(ctx, addr, password, token): """add user as a mailadm managed account. """ - with get_mailadm_db(ctx).write_transaction() as conn: + with write_connection() as conn: if token is None: if "@" not in addr: ctx.fail("invalid email address: {}".format(addr)) @@ -286,23 +269,18 @@ def add_user(ctx, addr, password, token): @click.command() @click.argument("addr", type=str, required=True) -@click.pass_context -def del_user(ctx, addr): +def del_user(addr): """remove e-mail address""" - with get_mailadm_db(ctx).write_transaction() as conn: - try: - conn.delete_email_account(addr) - except (DBError, MailcowError) as e: - ctx.fail("failed to delete e-mail account {}: {}".format(addr, e)) + with write_connection() as conn: + conn.del_user(addr=addr) @click.command() @option_dryrun -@click.pass_context def prune(ctx, dryrun): """prune expired users from postfix and dovecot configurations """ sysdate = int(time.time()) - with get_mailadm_db(ctx).write_transaction() as conn: + with write_connection() as conn: expired_users = conn.get_expired_users(sysdate) if not expired_users: click.secho("nothing to prune") From 4a9c9dd017078849a5d2205fa6254a6b198e15d8 Mon Sep 17 00:00:00 2001 From: missytake Date: Thu, 19 Aug 2021 17:31:44 +0200 Subject: [PATCH 14/64] move add_user to commands.py --- src/mailadm/bot.py | 6 +++--- src/mailadm/cmdline.py | 22 +++++----------------- src/mailadm/commands.py | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 20 deletions(-) diff --git a/src/mailadm/bot.py b/src/mailadm/bot.py index 4e9080c5..3c81097e 100644 --- a/src/mailadm/bot.py +++ b/src/mailadm/bot.py @@ -1,6 +1,6 @@ from deltachat import account_hookimpl, run_cmdline from mailadm.db import DB -import commands +from mailadm.commands import add_user, add_token import os @@ -15,14 +15,14 @@ def ac_incoming_message(self, command): if self.check_privileges(chat): command.create_chat() arguments = command.text.split(" ") - text = commands.add_token(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], self.db) + text = add_token(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]) command.chat.send_text(text) elif command.text.strip() == "/add-user": if self.check_privileges(chat): command.create_chat() arguments = command.text.split(" ") - text = cmdline.add_user(arguments[0], arguments[1], arguments[2]) + text = add_user(arguments[0], arguments[1], arguments[2]) command.chat.send_text(text) else: # unconditionally accept the chat diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index 6d588769..8a71761a 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -110,7 +110,6 @@ def setup_bot(ctx, email, password): conn.set_config("admingrpid", chat.id) - @click.command() def config(): """show and manipulate config settings. """ @@ -249,22 +248,11 @@ def init(ctx, web_endpoint, mail_domain, mailcow_endpoint, mailcow_token): def add_user(ctx, addr, password, token): """add user as a mailadm managed account. """ - with write_connection() as conn: - if token is None: - if "@" not in addr: - ctx.fail("invalid email address: {}".format(addr)) - - token_info = conn.get_tokeninfo_by_addr(addr) - if token_info is None: - ctx.fail("could not determine token for addr: {!r}".format(addr)) - else: - token_info = conn.get_tokeninfo_by_name(token) - if token_info is None: - ctx.fail("token does not exist: {!r}".format(token)) - try: - conn.add_email_account_tries(token_info, addr=addr, password=password, tries=1) - except (DBError, MailcowError) as e: - ctx.fail("failed to add e-mail account %s: %s" % (addr, e)) + result = mailadm.commands.add_user(token, addr, password) + if result["status"] == "error": + ctx.fail(result["message"]) + elif result["status"] == "success": + click.secho("Created {} with password: {}".format(result["message"].addr, result["message"].clear_pw)) @click.command() diff --git a/src/mailadm/commands.py b/src/mailadm/commands.py index b7965f11..4a8ffdd3 100644 --- a/src/mailadm/commands.py +++ b/src/mailadm/commands.py @@ -1,8 +1,11 @@ from mailadm.util import get_human_readable_id from mailadm.db import write_connection +from mailadm.conn import DBError def add_token(name, expiry, maxuse, prefix, token): + """Adds a token to create users + """ if token is None: token = expiry + "_" + get_human_readable_id(len=15) with write_connection() as conn: @@ -12,6 +15,36 @@ def add_token(name, expiry, maxuse, prefix, token): return dump_token_info(tc) +def add_user(token=None, addr=None, password=None): + """Adds a new user to be managed by mailadm + """ + with write_connection() as conn: + if token is None: + if "@" not in addr: + # there is probably a more pythonic solution to this. + # the goal is to display the error, whether the command came via CLI or delta bot. + return {"status": "error", + "message": "invalid email address: {}".format(addr)} + + token_info = conn.get_tokeninfo_by_addr(addr) + if token_info is None: + return {"status": "error", + "message": "could not determine token for addr: {!r}".format(addr)} + else: + token_info = conn.get_tokeninfo_by_name(token) + if token_info is None: + return {"status": "error", + "message": "token does not exist: {!r}".format(token)} + try: + user_info = conn.add_email_account(token_info, addr=addr, password=password) + except DBError as e: + return {"status": "error", + "message": "failed to add e-mail account {}: {}".format(addr, e)} + conn.gen_sysfiles() + return {"status": "success", + "message": user_info} + + def dump_token_info(token_info): """Format token info into a string """ From 6ea75d91d5a3c61992708a9b8598520561c838cd Mon Sep 17 00:00:00 2001 From: missytake Date: Fri, 20 Aug 2021 15:11:43 +0200 Subject: [PATCH 15/64] add setup-bot to CLI --- src/mailadm/cmdline.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index 8a71761a..7cbb0ed0 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -331,6 +331,7 @@ def migrate_db(ctx): conn.execute(q, ("path_virtual_mailboxes",)) +mailadm_main.add_command(setup_bot) mailadm_main.add_command(init) mailadm_main.add_command(config) mailadm_main.add_command(list_tokens) From 521e6cf8ecffda7ce31b2462d416e6ff430c7afa Mon Sep 17 00:00:00 2001 From: missytake Date: Fri, 20 Aug 2021 15:21:18 +0200 Subject: [PATCH 16/64] handle CLI options with click instead of argparse --- src/mailadm/cmdline.py | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index 7cbb0ed0..09a6e8e0 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -6,21 +6,13 @@ from __future__ import print_function -import time -import sys -import click -from click import style - import mailadm import mailadm.db from .conn import DBError, UserInfo from .mailcow import MailcowError import mailadm.util -import argparse import socket import time -import pwd -import grp import os import sys import click @@ -70,25 +62,19 @@ def get_mailadm_db(ctx, show=False, fail_missing_config=True): @click.command() @click.option("--email", type=str, default=None, help="name of email") @click.option("--password", type=str, default=None, help="name of password") +@click.option("db", type=str, default=os.getenv("MAILADM_HOME") + "/admbot.sqlite", + help="Delta Chat database for admbot account") @click.pass_context @account_hookimpl -def setup_bot(ctx, email, password): - parser = argparse.ArgumentParser(prog=ctx[0] if ctx else None) - parser.add_argument("db", action="store", help="database file") - parser.add_argument("--show-ffi", action="store_true", help="show low level ffi events") - parser.add_argument("--email", action="store", help="email address") - parser.add_argument("--password", action="store", help="password") - - args = parser.parse_args(ctx[1:]) - - ac = Account(args.db) +def setup_bot(ctx, email, password, db): + ac = Account(db) if not ac.is_configured(): - assert args.email and args.password, ( + assert email and password, ( "you must specify --email and --password once to configure this database/account" ) - ac.set_config("addr", args.email) - ac.set_config("mail_pw", args.password) + ac.set_config("addr", email) + ac.set_config("mail_pw", password) ac.set_config("mvbox_move", "0") ac.set_config("mvbox_watch", "0") ac.set_config("sentbox_watch", "0") From ac9fb42dff14230ddb4088dd9b00bbbd601c6345 Mon Sep 17 00:00:00 2001 From: missytake Date: Fri, 20 Aug 2021 15:23:25 +0200 Subject: [PATCH 17/64] fixing click error --- install_mailadm.sh | 2 +- src/mailadm/cmdline.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/install_mailadm.sh b/install_mailadm.sh index 73e0baf8..15dd4097 100755 --- a/install_mailadm.sh +++ b/install_mailadm.sh @@ -78,7 +78,7 @@ $MAILADM_HOME/venv/bin/mailadm gen-sysconfig \ $MAILADM_HOME/venv/bin/mailadm setup-bot \ --email $BOT_EMAIL \ --password $BOT_PASSWORD \ - $MAILADM_HOME/admbot.sqlite + --db $MAILADM_HOME/admbot.sqlite systemctl daemon-reload systemctl enable mailadm-web mailadm-prune diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index 09a6e8e0..f259a190 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -60,10 +60,10 @@ def get_mailadm_db(ctx, show=False, fail_missing_config=True): @click.command() +@click.option("--db", type=str, default=os.getenv("MAILADM_HOME") + "/admbot.sqlite", + help="Delta Chat database for admbot account", required=True) @click.option("--email", type=str, default=None, help="name of email") @click.option("--password", type=str, default=None, help="name of password") -@click.option("db", type=str, default=os.getenv("MAILADM_HOME") + "/admbot.sqlite", - help="Delta Chat database for admbot account") @click.pass_context @account_hookimpl def setup_bot(ctx, email, password, db): From dd6986ab8e7e92a1df95f4dda625e7bfac6e2407 Mon Sep 17 00:00:00 2001 From: missytake Date: Fri, 20 Aug 2021 15:42:45 +0200 Subject: [PATCH 18/64] actually start and stop the bot during setup --- src/mailadm/cmdline.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index f259a190..f25438ab 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -81,6 +81,7 @@ def setup_bot(ctx, email, password, db): ac.set_config("bot", "1") configtracker = ac.configure() configtracker.wait_finish() + ac.start_io() chat = ac.create_group_chat("Admin group on {}".format(socket.gethostname()), contacts=[], verified=True) @@ -92,6 +93,7 @@ def setup_bot(ctx, email, password, db): while chat.num_contacts() < 2: time.sleep(1) + ac.wait_shutdown() with read_connection() as conn: conn.set_config("admingrpid", chat.id) From 18a5c59f2d0dda88d20cd6e8a13d1d210a74a8b9 Mon Sep 17 00:00:00 2001 From: missytake Date: Fri, 20 Aug 2021 15:50:42 +0200 Subject: [PATCH 19/64] Enable joining by copy-pasting the invite --- src/mailadm/cmdline.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index f25438ab..29f01854 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -85,11 +85,13 @@ def setup_bot(ctx, email, password, db): chat = ac.create_group_chat("Admin group on {}".format(socket.gethostname()), contacts=[], verified=True) - qr = segno.make(chat.get_join_qr()) + chatinvite = chat.get_join_qr() + qr = segno.make(chatinvite) print("\nPlease scan this qr code to join a verified admin group chat:\n\n") qr.terminal() + print("\nAlternatively, copy-paste this invite to your Delta Chat desktop client:", chatinvite) - print("looping until more than one group member") + print("\n\nWaiting until you join the chat") while chat.num_contacts() < 2: time.sleep(1) From cbba24b40c9fd4db7c27af62ff76a577ca006a4a Mon Sep 17 00:00:00 2001 From: missytake Date: Fri, 20 Aug 2021 16:22:20 +0200 Subject: [PATCH 20/64] properly shutdown the bot after setup worked --- src/mailadm/cmdline.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index 29f01854..2f7f3888 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -91,11 +91,11 @@ def setup_bot(ctx, email, password, db): qr.terminal() print("\nAlternatively, copy-paste this invite to your Delta Chat desktop client:", chatinvite) - print("\n\nWaiting until you join the chat") + print("\nWaiting until you join the chat") while chat.num_contacts() < 2: time.sleep(1) - ac.wait_shutdown() + ac.shutdown() with read_connection() as conn: conn.set_config("admingrpid", chat.id) From 92a86a8be2e7923c4f7c705e5cd3ba4d1bccd8c1 Mon Sep 17 00:00:00 2001 From: missytake Date: Mon, 20 Sep 2021 13:35:52 +0200 Subject: [PATCH 21/64] enable logging for setup_bot --- src/mailadm/cmdline.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index 2f7f3888..d7a89630 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -81,7 +81,9 @@ def setup_bot(ctx, email, password, db): ac.set_config("bot", "1") configtracker = ac.configure() configtracker.wait_finish() - ac.start_io() + + ac.enable_logging() + ac.start_io() chat = ac.create_group_chat("Admin group on {}".format(socket.gethostname()), contacts=[], verified=True) From a27c62ac95edc45572b5a9ec33571f55ad47ee6e Mon Sep 17 00:00:00 2001 From: missytake Date: Mon, 20 Sep 2021 13:39:32 +0200 Subject: [PATCH 22/64] allow to set adminrpid as valid config parameter --- src/mailadm/conn.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mailadm/conn.py b/src/mailadm/conn.py index 9d5f8258..d11323dc 100644 --- a/src/mailadm/conn.py +++ b/src/mailadm/conn.py @@ -85,7 +85,8 @@ def get_config_items(self): return None def set_config(self, name, value): - ok = ["dbversion", "mail_domain", "web_endpoint", "mailcow_endpoint", "mailcow_token"] + ok = ["dbversion", "mail_domain", "web_endpoint", "mailcow_endpoint", "mailcow_token", + "admingrpid"] assert name in ok, name q = "INSERT OR REPLACE INTO config (name, value) VALUES (?, ?)" self.cursor().execute(q, (name, value)).fetchone() From d496a98a8a39b416b477e42f7f6fed2299f87cf5 Mon Sep 17 00:00:00 2001 From: missytake Date: Mon, 20 Sep 2021 13:41:24 +0200 Subject: [PATCH 23/64] db connection needs to be writable --- src/mailadm/cmdline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index d7a89630..df08b315 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -98,7 +98,7 @@ def setup_bot(ctx, email, password, db): time.sleep(1) ac.shutdown() - with read_connection() as conn: + with write_connection() as conn: conn.set_config("admingrpid", chat.id) From 74d655172b6351d79aebabdf614f4c70393174d3 Mon Sep 17 00:00:00 2001 From: missytake Date: Mon, 20 Sep 2021 14:39:15 +0200 Subject: [PATCH 24/64] we don't reaaally need logging at that point, now that it works. --- src/mailadm/cmdline.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index df08b315..9399d936 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -82,7 +82,6 @@ def setup_bot(ctx, email, password, db): configtracker = ac.configure() configtracker.wait_finish() - ac.enable_logging() ac.start_io() chat = ac.create_group_chat("Admin group on {}".format(socket.gethostname()), contacts=[], verified=True) From aff29bf614bdd0c03b3b61d7a69fb7e886877c4f Mon Sep 17 00:00:00 2001 From: missytake Date: Sun, 24 Oct 2021 12:38:19 +0200 Subject: [PATCH 25/64] fix dryrun-option for add_user CLI --- src/mailadm/cmdline.py | 5 +++-- src/mailadm/commands.py | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index 9399d936..2fdde552 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -235,11 +235,12 @@ def init(ctx, web_endpoint, mail_domain, mailcow_endpoint, mailcow_token): help="if not specified, generate a random password") @click.option("--token", type=str, default=None, help="name of token. if not specified, automatically use first token matching addr") +@option_dryrun @click.pass_context -def add_user(ctx, addr, password, token): +def add_user(ctx, addr, password, token, dryrun): """add user as a mailadm managed account. """ - result = mailadm.commands.add_user(token, addr, password) + result = mailadm.commands.add_user(token, addr, password, dryrun) if result["status"] == "error": ctx.fail(result["message"]) elif result["status"] == "success": diff --git a/src/mailadm/commands.py b/src/mailadm/commands.py index 4a8ffdd3..9eb6ce6e 100644 --- a/src/mailadm/commands.py +++ b/src/mailadm/commands.py @@ -15,7 +15,7 @@ def add_token(name, expiry, maxuse, prefix, token): return dump_token_info(tc) -def add_user(token=None, addr=None, password=None): +def add_user(token=None, addr=None, password=None, dryrun=False): """Adds a new user to be managed by mailadm """ with write_connection() as conn: @@ -40,7 +40,7 @@ def add_user(token=None, addr=None, password=None): except DBError as e: return {"status": "error", "message": "failed to add e-mail account {}: {}".format(addr, e)} - conn.gen_sysfiles() + conn.gen_sysfiles(dryrun) return {"status": "success", "message": user_info} From 60ce9c38cbf3ab0cd24b839204d49c6d9b8215bd Mon Sep 17 00:00:00 2001 From: missytake Date: Sun, 24 Oct 2021 13:28:24 +0200 Subject: [PATCH 26/64] finishing dump_token_info --- src/mailadm/commands.py | 6 +++--- tox.ini | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mailadm/commands.py b/src/mailadm/commands.py index 9eb6ce6e..fe624e01 100644 --- a/src/mailadm/commands.py +++ b/src/mailadm/commands.py @@ -53,7 +53,7 @@ def dump_token_info(token_info): accounts expire after: {} token was used {} of {} times token: {} - - url: - - + - url: {} + - QR data: {} """.format(token_info.name, token_info.prefix, token_info.expiry, token_info.usecount, token_info.maxuse, - token_info.token, token_info.get_web_url(), token_info.get_qr_url()) + token_info.token, token_info.get_web_url(), token_info.get_qr_uri()) diff --git a/tox.ini b/tox.ini index 93492290..2e9b8bbf 100644 --- a/tox.ini +++ b/tox.ini @@ -33,7 +33,7 @@ deps = restructuredtext_lint commands = rst-lint README.rst CHANGELOG.rst - flake8 --ignore=E128,E127,E126 --max-line-length 100 src/mailadm tests/ + flake8 --ignore=E128,E127,E126 --max-line-length 120 src/mailadm tests/ [testenv:check-manifest] skip_install = True From 880973ab0156d7103d37404cc2fdfda0a23fcb2e Mon Sep 17 00:00:00 2001 From: missytake Date: Sun, 24 Oct 2021 13:29:25 +0200 Subject: [PATCH 27/64] replacing segno with qrcode during bot setup --- src/mailadm/cmdline.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index 2fdde552..9177caec 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -17,7 +17,7 @@ import sys import click from click import style -import segno +import qrcode from mailadm.db import write_connection, read_connection, get_db_path import mailadm.commands @@ -87,9 +87,10 @@ def setup_bot(ctx, email, password, db): chat = ac.create_group_chat("Admin group on {}".format(socket.gethostname()), contacts=[], verified=True) chatinvite = chat.get_join_qr() - qr = segno.make(chatinvite) + qr = qrcode.QRCode() + qr.add_data(chatinvite) print("\nPlease scan this qr code to join a verified admin group chat:\n\n") - qr.terminal() + qr.print_ascii(invert=True) print("\nAlternatively, copy-paste this invite to your Delta Chat desktop client:", chatinvite) print("\nWaiting until you join the chat") From 0affa3bb2088dedb1000f0dfe9cfc2704ec68e76 Mon Sep 17 00:00:00 2001 From: missytake Date: Sun, 24 Oct 2021 18:07:36 +0200 Subject: [PATCH 28/64] moved list_tokens to commands.py --- src/mailadm/cmdline.py | 5 +---- src/mailadm/commands.py | 13 ++++++++++++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index 9177caec..fde0d883 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -115,10 +115,7 @@ def config(): @click.command() def list_tokens(): """list available tokens """ - with read_connection() as conn: - for name in conn.get_token_list(): - token_info = conn.get_tokeninfo_by_name(name) - dump_token_info(token_info) + click.secho(mailadm.commands.list_tokens()) @click.command() diff --git a/src/mailadm/commands.py b/src/mailadm/commands.py index fe624e01..0d9258fe 100644 --- a/src/mailadm/commands.py +++ b/src/mailadm/commands.py @@ -1,5 +1,5 @@ from mailadm.util import get_human_readable_id -from mailadm.db import write_connection +from mailadm.db import write_connection, read_connection from mailadm.conn import DBError @@ -45,6 +45,17 @@ def add_user(token=None, addr=None, password=None, dryrun=False): "message": user_info} +def list_tokens(): + """Print token info for all tokens + """ + output = [] + with read_connection() as conn: + for name in conn.get_token_list(): + token_info = conn.get_tokeninfo_by_name(name) + output.append(dump_token_info(token_info)) + return '\n'.join(output) + + def dump_token_info(token_info): """Format token info into a string """ From 8d48528832e1a06f5857b14fbb3c9bb11422ee0b Mon Sep 17 00:00:00 2001 From: missytake Date: Sun, 24 Oct 2021 18:08:10 +0200 Subject: [PATCH 29/64] tests: add_token has different output now --- tests/test_cmdline.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/test_cmdline.py b/tests/test_cmdline.py index 797cceea..c1b0b5c9 100644 --- a/tests/test_cmdline.py +++ b/tests/test_cmdline.py @@ -79,12 +79,11 @@ def test_tokens_add(self, mycmd, i): *DCACCOUNT*&n=test1 """) out = mycmd.run_ok(["list-tokens"], """ - *maxuse*50* - *usecount* + *of 50 times* *DCACCOUNT*&n=test1 """) for line in out.splitlines(): - parts = line.split("=") + parts = line.split(":") if len(parts) >= 2 and parts[0].strip() == "token": token = parts[1].strip().replace("_", "") assert token.isalnum() @@ -100,16 +99,16 @@ def test_tokens_add(self, mycmd, i): def test_tokens_add_maxuse(self, mycmd): mycmd.run_ok(["add-token", "test1", "--maxuse=10"], """ - *maxuse*10 + *of 10 times* *DCACCOUNT*&n=test1 """) mycmd.run_ok(["list-tokens"], """ - *maxuse*10* + *of 10 times* *DCACCOUNT*&n=test1 """) mycmd.run_ok(["mod-token", "--maxuse=1000", "test1"]) mycmd.run_ok(["list-tokens"], """ - *maxuse*1000* + *of 1000 times* *DCACCOUNT*&n=test1 """) From ada7b5bbb019fa23e777a82b987e12779ff86f09 Mon Sep 17 00:00:00 2001 From: missytake Date: Sun, 24 Oct 2021 18:27:16 +0200 Subject: [PATCH 30/64] fix crash when MAILADM_DB is not set --- src/mailadm/cmdline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index fde0d883..cfb511ee 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -60,7 +60,7 @@ def get_mailadm_db(ctx, show=False, fail_missing_config=True): @click.command() -@click.option("--db", type=str, default=os.getenv("MAILADM_HOME") + "/admbot.sqlite", +@click.option("--db", type=str, default=str(os.getenv("MAILADM_HOME")) + "/admbot.sqlite", help="Delta Chat database for admbot account", required=True) @click.option("--email", type=str, default=None, help="name of email") @click.option("--password", type=str, default=None, help="name of password") From 04dba83d6f17cdae726d7dd724d0f46988318c7e Mon Sep 17 00:00:00 2001 From: missytake Date: Sun, 24 Oct 2021 18:27:48 +0200 Subject: [PATCH 31/64] apparently run_fail can't parse runtimeErrors :/ --- tests/test_cmdline.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/test_cmdline.py b/tests/test_cmdline.py index c1b0b5c9..7a11f720 100644 --- a/tests/test_cmdline.py +++ b/tests/test_cmdline.py @@ -1,5 +1,6 @@ import os from random import randint +import sys import time import datetime import pytest @@ -60,9 +61,11 @@ def test_gen_qr_no_token(self, mycmd, tmpdir, monkeypatch): class TestTokens: def test_uninitialized(self, cmd): - cmd.run_fail(["list-tokens"], """ - *MAILADM_DB not set* - """) + try: + cmd.run_ok(["list-tokens"]) + except RuntimeError: + if "MAILADM_DB not set" not in sys.exc_info()[1].__str__(): + assert 0 def test_tokens(self, mycmd): mycmd.run_ok(["add-token", "oneweek", "--token=1w_Zeeg1RSOK4e3Nh0V", From ae70341504bcf05cf719ba233901431dc5b63b24 Mon Sep 17 00:00:00 2001 From: missytake Date: Sun, 24 Oct 2021 18:38:52 +0200 Subject: [PATCH 32/64] document development environment setup --- README.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.rst b/README.rst index 9565043e..0af680a1 100644 --- a/README.rst +++ b/README.rst @@ -23,3 +23,17 @@ setup & usage ------------- See the `docs/index.rst` file or https://mailadm.readthedocs.io for more info. + +Setup Development Environment +----------------------------- + +``` +git clone https://github.com/deltachat/mailadm +python3 -m venv venv +. venv/bin/activate +pip install pytest tox +pip install . +sudo apt install postfix # choose "No configuration" when asked +sudo systemctl disable postfix +sudo touch /etc/postfix/main.cf +``` From 66ecf1335418752a2c3cf303b573907a41a84738 Mon Sep 17 00:00:00 2001 From: missytake Date: Mon, 25 Oct 2021 19:15:29 +0200 Subject: [PATCH 33/64] don't activate services if running in docker --- install_mailadm.sh | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/install_mailadm.sh b/install_mailadm.sh index 15dd4097..f6b89529 100755 --- a/install_mailadm.sh +++ b/install_mailadm.sh @@ -80,6 +80,10 @@ $MAILADM_HOME/venv/bin/mailadm setup-bot \ --password $BOT_PASSWORD \ --db $MAILADM_HOME/admbot.sqlite -systemctl daemon-reload -systemctl enable mailadm-web mailadm-prune -systemctl restart mailadm-web mailadm-prune +# don't enable services if running in docker +if [ $(cat /proc/1/comm) = "systemd" ] +then + systemctl daemon-reload + systemctl enable mailadm-web mailadm-prune + systemctl restart mailadm-web mailadm-prune +fi From cd3549c431e6ebbf4d452e5b3329ed20d12c2f88 Mon Sep 17 00:00:00 2001 From: missytake Date: Mon, 25 Oct 2021 19:23:33 +0200 Subject: [PATCH 34/64] try to wait for a few seconds to complete the group invite --- src/mailadm/cmdline.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index cfb511ee..ebbc34d9 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -97,6 +97,8 @@ def setup_bot(ctx, email, password, db): while chat.num_contacts() < 2: time.sleep(1) + time.sleep(5) + ac.shutdown() with write_connection() as conn: conn.set_config("admingrpid", chat.id) From 0e32d2dfde5938e93208495220e73aa86361b960 Mon Sep 17 00:00:00 2001 From: missytake Date: Mon, 25 Oct 2021 19:47:49 +0200 Subject: [PATCH 35/64] bot: process /list-tokens command --- src/mailadm/bot.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/mailadm/bot.py b/src/mailadm/bot.py index 3c81097e..01f8025d 100644 --- a/src/mailadm/bot.py +++ b/src/mailadm/bot.py @@ -1,6 +1,6 @@ from deltachat import account_hookimpl, run_cmdline from mailadm.db import DB -from mailadm.commands import add_user, add_token +from mailadm.commands import add_user, add_token, list_tokens import os @@ -24,6 +24,12 @@ def ac_incoming_message(self, command): arguments = command.text.split(" ") text = add_user(arguments[0], arguments[1], arguments[2]) command.chat.send_text(text) + + elif command.text.strip() == "/list-tokens": + if self.check_privileges(chat): + command.create_chat() + command.chat.send_text(list_tokens()) + else: # unconditionally accept the chat command.create_chat() From 5f11d33f5f2677abded2a7e5ca4103041944223f Mon Sep 17 00:00:00 2001 From: missytake Date: Wed, 27 Oct 2021 17:10:52 +0200 Subject: [PATCH 36/64] bot: added /list-tokens to /help --- src/mailadm/bot.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/mailadm/bot.py b/src/mailadm/bot.py index 01f8025d..ecf1a2a5 100644 --- a/src/mailadm/bot.py +++ b/src/mailadm/bot.py @@ -10,7 +10,12 @@ def ac_incoming_message(self, command): print("process_incoming message:", command) chat = command.message.chat if command.text.strip() == "/help": - pass + command.create_chat() + text = ("/add-token name expiry prefix token maxuse" + "/add-user addr password token" + "/list-tokens") + command.chat.send_text(text) + elif command.text.strip() == "/add-token": if self.check_privileges(chat): command.create_chat() From 094364b8396bd4483650b5c18dee11fb851aecc5 Mon Sep 17 00:00:00 2001 From: missytake Date: Wed, 27 Oct 2021 17:22:05 +0200 Subject: [PATCH 37/64] notify admin group if it is overwritten by setup_bot --- src/mailadm/cmdline.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index ebbc34d9..61f9fdb5 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -100,6 +100,11 @@ def setup_bot(ctx, email, password, db): time.sleep(5) ac.shutdown() + with read_connection() as conn: + admingrpid_old = conn.config().admingrpid + if admingrpid_old: + oldgroup = ac.get_chat_by_id(admingrpid_old) + oldgroup.send_text("Someone created a new admin group on the command line. This one is not valid anymore.") with write_connection() as conn: conn.set_config("admingrpid", chat.id) From 28e69cbc5817f50bacad6fbedfad7bb09a582f24 Mon Sep 17 00:00:00 2001 From: missytake Date: Wed, 27 Oct 2021 20:37:29 +0200 Subject: [PATCH 38/64] setup bot: properly wait until member is added --- src/mailadm/cmdline.py | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index 61f9fdb5..94b507f2 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -18,6 +18,7 @@ import click from click import style import qrcode +from asyncio import Queue from mailadm.db import write_connection, read_connection, get_db_path import mailadm.commands @@ -68,6 +69,8 @@ def get_mailadm_db(ctx, show=False, fail_missing_config=True): @account_hookimpl def setup_bot(ctx, email, password, db): ac = Account(db) + q = Queue() + ac.add_account_plugin(EventWaiter(ac, q)) if not ac.is_configured(): assert email and password, ( @@ -94,17 +97,15 @@ def setup_bot(ctx, email, password, db): print("\nAlternatively, copy-paste this invite to your Delta Chat desktop client:", chatinvite) print("\nWaiting until you join the chat") - while chat.num_contacts() < 2: - time.sleep(1) + contact = q.get() - time.sleep(5) - - ac.shutdown() with read_connection() as conn: - admingrpid_old = conn.config().admingrpid + admingrpid_old = conn.config.admingrpid if admingrpid_old: oldgroup = ac.get_chat_by_id(admingrpid_old) - oldgroup.send_text("Someone created a new admin group on the command line. This one is not valid anymore.") + oldgroup.send_text("%s created a new admin group on the command line. This one is not valid anymore.", + (contact.addr,)) + ac.shutdown() with write_connection() as conn: conn.set_config("admingrpid", chat.id) @@ -328,6 +329,21 @@ def migrate_db(ctx): conn.execute(q, ("path_virtual_mailboxes",)) +class EventWaiter: + def __init__(self, account, queue=Queue(), timeout=None): + self.account = account + self._event_queue = queue + self._timeout = timeout + + @account_hookimpl + def ac_member_added(self, chat, contact, actor, message): + print("ac_member_added {} to chat {} from {}".format( + contact.addr, chat.id, actor or message.get_sender_contact().addr)) + for member in chat.get_contacts(): + print("chat member: {}".format(member.addr)) + self._event_queue.put(contact) + + mailadm_main.add_command(setup_bot) mailadm_main.add_command(init) mailadm_main.add_command(config) From 37f741932d0603229ad1bf33347df80c17d86bfd Mon Sep 17 00:00:00 2001 From: missytake Date: Wed, 27 Oct 2021 21:00:57 +0200 Subject: [PATCH 39/64] queue.get() is an async function --- src/mailadm/cmdline.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index 94b507f2..738a24e4 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -67,7 +67,7 @@ def get_mailadm_db(ctx, show=False, fail_missing_config=True): @click.option("--password", type=str, default=None, help="name of password") @click.pass_context @account_hookimpl -def setup_bot(ctx, email, password, db): +async def setup_bot(ctx, email, password, db): ac = Account(db) q = Queue() ac.add_account_plugin(EventWaiter(ac, q)) @@ -97,7 +97,7 @@ def setup_bot(ctx, email, password, db): print("\nAlternatively, copy-paste this invite to your Delta Chat desktop client:", chatinvite) print("\nWaiting until you join the chat") - contact = q.get() + contact = await q.get() with read_connection() as conn: admingrpid_old = conn.config.admingrpid From 7c4f107b95619511f7cac2cb6db1d0c17e015b3e Mon Sep 17 00:00:00 2001 From: missytake Date: Wed, 27 Oct 2021 21:08:35 +0200 Subject: [PATCH 40/64] Revert "setup bot: properly wait until member is added" This reverts commit 3dafbdf8bcd2327c5f3cb9d90137f43f198a388c. --- src/mailadm/cmdline.py | 32 ++++++++------------------------ 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index 738a24e4..61f9fdb5 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -18,7 +18,6 @@ import click from click import style import qrcode -from asyncio import Queue from mailadm.db import write_connection, read_connection, get_db_path import mailadm.commands @@ -67,10 +66,8 @@ def get_mailadm_db(ctx, show=False, fail_missing_config=True): @click.option("--password", type=str, default=None, help="name of password") @click.pass_context @account_hookimpl -async def setup_bot(ctx, email, password, db): +def setup_bot(ctx, email, password, db): ac = Account(db) - q = Queue() - ac.add_account_plugin(EventWaiter(ac, q)) if not ac.is_configured(): assert email and password, ( @@ -97,15 +94,17 @@ async def setup_bot(ctx, email, password, db): print("\nAlternatively, copy-paste this invite to your Delta Chat desktop client:", chatinvite) print("\nWaiting until you join the chat") - contact = await q.get() + while chat.num_contacts() < 2: + time.sleep(1) + time.sleep(5) + + ac.shutdown() with read_connection() as conn: - admingrpid_old = conn.config.admingrpid + admingrpid_old = conn.config().admingrpid if admingrpid_old: oldgroup = ac.get_chat_by_id(admingrpid_old) - oldgroup.send_text("%s created a new admin group on the command line. This one is not valid anymore.", - (contact.addr,)) - ac.shutdown() + oldgroup.send_text("Someone created a new admin group on the command line. This one is not valid anymore.") with write_connection() as conn: conn.set_config("admingrpid", chat.id) @@ -329,21 +328,6 @@ def migrate_db(ctx): conn.execute(q, ("path_virtual_mailboxes",)) -class EventWaiter: - def __init__(self, account, queue=Queue(), timeout=None): - self.account = account - self._event_queue = queue - self._timeout = timeout - - @account_hookimpl - def ac_member_added(self, chat, contact, actor, message): - print("ac_member_added {} to chat {} from {}".format( - contact.addr, chat.id, actor or message.get_sender_contact().addr)) - for member in chat.get_contacts(): - print("chat member: {}".format(member.addr)) - self._event_queue.put(contact) - - mailadm_main.add_command(setup_bot) mailadm_main.add_command(init) mailadm_main.add_command(config) From 0353eac54955d3520284f13492b9b39d22931e52 Mon Sep 17 00:00:00 2001 From: missytake Date: Wed, 27 Oct 2021 21:11:10 +0200 Subject: [PATCH 41/64] comment: properly waiting for member_added doesn't work --- src/mailadm/cmdline.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index 61f9fdb5..14236373 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -97,6 +97,7 @@ def setup_bot(ctx, email, password, db): while chat.num_contacts() < 2: time.sleep(1) + # it would be nicer to properly wait for the member_added event, but this function isn't async, so it doesn't work. time.sleep(5) ac.shutdown() From 26dd05fefca0fdf6d7736fcf7174aabf5fa01649 Mon Sep 17 00:00:00 2001 From: missytake Date: Wed, 27 Oct 2021 21:13:39 +0200 Subject: [PATCH 42/64] flush stdout to show print statements --- src/mailadm/cmdline.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index 14236373..b212fc1e 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -94,6 +94,7 @@ def setup_bot(ctx, email, password, db): print("\nAlternatively, copy-paste this invite to your Delta Chat desktop client:", chatinvite) print("\nWaiting until you join the chat") + sys.stdout.flush() # flush stdout to actually show the messages above while chat.num_contacts() < 2: time.sleep(1) From 19268f34a7d0dbe2667d7e62a0421da4cee9f2c8 Mon Sep 17 00:00:00 2001 From: missytake Date: Wed, 27 Oct 2021 21:18:37 +0200 Subject: [PATCH 43/64] config is an object, not a class --- src/mailadm/cmdline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index b212fc1e..aceb52c1 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -103,7 +103,7 @@ def setup_bot(ctx, email, password, db): ac.shutdown() with read_connection() as conn: - admingrpid_old = conn.config().admingrpid + admingrpid_old = conn.config.admingrpid if admingrpid_old: oldgroup = ac.get_chat_by_id(admingrpid_old) oldgroup.send_text("Someone created a new admin group on the command line. This one is not valid anymore.") From f6d50baba6f1b6778aa4207e99eb1497723cc54d Mon Sep 17 00:00:00 2001 From: missytake Date: Wed, 27 Oct 2021 22:00:39 +0200 Subject: [PATCH 44/64] readability: default settings are written to .env --- install_mailadm.sh | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/install_mailadm.sh b/install_mailadm.sh index f6b89529..afef3869 100755 --- a/install_mailadm.sh +++ b/install_mailadm.sh @@ -17,18 +17,19 @@ fi SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" # load variables from config file if [ ! -f $SCRIPTPATH/.env ]; then - echo "export MAIL_DOMAIN=example.org + echo "export MAIL_DOMAIN=example.org" > $SCRIPTPATH/.env + echo "" >> $SCRIPTPATH/.env + echo "export VMAIL_USER=vmail" >> $SCRIPTPATH/.env + echo "export VMAIL_HOME=/home/vmail" >> $SCRIPTPATH/.env + echo "export MAILADM_USER=mailadm" >> $SCRIPTPATH/.env + echo "export MAILADM_HOME=/var/lib/mailadm" >> $SCRIPTPATH/.env + echo "" >> $SCRIPTPATH/.env + echo "export WEB_ENDPOINT=https://example.org/new_email" >> $SCRIPTPATH/.env + echo "export LOCALHOST_WEB_PORT=3691" >> $SCRIPTPATH/.env + echo "" >> $SCRIPTPATH/.env + echo "export BOT_EMAIL=bot@example.org" >> $SCRIPTPATH/.env + echo "export BOT_PASSWORD=p4ssw0rd" >> $SCRIPTPATH/.env -export VMAIL_USER=vmail -export VMAIL_HOME=/home/vmail - -export MAILADM_USER=mailadm -export MAILADM_HOME=/var/lib/mailadm -export WEB_ENDPOINT=https://example.org/new_email -export LOCALHOST_WEB_PORT=3691 - -export BOT_EMAIL=bot@example.org -export BOT_PASSWORD=p4ssw0rd" > $SCRIPTPATH/.env echo "Can't get settings from $SCRIPTPATH/.env, please set environment variables there." exit 1 else From 7c6e0e241c01688a0548b13f7413cf27455e8891 Mon Sep 17 00:00:00 2001 From: missytake Date: Wed, 27 Oct 2021 22:01:38 +0200 Subject: [PATCH 45/64] We don't need to log outgoing messages for now --- src/mailadm/bot.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/mailadm/bot.py b/src/mailadm/bot.py index ecf1a2a5..e354e1d3 100644 --- a/src/mailadm/bot.py +++ b/src/mailadm/bot.py @@ -45,10 +45,6 @@ def ac_incoming_message(self, command): text = command.text command.chat.send_text("echoing from {}:\n{}".format(addr, text)) - @account_hookimpl - def ac_message_delivered(self, message): - print("ac_message_delivered:", message) - def check_privileges(self, chat): """ Checks whether the incoming message was in the admin group. From 5ee85fca3ead0c50c5c4520d73ea9a4e7966f2a5 Mon Sep 17 00:00:00 2001 From: missytake Date: Wed, 27 Oct 2021 22:22:05 +0200 Subject: [PATCH 46/64] check if command giver is in admingrp & message is signed & encrypted --- src/mailadm/bot.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/mailadm/bot.py b/src/mailadm/bot.py index e354e1d3..3287549a 100644 --- a/src/mailadm/bot.py +++ b/src/mailadm/bot.py @@ -8,7 +8,6 @@ class AdmBot: @account_hookimpl def ac_incoming_message(self, command): print("process_incoming message:", command) - chat = command.message.chat if command.text.strip() == "/help": command.create_chat() text = ("/add-token name expiry prefix token maxuse" @@ -17,21 +16,21 @@ def ac_incoming_message(self, command): command.chat.send_text(text) elif command.text.strip() == "/add-token": - if self.check_privileges(chat): + if self.check_privileges(command): command.create_chat() arguments = command.text.split(" ") text = add_token(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]) command.chat.send_text(text) elif command.text.strip() == "/add-user": - if self.check_privileges(chat): + if self.check_privileges(command): command.create_chat() arguments = command.text.split(" ") text = add_user(arguments[0], arguments[1], arguments[2]) command.chat.send_text(text) elif command.text.strip() == "/list-tokens": - if self.check_privileges(chat): + if self.check_privileges(command): command.create_chat() command.chat.send_text(list_tokens()) @@ -45,13 +44,16 @@ def ac_incoming_message(self, command): text = command.text command.chat.send_text("echoing from {}:\n{}".format(addr, text)) - def check_privileges(self, chat): + def check_privileges(self, command): """ Checks whether the incoming message was in the admin group. """ - if chat.is_group() and self.admingrpid == chat.id: - if chat.is_protected() and int(chat.num_contacts) >= 2: - return True + if command.chat.is_group() and self.admingrpid == command.chat.id: + if command.chat.is_protected() and command.chat.is_encrypted() and int(command.chat.num_contacts) >= 2: + if command.message.get_sender_contact() in command.chat.get_contacts(): + return True + else: + print("%s is not allowed to give commands to mailadm." % (command.message.get_sender_contact(),)) else: print("admin chat is broken. Group ID:" + self.admingrpid) raise Exception From 8be70142b8059879a8a1f9d3431a5776f6e6b924 Mon Sep 17 00:00:00 2001 From: missytake Date: Wed, 27 Oct 2021 22:22:44 +0200 Subject: [PATCH 47/64] Update tests/conftest.py --- tests/conftest.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index abba544d..7cf9344d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -71,8 +71,9 @@ def _perform_match(output, fnl): @pytest.fixture -def cmd(): +def cmd(tmpdir): """ invoke a command line subcommand. """ + os.environ["MAILADM_HOME"] = str(tmpdir) from mailadm.cmdline import mailadm_main return ClickRunner(mailadm_main) From f645bdab8b353a54f4cef7181922a671cbd19eb4 Mon Sep 17 00:00:00 2001 From: missytake Date: Wed, 27 Oct 2021 22:23:53 +0200 Subject: [PATCH 48/64] missing import in conftest.py --- tests/conftest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/conftest.py b/tests/conftest.py index 7cf9344d..cab0c6cf 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,6 +2,7 @@ import os import pwd import grp +import os import collections from pathlib import Path From 878cfa987c80a4351a7c21a29196176f7d67d835 Mon Sep 17 00:00:00 2001 From: missytake Date: Mon, 20 Dec 2021 17:03:24 +0100 Subject: [PATCH 49/64] get DB path from constructor --- src/mailadm/bot.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/mailadm/bot.py b/src/mailadm/bot.py index 3287549a..5598669a 100644 --- a/src/mailadm/bot.py +++ b/src/mailadm/bot.py @@ -3,8 +3,13 @@ from mailadm.commands import add_user, add_token, list_tokens import os - class AdmBot: + def __init__(self, db): + self.db = db + with self.db.read_connection() as conn: + config = conn.config() + self.admingrpid = config.admingrpid + @account_hookimpl def ac_incoming_message(self, command): print("process_incoming message:", command) @@ -62,9 +67,10 @@ def check_privileges(self, command): return False -def main(argv=None): - run_cmdline(argv=argv, account_plugins=[AdmBot()]) +def main(db, argv=None): + run_cmdline(argv=argv, account_plugins=[AdmBot(db)]) if __name__ == "__main__": - main() + db = DB(os.getenv("MAILADM_DB")) + main(db) From 701c1fe51319b13e26873df2b01db22fd921e099 Mon Sep 17 00:00:00 2001 From: missytake Date: Mon, 20 Dec 2021 17:09:16 +0100 Subject: [PATCH 50/64] bot: refactored incoming message processing --- src/mailadm/bot.py | 38 +++++++++++++------------------------- 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/src/mailadm/bot.py b/src/mailadm/bot.py index 5598669a..f4964cf0 100644 --- a/src/mailadm/bot.py +++ b/src/mailadm/bot.py @@ -1,3 +1,4 @@ +import deltachat from deltachat import account_hookimpl, run_cmdline from mailadm.db import DB from mailadm.commands import add_user, add_token, list_tokens @@ -11,43 +12,30 @@ def __init__(self, db): self.admingrpid = config.admingrpid @account_hookimpl - def ac_incoming_message(self, command): + def ac_incoming_message(self, command: deltachat.Message): print("process_incoming message:", command) + command.create_chat() + if not self.check_privileges(command): + command.chat.send_text("Sorry, I only take commands from the admin group.") + if command.text.strip() == "/help": - command.create_chat() text = ("/add-token name expiry prefix token maxuse" "/add-user addr password token" "/list-tokens") command.chat.send_text(text) elif command.text.strip() == "/add-token": - if self.check_privileges(command): - command.create_chat() - arguments = command.text.split(" ") - text = add_token(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]) - command.chat.send_text(text) + arguments = command.text.split(" ") + text = add_token(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]) + command.chat.send_text(text) elif command.text.strip() == "/add-user": - if self.check_privileges(command): - command.create_chat() - arguments = command.text.split(" ") - text = add_user(arguments[0], arguments[1], arguments[2]) - command.chat.send_text(text) + arguments = command.text.split(" ") + text = add_user(arguments[0], arguments[1], arguments[2]) + command.chat.send_text(text) elif command.text.strip() == "/list-tokens": - if self.check_privileges(command): - command.create_chat() - command.chat.send_text(list_tokens()) - - else: - # unconditionally accept the chat - command.create_chat() - addr = command.get_sender_contact().addr - if command.is_system_message(): - command.chat.send_text("echoing system message from {}:\n{}".format(addr, command)) - else: - text = command.text - command.chat.send_text("echoing from {}:\n{}".format(addr, text)) + command.chat.send_text(list_tokens()) def check_privileges(self, command): """ From edde100552b9580e7996127d08658ce8c805381d Mon Sep 17 00:00:00 2001 From: missytake Date: Mon, 20 Dec 2021 17:13:41 +0100 Subject: [PATCH 51/64] stop using broad exceptions like this --- src/mailadm/bot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mailadm/bot.py b/src/mailadm/bot.py index f4964cf0..71b2f296 100644 --- a/src/mailadm/bot.py +++ b/src/mailadm/bot.py @@ -48,8 +48,8 @@ def check_privileges(self, command): else: print("%s is not allowed to give commands to mailadm." % (command.message.get_sender_contact(),)) else: - print("admin chat is broken. Group ID:" + self.admingrpid) - raise Exception + print("admin chat is broken. Try `mailadm setup-bot`. Group ID:" + self.admingrpid) + raise ValueError else: # reply "This command needs to be sent to the admin group" return False From ced8e306d4e1e139ba8ae834a3f91fc4a2a91e7a Mon Sep 17 00:00:00 2001 From: missytake Date: Mon, 20 Dec 2021 17:52:40 +0100 Subject: [PATCH 52/64] reverted 3b6827b db refactoring --- src/mailadm/cmdline.py | 74 ++++++++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 28 deletions(-) diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index aceb52c1..02aa55ac 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -19,7 +19,7 @@ from click import style import qrcode -from mailadm.db import write_connection, read_connection, get_db_path +import mailadm.db import mailadm.commands import mailadm.util from .conn import DBError @@ -31,7 +31,6 @@ "-n", "--dryrun", is_flag=True, help="don't change any files, only show what would be changed.") - @click.command(cls=click.Group, context_settings=dict(help_option_names=["-h", "--help"])) @click.version_option() @click.pass_context @@ -53,7 +52,7 @@ def get_mailadm_db(ctx, show=False, fail_missing_config=True): if show: click.secho("using db: {}".format(db_path), file=sys.stderr) if fail_missing_config: - with read_connection() as conn: + with db.read_connection() as conn: if not conn.is_initialized(): ctx.fail("database not initialized, use 'init' subcommand to do so") return db @@ -98,40 +97,47 @@ def setup_bot(ctx, email, password, db): while chat.num_contacts() < 2: time.sleep(1) - # it would be nicer to properly wait for the member_added event, but this function isn't async, so it doesn't work. - time.sleep(5) - - ac.shutdown() - with read_connection() as conn: - admingrpid_old = conn.config.admingrpid + with db.read_connection() as rconn: + admingrpid_old = rconn.config.admingrpid if admingrpid_old: oldgroup = ac.get_chat_by_id(admingrpid_old) oldgroup.send_text("Someone created a new admin group on the command line. This one is not valid anymore.") - with write_connection() as conn: - conn.set_config("admingrpid", chat.id) + + # it would be nicer to properly wait for the member_added event, but this function isn't async, so it doesn't work. + time.sleep(5) + ac.shutdown() + + db = get_mailadm_db(ctx) + with db.write_transaction() as wconn: + wconn.set_config("admingrpid", chat.id) @click.command() -def config(): +@click.pass_context +def config(ctx): """show and manipulate config settings. """ - with read_connection() as conn: + db = get_mailadm_db(ctx) + with db.read_connection() as rconn: click.secho("** mailadm version: {}".format(mailadm.__version__)) - click.secho("** mailadm database path: {}".format(get_db_path())) - for name, val in conn.get_config_items(): + click.secho("** mailadm database path: {}".format(db.path)) + for name, val in rconn.get_config_items(): click.secho("{:22s} {}".format(name, val)) @click.command() -def list_tokens(): +@click.pass_context +def list_tokens(ctx): """list available tokens """ click.secho(mailadm.commands.list_tokens()) @click.command() @click.option("--token", type=str, default=None, help="name of token") -def list_users(token): +@click.pass_context +def list_users(ctx, token): """list users """ - with read_connection() as conn: + db = get_mailadm_db(ctx) + with db.read_connection() as conn: for user_info in conn.get_user_list(token=token): click.secho("{} [token={}]".format(user_info.addr, user_info.token_name)) @@ -156,10 +162,14 @@ def dump_token_info(token_info): @click.option("--prefix", type=str, default="tmp.", help="prefix for all e-mail addresses for this token") @click.option("--token", type=str, default=None, help="name of token to be used") -def add_token(name, expiry, maxuse, prefix, token): +@click.pass_context +def add_token(ctx, name, expiry, maxuse, prefix, token): """add new token for generating new e-mail addresses """ - click.secho(mailadm.commands.add_token(name, expiry, maxuse, prefix, token)) + from mailadm.util import get_human_readable_id + + db = get_mailadm_db(ctx) + mailadm.commands.add_token(name, expiry, maxuse, prefix, token, db) @click.command() @@ -170,10 +180,13 @@ def add_token(name, expiry, maxuse, prefix, token): help="maximum number of accounts this token can create, default is not to change") @click.option("--prefix", type=str, default=None, help="prefix for all e-mail addresses for this token, default is not to change") -def mod_token(name, expiry, prefix, maxuse): +@click.pass_context +def mod_token(ctx, name, expiry, prefix, maxuse): """modify a token selectively """ - with write_connection() as conn: + db = get_mailadm_db(ctx) + + with db.write_transaction() as conn: conn.mod_token(name=name, expiry=expiry, maxuse=maxuse, prefix=prefix) tc = conn.get_tokeninfo_by_name(name) dump_token_info(tc) @@ -181,9 +194,11 @@ def mod_token(name, expiry, prefix, maxuse): @click.command() @click.argument("name", type=str, required=True) -def del_token(name): +@click.pass_context +def del_token(ctx, name): """remove named token""" - with write_connection() as conn: + db = get_mailadm_db(ctx) + with db.write_transaction() as conn: conn.del_token(name=name) @@ -194,7 +209,8 @@ def gen_qr(ctx, tokenname): """generate qr code image for a token. """ from .gen_qr import gen_qr - with read_connection() as conn: + db = get_mailadm_db(ctx) + with db.read_connection() as conn: token_info = conn.get_tokeninfo_by_name(tokenname) config = conn.config @@ -256,18 +272,20 @@ def add_user(ctx, addr, password, token, dryrun): @click.command() @click.argument("addr", type=str, required=True) -def del_user(addr): +@click.pass_context +def del_user(ctx, addr): """remove e-mail address""" - with write_connection() as conn: + with get_mailadm_db(ctx).write_transaction() as conn: conn.del_user(addr=addr) @click.command() @option_dryrun +@click.pass_context def prune(ctx, dryrun): """prune expired users from postfix and dovecot configurations """ sysdate = int(time.time()) - with write_connection() as conn: + with get_mailadm_db(ctx).write_transaction() as conn: expired_users = conn.get_expired_users(sysdate) if not expired_users: click.secho("nothing to prune") From 3085b268b6e9fc16543538754a8bd96ce19121ef Mon Sep 17 00:00:00 2001 From: missytake Date: Mon, 20 Dec 2021 18:08:36 +0100 Subject: [PATCH 53/64] more db call refactoring --- src/mailadm/cmdline.py | 8 +++++--- src/mailadm/commands.py | 13 ++++++------- src/mailadm/db.py | 10 ---------- tests/test_cmdline.py | 8 +++----- 4 files changed, 14 insertions(+), 25 deletions(-) diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index 02aa55ac..962de071 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -128,7 +128,8 @@ def config(ctx): @click.pass_context def list_tokens(ctx): """list available tokens """ - click.secho(mailadm.commands.list_tokens()) + db = get_mailadm_db(ctx) + click.secho(mailadm.commands.list_tokens(db)) @click.command() @@ -169,7 +170,7 @@ def add_token(ctx, name, expiry, maxuse, prefix, token): from mailadm.util import get_human_readable_id db = get_mailadm_db(ctx) - mailadm.commands.add_token(name, expiry, maxuse, prefix, token, db) + click.secho(mailadm.commands.add_token(db, name, expiry, maxuse, prefix, token)) @click.command() @@ -263,7 +264,8 @@ def init(ctx, web_endpoint, mail_domain, mailcow_endpoint, mailcow_token): def add_user(ctx, addr, password, token, dryrun): """add user as a mailadm managed account. """ - result = mailadm.commands.add_user(token, addr, password, dryrun) + db = get_mailadm_db(ctx) + result = mailadm.commands.add_user(db, token, addr, password, dryrun) if result["status"] == "error": ctx.fail(result["message"]) elif result["status"] == "success": diff --git a/src/mailadm/commands.py b/src/mailadm/commands.py index 0d9258fe..76007748 100644 --- a/src/mailadm/commands.py +++ b/src/mailadm/commands.py @@ -1,24 +1,23 @@ from mailadm.util import get_human_readable_id -from mailadm.db import write_connection, read_connection from mailadm.conn import DBError -def add_token(name, expiry, maxuse, prefix, token): +def add_token(db, name, expiry, maxuse, prefix, token): """Adds a token to create users """ if token is None: token = expiry + "_" + get_human_readable_id(len=15) - with write_connection() as conn: + with db.write_transaction() as conn: info = conn.add_token(name=name, token=token, expiry=expiry, maxuse=maxuse, prefix=prefix) tc = conn.get_tokeninfo_by_name(info.name) return dump_token_info(tc) -def add_user(token=None, addr=None, password=None, dryrun=False): +def add_user(db, token=None, addr=None, password=None, dryrun=False): """Adds a new user to be managed by mailadm """ - with write_connection() as conn: + with db.write_transaction() as conn: if token is None: if "@" not in addr: # there is probably a more pythonic solution to this. @@ -45,11 +44,11 @@ def add_user(token=None, addr=None, password=None, dryrun=False): "message": user_info} -def list_tokens(): +def list_tokens(db): """Print token info for all tokens """ output = [] - with read_connection() as conn: + with db.read_connection() as conn: for name in conn.get_token_list(): token_info = conn.get_tokeninfo_by_name(name) output.append(dump_token_info(token_info)) diff --git a/src/mailadm/db.py b/src/mailadm/db.py index d658b1ba..f61691f3 100644 --- a/src/mailadm/db.py +++ b/src/mailadm/db.py @@ -8,16 +8,6 @@ from .conn import Connection -def read_connection(): - mailadm_db = DB(get_db_path()) - return mailadm_db.read_connection() - - -def write_connection(): - mailadm_db = DB(get_db_path()) - return mailadm_db.write_transaction() - - def get_db_path(): db_path = os.environ.get("MAILADM_DB", "/mailadm/docker-data/mailadm.db") try: diff --git a/tests/test_cmdline.py b/tests/test_cmdline.py index 7a11f720..1a78ac50 100644 --- a/tests/test_cmdline.py +++ b/tests/test_cmdline.py @@ -61,11 +61,9 @@ def test_gen_qr_no_token(self, mycmd, tmpdir, monkeypatch): class TestTokens: def test_uninitialized(self, cmd): - try: - cmd.run_ok(["list-tokens"]) - except RuntimeError: - if "MAILADM_DB not set" not in sys.exc_info()[1].__str__(): - assert 0 + cmd.run_fail(["list-tokens"],""" + *MAILADM_DB not set* + """) def test_tokens(self, mycmd): mycmd.run_ok(["add-token", "oneweek", "--token=1w_Zeeg1RSOK4e3Nh0V", From 293a794202205ad681d24a354f5b7cae238fc3b9 Mon Sep 17 00:00:00 2001 From: missytake Date: Mon, 20 Dec 2021 18:13:53 +0100 Subject: [PATCH 54/64] commands need a db object now --- src/mailadm/bot.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mailadm/bot.py b/src/mailadm/bot.py index 71b2f296..8c3d6114 100644 --- a/src/mailadm/bot.py +++ b/src/mailadm/bot.py @@ -26,16 +26,16 @@ def ac_incoming_message(self, command: deltachat.Message): elif command.text.strip() == "/add-token": arguments = command.text.split(" ") - text = add_token(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]) + text = add_token(self.db, arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]) command.chat.send_text(text) elif command.text.strip() == "/add-user": arguments = command.text.split(" ") - text = add_user(arguments[0], arguments[1], arguments[2]) + text = add_user(self.db, arguments[0], arguments[1], arguments[2]) command.chat.send_text(text) elif command.text.strip() == "/list-tokens": - command.chat.send_text(list_tokens()) + command.chat.send_text(list_tokens(self.db)) def check_privileges(self, command): """ From b45c0e75179bbdc615106df2981f36b7cc370380 Mon Sep 17 00:00:00 2001 From: missytake Date: Mon, 20 Dec 2021 18:35:58 +0100 Subject: [PATCH 55/64] wait for a proper ac_member_added event --- src/mailadm/bot.py | 14 ++++++++++++++ src/mailadm/cmdline.py | 7 +++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/mailadm/bot.py b/src/mailadm/bot.py index 8c3d6114..1c147287 100644 --- a/src/mailadm/bot.py +++ b/src/mailadm/bot.py @@ -3,6 +3,20 @@ from mailadm.db import DB from mailadm.commands import add_user, add_token, list_tokens import os +from threading import Event + + +class SetupPlugin: + def __init__(self, admingrpid): + self.member_added = Event() + self.admingrpid = admingrpid + + @account_hookimpl + def ac_member_added(self, chat: deltachat.Chat, contact, actor, message): + assert chat.num_contacts() == 2 + if chat.id == self.admingrpid: + self.member_added.set() + class AdmBot: def __init__(self, db): diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index 962de071..4307ab0f 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -23,6 +23,7 @@ import mailadm.commands import mailadm.util from .conn import DBError +from .bot import SetupPlugin from deltachat import Account, account_hookimpl @@ -85,6 +86,9 @@ def setup_bot(ctx, email, password, db): chat = ac.create_group_chat("Admin group on {}".format(socket.gethostname()), contacts=[], verified=True) + setupplugin = SetupPlugin(chat.id) + ac.add_account_plugin(setupplugin) + chatinvite = chat.get_join_qr() qr = qrcode.QRCode() qr.add_data(chatinvite) @@ -94,8 +98,7 @@ def setup_bot(ctx, email, password, db): print("\nWaiting until you join the chat") sys.stdout.flush() # flush stdout to actually show the messages above - while chat.num_contacts() < 2: - time.sleep(1) + setupplugin.member_added.wait() with db.read_connection() as rconn: admingrpid_old = rconn.config.admingrpid From fb22dbad973c7c72e122f6c135c6890a5dcb2133 Mon Sep 17 00:00:00 2001 From: missytake Date: Tue, 21 Dec 2021 11:34:37 +0100 Subject: [PATCH 56/64] tricky: in this functions there are two databases. don't confuse them --- src/mailadm/cmdline.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index 4307ab0f..28de973a 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -67,6 +67,13 @@ def get_mailadm_db(ctx, show=False, fail_missing_config=True): @click.pass_context @account_hookimpl def setup_bot(ctx, email, password, db): + """initialize the deltachat bot as an alternative command interface. + + :param ctx: the click object passing the CLI environment + :param email: the email account the deltachat bot will use for receiving commands + :param password: the password to the bot's email account + :param db: the path to the deltachat database of the bot - NOT the path to the mailadm database! + """ ac = Account(db) if not ac.is_configured(): @@ -100,7 +107,8 @@ def setup_bot(ctx, email, password, db): sys.stdout.flush() # flush stdout to actually show the messages above setupplugin.member_added.wait() - with db.read_connection() as rconn: + mailadmdb = get_mailadm_db(ctx) + with mailadmdb.read_connection() as rconn: admingrpid_old = rconn.config.admingrpid if admingrpid_old: oldgroup = ac.get_chat_by_id(admingrpid_old) @@ -110,8 +118,7 @@ def setup_bot(ctx, email, password, db): time.sleep(5) ac.shutdown() - db = get_mailadm_db(ctx) - with db.write_transaction() as wconn: + with mailadmdb.write_transaction() as wconn: wconn.set_config("admingrpid", chat.id) From 0dcf62564227e79e1bde8f445942cc1044f3df5c Mon Sep 17 00:00:00 2001 From: missytake Date: Tue, 21 Dec 2021 11:40:59 +0100 Subject: [PATCH 57/64] better way of getting MAIL_DOMAIN --- src/mailadm/cmdline.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index 28de973a..56a7afce 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -91,7 +91,7 @@ def setup_bot(ctx, email, password, db): ac.start_io() - chat = ac.create_group_chat("Admin group on {}".format(socket.gethostname()), contacts=[], verified=True) + chat = ac.create_group_chat("Admin group on {}".format(os.environ["MAIL_DOMAIN"]), contacts=[], verified=True) setupplugin = SetupPlugin(chat.id) ac.add_account_plugin(setupplugin) @@ -106,6 +106,8 @@ def setup_bot(ctx, email, password, db): print("\nWaiting until you join the chat") sys.stdout.flush() # flush stdout to actually show the messages above setupplugin.member_added.wait() + chat.send_text("Welcome to the Admin group on %s! Type /help to get an overview over existing commands." % + (os.environ["MAIL_DOMAIN"],)) mailadmdb = get_mailadm_db(ctx) with mailadmdb.read_connection() as rconn: From f57068164d017c68150c8e0a1642c7e9730dc100 Mon Sep 17 00:00:00 2001 From: missytake Date: Tue, 21 Dec 2021 11:45:43 +0100 Subject: [PATCH 58/64] we can only store strings in the mailadmdb config table --- src/mailadm/cmdline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index 56a7afce..5162b3fe 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -113,7 +113,7 @@ def setup_bot(ctx, email, password, db): with mailadmdb.read_connection() as rconn: admingrpid_old = rconn.config.admingrpid if admingrpid_old: - oldgroup = ac.get_chat_by_id(admingrpid_old) + oldgroup = ac.get_chat_by_id(int(admingrpid_old)) oldgroup.send_text("Someone created a new admin group on the command line. This one is not valid anymore.") # it would be nicer to properly wait for the member_added event, but this function isn't async, so it doesn't work. From 0f64e559b2dad1a9b377c48919e284cca708e283 Mon Sep 17 00:00:00 2001 From: missytake Date: Tue, 21 Dec 2021 13:31:04 +0100 Subject: [PATCH 59/64] properly wait with shutting down until all messages are sent. --- src/mailadm/bot.py | 6 ++++++ src/mailadm/cmdline.py | 7 ++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/mailadm/bot.py b/src/mailadm/bot.py index 1c147287..9858d9a4 100644 --- a/src/mailadm/bot.py +++ b/src/mailadm/bot.py @@ -10,6 +10,7 @@ class SetupPlugin: def __init__(self, admingrpid): self.member_added = Event() self.admingrpid = admingrpid + self.message_sent = Event() @account_hookimpl def ac_member_added(self, chat: deltachat.Chat, contact, actor, message): @@ -17,6 +18,11 @@ def ac_member_added(self, chat: deltachat.Chat, contact, actor, message): if chat.id == self.admingrpid: self.member_added.set() + @account_hookimpl + def ac_outgoing_message(self, message: deltachat.Message): + if not message.is_system_message(): + self.message_sent.set() + class AdmBot: def __init__(self, db): diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index 5162b3fe..f288b675 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -106,6 +106,7 @@ def setup_bot(ctx, email, password, db): print("\nWaiting until you join the chat") sys.stdout.flush() # flush stdout to actually show the messages above setupplugin.member_added.wait() + setupplugin.message_sent.unset() chat.send_text("Welcome to the Admin group on %s! Type /help to get an overview over existing commands." % (os.environ["MAIL_DOMAIN"],)) @@ -113,11 +114,11 @@ def setup_bot(ctx, email, password, db): with mailadmdb.read_connection() as rconn: admingrpid_old = rconn.config.admingrpid if admingrpid_old: + setupplugin.message_sent.wait() + setupplugin.message_sent.unset() oldgroup = ac.get_chat_by_id(int(admingrpid_old)) oldgroup.send_text("Someone created a new admin group on the command line. This one is not valid anymore.") - - # it would be nicer to properly wait for the member_added event, but this function isn't async, so it doesn't work. - time.sleep(5) + setupplugin.message_sent.wait() ac.shutdown() with mailadmdb.write_transaction() as wconn: From e0d0ba52306ad66841e8deb52a64d029f2c14004 Mon Sep 17 00:00:00 2001 From: missytake Date: Tue, 21 Dec 2021 13:38:21 +0100 Subject: [PATCH 60/64] unset() is called clear(), actually --- src/mailadm/cmdline.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index f288b675..c299a94d 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -106,7 +106,7 @@ def setup_bot(ctx, email, password, db): print("\nWaiting until you join the chat") sys.stdout.flush() # flush stdout to actually show the messages above setupplugin.member_added.wait() - setupplugin.message_sent.unset() + setupplugin.message_sent.clear() chat.send_text("Welcome to the Admin group on %s! Type /help to get an overview over existing commands." % (os.environ["MAIL_DOMAIN"],)) @@ -115,7 +115,7 @@ def setup_bot(ctx, email, password, db): admingrpid_old = rconn.config.admingrpid if admingrpid_old: setupplugin.message_sent.wait() - setupplugin.message_sent.unset() + setupplugin.message_sent.clear() oldgroup = ac.get_chat_by_id(int(admingrpid_old)) oldgroup.send_text("Someone created a new admin group on the command line. This one is not valid anymore.") setupplugin.message_sent.wait() From 8a43793c672fa53aaf7ffed8bfda6d271feb57a3 Mon Sep 17 00:00:00 2001 From: missytake Date: Tue, 21 Dec 2021 14:05:05 +0100 Subject: [PATCH 61/64] waited for the wrong event --- src/mailadm/bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mailadm/bot.py b/src/mailadm/bot.py index 9858d9a4..cc415d02 100644 --- a/src/mailadm/bot.py +++ b/src/mailadm/bot.py @@ -19,7 +19,7 @@ def ac_member_added(self, chat: deltachat.Chat, contact, actor, message): self.member_added.set() @account_hookimpl - def ac_outgoing_message(self, message: deltachat.Message): + def ac_message_delivered(self, message: deltachat.Message): if not message.is_system_message(): self.message_sent.set() From 5f59d1c3a52637ee2e481a41968cb9903961a539 Mon Sep 17 00:00:00 2001 From: missytake Date: Tue, 21 Dec 2021 14:10:45 +0100 Subject: [PATCH 62/64] fixing lint issues --- src/mailadm/cmdline.py | 4 +--- tests/test_cmdline.py | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index c299a94d..178668ca 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -11,7 +11,6 @@ from .conn import DBError, UserInfo from .mailcow import MailcowError import mailadm.util -import socket import time import os import sys @@ -32,6 +31,7 @@ "-n", "--dryrun", is_flag=True, help="don't change any files, only show what would be changed.") + @click.command(cls=click.Group, context_settings=dict(help_option_names=["-h", "--help"])) @click.version_option() @click.pass_context @@ -180,8 +180,6 @@ def dump_token_info(token_info): def add_token(ctx, name, expiry, maxuse, prefix, token): """add new token for generating new e-mail addresses """ - from mailadm.util import get_human_readable_id - db = get_mailadm_db(ctx) click.secho(mailadm.commands.add_token(db, name, expiry, maxuse, prefix, token)) diff --git a/tests/test_cmdline.py b/tests/test_cmdline.py index 1a78ac50..c1b0b5c9 100644 --- a/tests/test_cmdline.py +++ b/tests/test_cmdline.py @@ -1,6 +1,5 @@ import os from random import randint -import sys import time import datetime import pytest @@ -61,7 +60,7 @@ def test_gen_qr_no_token(self, mycmd, tmpdir, monkeypatch): class TestTokens: def test_uninitialized(self, cmd): - cmd.run_fail(["list-tokens"],""" + cmd.run_fail(["list-tokens"], """ *MAILADM_DB not set* """) From 87c7d64e821af3cffeb6890ad8504fde9dbe2d89 Mon Sep 17 00:00:00 2001 From: missytake Date: Thu, 23 Jun 2022 13:12:39 +0200 Subject: [PATCH 63/64] tests: fixed how CLI output gets parsed --- tests/test_cmdline.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_cmdline.py b/tests/test_cmdline.py index c1b0b5c9..c270368d 100644 --- a/tests/test_cmdline.py +++ b/tests/test_cmdline.py @@ -79,7 +79,7 @@ def test_tokens_add(self, mycmd, i): *DCACCOUNT*&n=test1 """) out = mycmd.run_ok(["list-tokens"], """ - *of 50 times* + *maxuse = 50* *DCACCOUNT*&n=test1 """) for line in out.splitlines(): @@ -99,16 +99,16 @@ def test_tokens_add(self, mycmd, i): def test_tokens_add_maxuse(self, mycmd): mycmd.run_ok(["add-token", "test1", "--maxuse=10"], """ - *of 10 times* + *maxuse = 10* *DCACCOUNT*&n=test1 """) mycmd.run_ok(["list-tokens"], """ - *of 10 times* + *maxuse = 10* *DCACCOUNT*&n=test1 """) mycmd.run_ok(["mod-token", "--maxuse=1000", "test1"]) mycmd.run_ok(["list-tokens"], """ - *of 1000 times* + *maxuse = 1000* *DCACCOUNT*&n=test1 """) From ccca381eabc99d9ff3aca2bb9dd5a16718a59828 Mon Sep 17 00:00:00 2001 From: missytake Date: Tue, 28 Jun 2022 10:53:03 +0200 Subject: [PATCH 64/64] mvbox_watch doesn't exist anymore (core#2906) --- src/mailadm/cmdline.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mailadm/cmdline.py b/src/mailadm/cmdline.py index 178668ca..b4c0c599 100644 --- a/src/mailadm/cmdline.py +++ b/src/mailadm/cmdline.py @@ -83,7 +83,6 @@ def setup_bot(ctx, email, password, db): ac.set_config("addr", email) ac.set_config("mail_pw", password) ac.set_config("mvbox_move", "0") - ac.set_config("mvbox_watch", "0") ac.set_config("sentbox_watch", "0") ac.set_config("bot", "1") configtracker = ac.configure()