commit c1b4851c97e7a589cbae11eb85ace02b1cc22ab8 Author: MasterofJOKers Date: Tue Mar 23 23:36:19 2021 +0100 Add first version of sievedit Includes set-active and edit commands. diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..4ffaa3f --- /dev/null +++ b/setup.cfg @@ -0,0 +1,24 @@ +[metadata] +name = sievedit +version = 0.1 +description = Simple tool to + +[options] +packages = find: +install_requires = + click + python-editor + clintermission + sievelib + +[options.entry_points] +console_scripts = + sievedit = sievedit:main + +[options.packages.find] +exclude=env + +[flake8] +max-line-length = 120 +exclude = .git,__pycache__,*.egg-info,*lib/python* +ignore = E241,E741,W503,W504 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..056ba45 --- /dev/null +++ b/setup.py @@ -0,0 +1,4 @@ +import setuptools + + +setuptools.setup() diff --git a/sievedit.py b/sievedit.py new file mode 100644 index 0000000..54acaa5 --- /dev/null +++ b/sievedit.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python3 +import difflib +import functools + +import click +from clintermission import cli_select_item +import editor +from sievelib.managesieve import Client + + +def default_options(f): + @click.option('--server', prompt=True, help='sieve server to connect to') + @click.option('-u', '--user', prompt=True, help='user to connect as') + @click.option('--password', prompt=True, hide_input=True, + help='Passwort to connect with. It\'s discouraged to pass passwords.' + 'Use the automatic prompt instead.') + @click.option('--starttls/--no-starttls', default=True, help='Connect with starttls') + @functools.wraps(f) + def wrapper(*args, **kwargs): + f(*args, **kwargs) + + return wrapper + + +@click.group() +def main(): + pass + + +@main.command() +@default_options +@click.option('--script-name', + help='script name to set as active. If not provided, choices will be presented.') +def active(server, user, password, starttls, script_name): + """Connect to a SIEVE server and set a script as active""" + c = Client(server) + c.connect(user, password, starttls=starttls) + active, scripts = c.listscripts() + + if not script_name: + if len(scripts + [active]) == 1: + click.echo('Only a single script available: {}'.format((scripts + [active])[0])) + _, script_name = cli_select_item(zip(sorted(['{} (active)'.format(active)] + scripts), + sorted([active] + scripts))) + + if active == script_name: + click.echo('Script {} is already active.'.format(script_name)) + return + + if script_name not in scripts: + click.echo('Error: script {} cannot be found. Valid choices are: {}' + .format(script_name, ', '.join(scripts))) + return 1 + + c.setactive(script_name) + + +DEFAULT_TEMPLATE = """\ +# Example script. Edit as you like +require ["fileinto"]; +# rule:[amazon] +if header :contains "To" "amazon@" { + fileinto "INBOX.amazon"; + stop; +} +# rule:[games] +if anyof (header :contains "To" "gw2@", + header :contains "To" "humblebundle@", + header :contains "To" "steam@", + header :contains "To" "socialclub@", + header :contains "To" "uplay@" ) { + fileinto "INBOX.games"; + stop; +} +# rule:[paypal] +if header :contains "To" "paypal@" { + fileinto "INBOX.paypal"; + stop; +} +""" + + +@main.command() +@default_options +@click.option('--script-name', help='Edit this script or create it') +def edit(server, user, password, starttls, script_name): + """Connect to a SIEVE server, download, edit and upload a script""" + c = Client(server) + c.connect(user, password, starttls=starttls) + active, scripts = c.listscripts() + + if not script_name: + _, script_name = cli_select_item(zip(sorted(['{} (active)'.format(active)] + scripts), + sorted([active] + scripts))) + if script_name not in (scripts + [active]): + msg = 'Script {} does not exist. Create new script?' + click.confirm(msg.format(script_name), abort=True) + original_content = DEFAULT_TEMPLATE + newly_created = True + else: + original_content = c.getscript(script_name) + newly_created = False + + new_content = editor.edit(contents=original_content).decode('utf-8') + if new_content.strip() == original_content and not newly_created: + click.echo('No changes. Aborting.') + return + + while not c.checkscript(new_content): + click.confirm('Script is invalid. Try again?', abort=True) + new_content = editor.edit(contents=new_content).decode('utf-8') + if new_content == original_content and not newly_created: + click.echo('No changes. Aborting.') + return + + diff = difflib.unified_diff(original_content.splitlines(), new_content.splitlines(), + fromfile='before', tofile='after') + if not newly_created: + click.echo('Following changes were made:\n{}'.format('\n'.join(diff))) + click.confirm('Upload new version?', abort=True) + + c.putscript(script_name, new_content) + + +if __name__ == '__main__': + main()