You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

129 lines
4.2 KiB

#!/usr/bin/env python3
import difflib
import functools
from typing import Callable
import click
from clintermission import cli_select_item # type: ignore
import editor # type: ignore
from sievelib.managesieve import Client # type: ignore
def default_options(f: Callable) -> Callable:
@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: str, user: str, password: str, starttls: bool, script_name: str) -> int:
"""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 0
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)
return 0
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: str, user: str, password: str, starttls: bool, script_name: str) -> None:
"""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()